/[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 801 by astrand, Tue Nov 23 13:29:12 2004 UTC revision 1318 by astrand, Thu Nov 2 11:55:59 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 = strdup("/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            g_dsp_busy = 0;
93  }  }
94    
95  BOOL  BOOL
96  wave_out_format_supported(WAVEFORMATEX * pwfx)  oss_format_supported(WAVEFORMATEX * pwfx)
97  {  {
98          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)          if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
99                  return False;                  return False;
# Line 84  wave_out_format_supported(WAVEFORMATEX * Line 106  wave_out_format_supported(WAVEFORMATEX *
106  }  }
107    
108  BOOL  BOOL
109  wave_out_set_format(WAVEFORMATEX * pwfx)  oss_set_format(WAVEFORMATEX * pwfx)
110  {  {
111          int stereo, format, fragments;          int stereo, format, fragments;
112            static BOOL driver_broken = False;
113    
114          ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL);          ioctl(g_dsp_fd, SNDCTL_DSP_RESET, NULL);
115          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 119  wave_out_set_format(WAVEFORMATEX * pwfx)
119          else if (pwfx->wBitsPerSample == 16)          else if (pwfx->wBitsPerSample == 16)
120                  format = AFMT_S16_LE;                  format = AFMT_S16_LE;
121    
122          g_samplewidth = pwfx->wBitsPerSample / 8;          samplewidth = pwfx->wBitsPerSample / 8;
123    
124          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)          if (ioctl(g_dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
125          {          {
126                  perror("SNDCTL_DSP_SETFMT");                  perror("SNDCTL_DSP_SETFMT");
127                  close(g_dsp_fd);                  oss_close();
128                  return False;                  return False;
129          }          }
130    
131          if (pwfx->nChannels == 2)          if (pwfx->nChannels == 2)
132          {          {
133                  stereo = 1;                  stereo = 1;
134                  g_samplewidth *= 2;                  samplewidth *= 2;
135          }          }
136          else          else
137          {          {
# Line 118  wave_out_set_format(WAVEFORMATEX * pwfx) Line 141  wave_out_set_format(WAVEFORMATEX * pwfx)
141          if (ioctl(g_dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)          if (ioctl(g_dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
142          {          {
143                  perror("SNDCTL_DSP_CHANNELS");                  perror("SNDCTL_DSP_CHANNELS");
144                  close(g_dsp_fd);                  oss_close();
145                  return False;                  return False;
146          }          }
147    
148          g_snd_rate = pwfx->nSamplesPerSec;          oss_driver.need_resampling = 0;
149          if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &g_snd_rate) == -1)          snd_rate = pwfx->nSamplesPerSec;
150            if (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
151          {          {
152                  perror("SNDCTL_DSP_SPEED");                  int rates[] = { 44100, 48000, 0 };
153                  close(g_dsp_fd);                  int *prates = rates;
154                  return False;  
155                    while (*prates != 0)
156                    {
157                            if ((pwfx->nSamplesPerSec != *prates)
158                                && (ioctl(g_dsp_fd, SNDCTL_DSP_SPEED, prates) != -1))
159                            {
160                                    oss_driver.need_resampling = 1;
161                                    snd_rate = *prates;
162                                    if (rdpsnd_dsp_resample_set
163                                        (snd_rate, pwfx->wBitsPerSample, pwfx->nChannels) == False)
164                                    {
165                                            error("rdpsnd_dsp_resample_set failed");
166                                            oss_close();
167                                            return False;
168                                    }
169    
170                                    break;
171                            }
172                            prates++;
173                    }
174    
175                    if (*prates == 0)
176                    {
177                            perror("SNDCTL_DSP_SPEED");
178                            oss_close();
179                            return False;
180                    }
181          }          }
182    
183          /* try to get 7 fragments of 2^12 bytes size */          /* try to get 12 fragments of 2^12 bytes size */
184          fragments = (7 << 16) + 12;          fragments = (12 << 16) + 12;
185          ioctl(g_dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);          ioctl(g_dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
186    
187          if (!g_driver_broken)          if (!driver_broken)
188          {          {
189                  audio_buf_info info;                  audio_buf_info info;
190    
# Line 142  wave_out_set_format(WAVEFORMATEX * pwfx) Line 192  wave_out_set_format(WAVEFORMATEX * pwfx)
192                  if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)                  if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
193                  {                  {
194                          perror("SNDCTL_DSP_GETOSPACE");                          perror("SNDCTL_DSP_GETOSPACE");
195                          close(g_dsp_fd);                          oss_close();
196                          return False;                          return False;
197                  }                  }
198    
# Line 151  wave_out_set_format(WAVEFORMATEX * pwfx) Line 201  wave_out_set_format(WAVEFORMATEX * pwfx)
201                          fprintf(stderr,                          fprintf(stderr,
202                                  "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",                                  "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
203                                  info.fragments, info.fragstotal, info.fragsize);                                  info.fragments, info.fragstotal, info.fragsize);
204                          g_driver_broken = True;                          driver_broken = True;
205                  }                  }
206          }          }
207    
# Line 159  wave_out_set_format(WAVEFORMATEX * pwfx) Line 209  wave_out_set_format(WAVEFORMATEX * pwfx)
209  }  }
210    
211  void  void
212  wave_out_volume(uint16 left, uint16 right)  oss_volume(uint16 left, uint16 right)
213  {  {
         static BOOL use_dev_mixer = False;  
214          uint32 volume;          uint32 volume;
         int fd_mix = -1;  
215    
216          volume = left / (65536 / 100);          volume = left / (65536 / 100);
217          volume |= right / (65536 / 100) << 8;          volume |= right / (65536 / 100) << 8;
218    
         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);  
         }  
   
219          if (ioctl(g_dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)          if (ioctl(g_dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
220          {          {
221                  perror("MIXER_WRITE(SOUND_MIXER_PCM)");                  warning("hardware volume control unavailable, falling back to software volume control!\n");
222                  use_dev_mixer = True;                  oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
223                    rdpsnd_dsp_softvol_set(left, right);
224                  return;                  return;
225          }          }
226  }  }
227    
228  void  void
229  wave_out_write(STREAM s, uint16 tick, uint8 index)  oss_play(void)
230  {  {
231          struct audio_packet *packet = &packet_queue[queue_hi];          struct audio_packet *packet;
232          unsigned int next_hi = (queue_hi + 1) % MAX_QUEUE;          ssize_t len;
233            STREAM out;
234    
235          if (next_hi == queue_lo)          if (rdpsnd_queue_empty())
236          {          {
237                  error("No space to queue audio packet\n");                  g_dsp_busy = 0;
238                  return;                  return;
239          }          }
240    
241          queue_hi = next_hi;          packet = rdpsnd_queue_current_packet();
242            out = &packet->s;
         packet->s = *s;  
         packet->tick = tick;  
         packet->index = index;  
         packet->s.p += 4;  
243    
244          /* we steal the data buffer from s, give it a new one */          len = out->end - out->p;
         s->data = (uint8 *) malloc(s->size);  
245    
246          if (!g_dsp_busy)          len = write(g_dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
247                  wave_out_play();          if (len == -1)
248  }          {
249                    if (errno != EWOULDBLOCK)
250                            perror("write audio");
251                    g_dsp_busy = 1;
252                    return;
253            }
254    
255  void          out->p += len;
 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;  
256    
257          while (1)          if (out->p == out->end)
258          {          {
259                  if (queue_lo == queue_hi)                  int delay_bytes;
260                  {                  unsigned long delay_us;
261                          g_dsp_busy = 0;                  audio_buf_info info;
                         return;  
                 }  
   
                 packet = &packet_queue[queue_lo];  
                 out = &packet->s;  
262    
263                  if (!started)                  if (in_esddsp)
264                  {                  {
265                          gettimeofday(&tv, NULL);                          /* EsounD has no way of querying buffer status, so we have to
266                          startedat_us = tv.tv_usec;                           * go with a fixed size. */
267                          startedat_s = tv.tv_sec;                          delay_bytes = out->size;
                         started = True;  
268                  }                  }
269                    else
                 len = out->end - out->p;  
   
                 if (!g_driver_broken)  
270                  {                  {
271                          memset(&info, 0, sizeof(info));  #ifdef SNDCTL_DSP_GETODELAY
272                          if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)                          delay_bytes = 0;
273                          {                          if (ioctl(g_dsp_fd, SNDCTL_DSP_GETODELAY, &delay_bytes) == -1)
274                                  perror("SNDCTL_DSP_GETOSPACE");                                  delay_bytes = -1;
275                                  return;  #else
276                          }                          delay_bytes = -1;
277    #endif
278    
279                          if (info.fragments == 0)                          if (delay_bytes == -1)
280                          {                          {
281                                  g_dsp_busy = 1;                                  if (ioctl(g_dsp_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
282                                  return;                                          delay_bytes = info.fragstotal * info.fragsize - info.bytes;
283                          }                                  else
284                                            delay_bytes = out->size;
                         if (info.fragments * info.fragsize < len  
                             && info.fragments * info.fragsize > 0)  
                         {  
                                 len = info.fragments * info.fragsize;  
285                          }                          }
286                  }                  }
287    
288                    delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
289                    rdpsnd_queue_next(delay_us);
290            }
291            else
292            {
293                    g_dsp_busy = 1;
294            }
295    
296                  len = write(g_dsp_fd, out->p, len);          return;
297                  if (len == -1)  }
                 {  
                         if (errno != EWOULDBLOCK)  
                                 perror("write audio");  
                         g_dsp_busy = 1;  
                         return;  
                 }  
298    
299                  out->p += len;  struct audio_driver *
300                  if (out->p == out->end)  oss_register(char *options)
301                  {  {
302                          long long duration;          oss_driver.wave_out_open = oss_open;
303                          long elapsed;          oss_driver.wave_out_close = oss_close;
304            oss_driver.wave_out_format_supported = oss_format_supported;
305            oss_driver.wave_out_set_format = oss_set_format;
306            oss_driver.wave_out_volume = oss_volume;
307            oss_driver.wave_out_play = oss_play;
308            oss_driver.name = xstrdup("oss");
309            oss_driver.description =
310                    xstrdup("OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV");
311            oss_driver.need_byteswap_on_be = 0;
312            oss_driver.need_resampling = 0;
313            oss_driver.next = NULL;
314    
315                          gettimeofday(&tv, NULL);          if (options)
316                          duration = (out->size * (1000000 / (g_samplewidth * g_snd_rate)));          {
317                          elapsed = (tv.tv_sec - startedat_s) * 1000000 + (tv.tv_usec - startedat_us);                  dsp_dev = xstrdup(options);
318            }
319            else
320            {
321                    dsp_dev = getenv("AUDIODEV");
322    
323                          if (elapsed >= (duration * 85) / 100)                  if (dsp_dev == NULL)
324                          {                  {
325                                  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;  
                         }  
326                  }                  }
327          }          }
328    
329            return &oss_driver;
330  }  }

Legend:
Removed from v.801  
changed lines
  Added in v.1318

  ViewVC Help
Powered by ViewVC 1.1.26