/[rdesktop]/jpeg/rdesktop/trunk/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 /jpeg/rdesktop/trunk/rdpsnd_oss.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.26