VirtualBox

source: vbox/trunk/src/VBox/RDP/client/rdpsnd_oss.c@ 52901

Last change on this file since 52901 was 37224, checked in by vboxsync, 14 years ago

RDP/client: fix OSE

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette