VirtualBox

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

Last change on this file since 21395 was 11982, checked in by vboxsync, 16 years ago

All: license header changes for 2.0 (OSE headers, add Sun GPL/LGPL disclaimer)

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