/[rdesktop]/jpeg/rdesktop/trunk/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 /jpeg/rdesktop/trunk/rdpsnd_oss.c

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

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

Legend:
Removed from v.499  
changed lines
  Added in v.1481

  ViewVC Help
Powered by ViewVC 1.1.26