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

Legend:
Removed from v.778  
changed lines
  Added in v.1302

  ViewVC Help
Powered by ViewVC 1.1.26