VirtualBox

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

Last change on this file since 10076 was 9902, checked in by vboxsync, 16 years ago

Added rdesktop 1.6.0.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 9.3 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 [email protected] 2003
6 Copyright 2006-2007 Pierre Ossman <[email protected]> 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
47static int dsp_fd = -1;
48static int dsp_mode;
49static int dsp_refs;
50
51static RD_BOOL dsp_configured;
52static RD_BOOL dsp_broken;
53
54static RD_BOOL dsp_out;
55static RD_BOOL dsp_in;
56
57static int stereo;
58static int format;
59static uint32 snd_rate;
60static short samplewidth;
61static char *dsp_dev;
62static RD_BOOL in_esddsp;
63
64/* This is a just a forward declaration */
65static struct audio_driver oss_driver;
66
67void oss_play(void);
68void oss_record(void);
69
70void
71oss_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
72{
73 if (dsp_fd == -1)
74 return;
75
76 if (dsp_out && !rdpsnd_queue_empty())
77 FD_SET(dsp_fd, wfds);
78 if (dsp_in)
79 FD_SET(dsp_fd, rfds);
80 if (dsp_fd > *n)
81 *n = dsp_fd;
82}
83
84void
85oss_check_fds(fd_set * rfds, fd_set * wfds)
86{
87 if (FD_ISSET(dsp_fd, wfds))
88 oss_play();
89 if (FD_ISSET(dsp_fd, rfds))
90 oss_record();
91}
92
93static RD_BOOL
94detect_esddsp(void)
95{
96 struct stat s;
97 char *preload;
98
99 if (fstat(dsp_fd, &s) == -1)
100 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
115RD_BOOL
116oss_open(int fallback)
117{
118 int caps;
119
120 if (dsp_fd != -1)
121 {
122 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 return False;
132 }
133
134 dsp_configured = False;
135 dsp_broken = False;
136
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 in_esddsp = detect_esddsp();
165
166 return True;
167}
168
169void
170oss_close(void)
171{
172 dsp_refs--;
173
174 if (dsp_refs != 0)
175 return;
176
177 close(dsp_fd);
178 dsp_fd = -1;
179}
180
181RD_BOOL
182oss_open_out(void)
183{
184 if (!oss_open(O_WRONLY))
185 return False;
186
187 dsp_out = True;
188
189 return True;
190}
191
192void
193oss_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
204RD_BOOL
205oss_open_in(void)
206{
207 if (!oss_open(O_RDONLY))
208 return False;
209
210 dsp_in = True;
211
212 return True;
213}
214
215void
216oss_close_in(void)
217{
218 oss_close();
219
220 dsp_in = False;
221}
222
223RD_BOOL
224oss_format_supported(RD_WAVEFORMATEX * pwfx)
225{
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
236RD_BOOL
237oss_set_format(RD_WAVEFORMATEX * pwfx)
238{
239 int fragments;
240 static RD_BOOL driver_broken = False;
241
242 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 ioctl(dsp_fd, SNDCTL_DSP_RESET, NULL);
259 ioctl(dsp_fd, SNDCTL_DSP_SYNC, NULL);
260
261 if (pwfx->wBitsPerSample == 8)
262 format = AFMT_U8;
263 else if (pwfx->wBitsPerSample == 16)
264 format = AFMT_S16_LE;
265
266 samplewidth = pwfx->wBitsPerSample / 8;
267
268 if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1)
269 {
270 perror("SNDCTL_DSP_SETFMT");
271 oss_close();
272 return False;
273 }
274
275 if (pwfx->nChannels == 2)
276 {
277 stereo = 1;
278 samplewidth *= 2;
279 }
280 else
281 {
282 stereo = 0;
283 }
284
285 if (ioctl(dsp_fd, SNDCTL_DSP_STEREO, &stereo) == -1)
286 {
287 perror("SNDCTL_DSP_CHANNELS");
288 oss_close();
289 return False;
290 }
291
292 oss_driver.need_resampling = 0;
293 snd_rate = pwfx->nSamplesPerSec;
294 if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &snd_rate) == -1)
295 {
296 uint32 rates[] = { 44100, 48000, 0 };
297 uint32 *prates = rates;
298
299 while (*prates != 0)
300 {
301 if ((pwfx->nSamplesPerSec != *prates)
302 && (ioctl(dsp_fd, SNDCTL_DSP_SPEED, prates) != -1))
303 {
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 oss_close();
311 return False;
312 }
313
314 break;
315 }
316 prates++;
317 }
318
319 if (*prates == 0)
320 {
321 perror("SNDCTL_DSP_SPEED");
322 oss_close();
323 return False;
324 }
325 }
326
327 /* try to get 12 fragments of 2^12 bytes size */
328 fragments = (12 << 16) + 12;
329 ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
330
331 if (!driver_broken)
332 {
333 audio_buf_info info;
334
335 memset(&info, 0, sizeof(info));
336 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
337 {
338 perror("SNDCTL_DSP_GETOSPACE");
339 oss_close();
340 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 driver_broken = True;
349 }
350 }
351
352 dsp_configured = True;
353
354 return True;
355}
356
357void
358oss_volume(uint16 left, uint16 right)
359{
360 uint32 volume;
361
362 volume = left / (65536 / 100);
363 volume |= right / (65536 / 100) << 8;
364
365 if (ioctl(dsp_fd, MIXER_WRITE(SOUND_MIXER_PCM), &volume) == -1)
366 {
367 warning("hardware volume control unavailable, falling back to software volume control!\n");
368 oss_driver.wave_out_volume = rdpsnd_dsp_softvol_set;
369 rdpsnd_dsp_softvol_set(left, right);
370 return;
371 }
372}
373
374void
375oss_play(void)
376{
377 struct audio_packet *packet;
378 ssize_t len;
379 STREAM out;
380
381 /* We shouldn't be called if the queue is empty, but still */
382 if (rdpsnd_queue_empty())
383 return;
384
385 packet = rdpsnd_queue_current_packet();
386 out = &packet->s;
387
388 len = out->end - out->p;
389
390 len = write(dsp_fd, out->p, (len > MAX_LEN) ? MAX_LEN : len);
391 if (len == -1)
392 {
393 if (errno != EWOULDBLOCK)
394 {
395 if (!dsp_broken)
396 perror("RDPSND: write()");
397 dsp_broken = True;
398 rdpsnd_queue_next(0);
399 }
400 return;
401 }
402
403 dsp_broken = False;
404
405 out->p += len;
406
407 if (out->p == out->end)
408 {
409 int delay_bytes;
410 unsigned long delay_us;
411 audio_buf_info info;
412
413 if (in_esddsp)
414 {
415 /* EsounD has no way of querying buffer status, so we have to
416 * go with a fixed size. */
417 delay_bytes = out->size;
418 }
419 else
420 {
421#ifdef SNDCTL_DSP_GETODELAY
422 delay_bytes = 0;
423 if (ioctl(dsp_fd, SNDCTL_DSP_GETODELAY, &delay_bytes) == -1)
424 delay_bytes = -1;
425#else
426 delay_bytes = -1;
427#endif
428
429 if (delay_bytes == -1)
430 {
431 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &info) != -1)
432 delay_bytes = info.fragstotal * info.fragsize - info.bytes;
433 else
434 delay_bytes = out->size;
435 }
436 }
437
438 delay_us = delay_bytes * (1000000 / (samplewidth * snd_rate));
439 rdpsnd_queue_next(delay_us);
440 }
441}
442
443void
444oss_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 {
454 if (!dsp_broken)
455 perror("RDPSND: read()");
456 dsp_broken = True;
457 rdpsnd_queue_next(0);
458 }
459 return;
460 }
461
462 dsp_broken = False;
463
464 rdpsnd_record(buffer, len);
465}
466
467struct audio_driver *
468oss_register(char *options)
469{
470 memset(&oss_driver, 0, sizeof(oss_driver));
471
472 oss_driver.name = "oss";
473 oss_driver.description =
474 "OSS output driver, default device: " DEFAULTDEVICE " or $AUDIODEV";
475
476 oss_driver.add_fds = oss_add_fds;
477 oss_driver.check_fds = oss_check_fds;
478
479 oss_driver.wave_out_open = oss_open_out;
480 oss_driver.wave_out_close = oss_close_out;
481 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
485 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 oss_driver.need_byteswap_on_be = 0;
492 oss_driver.need_resampling = 0;
493
494 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 dsp_dev = DEFAULTDEVICE;
505 }
506 }
507
508 return &oss_driver;
509}
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