/[rdesktop]/sourceforge.net/trunk/rdesktop/rdpsnd_oss.c
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Diff of /sourceforge.net/trunk/rdesktop/rdpsnd_oss.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 504 by stargo, Sun Oct 19 11:59:41 2003 UTC revision 1302 by ossman_, Thu Oct 26 09:47:17 2006 UTC
# Line 1  Line 1 
1  /*  /* -*- c-basic-offset: 8 -*-
2     rdesktop: A Remote Desktop Protocol client.     rdesktop: A Remote Desktop Protocol client.
3     Sound Channel Process Functions - Open Sound System     Sound Channel Process Functions - Open Sound System
4     Copyright (C) Matthew Chapman 2003     Copyright (C) Matthew Chapman 2003
# Line 19  Line 19 
19     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */  */
21    
22    /*
23       This is a workaround for Esound bug 312665.
24       FIXME: Remove this when Esound is fixed.
25    */
26    #ifdef _FILE_OFFSET_BITS
27    #undef _FILE_OFFSET_BITS
28    #endif
29    
30  #include "rdesktop.h"  #include "rdesktop.h"
31    #include "rdpsnd.h"
32    #include "rdpsnd_dsp.h"
33  #include <unistd.h>  #include <unistd.h>
34  #include <fcntl.h>  #include <fcntl.h>
35  #include <errno.h>  #include <errno.h>
36    #include <unistd.h>
37    #include <sys/time.h>
38  #include <sys/ioctl.h>  #include <sys/ioctl.h>
39  #include <sys/soundcard.h>  #include <sys/soundcard.h>
40    #include <sys/types.h>
41    #include <sys/stat.h>
42    
43  #define MAX_QUEUE       10  #define DEFAULTDEVICE   "/dev/dsp"
44    #define MAX_LEN         512
45    
46  int g_dsp_fd;  static int snd_rate;
47  BOOL g_dsp_busy = False;  static short samplewidth;
48    static char *dsp_dev;
49    static struct audio_driver oss_driver;
50    static BOOL in_esddsp;
51    
52  static struct audio_packet  static BOOL
53    detect_esddsp(void)
54  {  {
55          struct stream s;          struct stat s;
56          uint16 tick;          char *preload;
57          uint8 index;  
58  } packet_queue[MAX_QUEUE];          if (fstat(g_dsp_fd, &s) == -1)
59  static unsigned int queue_hi, queue_lo;                  return False;
60    
61            if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
62                    return False;
63    
64            preload = getenv("LD_PRELOAD");
65            if (preload == NULL)
66                    return False;
67    
68            if (strstr(preload, "esddsp") == NULL)
69                    return False;
70    
71            return True;
72    }
73    
74  BOOL  BOOL
75  wave_out_open(void)  oss_open(void)
76  {  {
77          char *dsp_dev = "/dev/dsp";          if ((g_dsp_fd = open(dsp_dev, O_WRONLY)) == -1)
   
         if ((g_dsp_fd = open(dsp_dev, O_WRONLY | O_NONBLOCK)) == -1)  
78          {          {
79                  perror(dsp_dev);                  perror(dsp_dev);
80                  return False;                  return False;
81          }          }
82    
83          /* Non-blocking so that user interface is responsive */          in_esddsp = detect_esddsp();
84          fcntl(g_dsp_fd, F_SETFL, fcntl(g_dsp_fd, F_GETFL) | O_NONBLOCK);  
85          return True;          return True;
86  }  }
87    
88  void  void
89  wave_out_close(void)  oss_close(void)
90  {  {
91          close(g_dsp_fd);          close(g_dsp_fd);
92  }  }
93    
94  BOOL  BOOL
95  wave_out_format_supported(WAVEFORMATEX * pwfx)  oss_format_supported(WAVEFORMATEX * pwfx)
96  {  {
97          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
98                  return False;                  return False;
# Line 75  wave_out_format_supported(WAVEFORMATEX * Line 105  wave_out_format_supported(WAVEFORMATEX *
105  }  }
106    
107  BOOL  BOOL
108  wave_out_set_format(WAVEFORMATEX * pwfx)  oss_set_format(WAVEFORMATEX * pwfx)
109  {  {
110          int speed, channels, format;          int stereo, format, fragments;
111            static BOOL driver_broken = False;
112    
113          ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL);          ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL);
114          ioctl(g_dsp_fd, SNDCTL_DSP_SYNC, NULL);          ioctl(g_dsp_fd, SNDCTL_DSP_SYNC, NULL);
# Line 87  wave_out_set_format(WAVEFORMATEX * pwfx) Line 118  wave_out_set_format(WAVEFORMATEX * pwfx)
118          else if (pwfx->wBitsPerSample == 16)          else if (pwfx->wBitsPerSample == 16)
119                  format = AFMT_S16_LE;                  format = AFMT_S16_LE;
120    
121            samplewidth = pwfx->wBitsPerSample / 8;
122    
123          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
124          {          {
125                  perror("SNDCTL_DSP_SETFMT");                  perror("SNDCTL_DSP_SETFMT");
# Line 94  wave_out_set_format(WAVEFORMATEX * pwfx) Line 127  wave_out_set_format(WAVEFORMATEX * pwfx)
127                  return False;                  return False;
128          }          }
129    
130          channels = pwfx->nChannels;          if (pwfx->nChannels == 2)
131          if (ioctl(g_dsp_fd, SNDCTL_DSP_CHANNELS, &channels) == -1)          {
132                    stereo = 1;
133                    samplewidth *= 2;
134            }
135            else
136            {
137                    stereo = 0;
138            }
139    
140            if (ioctl(g_dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
141          {          {
142                  perror("SNDCTL_DSP_CHANNELS");                  perror("SNDCTL_DSP_CHANNELS");
143                  close(g_dsp_fd);                  close(g_dsp_fd);
144                  return False;                  return False;
145          }          }
146    
147          speed = pwfx->nSamplesPerSec;          oss_driver.need_resampling = 0;
148          if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &speed) == -1)          snd_rate = pwfx->nSamplesPerSec;
149            if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
150          {          {
151                  perror("SNDCTL_DSP_SPEED");                  int rates[] = { 44100, 48000, 0 };
152                  close(g_dsp_fd);                  int *prates = rates;
153                  return False;  
154                    while (*prates != 0)
155                    {
156                            if ((pwfx->nSamplesPerSec != *prates)
157                                && (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, prates) != -1))
158                            {
159                                    oss_driver.need_resampling = 1;
160                                    snd_rate = *prates;
161                                    if (rdpsnd_dsp_resample_set
162                                        (snd_rate, pwfx->wBitsPerSample, pwfx->nChannels) == False)
163                                    {
164                                            error("rdpsnd_dsp_resample_set failed");
165                                            close(g_dsp_fd);
166                                            return False;
167                                    }
168    
169                                    break;
170                            }
171                            prates++;
172                    }
173    
174                    if (*prates == 0)
175                    {
176                            perror("SNDCTL_DSP_SPEED");
177                            close(g_dsp_fd);
178                            return False;
179                    }
180            }
181    
182            /* try to get 12 fragments of 2^12 bytes size */
183            fragments = (12 << 16) + 12;
184            ioctl(g_dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
185    
186            if (!driver_broken)
187            {
188                    audio_buf_info info;
189    
190                    memset(&info, 0, sizeof(info));
191                    if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
192                    {
193                            perror("SNDCTL_DSP_GETOSPACE");
194                            close(g_dsp_fd);
195                            return False;
196                    }
197    
198                    if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0)
199                    {
200                            fprintf(stderr,
201                                    "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
202                                    info.fragments, info.fragstotal, info.fragsize);
203                            driver_broken = True;
204                    }
205          }          }
206    
207          return True;          return True;
208  }  }
209    
210  void  void
211  wave_out_volume(uint16 left, uint16 right)  oss_volume(uint16 left, uint16 right)
212  {  {
213          uint32 volume;          uint32 volume;
214    
215          volume = left / (65536 / 100);          volume = left / (65536 / 100);
216          volume |= right / (65536 / 100) << 8;          volume |= right / (65536 / 100) << 8;
217    
218          if (ioctl(g_dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)          if (ioctl(g_dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
219          {          {
220                  perror("MIXER_WRITE(SOUND_MIXER_PCM)");                  warning("hardware volume control unavailable, falling back to software volume control!\n");
221                    oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
222                    rdpsnd_dsp_softvol_set(left, right);
223                  return;                  return;
224          }          }
225  }  }
226    
227  void  void
228  wave_out_write(STREAM s, uint16 tick, uint8 index)  oss_play(void)
229  {  {
230          struct audio_packet *packet = &packet_queue[queue_hi];          struct audio_packet *packet;
231          unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;          ssize_t len;
232            STREAM out;
233    
234          if (next_hi == queue_lo)          if (rdpsnd_queue_empty())
235          {          {
236                  error("No space to queue audio packet\n");                  g_dsp_busy = 0;
237                  return;                  return;
238          }          }
239    
240          queue_hi = next_hi;          packet = rdpsnd_queue_current_packet();
241            out = &packet->s;
242    
243          packet->s = *s;          len = out->end - out->p;
         packet->tick = tick;  
         packet->index = index;  
         packet->s.p += 4;  
244    
245          /* we steal the data buffer from s, give it a new one */          len = write(g_dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
246          s->data = malloc(s->size);          if (len == -1)
247            {
248                    if (errno != EWOULDBLOCK)
249                            perror("write audio");
250                    g_dsp_busy = 1;
251                    return;
252            }
253    
254          if (!g_dsp_busy)          out->p += len;
                 wave_out_play();  
 }  
255    
256  void          if (out->p == out->end)
 wave_out_play(void)  
 {  
         struct audio_packet *packet;  
         ssize_t len;  
         STREAM out;  
   
         while (1)  
257          {          {
258                  if (queue_lo == queue_hi)                  int delay_bytes;
259                    unsigned long delay_us;
260                    audio_buf_info info;
261    
262                    if (in_esddsp)
263                  {                  {
264                          g_dsp_busy = 0;                          /* EsounD has no way of querying buffer status, so we have to
265                          return;                           * go with a fixed size. */
266                            delay_bytes = out->size;
267                  }                  }
268                    else
                 packet = &packet_queue[queue_lo];  
                 out = &packet->s;  
   
                 len = write(g_dsp_fd, out->p, out->end - out->p);  
                 if (len == -1)  
269                  {                  {
270                          if (errno != EWOULDBLOCK)  #ifdef SNDCTL_DSP_GETODELAY
271                                  perror("write audio");                          delay_bytes = 0;
272                          g_dsp_busy = 1;                          if (ioctl(g_dsp_fd, SNDCTL_DSP_GETODELAY, &delay_bytes) == -1)
273                          return;                                  delay_bytes = -1;
274    #else
275                            delay_bytes = -1;
276    #endif
277    
278                            if (delay_bytes == -1)
279                            {
280                                    if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
281                                            delay_bytes = info.fragstotal * info.fragsize - info.bytes;
282                                    else
283                                            delay_bytes = out->size;
284                            }
285                  }                  }
286    
287                  out->p += len;                  delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
288                  if (out->p == out->end)                  rdpsnd_queue_next(delay_us);
289            }
290            else
291            {
292                    g_dsp_busy = 1;
293            }
294    
295            return;
296    }
297    
298    struct audio_driver *
299    oss_register(char *options)
300    {
301            oss_driver.wave_out_open = oss_open;
302            oss_driver.wave_out_close = oss_close;
303            oss_driver.wave_out_format_supported = oss_format_supported;
304            oss_driver.wave_out_set_format = oss_set_format;
305            oss_driver.wave_out_volume = oss_volume;
306            oss_driver.wave_out_play = oss_play;
307            oss_driver.name = xstrdup("oss");
308            oss_driver.description =
309                    xstrdup("OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV");
310            oss_driver.need_byteswap_on_be = 0;
311            oss_driver.need_resampling = 0;
312            oss_driver.next = NULL;
313    
314            if (options)
315            {
316                    dsp_dev = xstrdup(options);
317            }
318            else
319            {
320                    dsp_dev = getenv("AUDIODEV");
321    
322                    if (dsp_dev == NULL)
323                  {                  {
324                          rdpsnd_send_completion(packet->tick, packet->index);                          dsp_dev = xstrdup(DEFAULTDEVICE);
                         free(out->data);  
                         queue_lo = (queue_lo + 1) % MAX_QUEUE;  
325                  }                  }
326          }          }
327    
328            return &oss_driver;
329  }  }

Legend:
Removed from v.504  
changed lines
  Added in v.1302

  ViewVC Help
Powered by ViewVC 1.1.26