/[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 1256 by stargo, Sun Sep 17 11:42:22 2006 UTC revision 1481 by astrand, Thu Oct 2 18:21:58 2008 UTC
# Line 1  Line 1 
1  /* -*- c-basic-offset: 8 -*-  /* -*- 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 27  Line 28 
28  #undef _FILE_OFFSET_BITS  #undef _FILE_OFFSET_BITS
29  #endif  #endif
30    
31    #include <assert.h>
32    
33  #include "rdesktop.h"  #include "rdesktop.h"
34  #include "rdpsnd.h"  #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>  #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"  #define DEFAULTDEVICE   "/dev/dsp"
47  #define MAX_LEN         512  #define MAX_LEN         512
48    
49  static int snd_rate;  static int dsp_fd = -1;
50    static int dsp_mode;
51    
52    static RD_BOOL dsp_configured;
53    static RD_BOOL dsp_broken;
54    
55    static int stereo;
56    static int format;
57    static uint32 snd_rate;
58  static short samplewidth;  static short samplewidth;
59  static char *dsp_dev;  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 void
70    oss_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
71    {
72            if (dsp_fd == -1)
73                    return;
74    
75            if ((dsp_mode == O_WRONLY || dsp_mode == O_RDWR) && !rdpsnd_queue_empty())
76                    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            if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
102                    return False;
103    
104            preload = getenv("LD_PRELOAD");
105            if (preload == NULL)
106                    return False;
107    
108  BOOL          if (strstr(preload, "esddsp") == NULL)
109  oss_open(void)                  return False;
110    
111            return True;
112    }
113    
114    
115    static void
116    oss_restore_format()
117    {
118            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    
137    static RD_BOOL
138    oss_open(int wanted)
139  {  {
140          if ((g_dsp_fd = open(dsp_dev, O_WRONLY)) == -1)          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            in_esddsp = detect_esddsp();
181    
182          return True;          return True;
183  }  }
184    
185  void  static void
186  oss_close(void)  oss_close(void)
187  {  {
188          close(g_dsp_fd);          close(dsp_fd);
189            dsp_fd = -1;
190  }  }
191    
192  BOOL  static RD_BOOL
193  oss_format_supported(WAVEFORMATEX * pwfx)  oss_open_out(void)
194    {
195            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    static RD_BOOL
237    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  oss_format_supported(WAVEFORMATEX * pwfx Line 246  oss_format_supported(WAVEFORMATEX * pwfx
246          return True;          return True;
247  }  }
248    
249  BOOL  static RD_BOOL
250  oss_set_format(WAVEFORMATEX * pwfx)  oss_set_format(RD_WAVEFORMATEX * pwfx)
251  {  {
252          int stereo, format, fragments;          int fragments;
253          static BOOL driver_broken = False;          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;
# Line 90  oss_set_format(WAVEFORMATEX * pwfx) Line 280  oss_set_format(WAVEFORMATEX * pwfx)
280    
281          samplewidth = pwfx->wBitsPerSample / 8;          samplewidth = pwfx->wBitsPerSample / 8;
282    
283          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)          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    
# Line 107  oss_set_format(WAVEFORMATEX * pwfx) Line 297  oss_set_format(WAVEFORMATEX * pwfx)
297                  stereo = 0;                  stereo = 0;
298          }          }
299    
300          if (ioctl(g_dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)          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            oss_driver.need_resampling = 0;
308          snd_rate = pwfx->nSamplesPerSec;          snd_rate = pwfx->nSamplesPerSec;
309          if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)          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 */          /* try to get 12 fragments of 2^12 bytes size */
343          fragments = (12 << 16) + 12;          fragments = (12 << 16) + 12;
344          ioctl(g_dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);          ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
345    
346          if (!driver_broken)          if (!driver_broken)
347          {          {
348                  audio_buf_info info;                  audio_buf_info info;
349    
350                  memset(&info, 0, sizeof(info));                  memset(&info, 0, sizeof(info));
351                  if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)                  if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
352                  {                  {
353                          perror("SNDCTL_DSP_GETOSPACE");                          perror("SNDCTL_DSP_GETOSPACE");
354                          close(g_dsp_fd);                          oss_close();
355                          return False;                          return False;
356                  }                  }
357    
# Line 147  oss_set_format(WAVEFORMATEX * pwfx) Line 364  oss_set_format(WAVEFORMATEX * pwfx)
364                  }                  }
365          }          }
366    
367            dsp_configured = True;
368    
369          return True;          return True;
370  }  }
371    
372  void  static void
373  oss_volume(uint16 left, uint16 right)  oss_volume(uint16 left, uint16 right)
374  {  {
         static BOOL use_dev_mixer = False;  
375          uint32 volume;          uint32 volume;
         int fd_mix = -1;  
376    
377          volume = left / (65536 / 100);          volume = left / (65536 / 100);
378          volume |= right / (65536 / 100) << 8;          volume |= right / (65536 / 100) << 8;
379    
380          if (use_dev_mixer)          if (ioctl(dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
         {  
                 if ((fd_mix = open("/dev/mixer", O_RDWR | O_NONBLOCK)) == -1)  
                 {  
                         perror("open /dev/mixer");  
                         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)  
381          {          {
382                  perror("MIXER_WRITE(SOUND_MIXER_PCM)");                  warning("hardware volume control unavailable, falling back to software volume control!\n");
383                  use_dev_mixer = True;                  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  oss_play(void)  oss_play(void)
391  {  {
392          struct audio_packet *packet;          struct audio_packet *packet;
393          ssize_t len;          ssize_t len;
394          STREAM out;          STREAM out;
         static long startedat_us;  
         static long startedat_s;  
         static BOOL started = False;  
         struct timeval tv;  
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())          if (rdpsnd_queue_empty())
         {  
                 g_dsp_busy = 0;  
400                  return;                  return;
         }  
401    
402          packet = rdpsnd_queue_current_packet();          packet = rdpsnd_queue_current_packet();
403          out = &packet->s;          out = &packet->s;
404    
         if (!started)  
         {  
                 gettimeofday(&tv, NULL);  
                 startedat_us = tv.tv_usec;  
                 startedat_s = tv.tv_sec;  
                 started = True;  
         }  
   
405          len = out->end - out->p;          len = out->end - out->p;
406    
407          len = write(g_dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);          len = write(dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
408          if (len == -1)          if (len == -1)
409          {          {
410                  if (errno != EWOULDBLOCK)                  if (errno != EWOULDBLOCK)
411                          perror("write audio");                  {
412                  g_dsp_busy = 1;                          if (!dsp_broken)
413                                    perror("RDPSND: write()");
414                            dsp_broken = True;
415                            rdpsnd_queue_next(0);
416                    }
417                  return;                  return;
418          }          }
419    
420            dsp_broken = False;
421    
422          out->p += len;          out->p += len;
423    
424          if (out->p == out->end)          if (out->p == out->end)
425          {          {
426                  long long duration;                  int delay_bytes;
427                  long elapsed;                  unsigned long delay_us;
428                    audio_buf_info info;
429    
430                  gettimeofday(&tv, NULL);                  if (in_esddsp)
431                  duration = (out->size * (1000000 / (samplewidth * snd_rate)));                  {
432                  elapsed = (tv.tv_sec - startedat_s) * 1000000 + (tv.tv_usec - startedat_us);                          /* EsounD has no way of querying buffer status, so we have to
433                             * go with a fixed size. */
434                  if (elapsed >= (duration * 85) / 100)                          delay_bytes = out->size;
                 {  
                         /* We need to add 50 to tell windows that time has passed while  
                          * playing this packet */  
                         rdpsnd_send_completion(packet->tick + 50, packet->index);  
                         rdpsnd_queue_next();  
                         started = False;  
435                  }                  }
436                  else                  else
437                  {                  {
438                          g_dsp_busy = 1;  #ifdef SNDCTL_DSP_GETODELAY
439                          return;                          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                    delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
456                    rdpsnd_queue_next(delay_us);
457          }          }
458          g_dsp_busy = 1;  }
459          return;  
460    static void
461    oss_record(void)
462    {
463            char buffer[32768];
464            int len;
465    
466            assert(dsp_fd != -1);
467    
468            len = read(dsp_fd, buffer, sizeof(buffer));
469            if (len == -1)
470            {
471                    if (errno != EWOULDBLOCK)
472                    {
473                            if (!dsp_broken)
474                                    perror("RDPSND: read()");
475                            dsp_broken = True;
476                            rdpsnd_queue_next(0);
477                    }
478                    return;
479            }
480    
481            dsp_broken = False;
482    
483            rdpsnd_record(buffer, len);
484  }  }
485    
486  struct audio_driver *  struct audio_driver *
487  oss_register(char *options)  oss_register(char *options)
488  {  {
489          static struct audio_driver oss_driver;          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.wave_out_write = rdpsnd_queue_write;          oss_driver.add_fds = oss_add_fds;
496          oss_driver.wave_out_open = oss_open;          oss_driver.check_fds = oss_check_fds;
497          oss_driver.wave_out_close = oss_close;  
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;          oss_driver.wave_out_format_supported = oss_format_supported;
501          oss_driver.wave_out_set_format = oss_set_format;          oss_driver.wave_out_set_format = oss_set_format;
502          oss_driver.wave_out_volume = oss_volume;          oss_driver.wave_out_volume = oss_volume;
503          oss_driver.wave_out_play = oss_play;  
504          oss_driver.name = xstrdup("oss");          oss_driver.wave_in_open = oss_open_in;
505          oss_driver.description =          oss_driver.wave_in_close = oss_close_in;
506                  xstrdup("OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV");          oss_driver.wave_in_format_supported = oss_format_supported;
507          oss_driver.next = NULL;          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)          if (options)
514          {          {
# Line 279  oss_register(char *options) Line 520  oss_register(char *options)
520    
521                  if (dsp_dev == NULL)                  if (dsp_dev == NULL)
522                  {                  {
523                          dsp_dev = xstrdup(DEFAULTDEVICE);                          dsp_dev = DEFAULTDEVICE;
524                  }                  }
525          }          }
526    

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

  ViewVC Help
Powered by ViewVC 1.1.26