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

Legend:
Removed from v.1227  
changed lines
  Added in v.1365

  ViewVC Help
Powered by ViewVC 1.1.26