/[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 753 by stargo, Mon Aug 23 11:13:50 2004 UTC revision 1382 by jsorg71, Thu Jan 18 05:42:11 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-2007
5     Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003     Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003
6       Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
7    
8     This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
# Line 19  Line 20 
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21  */  */
22    
23    /*
24       This is a workaround for Esound bug 312665.
25       FIXME: Remove this when Esound is fixed.
26    */
27    #ifdef _FILE_OFFSET_BITS
28    #undef _FILE_OFFSET_BITS
29    #endif
30    
31  #include "rdesktop.h"  #include "rdesktop.h"
32    #include "rdpsnd.h"
33    #include "rdpsnd_dsp.h"
34  #include <unistd.h>  #include <unistd.h>
35  #include <fcntl.h>  #include <fcntl.h>
36  #include <errno.h>  #include <errno.h>
37    #include <unistd.h>
38  #include <sys/time.h>  #include <sys/time.h>
39  #include <sys/ioctl.h>  #include <sys/ioctl.h>
40  #include <sys/soundcard.h>  #include <sys/soundcard.h>
41    #include <sys/types.h>
42    #include <sys/stat.h>
43    
44  #define MAX_QUEUE       10  #define DEFAULTDEVICE   "/dev/dsp"
45    #define MAX_LEN         512
46    
47  int g_dsp_fd;  static int dsp_fd = -1;
48  BOOL g_dsp_busy = False;  static int dsp_mode;
49  static int g_snd_rate;  static int dsp_refs;
50  static short g_samplewidth;  
51    static RD_BOOL dsp_configured;
52    static RD_BOOL dsp_broken;
53    
54    static RD_BOOL dsp_out;
55    static RD_BOOL dsp_in;
56    
57    static int stereo;
58    static int format;
59    static uint32 snd_rate;
60    static short samplewidth;
61    static char *dsp_dev;
62    static RD_BOOL in_esddsp;
63    
64  static struct audio_packet  /* This is a just a forward declaration */
65    static struct audio_driver oss_driver;
66    
67    void oss_play(void);
68    void oss_record(void);
69    
70    void
71    oss_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
72  {  {
73          struct stream s;          if (dsp_fd == -1)
74          uint16 tick;                  return;
75          uint8 index;  
76  } packet_queue[MAX_QUEUE];          if (dsp_out && !rdpsnd_queue_empty())
77  static unsigned int queue_hi, queue_lo;                  FD_SET(dsp_fd, wfds);
78            if (dsp_in)
79                    FD_SET(dsp_fd, rfds);
80            if (dsp_fd > *n)
81                    *n = dsp_fd;
82    }
83    
84    void
85    oss_check_fds(fd_set * rfds, fd_set * wfds)
86    {
87            if (FD_ISSET(dsp_fd, wfds))
88                    oss_play();
89            if (FD_ISSET(dsp_fd, rfds))
90                    oss_record();
91    }
92    
93    static RD_BOOL
94    detect_esddsp(void)
95    {
96            struct stat s;
97            char *preload;
98    
99            if (fstat(dsp_fd, &s) == -1)
100                    return False;
101    
102  BOOL          if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
103  wave_out_open(void)                  return False;
104    
105            preload = getenv("LD_PRELOAD");
106            if (preload == NULL)
107                    return False;
108    
109            if (strstr(preload, "esddsp") == NULL)
110                    return False;
111    
112            return True;
113    }
114    
115    RD_BOOL
116    oss_open(int fallback)
117  {  {
118          char *dsp_dev = getenv("AUDIODEV");          int caps;
119    
120          if (dsp_dev == NULL)          if (dsp_fd != -1)
121          {          {
122                  dsp_dev = "/dev/dsp";                  dsp_refs++;
123    
124                    if (dsp_mode == O_RDWR)
125                            return True;
126    
127                    if (dsp_mode == fallback)
128                            return True;
129    
130                    dsp_refs--;
131                    return False;
132          }          }
133    
134          if ((g_dsp_fd = open(dsp_dev, O_WRONLY | O_NONBLOCK)) == -1)          dsp_configured = False;
135            dsp_broken = False;
136    
137            dsp_mode = O_RDWR;
138            dsp_fd = open(dsp_dev, O_RDWR | O_NONBLOCK);
139            if (dsp_fd != -1)
140          {          {
141                  perror(dsp_dev);                  ioctl(dsp_fd, SNDCTL_DSP_SETDUPLEX, 0);
142                  return False;  
143                    if ((ioctl(dsp_fd, SNDCTL_DSP_GETCAPS, &caps) < 0) || !(caps & DSP_CAP_DUPLEX))
144                    {
145                            close(dsp_fd);
146                            dsp_fd = -1;
147                    }
148            }
149    
150            if (dsp_fd == -1)
151            {
152                    dsp_mode = fallback;
153    
154                    dsp_fd = open(dsp_dev, dsp_mode | O_NONBLOCK);
155                    if (dsp_fd == -1)
156                    {
157                            perror(dsp_dev);
158                            return False;
159                    }
160          }          }
161    
162          /* Non-blocking so that user interface is responsive */          dsp_refs++;
163          fcntl(g_dsp_fd, F_SETFL, fcntl(g_dsp_fd, F_GETFL) | O_NONBLOCK);  
164            in_esddsp = detect_esddsp();
165    
166            return True;
167    }
168    
169    void
170    oss_close(void)
171    {
172            dsp_refs--;
173    
174            if (dsp_refs != 0)
175                    return;
176    
177            close(dsp_fd);
178            dsp_fd = -1;
179    }
180    
181    RD_BOOL
182    oss_open_out(void)
183    {
184            if (!oss_open(O_WRONLY))
185                    return False;
186    
187            dsp_out = True;
188    
189          return True;          return True;
190  }  }
191    
192  void  void
193  wave_out_close(void)  oss_close_out(void)
194  {  {
195          close(g_dsp_fd);          oss_close();
196    
197            /* Ack all remaining packets */
198            while (!rdpsnd_queue_empty())
199                    rdpsnd_queue_next(0);
200    
201            dsp_out = False;
202  }  }
203    
204  BOOL  RD_BOOL
205  wave_out_format_supported(WAVEFORMATEX * pwfx)  oss_open_in(void)
206    {
207            if (!oss_open(O_RDONLY))
208                    return False;
209    
210            dsp_in = True;
211    
212            return True;
213    }
214    
215    void
216    oss_close_in(void)
217    {
218            oss_close();
219    
220            dsp_in = False;
221    }
222    
223    RD_BOOL
224    oss_format_supported(RD_WAVEFORMATEX * pwfx)
225  {  {
226          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
227                  return False;                  return False;
# Line 82  wave_out_format_supported(WAVEFORMATEX * Line 233  wave_out_format_supported(WAVEFORMATEX *
233          return True;          return True;
234  }  }
235    
236  BOOL  RD_BOOL
237  wave_out_set_format(WAVEFORMATEX * pwfx)  oss_set_format(RD_WAVEFORMATEX * pwfx)
238  {  {
239          int channels, format;          int fragments;
240            static RD_BOOL driver_broken = False;
241    
242            if (dsp_configured)
243            {
244                    if ((pwfx->wBitsPerSample == 8) && (format != AFMT_U8))
245                            return False;
246                    if ((pwfx->wBitsPerSample == 16) && (format != AFMT_S16_LE))
247                            return False;
248    
249                    if ((pwfx->nChannels == 2) != !!stereo)
250                            return False;
251    
252          ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL);                  if (pwfx->nSamplesPerSec != snd_rate)
253          ioctl(g_dsp_fd, SNDCTL_DSP_SYNC, NULL);                          return False;
254    
255                    return True;
256            }
257    
258            ioctl(dsp_fd, SNDCTL_DSP_RESET, NULL);
259            ioctl(dsp_fd, SNDCTL_DSP_SYNC, NULL);
260    
261          if (pwfx->wBitsPerSample == 8)          if (pwfx->wBitsPerSample == 8)
262                  format = AFMT_U8;                  format = AFMT_U8;
263          else if (pwfx->wBitsPerSample == 16)          else if (pwfx->wBitsPerSample == 16)
264                  format = AFMT_S16_LE;                  format = AFMT_S16_LE;
265    
266          g_samplewidth = pwfx->wBitsPerSample / 8;          samplewidth = pwfx->wBitsPerSample / 8;
267    
268          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)          if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
269          {          {
270                  perror("SNDCTL_DSP_SETFMT");                  perror("SNDCTL_DSP_SETFMT");
271                  close(g_dsp_fd);                  oss_close();
272                  return False;                  return False;
273          }          }
274    
275          channels = pwfx->nChannels;          if (pwfx->nChannels == 2)
276          if (ioctl(g_dsp_fd, SNDCTL_DSP_CHANNELS, &channels) == -1)          {
277                    stereo = 1;
278                    samplewidth *= 2;
279            }
280            else
281            {
282                    stereo = 0;
283            }
284    
285            if (ioctl(dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
286          {          {
287                  perror("SNDCTL_DSP_CHANNELS");                  perror("SNDCTL_DSP_CHANNELS");
288                  close(g_dsp_fd);                  oss_close();
289                  return False;                  return False;
290          }          }
291    
292          if (channels == 2)          oss_driver.need_resampling = 0;
293            snd_rate = pwfx->nSamplesPerSec;
294            if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
295          {          {
296                  g_samplewidth *= 2;                  uint32 rates[] = { 44100, 48000, 0 };
297                    uint32 *prates = rates;
298    
299                    while (*prates != 0)
300                    {
301                            if ((pwfx->nSamplesPerSec != *prates)
302                                && (ioctl(dsp_fd, SNDCTL_DSP_SPEED, prates) != -1))
303                            {
304                                    oss_driver.need_resampling = 1;
305                                    snd_rate = *prates;
306                                    if (rdpsnd_dsp_resample_set
307                                        (snd_rate, pwfx->wBitsPerSample, pwfx->nChannels) == False)
308                                    {
309                                            error("rdpsnd_dsp_resample_set failed");
310                                            oss_close();
311                                            return False;
312                                    }
313    
314                                    break;
315                            }
316                            prates++;
317                    }
318    
319                    if (*prates == 0)
320                    {
321                            perror("SNDCTL_DSP_SPEED");
322                            oss_close();
323                            return False;
324                    }
325          }          }
326    
327          g_snd_rate = pwfx->nSamplesPerSec;          /* try to get 12 fragments of 2^12 bytes size */
328          if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &g_snd_rate) == -1)          fragments = (12 << 16) + 12;
329            ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
330    
331            if (!driver_broken)
332          {          {
333                  perror("SNDCTL_DSP_SPEED");                  audio_buf_info info;
334                  close(g_dsp_fd);  
335                  return False;                  memset(&info, 0, sizeof(info));
336                    if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
337                    {
338                            perror("SNDCTL_DSP_GETOSPACE");
339                            oss_close();
340                            return False;
341                    }
342    
343                    if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0)
344                    {
345                            fprintf(stderr,
346                                    "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
347                                    info.fragments, info.fragstotal, info.fragsize);
348                            driver_broken = True;
349                    }
350          }          }
351    
352            dsp_configured = True;
353    
354          return True;          return True;
355  }  }
356    
357  void  void
358  wave_out_volume(uint16 left, uint16 right)  oss_volume(uint16 left, uint16 right)
359  {  {
         static BOOL use_dev_mixer = False;  
360          uint32 volume;          uint32 volume;
         int fd_mix = -1;  
361    
362          volume = left / (65536 / 100);          volume = left / (65536 / 100);
363          volume |= right / (65536 / 100) << 8;          volume |= right / (65536 / 100) << 8;
364    
365          if (use_dev_mixer)          if (ioctl(dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
366          {          {
367                  if ((fd_mix = open("/dev/mixer", O_RDWR | O_NONBLOCK)) == -1)                  warning("hardware volume control unavailable, falling back to software volume control!\n");
368                  {                  oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
369                          perror("open /dev/mixer");                  rdpsnd_dsp_softvol_set(left, right);
                         return;  
                 }  
   
                 if (ioctl(fd_mix, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)  
                 {  
                         perror("MIXER_WRITE(SOUND_MIXER_PCM)");  
                         return;  
                 }  
   
                 close(fd_mix);  
         }  
   
         if (ioctl(g_dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)  
         {  
                 perror("MIXER_WRITE(SOUND_MIXER_PCM)");  
                 use_dev_mixer = True;  
370                  return;                  return;
371          }          }
372  }  }
373    
374  void  void
375  wave_out_write(STREAM s, uint16 tick, uint8 index)  oss_play(void)
376  {  {
377          struct audio_packet *packet = &packet_queue[queue_hi];          struct audio_packet *packet;
378          unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;          ssize_t len;
379            STREAM out;
380    
381          if (next_hi == queue_lo)          /* We shouldn't be called if the queue is empty, but still */
382            if (rdpsnd_queue_empty())
383                    return;
384    
385            packet = rdpsnd_queue_current_packet();
386            out = &packet->s;
387    
388            len = out->end - out->p;
389    
390            len = write(dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
391            if (len == -1)
392          {          {
393                  error("No space to queue audio packet\n");                  if (errno != EWOULDBLOCK)
394                    {
395                            if (!dsp_broken)
396                                    perror("RDPSND: write()");
397                            dsp_broken = True;
398                            rdpsnd_queue_next(0);
399                    }
400                  return;                  return;
401          }          }
402    
403          queue_hi = next_hi;          dsp_broken = False;
404    
405          packet->s = *s;          out->p += len;
         packet->tick = tick;  
         packet->index = index;  
         packet->s.p += 4;  
406    
407          /* we steal the data buffer from s, give it a new one */          if (out->p == out->end)
408          s->data = malloc(s->size);          {
409                    int delay_bytes;
410                    unsigned long delay_us;
411                    audio_buf_info info;
412    
413          if (!g_dsp_busy)                  if (in_esddsp)
414                  wave_out_play();                  {
415                            /* EsounD has no way of querying buffer status, so we have to
416                             * go with a fixed size. */
417                            delay_bytes = out->size;
418                    }
419                    else
420                    {
421    #ifdef SNDCTL_DSP_GETODELAY
422                            delay_bytes = 0;
423                            if (ioctl(dsp_fd, SNDCTL_DSP_GETODELAY, &delay_bytes) == -1)
424                                    delay_bytes = -1;
425    #else
426                            delay_bytes = -1;
427    #endif
428    
429                            if (delay_bytes == -1)
430                            {
431                                    if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
432                                            delay_bytes = info.fragstotal * info.fragsize - info.bytes;
433                                    else
434                                            delay_bytes = out->size;
435                            }
436                    }
437    
438                    delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
439                    rdpsnd_queue_next(delay_us);
440            }
441  }  }
442    
443  void  void
444  wave_out_play(void)  oss_record(void)
445  {  {
446          struct audio_packet *packet;          char buffer[32768];
447          ssize_t len;          int len;
         STREAM out;  
         static long startedat_us;  
         static long startedat_s;  
         static BOOL started = False;  
         struct timeval tv;  
448    
449          while (1)          len = read(dsp_fd, buffer, sizeof(buffer));
450            if (len == -1)
451          {          {
452                  if (queue_lo == queue_hi)                  if (errno != EWOULDBLOCK)
453                  {                  {
454                          g_dsp_busy = 0;                          if (!dsp_broken)
455                          return;                                  perror("RDPSND: read()");
456                            dsp_broken = True;
457                            rdpsnd_queue_next(0);
458                  }                  }
459                    return;
460            }
461    
462                  packet = &packet_queue[queue_lo];          dsp_broken = False;
                 out = &packet->s;  
463    
464                  if (!started)          rdpsnd_record(buffer, len);
465                  {  }
                         gettimeofday(&tv, NULL);  
                         startedat_us = tv.tv_usec;  
                         startedat_s = tv.tv_sec;  
                         started = True;  
                 }  
466    
467                  len = write(g_dsp_fd, out->p, out->end - out->p);  struct audio_driver *
468                  if (len == -1)  oss_register(char *options)
469                  {  {
470                          if (errno != EWOULDBLOCK)          memset(&oss_driver, 0, sizeof(oss_driver));
                                 perror("write audio");  
                         g_dsp_busy = 1;  
                         return;  
                 }  
471    
472                  out->p += len;          oss_driver.name = "oss";
473                  if (out->p == out->end)          oss_driver.description =
474                  {                  "OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV";
475                          long long duration;  
476                          long elapsed;          oss_driver.add_fds = oss_add_fds;
477            oss_driver.check_fds = oss_check_fds;
478    
479            oss_driver.wave_out_open = oss_open_out;
480            oss_driver.wave_out_close = oss_close_out;
481            oss_driver.wave_out_format_supported = oss_format_supported;
482            oss_driver.wave_out_set_format = oss_set_format;
483            oss_driver.wave_out_volume = oss_volume;
484    
485            oss_driver.wave_in_open = oss_open_in;
486            oss_driver.wave_in_close = oss_close_in;
487            oss_driver.wave_in_format_supported = oss_format_supported;
488            oss_driver.wave_in_set_format = oss_set_format;
489            oss_driver.wave_in_volume = NULL;       /* FIXME */
490    
491                          gettimeofday(&tv, NULL);          oss_driver.need_byteswap_on_be = 0;
492                          duration = (out->size * (1000000 / (g_samplewidth * g_snd_rate)));          oss_driver.need_resampling = 0;
                         elapsed = (tv.tv_sec - startedat_s) * 1000000 + (tv.tv_usec - startedat_us);  
493    
494                          if (elapsed >= (duration * 85) / 100)          if (options)
495                          {          {
496                                  rdpsnd_send_completion(packet->tick, packet->index);                  dsp_dev = xstrdup(options);
497                                  free(out->data);          }
498                                  queue_lo = (queue_lo + 1) % MAX_QUEUE;          else
499                                  started = False;          {
500                          }                  dsp_dev = getenv("AUDIODEV");
501                          else  
502                          {                  if (dsp_dev == NULL)
503                                  g_dsp_busy = 1;                  {
504                                  return;                          dsp_dev = DEFAULTDEVICE;
                         }  
505                  }                  }
506          }          }
507    
508            return &oss_driver;
509  }  }

Legend:
Removed from v.753  
changed lines
  Added in v.1382

  ViewVC Help
Powered by ViewVC 1.1.26