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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1361 - (show annotations)
Tue Jan 2 16:37:54 2007 UTC (17 years, 4 months ago) by ossman_
File MIME type: text/plain
File size: 9241 byte(s)
Add capture support to the oss backend.

1 /* -*- c-basic-offset: 8 -*-
2 rdesktop: A Remote Desktop Protocol client.
3 Sound Channel Process Functions - Open Sound System
4 Copyright (C) Matthew Chapman 2003
5 Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003
6 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
7
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 /*
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 #include "rdesktop.h"
32 #include "rdpsnd.h"
33 #include "rdpsnd_dsp.h"
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <sys/time.h>
39 #include <sys/ioctl.h>
40 #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 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 if (strstr(preload, "esddsp") == NULL)
109 return False;
110
111 return True;
112 }
113
114 BOOL
115 oss_open(int fallback)
116 {
117 int caps;
118
119 if (dsp_fd != -1)
120 {
121 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 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 ioctl(dsp_fd, SNDCTL_DSP_SETDUPLEX, 0);
140
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 if (dsp_fd == -1)
149 {
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;
165 }
166
167 void
168 oss_close(void)
169 {
170 dsp_refs--;
171
172 if (dsp_refs != 0)
173 return;
174
175 close(dsp_fd);
176 dsp_fd = -1;
177 }
178
179 BOOL
180 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(WAVEFORMATEX * pwfx)
223 {
224 if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
225 return False;
226 if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2))
227 return False;
228 if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16))
229 return False;
230
231 return True;
232 }
233
234 BOOL
235 oss_set_format(WAVEFORMATEX * pwfx)
236 {
237 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(dsp_fd, SNDCTL_DSP_RESET, NULL);
257 ioctl(dsp_fd, SNDCTL_DSP_SYNC, NULL);
258
259 if (pwfx->wBitsPerSample == 8)
260 format = AFMT_U8;
261 else if (pwfx->wBitsPerSample == 16)
262 format = AFMT_S16_LE;
263
264 samplewidth = pwfx->wBitsPerSample / 8;
265
266 if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
267 {
268 perror("SNDCTL_DSP_SETFMT");
269 oss_close();
270 return False;
271 }
272
273 if (pwfx->nChannels == 2)
274 {
275 stereo = 1;
276 samplewidth *= 2;
277 }
278 else
279 {
280 stereo = 0;
281 }
282
283 if (ioctl(dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
284 {
285 perror("SNDCTL_DSP_CHANNELS");
286 oss_close();
287 return False;
288 }
289
290 oss_driver.need_resampling = 0;
291 snd_rate = pwfx->nSamplesPerSec;
292 if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
293 {
294 int rates[] = { 44100, 48000, 0 };
295 int *prates = rates;
296
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 12 fragments of 2^12 bytes size */
326 fragments = (12 << 16) + 12;
327 ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
328
329 if (!driver_broken)
330 {
331 audio_buf_info info;
332
333 memset(&info, 0, sizeof(info));
334 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
335 {
336 perror("SNDCTL_DSP_GETOSPACE");
337 oss_close();
338 return False;
339 }
340
341 if (info.fragments == 0 || info.fragstotal == 0 || info.fragsize == 0)
342 {
343 fprintf(stderr,
344 "Broken OSS-driver detected: fragments: %d, fragstotal: %d, fragsize: %d\n",
345 info.fragments, info.fragstotal, info.fragsize);
346 driver_broken = True;
347 }
348 }
349
350 dsp_configured = True;
351
352 return True;
353 }
354
355 void
356 oss_volume(uint16 left, uint16 right)
357 {
358 uint32 volume;
359
360 volume = left / (65536 / 100);
361 volume |= right / (65536 / 100) << 8;
362
363 if (ioctl(dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
364 {
365 warning("hardware volume control unavailable, falling back to software volume control!\n");
366 oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
367 rdpsnd_dsp_softvol_set(left, right);
368 return;
369 }
370 }
371
372 void
373 oss_play(void)
374 {
375 struct audio_packet *packet;
376 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 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 if (errno != EWOULDBLOCK)
392 perror("write audio");
393 return;
394 }
395
396 out->p += len;
397
398 if (out->p == out->end)
399 {
400 int delay_bytes;
401 unsigned long delay_us;
402 audio_buf_info info;
403
404 if (in_esddsp)
405 {
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 delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
430 rdpsnd_queue_next(delay_us);
431 }
432 }
433
434 void
435 oss_record(void)
436 {
437 char buffer[32768];
438 int len;
439
440 len = read(dsp_fd, buffer, sizeof(buffer));
441 if (len == -1)
442 {
443 if (errno != EWOULDBLOCK)
444 perror("read audio");
445 return;
446 }
447
448 rdpsnd_record(buffer, len);
449 }
450
451 struct audio_driver *
452 oss_register(char *options)
453 {
454 memset(&oss_driver, 0, sizeof(oss_driver));
455
456 oss_driver.name = "oss";
457 oss_driver.description =
458 "OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV";
459
460 oss_driver.add_fds = oss_add_fds;
461 oss_driver.check_fds = oss_check_fds;
462
463 oss_driver.wave_out_open = oss_open_out;
464 oss_driver.wave_out_close = oss_close_out;
465 oss_driver.wave_out_format_supported = oss_format_supported;
466 oss_driver.wave_out_set_format = oss_set_format;
467 oss_driver.wave_out_volume = oss_volume;
468
469 oss_driver.wave_in_open = oss_open_in;
470 oss_driver.wave_in_close = oss_close_in;
471 oss_driver.wave_in_format_supported = oss_format_supported;
472 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 if (options)
479 {
480 dsp_dev = xstrdup(options);
481 }
482 else
483 {
484 dsp_dev = getenv("AUDIODEV");
485
486 if (dsp_dev == NULL)
487 {
488 dsp_dev = DEFAULTDEVICE;
489 }
490 }
491
492 return &oss_driver;
493 }

  ViewVC Help
Powered by ViewVC 1.1.26