/[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

Annotation of /sourceforge.net/trunk/rdesktop/rdpsnd_oss.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1376 - (hide annotations)
Wed Jan 10 09:15:15 2007 UTC (17 years, 5 months ago) by ossman_
File MIME type: text/plain
File size: 9542 byte(s)
A bit more robust handling on read() or write() errors.

1 astrand 963 /* -*- c-basic-offset: 8 -*-
2 matthewc 475 rdesktop: A Remote Desktop Protocol client.
3     Sound Channel Process Functions - Open Sound System
4 jsorg71 1365 Copyright (C) Matthew Chapman 2003-2007
5 matthewc 475 Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003
6 jsorg71 1365 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB
7 matthewc 475
8     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
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12    
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     GNU General Public License for more details.
17    
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21     */
22    
23 astrand 1227 /*
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 matthewc 475 #include "rdesktop.h"
32 stargo 1254 #include "rdpsnd.h"
33 stargo 1269 #include "rdpsnd_dsp.h"
34 matthewc 475 #include <unistd.h>
35     #include <fcntl.h>
36     #include <errno.h>
37 ossman_ 1302 #include <unistd.h>
38 stargo 510 #include <sys/time.h>
39 matthewc 475 #include <sys/ioctl.h>
40     #include <sys/soundcard.h>
41 ossman_ 1302 #include <sys/types.h>
42     #include <sys/stat.h>
43 matthewc 475
44 stargo 1254 #define DEFAULTDEVICE "/dev/dsp"
45 stargo 1247 #define MAX_LEN 512
46 matthewc 475
47 ossman_ 1346 static int dsp_fd = -1;
48 ossman_ 1361 static int dsp_mode;
49     static int dsp_refs;
50 ossman_ 1346
51 jsorg71 1372 static RD_BOOL dsp_configured;
52 ossman_ 1376 static RD_BOOL dsp_broken;
53 ossman_ 1361
54 jsorg71 1372 static RD_BOOL dsp_out;
55     static RD_BOOL dsp_in;
56 ossman_ 1361
57     static int stereo;
58     static int format;
59 stargo 1247 static int snd_rate;
60     static short samplewidth;
61 stargo 1255 static char *dsp_dev;
62 jsorg71 1372 static RD_BOOL in_esddsp;
63 matthewc 475
64 ossman_ 1345 /* This is a just a forward declaration */
65     static struct audio_driver oss_driver;
66    
67 ossman_ 1346 void oss_play(void);
68 ossman_ 1361 void oss_record(void);
69 ossman_ 1346
70     void
71     oss_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
72     {
73     if (dsp_fd == -1)
74     return;
75    
76 ossman_ 1361 if (dsp_out && !rdpsnd_queue_empty())
77     FD_SET(dsp_fd, wfds);
78     if (dsp_in)
79     FD_SET(dsp_fd, rfds);
80 ossman_ 1346 if (dsp_fd > *n)
81     *n = dsp_fd;
82     }
83    
84     void
85     oss_check_fds(fd_set * rfds, fd_set * wfds)
86     {
87     if (FD_ISSET(dsp_fd, wfds))
88     oss_play();
89 ossman_ 1361 if (FD_ISSET(dsp_fd, rfds))
90     oss_record();
91 ossman_ 1346 }
92    
93 jsorg71 1372 static RD_BOOL
94 ossman_ 1302 detect_esddsp(void)
95     {
96     struct stat s;
97     char *preload;
98    
99 ossman_ 1346 if (fstat(dsp_fd, &s) == -1)
100 ossman_ 1302 return False;
101    
102     if (S_ISCHR(s.st_mode) || S_ISBLK(s.st_mode))
103     return False;
104    
105     preload = getenv("LD_PRELOAD");
106     if (preload == NULL)
107     return False;
108    
109     if (strstr(preload, "esddsp") == NULL)
110     return False;
111    
112     return True;
113     }
114    
115 jsorg71 1372 RD_BOOL
116 ossman_ 1361 oss_open(int fallback)
117 matthewc 475 {
118 ossman_ 1361 int caps;
119    
120     if (dsp_fd != -1)
121 matthewc 475 {
122 ossman_ 1361 dsp_refs++;
123    
124     if (dsp_mode == O_RDWR)
125     return True;
126    
127     if (dsp_mode == fallback)
128     return True;
129    
130     dsp_refs--;
131 matthewc 475 return False;
132     }
133    
134 ossman_ 1361 dsp_configured = False;
135 ossman_ 1376 dsp_broken = False;
136 ossman_ 1361
137     dsp_mode = O_RDWR;
138     dsp_fd = open(dsp_dev, O_RDWR | O_NONBLOCK);
139     if (dsp_fd != -1)
140     {
141     ioctl(dsp_fd, SNDCTL_DSP_SETDUPLEX, 0);
142    
143     if ((ioctl(dsp_fd, SNDCTL_DSP_GETCAPS, &caps) < 0) || !(caps & DSP_CAP_DUPLEX))
144     {
145     close(dsp_fd);
146     dsp_fd = -1;
147     }
148     }
149    
150     if (dsp_fd == -1)
151     {
152     dsp_mode = fallback;
153    
154     dsp_fd = open(dsp_dev, dsp_mode | O_NONBLOCK);
155     if (dsp_fd == -1)
156     {
157     perror(dsp_dev);
158     return False;
159     }
160     }
161    
162     dsp_refs++;
163    
164 ossman_ 1302 in_esddsp = detect_esddsp();
165    
166 matthewc 475 return True;
167     }
168    
169     void
170 stargo 1255 oss_close(void)
171 matthewc 475 {
172 ossman_ 1361 dsp_refs--;
173    
174     if (dsp_refs != 0)
175     return;
176    
177 ossman_ 1346 close(dsp_fd);
178     dsp_fd = -1;
179 matthewc 475 }
180    
181 jsorg71 1372 RD_BOOL
182 ossman_ 1361 oss_open_out(void)
183     {
184     if (!oss_open(O_WRONLY))
185     return False;
186    
187     dsp_out = True;
188    
189     return True;
190     }
191    
192     void
193     oss_close_out(void)
194     {
195     oss_close();
196    
197     /* Ack all remaining packets */
198     while (!rdpsnd_queue_empty())
199     rdpsnd_queue_next(0);
200    
201     dsp_out = False;
202     }
203    
204 jsorg71 1372 RD_BOOL
205 ossman_ 1361 oss_open_in(void)
206     {
207     if (!oss_open(O_RDONLY))
208     return False;
209    
210     dsp_in = True;
211    
212     return True;
213     }
214    
215     void
216     oss_close_in(void)
217     {
218     oss_close();
219    
220     dsp_in = False;
221     }
222    
223 jsorg71 1372 RD_BOOL
224 jsorg71 1364 oss_format_supported(RD_WAVEFORMATEX * pwfx)
225 matthewc 475 {
226     if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
227     return False;
228     if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2))
229     return False;
230     if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16))
231     return False;
232    
233     return True;
234     }
235    
236 jsorg71 1372 RD_BOOL
237 jsorg71 1364 oss_set_format(RD_WAVEFORMATEX * pwfx)
238 matthewc 475 {
239 ossman_ 1361 int fragments;
240 jsorg71 1372 static RD_BOOL driver_broken = False;
241 matthewc 475
242 ossman_ 1361 if (dsp_configured)
243     {
244     if ((pwfx->wBitsPerSample == 8) && (format != AFMT_U8))
245     return False;
246     if ((pwfx->wBitsPerSample == 16) && (format != AFMT_S16_LE))
247     return False;
248    
249     if ((pwfx->nChannels == 2) != !!stereo)
250     return False;
251    
252     if (pwfx->nSamplesPerSec != snd_rate)
253     return False;
254    
255     return True;
256     }
257    
258 ossman_ 1346 ioctl(dsp_fd, SNDCTL_DSP_RESET, NULL);
259     ioctl(dsp_fd, SNDCTL_DSP_SYNC, NULL);
260 matthewc 475
261     if (pwfx->wBitsPerSample == 8)
262     format = AFMT_U8;
263     else if (pwfx->wBitsPerSample == 16)
264     format = AFMT_S16_LE;
265    
266 stargo 1247 samplewidth = pwfx->wBitsPerSample / 8;
267 stargo 510
268 ossman_ 1346 if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
269 matthewc 475 {
270     perror("SNDCTL_DSP_SETFMT");
271 astrand 1318 oss_close();
272 matthewc 475 return False;
273     }
274    
275 stargo 760 if (pwfx->nChannels == 2)
276 matthewc 475 {
277 stargo 760 stereo = 1;
278 stargo 1247 samplewidth *= 2;
279 stargo 760 }
280     else
281     {
282     stereo = 0;
283     }
284    
285 ossman_ 1346 if (ioctl(dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
286 stargo 760 {
287 matthewc 475 perror("SNDCTL_DSP_CHANNELS");
288 astrand 1318 oss_close();
289 matthewc 475 return False;
290     }
291    
292 stargo 1286 oss_driver.need_resampling = 0;
293 stargo 1247 snd_rate = pwfx->nSamplesPerSec;
294 ossman_ 1346 if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
295 stargo 510 {
296 stargo 1286 int rates[] = { 44100, 48000, 0 };
297     int *prates = rates;
298    
299     while (*prates != 0)
300     {
301     if ((pwfx->nSamplesPerSec != *prates)
302 ossman_ 1346 && (ioctl(dsp_fd, SNDCTL_DSP_SPEED, prates) != -1))
303 stargo 1286 {
304     oss_driver.need_resampling = 1;
305     snd_rate = *prates;
306     if (rdpsnd_dsp_resample_set
307     (snd_rate, pwfx->wBitsPerSample, pwfx->nChannels) == False)
308     {
309     error("rdpsnd_dsp_resample_set failed");
310 astrand 1318 oss_close();
311 stargo 1286 return False;
312     }
313    
314     break;
315     }
316     prates++;
317     }
318    
319     if (*prates == 0)
320     {
321     perror("SNDCTL_DSP_SPEED");
322 astrand 1318 oss_close();
323 stargo 1286 return False;
324     }
325 matthewc 475 }
326    
327 stargo 1247 /* try to get 12 fragments of 2^12 bytes size */
328     fragments = (12 << 16) + 12;
329 ossman_ 1346 ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
330 stargo 761
331 stargo 1247 if (!driver_broken)
332 stargo 761 {
333     audio_buf_info info;
334    
335 astrand 801 memset(&info, 0, sizeof(info));
336 ossman_ 1346 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
337 stargo 761 {
338     perror("SNDCTL_DSP_GETOSPACE");
339 astrand 1318 oss_close();
340 stargo 761 return False;
341     }
342    
343     if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0)
344     {
345     fprintf(stderr,
346     "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
347     info.fragments, info.fragstotal, info.fragsize);
348 stargo 1247 driver_broken = True;
349 stargo 761 }
350     }
351    
352 ossman_ 1361 dsp_configured = True;
353    
354 matthewc 475 return True;
355     }
356    
357     void
358 stargo 1255 oss_volume(uint16 left, uint16 right)
359 stargo 491 {
360     uint32 volume;
361    
362 astrand 499 volume = left / (65536 / 100);
363     volume |= right / (65536 / 100) << 8;
364 stargo 509
365 ossman_ 1346 if (ioctl(dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
366 stargo 491 {
367 stargo 1275 warning("hardware volume control unavailable, falling back to software volume control!\n");
368 stargo 1269 oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
369 stargo 1275 rdpsnd_dsp_softvol_set(left, right);
370 stargo 491 return;
371     }
372     }
373    
374     void
375 stargo 1255 oss_play(void)
376 matthewc 475 {
377     struct audio_packet *packet;
378     ssize_t len;
379     STREAM out;
380    
381 ossman_ 1346 /* We shouldn't be called if the queue is empty, but still */
382 stargo 1254 if (rdpsnd_queue_empty())
383 stargo 1247 return;
384 matthewc 475
385 stargo 1254 packet = rdpsnd_queue_current_packet();
386 stargo 1247 out = &packet->s;
387 matthewc 475
388 stargo 1247 len = out->end - out->p;
389 stargo 761
390 ossman_ 1346 len = write(dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
391 stargo 1247 if (len == -1)
392     {
393     if (errno != EWOULDBLOCK)
394 ossman_ 1376 {
395     if (!dsp_broken)
396     perror("RDPSND: write()");
397     dsp_broken = True;
398     rdpsnd_queue_next(0);
399     }
400 stargo 1247 return;
401     }
402 stargo 761
403 ossman_ 1376 dsp_broken = False;
404    
405 stargo 1247 out->p += len;
406 ossman_ 1302
407 stargo 1247 if (out->p == out->end)
408     {
409 ossman_ 1302 int delay_bytes;
410     unsigned long delay_us;
411     audio_buf_info info;
412 stargo 761
413 ossman_ 1302 if (in_esddsp)
414 matthewc 475 {
415 ossman_ 1302 /* EsounD has no way of querying buffer status, so we have to
416     * go with a fixed size. */
417     delay_bytes = out->size;
418 stargo 1247 }
419     else
420     {
421 ossman_ 1302 #ifdef SNDCTL_DSP_GETODELAY
422     delay_bytes = 0;
423 ossman_ 1346 if (ioctl(dsp_fd, SNDCTL_DSP_GETODELAY, &delay_bytes) == -1)
424 ossman_ 1302 delay_bytes = -1;
425     #else
426     delay_bytes = -1;
427     #endif
428    
429     if (delay_bytes == -1)
430     {
431 ossman_ 1346 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
432 ossman_ 1302 delay_bytes = info.fragstotal * info.fragsize - info.bytes;
433     else
434     delay_bytes = out->size;
435     }
436 matthewc 475 }
437 ossman_ 1302
438     delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
439     rdpsnd_queue_next(delay_us);
440 matthewc 475 }
441     }
442 stargo 1255
443 ossman_ 1361 void
444     oss_record(void)
445     {
446     char buffer[32768];
447     int len;
448    
449     len = read(dsp_fd, buffer, sizeof(buffer));
450     if (len == -1)
451     {
452     if (errno != EWOULDBLOCK)
453 ossman_ 1376 {
454     if (!dsp_broken)
455     perror("RDPSND: read()");
456     dsp_broken = True;
457     rdpsnd_queue_next(0);
458     }
459 ossman_ 1361 return;
460     }
461    
462 ossman_ 1376 dsp_broken = False;
463    
464 ossman_ 1361 rdpsnd_record(buffer, len);
465     }
466    
467 stargo 1351 struct audio_driver *
468     oss_register(char *options)
469     {
470 ossman_ 1356 memset(&oss_driver, 0, sizeof(oss_driver));
471    
472     oss_driver.name = "oss";
473 stargo 1351 oss_driver.description =
474 ossman_ 1356 "OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV";
475 ossman_ 1345
476 stargo 1351 oss_driver.add_fds = oss_add_fds;
477     oss_driver.check_fds = oss_check_fds;
478 ossman_ 1346
479 ossman_ 1361 oss_driver.wave_out_open = oss_open_out;
480     oss_driver.wave_out_close = oss_close_out;
481 stargo 1351 oss_driver.wave_out_format_supported = oss_format_supported;
482     oss_driver.wave_out_set_format = oss_set_format;
483     oss_driver.wave_out_volume = oss_volume;
484 ossman_ 1345
485 ossman_ 1361 oss_driver.wave_in_open = oss_open_in;
486     oss_driver.wave_in_close = oss_close_in;
487     oss_driver.wave_in_format_supported = oss_format_supported;
488     oss_driver.wave_in_set_format = oss_set_format;
489     oss_driver.wave_in_volume = NULL; /* FIXME */
490    
491 stargo 1351 oss_driver.need_byteswap_on_be = 0;
492     oss_driver.need_resampling = 0;
493 ossman_ 1345
494 stargo 1255 if (options)
495     {
496     dsp_dev = xstrdup(options);
497     }
498     else
499     {
500     dsp_dev = getenv("AUDIODEV");
501    
502     if (dsp_dev == NULL)
503     {
504 ossman_ 1356 dsp_dev = DEFAULTDEVICE;
505 stargo 1255 }
506     }
507    
508     return &oss_driver;
509     }

  ViewVC Help
Powered by ViewVC 1.1.26