/[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 751 by stargo, Fri Aug 13 09:43:31 2004 UTC revision 1356 by ossman_, Tue Jan 2 11:39:56 2007 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>  #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 dsp_fd = -1;
47  BOOL g_dsp_busy = False;  static BOOL dsp_busy;
 static int g_snd_rate;  
 static short g_samplewidth;  
   
 static struct audio_packet  
 {  
         struct stream s;  
         uint16 tick;  
         uint8 index;  
 } packet_queue[MAX_QUEUE];  
 static unsigned int queue_hi, queue_lo;  
48    
49  BOOL  static int snd_rate;
50  wave_out_open(void)  static short samplewidth;
51    static char *dsp_dev;
52    static BOOL in_esddsp;
53    
54    /* This is a just a forward declaration */
55    static struct audio_driver oss_driver;
56    
57    void oss_play(void);
58    
59    void
60    oss_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
61  {  {
62          char *dsp_dev = getenv("AUDIODEV");          if (dsp_fd == -1)
63                    return;
64    
65          if (dsp_dev == NULL)          if (rdpsnd_queue_empty())
66          {                  return;
67                  dsp_dev = "/dev/dsp";  
68          }          FD_SET(dsp_fd, wfds);
69            if (dsp_fd > *n)
70                    *n = dsp_fd;
71    }
72    
73    void
74    oss_check_fds(fd_set * rfds, fd_set * wfds)
75    {
76            if (FD_ISSET(dsp_fd, wfds))
77                    oss_play();
78    }
79    
80    static BOOL
81    detect_esddsp(void)
82    {
83            struct stat s;
84            char *preload;
85    
86            if (fstat(dsp_fd, &s) == -1)
87                    return False;
88    
89            if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
90                    return False;
91    
92          if ((g_dsp_fd = open(dsp_dev, O_WRONLY | O_NONBLOCK)) == -1)          preload = getenv("LD_PRELOAD");
93            if (preload == NULL)
94                    return False;
95    
96            if (strstr(preload, "esddsp") == NULL)
97                    return False;
98    
99            return True;
100    }
101    
102    BOOL
103    oss_open(void)
104    {
105            if ((dsp_fd = open(dsp_dev, O_WRONLY)) == -1)
106          {          {
107                  perror(dsp_dev);                  perror(dsp_dev);
108                  return False;                  return False;
109          }          }
110    
111          /* Non-blocking so that user interface is responsive */          in_esddsp = detect_esddsp();
112          fcntl(g_dsp_fd, F_SETFL, fcntl(g_dsp_fd, F_GETFL) | O_NONBLOCK);  
113          return True;          return True;
114  }  }
115    
116  void  void
117  wave_out_close(void)  oss_close(void)
118  {  {
119          close(g_dsp_fd);          close(dsp_fd);
120            dsp_fd = -1;
121            dsp_busy = False;
122  }  }
123    
124  BOOL  BOOL
125  wave_out_format_supported(WAVEFORMATEX * pwfx)  oss_format_supported(WAVEFORMATEX * pwfx)
126  {  {
127          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
128                  return False;                  return False;
# Line 83  wave_out_format_supported(WAVEFORMATEX * Line 135  wave_out_format_supported(WAVEFORMATEX *
135  }  }
136    
137  BOOL  BOOL
138  wave_out_set_format(WAVEFORMATEX * pwfx)  oss_set_format(WAVEFORMATEX * pwfx)
139  {  {
140          int channels, format;          int stereo, format, fragments;
141            static BOOL driver_broken = False;
142    
143          ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL);          ioctl(dsp_fd, SNDCTL_DSP_RESET, NULL);
144          ioctl(g_dsp_fd, SNDCTL_DSP_SYNC, NULL);          ioctl(dsp_fd, SNDCTL_DSP_SYNC, NULL);
145    
146          if (pwfx->wBitsPerSample == 8)          if (pwfx->wBitsPerSample == 8)
147                  format = AFMT_U8;                  format = AFMT_U8;
148          else if (pwfx->wBitsPerSample == 16)          else if (pwfx->wBitsPerSample == 16)
149                  format = AFMT_S16_LE;                  format = AFMT_S16_LE;
150    
151          g_samplewidth = pwfx->wBitsPerSample / 8;          samplewidth = pwfx->wBitsPerSample / 8;
152    
153          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)          if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
154          {          {
155                  perror("SNDCTL_DSP_SETFMT");                  perror("SNDCTL_DSP_SETFMT");
156                  close(g_dsp_fd);                  oss_close();
157                  return False;                  return False;
158          }          }
159    
160          channels = pwfx->nChannels;          if (pwfx->nChannels == 2)
         if (ioctl(g_dsp_fd, SNDCTL_DSP_CHANNELS, &channels) == -1)  
161          {          {
162                  perror("SNDCTL_DSP_CHANNELS");                  stereo = 1;
163                  close(g_dsp_fd);                  samplewidth *= 2;
                 return False;  
164          }          }
165            else
         if (channels == 2)  
166          {          {
167                  g_samplewidth *= 2;                  stereo = 0;
168          }          }
169    
170          g_snd_rate = pwfx->nSamplesPerSec;          if (ioctl(dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
         if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &g_snd_rate) == -1)  
171          {          {
172                  perror("SNDCTL_DSP_SPEED");                  perror("SNDCTL_DSP_CHANNELS");
173                  close(g_dsp_fd);                  oss_close();
174                  return False;                  return False;
175          }          }
176    
177          return True;          oss_driver.need_resampling = 0;
178  }          snd_rate = pwfx->nSamplesPerSec;
179            if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
180            {
181                    int rates[] = { 44100, 48000, 0 };
182                    int *prates = rates;
183    
184  void                  while (*prates != 0)
185  wave_out_volume(uint16 left, uint16 right)                  {
186  {                          if ((pwfx->nSamplesPerSec != *prates)
187          static BOOL use_dev_mixer = False;                              && (ioctl(dsp_fd, SNDCTL_DSP_SPEED, prates) != -1))
188          uint32 volume;                          {
189          int fd_mix = -1;                                  oss_driver.need_resampling = 1;
190                                    snd_rate = *prates;
191                                    if (rdpsnd_dsp_resample_set
192                                        (snd_rate, pwfx->wBitsPerSample, pwfx->nChannels) == False)
193                                    {
194                                            error("rdpsnd_dsp_resample_set failed");
195                                            oss_close();
196                                            return False;
197                                    }
198    
199          volume = left / (65536 / 100);                                  break;
200          volume |= right / (65536 / 100) << 8;                          }
201                            prates++;
202                    }
203    
204          if (use_dev_mixer)                  if (*prates == 0)
         {  
                 if ((fd_mix = open("/dev/mixer", O_RDWR | O_NONBLOCK)) == -1)  
205                  {                  {
206                          perror("open /dev/mixer");                          perror("SNDCTL_DSP_SPEED");
207                          return;                          oss_close();
208                            return False;
209                  }                  }
210            }
211    
212                  if (ioctl(fd_mix, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)          /* try to get 12 fragments of 2^12 bytes size */
213            fragments = (12 << 16) + 12;
214            ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
215    
216            if (!driver_broken)
217            {
218                    audio_buf_info info;
219    
220                    memset(&info, 0, sizeof(info));
221                    if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
222                  {                  {
223                          perror("MIXER_WRITE(SOUND_MIXER_PCM)");                          perror("SNDCTL_DSP_GETOSPACE");
224                          return;                          oss_close();
225                            return False;
226                  }                  }
227    
228                  close(fd_mix);                  if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0)
229                    {
230                            fprintf(stderr,
231                                    "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
232                                    info.fragments, info.fragstotal, info.fragsize);
233                            driver_broken = True;
234                    }
235          }          }
236    
237          if (ioctl(g_dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)          return True;
         {  
                 perror("MIXER_WRITE(SOUND_MIXER_PCM)");  
                 use_dev_mixer = True;  
                 return;  
         }  
238  }  }
239    
240  void  void
241  wave_out_write(STREAM s, uint16 tick, uint8 index)  oss_volume(uint16 left, uint16 right)
242  {  {
243          struct audio_packet *packet = &packet_queue[queue_hi];          uint32 volume;
244          unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;  
245            volume = left / (65536 / 100);
246            volume |= right / (65536 / 100) << 8;
247    
248          if (next_hi == queue_lo)          if (ioctl(dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
249          {          {
250                  error("No space to queue audio packet\n");                  warning("hardware volume control unavailable, falling back to software volume control!\n");
251                    oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
252                    rdpsnd_dsp_softvol_set(left, right);
253                  return;                  return;
254          }          }
   
         queue_hi = next_hi;  
   
         packet->s = *s;  
         packet->tick = tick;  
         packet->index = index;  
         packet->s.p += 4;  
   
         /* we steal the data buffer from s, give it a new one */  
         s->data = malloc(s->size);  
   
         if (!g_dsp_busy)  
                 wave_out_play();  
255  }  }
256    
257  void  void
258  wave_out_play(void)  oss_play(void)
259  {  {
260          struct audio_packet *packet;          struct audio_packet *packet;
261          ssize_t len;          ssize_t len;
262          STREAM out;          STREAM out;
         static long startedat_us;  
         static long startedat_s;  
         static BOOL started = False;  
         struct timeval tv;  
263    
264          while (1)          /* We shouldn't be called if the queue is empty, but still */
265            if (rdpsnd_queue_empty())
266                    return;
267    
268            packet = rdpsnd_queue_current_packet();
269            out = &packet->s;
270    
271            len = out->end - out->p;
272    
273            len = write(dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
274            if (len == -1)
275          {          {
276                  if (queue_lo == queue_hi)                  if (errno != EWOULDBLOCK)
277                  {                          perror("write audio");
278                          g_dsp_busy = 0;                  return;
279                          return;          }
                 }  
280    
281                  packet = &packet_queue[queue_lo];          out->p += len;
                 out = &packet->s;  
282    
283                  if (!started)          if (out->p == out->end)
284                  {          {
285                          gettimeofday(&tv, NULL);                  int delay_bytes;
286                          startedat_us = tv.tv_usec;                  unsigned long delay_us;
287                          startedat_s = tv.tv_sec;                  audio_buf_info info;
                         started = True;  
                 }  
288    
289                  len = write(g_dsp_fd, out->p, out->end - out->p);                  if (in_esddsp)
                 if (len == -1)  
290                  {                  {
291                          if (errno != EWOULDBLOCK)                          /* EsounD has no way of querying buffer status, so we have to
292                                  perror("write audio");                           * go with a fixed size. */
293                          g_dsp_busy = 1;                          delay_bytes = out->size;
                         return;  
294                  }                  }
295                    else
                 out->p += len;  
                 if (out->p == out->end)  
296                  {                  {
297                          long long duration;  #ifdef SNDCTL_DSP_GETODELAY
298                          long elapsed;                          delay_bytes = 0;
299                            if (ioctl(dsp_fd, SNDCTL_DSP_GETODELAY, &delay_bytes) == -1)
300                          gettimeofday(&tv, NULL);                                  delay_bytes = -1;
301                          duration = (out->size * (1000000 / (g_samplewidth * g_snd_rate)));  #else
302                          elapsed = (tv.tv_sec - startedat_s) * 1000000 + (tv.tv_usec - startedat_us);                          delay_bytes = -1;
303    #endif
304    
305                          if (elapsed >= (duration * 7) / 10)                          if (delay_bytes == -1)
306                          {                          {
307                                  rdpsnd_send_completion(packet->tick, packet->index);                                  if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
308                                  free(out->data);                                          delay_bytes = info.fragstotal * info.fragsize - info.bytes;
309                                  queue_lo = (queue_lo + 1) % MAX_QUEUE;                                  else
310                                  started = False;                                          delay_bytes = out->size;
                         }  
                         else  
                         {  
                                 g_dsp_busy = 1;  
                                 return;  
311                          }                          }
312                  }                  }
313    
314                    delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
315                    rdpsnd_queue_next(delay_us);
316          }          }
317  }  }
318    
319    struct audio_driver *
320    oss_register(char *options)
321    {
322            memset(&oss_driver, 0, sizeof(oss_driver));
323    
324            oss_driver.name = "oss";
325            oss_driver.description =
326                    "OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV";
327    
328            oss_driver.add_fds = oss_add_fds;
329            oss_driver.check_fds = oss_check_fds;
330    
331            oss_driver.wave_out_open = oss_open;
332            oss_driver.wave_out_close = oss_close;
333            oss_driver.wave_out_format_supported = oss_format_supported;
334            oss_driver.wave_out_set_format = oss_set_format;
335            oss_driver.wave_out_volume = oss_volume;
336    
337            oss_driver.need_byteswap_on_be = 0;
338            oss_driver.need_resampling = 0;
339    
340            if (options)
341            {
342                    dsp_dev = xstrdup(options);
343            }
344            else
345            {
346                    dsp_dev = getenv("AUDIODEV");
347    
348                    if (dsp_dev == NULL)
349                    {
350                            dsp_dev = DEFAULTDEVICE;
351                    }
352            }
353    
354            return &oss_driver;
355    }

Legend:
Removed from v.751  
changed lines
  Added in v.1356

  ViewVC Help
Powered by ViewVC 1.1.26