1 |
/* -*- c-basic-offset: 8 -*- |
2 |
rdesktop: A Remote Desktop Protocol client. |
3 |
Sound Channel Process Functions - alsa-driver |
4 |
Copyright (C) Matthew Chapman 2003-2008 |
5 |
Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003 |
6 |
Copyright (C) Michael Gernoth mike@zerfleddert.de 2006-2008 |
7 |
Copyright 2006-2008 Pierre Ossman <ossman@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 2 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, write to the Free Software |
21 |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
22 |
*/ |
23 |
|
24 |
#include "rdesktop.h" |
25 |
#include "rdpsnd.h" |
26 |
#include "rdpsnd_dsp.h" |
27 |
#include <unistd.h> |
28 |
#include <fcntl.h> |
29 |
#include <errno.h> |
30 |
#include <alsa/asoundlib.h> |
31 |
#include <sys/time.h> |
32 |
|
33 |
#define DEFAULTDEVICE "default" |
34 |
#define MAX_FRAMES 32 |
35 |
|
36 |
static struct pollfd pfds_out[32]; |
37 |
static int num_fds_out; |
38 |
|
39 |
static struct pollfd pfds_in[32]; |
40 |
static int num_fds_in; |
41 |
|
42 |
static snd_pcm_t *out_handle = NULL; |
43 |
static snd_pcm_t *in_handle = NULL; |
44 |
|
45 |
static RD_BOOL reopened; |
46 |
|
47 |
static short samplewidth_out; |
48 |
static int audiochannels_out; |
49 |
static unsigned int rate_out; |
50 |
|
51 |
static short samplewidth_in; |
52 |
static int audiochannels_in; |
53 |
static unsigned int rate_in; |
54 |
|
55 |
static char *pcm_name; |
56 |
|
57 |
void alsa_play(void); |
58 |
void alsa_record(void); |
59 |
|
60 |
void |
61 |
alsa_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv) |
62 |
{ |
63 |
int err; |
64 |
struct pollfd *f; |
65 |
|
66 |
if (out_handle && !rdpsnd_queue_empty()) |
67 |
{ |
68 |
num_fds_out = snd_pcm_poll_descriptors_count(out_handle); |
69 |
|
70 |
if (num_fds_out > sizeof(pfds_out) / sizeof(*pfds_out)) |
71 |
return; |
72 |
|
73 |
err = snd_pcm_poll_descriptors(out_handle, pfds_out, num_fds_out); |
74 |
if (err < 0) |
75 |
return; |
76 |
|
77 |
for (f = pfds_out; f < &pfds_out[num_fds_out]; f++) |
78 |
{ |
79 |
if (f->events & POLLIN) |
80 |
FD_SET(f->fd, rfds); |
81 |
if (f->events & POLLOUT) |
82 |
FD_SET(f->fd, wfds); |
83 |
if (f->fd > *n && (f->events & (POLLIN | POLLOUT))) |
84 |
*n = f->fd; |
85 |
} |
86 |
} |
87 |
|
88 |
if (in_handle) |
89 |
{ |
90 |
num_fds_in = snd_pcm_poll_descriptors_count(in_handle); |
91 |
|
92 |
if (num_fds_in > sizeof(pfds_in) / sizeof(*pfds_in)) |
93 |
return; |
94 |
|
95 |
err = snd_pcm_poll_descriptors(in_handle, pfds_in, num_fds_in); |
96 |
if (err < 0) |
97 |
return; |
98 |
|
99 |
for (f = pfds_in; f < &pfds_in[num_fds_in]; f++) |
100 |
{ |
101 |
if (f->events & POLLIN) |
102 |
FD_SET(f->fd, rfds); |
103 |
if (f->events & POLLOUT) |
104 |
FD_SET(f->fd, wfds); |
105 |
if (f->fd > *n && (f->events & (POLLIN | POLLOUT))) |
106 |
*n = f->fd; |
107 |
} |
108 |
} |
109 |
} |
110 |
|
111 |
void |
112 |
alsa_check_fds(fd_set * rfds, fd_set * wfds) |
113 |
{ |
114 |
struct pollfd *f; |
115 |
int err; |
116 |
unsigned short revents; |
117 |
|
118 |
if (out_handle && !rdpsnd_queue_empty()) |
119 |
{ |
120 |
for (f = pfds_out; f < &pfds_out[num_fds_out]; f++) |
121 |
{ |
122 |
f->revents = 0; |
123 |
if (f->fd != -1) |
124 |
{ |
125 |
/* Fixme: This doesn't properly deal with things like POLLHUP */ |
126 |
if (FD_ISSET(f->fd, rfds)) |
127 |
f->revents |= POLLIN; |
128 |
if (FD_ISSET(f->fd, wfds)) |
129 |
f->revents |= POLLOUT; |
130 |
} |
131 |
} |
132 |
|
133 |
err = snd_pcm_poll_descriptors_revents(out_handle, pfds_out, num_fds_out, &revents); |
134 |
if (err < 0) |
135 |
return; |
136 |
|
137 |
if (revents & POLLOUT) |
138 |
alsa_play(); |
139 |
} |
140 |
|
141 |
|
142 |
if (in_handle) |
143 |
{ |
144 |
for (f = pfds_in; f < &pfds_in[num_fds_in]; f++) |
145 |
{ |
146 |
f->revents = 0; |
147 |
if (f->fd != -1) |
148 |
{ |
149 |
/* Fixme: This doesn't properly deal with things like POLLHUP */ |
150 |
if (FD_ISSET(f->fd, rfds)) |
151 |
f->revents |= POLLIN; |
152 |
if (FD_ISSET(f->fd, wfds)) |
153 |
f->revents |= POLLOUT; |
154 |
} |
155 |
} |
156 |
|
157 |
err = snd_pcm_poll_descriptors_revents(in_handle, pfds_in, num_fds_in, &revents); |
158 |
if (err < 0) |
159 |
return; |
160 |
|
161 |
if (revents & POLLIN) |
162 |
alsa_record(); |
163 |
} |
164 |
} |
165 |
|
166 |
static RD_BOOL |
167 |
alsa_set_format(snd_pcm_t * pcm, RD_WAVEFORMATEX * pwfx) |
168 |
{ |
169 |
snd_pcm_hw_params_t *hwparams = NULL; |
170 |
int err; |
171 |
unsigned int buffertime; |
172 |
short samplewidth; |
173 |
int audiochannels; |
174 |
unsigned int rate; |
175 |
|
176 |
samplewidth = pwfx->wBitsPerSample / 8; |
177 |
|
178 |
if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0) |
179 |
{ |
180 |
error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err)); |
181 |
return False; |
182 |
} |
183 |
|
184 |
if ((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0) |
185 |
{ |
186 |
error("snd_pcm_hw_params_any: %s\n", snd_strerror(err)); |
187 |
return False; |
188 |
} |
189 |
|
190 |
if ((err = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) |
191 |
{ |
192 |
error("snd_pcm_hw_params_set_access: %s\n", snd_strerror(err)); |
193 |
return False; |
194 |
} |
195 |
|
196 |
if (pwfx->wBitsPerSample == 16) |
197 |
{ |
198 |
if ((err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE)) < 0) |
199 |
{ |
200 |
error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err)); |
201 |
return False; |
202 |
} |
203 |
} |
204 |
else |
205 |
{ |
206 |
if ((err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S8)) < 0) |
207 |
{ |
208 |
error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err)); |
209 |
return False; |
210 |
} |
211 |
} |
212 |
|
213 |
#if 0 |
214 |
if ((err = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 1)) < 0) |
215 |
{ |
216 |
error("snd_pcm_hw_params_set_rate_resample: %s\n", snd_strerror(err)); |
217 |
return False; |
218 |
} |
219 |
#endif |
220 |
|
221 |
rate = pwfx->nSamplesPerSec; |
222 |
if ((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0) |
223 |
{ |
224 |
error("snd_pcm_hw_params_set_rate_near: %s\n", snd_strerror(err)); |
225 |
return False; |
226 |
} |
227 |
|
228 |
audiochannels = pwfx->nChannels; |
229 |
if ((err = snd_pcm_hw_params_set_channels(pcm, hwparams, pwfx->nChannels)) < 0) |
230 |
{ |
231 |
error("snd_pcm_hw_params_set_channels: %s\n", snd_strerror(err)); |
232 |
return False; |
233 |
} |
234 |
|
235 |
|
236 |
buffertime = 500000; /* microseconds */ |
237 |
if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams, &buffertime, 0)) < 0) |
238 |
{ |
239 |
error("snd_pcm_hw_params_set_buffer_time_near: %s\n", snd_strerror(err)); |
240 |
return False; |
241 |
} |
242 |
|
243 |
if ((err = snd_pcm_hw_params(pcm, hwparams)) < 0) |
244 |
{ |
245 |
error("snd_pcm_hw_params: %s\n", snd_strerror(err)); |
246 |
return False; |
247 |
} |
248 |
|
249 |
snd_pcm_hw_params_free(hwparams); |
250 |
|
251 |
if ((err = snd_pcm_prepare(pcm)) < 0) |
252 |
{ |
253 |
error("snd_pcm_prepare: %s\n", snd_strerror(err)); |
254 |
return False; |
255 |
} |
256 |
|
257 |
reopened = True; |
258 |
|
259 |
return True; |
260 |
} |
261 |
|
262 |
RD_BOOL |
263 |
alsa_open_out(void) |
264 |
{ |
265 |
int err; |
266 |
|
267 |
if ((err = snd_pcm_open(&out_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) |
268 |
{ |
269 |
error("snd_pcm_open: %s\n", snd_strerror(err)); |
270 |
return False; |
271 |
} |
272 |
|
273 |
reopened = True; |
274 |
|
275 |
return True; |
276 |
} |
277 |
|
278 |
void |
279 |
alsa_close_out(void) |
280 |
{ |
281 |
/* Ack all remaining packets */ |
282 |
while (!rdpsnd_queue_empty()) |
283 |
rdpsnd_queue_next(0); |
284 |
|
285 |
if (out_handle) |
286 |
{ |
287 |
snd_pcm_close(out_handle); |
288 |
out_handle = NULL; |
289 |
} |
290 |
} |
291 |
|
292 |
RD_BOOL |
293 |
alsa_format_supported(RD_WAVEFORMATEX * pwfx) |
294 |
{ |
295 |
#if 0 |
296 |
int err; |
297 |
snd_pcm_hw_params_t *hwparams = NULL; |
298 |
|
299 |
if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0) |
300 |
{ |
301 |
error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err)); |
302 |
return False; |
303 |
} |
304 |
|
305 |
if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0) |
306 |
{ |
307 |
error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err)); |
308 |
return False; |
309 |
} |
310 |
snd_pcm_hw_params_free(hwparams); |
311 |
#endif |
312 |
|
313 |
if (pwfx->wFormatTag != WAVE_FORMAT_PCM) |
314 |
return False; |
315 |
if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2)) |
316 |
return False; |
317 |
if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16)) |
318 |
return False; |
319 |
if ((pwfx->nSamplesPerSec != 44100) && (pwfx->nSamplesPerSec != 22050)) |
320 |
return False; |
321 |
|
322 |
return True; |
323 |
} |
324 |
|
325 |
RD_BOOL |
326 |
alsa_set_format_out(RD_WAVEFORMATEX * pwfx) |
327 |
{ |
328 |
if (!alsa_set_format(out_handle, pwfx)) |
329 |
return False; |
330 |
|
331 |
samplewidth_out = pwfx->wBitsPerSample / 8; |
332 |
audiochannels_out = pwfx->nChannels; |
333 |
rate_out = pwfx->nSamplesPerSec; |
334 |
|
335 |
return True; |
336 |
} |
337 |
|
338 |
void |
339 |
alsa_play(void) |
340 |
{ |
341 |
struct audio_packet *packet; |
342 |
STREAM out; |
343 |
int len; |
344 |
static long prev_s, prev_us; |
345 |
unsigned int duration; |
346 |
struct timeval tv; |
347 |
int next_tick; |
348 |
|
349 |
if (reopened) |
350 |
{ |
351 |
reopened = False; |
352 |
gettimeofday(&tv, NULL); |
353 |
prev_s = tv.tv_sec; |
354 |
prev_us = tv.tv_usec; |
355 |
} |
356 |
|
357 |
/* We shouldn't be called if the queue is empty, but still */ |
358 |
if (rdpsnd_queue_empty()) |
359 |
return; |
360 |
|
361 |
packet = rdpsnd_queue_current_packet(); |
362 |
out = &packet->s; |
363 |
|
364 |
next_tick = rdpsnd_queue_next_tick(); |
365 |
|
366 |
len = (out->end - out->p) / (samplewidth_out * audiochannels_out); |
367 |
if ((len = snd_pcm_writei(out_handle, out->p, ((MAX_FRAMES < len) ? MAX_FRAMES : len))) < 0) |
368 |
{ |
369 |
printf("Fooo!\n"); |
370 |
snd_pcm_prepare(out_handle); |
371 |
len = 0; |
372 |
} |
373 |
out->p += (len * samplewidth_out * audiochannels_out); |
374 |
|
375 |
gettimeofday(&tv, NULL); |
376 |
|
377 |
duration = ((tv.tv_sec - prev_s) * 1000000 + (tv.tv_usec - prev_us)) / 1000; |
378 |
|
379 |
if (packet->tick > next_tick) |
380 |
next_tick += 65536; |
381 |
|
382 |
if ((out->p == out->end) || duration > next_tick - packet->tick + 500) |
383 |
{ |
384 |
snd_pcm_sframes_t delay_frames; |
385 |
unsigned long delay_us; |
386 |
|
387 |
prev_s = tv.tv_sec; |
388 |
prev_us = tv.tv_usec; |
389 |
|
390 |
if (abs((next_tick - packet->tick) - duration) > 20) |
391 |
{ |
392 |
DEBUG(("duration: %d, calc: %d, ", duration, next_tick - packet->tick)); |
393 |
DEBUG(("last: %d, is: %d, should: %d\n", packet->tick, |
394 |
(packet->tick + duration) % 65536, next_tick % 65536)); |
395 |
} |
396 |
|
397 |
if (snd_pcm_delay(out_handle, &delay_frames) < 0) |
398 |
delay_frames = out->size / (samplewidth_out * audiochannels_out); |
399 |
if (delay_frames < 0) |
400 |
delay_frames = 0; |
401 |
|
402 |
delay_us = delay_frames * (1000000 / rate_out); |
403 |
|
404 |
rdpsnd_queue_next(delay_us); |
405 |
} |
406 |
} |
407 |
|
408 |
RD_BOOL |
409 |
alsa_open_in(void) |
410 |
{ |
411 |
int err; |
412 |
|
413 |
if ((err = |
414 |
snd_pcm_open(&in_handle, pcm_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK)) < 0) |
415 |
{ |
416 |
error("snd_pcm_open: %s\n", snd_strerror(err)); |
417 |
return False; |
418 |
} |
419 |
|
420 |
return True; |
421 |
} |
422 |
|
423 |
void |
424 |
alsa_close_in(void) |
425 |
{ |
426 |
if (in_handle) |
427 |
{ |
428 |
snd_pcm_close(in_handle); |
429 |
in_handle = NULL; |
430 |
} |
431 |
} |
432 |
|
433 |
RD_BOOL |
434 |
alsa_set_format_in(RD_WAVEFORMATEX * pwfx) |
435 |
{ |
436 |
int err; |
437 |
|
438 |
if (!alsa_set_format(in_handle, pwfx)) |
439 |
return False; |
440 |
|
441 |
if ((err = snd_pcm_start(in_handle)) < 0) |
442 |
{ |
443 |
error("snd_pcm_start: %s\n", snd_strerror(err)); |
444 |
return False; |
445 |
} |
446 |
|
447 |
samplewidth_in = pwfx->wBitsPerSample / 8; |
448 |
audiochannels_in = pwfx->nChannels; |
449 |
rate_in = pwfx->nSamplesPerSec; |
450 |
|
451 |
return True; |
452 |
} |
453 |
|
454 |
void |
455 |
alsa_record(void) |
456 |
{ |
457 |
int len; |
458 |
char buffer[32768]; |
459 |
|
460 |
len = snd_pcm_readi(in_handle, buffer, |
461 |
sizeof(buffer) / (samplewidth_in * audiochannels_in)); |
462 |
if (len < 0) |
463 |
{ |
464 |
snd_pcm_prepare(in_handle); |
465 |
len = 0; |
466 |
} |
467 |
|
468 |
rdpsnd_record(buffer, len * samplewidth_in * audiochannels_in); |
469 |
} |
470 |
|
471 |
struct audio_driver * |
472 |
alsa_register(char *options) |
473 |
{ |
474 |
static struct audio_driver alsa_driver; |
475 |
|
476 |
memset(&alsa_driver, 0, sizeof(alsa_driver)); |
477 |
|
478 |
alsa_driver.name = "alsa"; |
479 |
alsa_driver.description = "ALSA output driver, default device: " DEFAULTDEVICE; |
480 |
|
481 |
alsa_driver.add_fds = alsa_add_fds; |
482 |
alsa_driver.check_fds = alsa_check_fds; |
483 |
|
484 |
alsa_driver.wave_out_open = alsa_open_out; |
485 |
alsa_driver.wave_out_close = alsa_close_out; |
486 |
alsa_driver.wave_out_format_supported = alsa_format_supported; |
487 |
alsa_driver.wave_out_set_format = alsa_set_format_out; |
488 |
alsa_driver.wave_out_volume = rdpsnd_dsp_softvol_set; |
489 |
|
490 |
alsa_driver.wave_in_open = alsa_open_in; |
491 |
alsa_driver.wave_in_close = alsa_close_in; |
492 |
alsa_driver.wave_in_format_supported = alsa_format_supported; |
493 |
alsa_driver.wave_in_set_format = alsa_set_format_in; |
494 |
alsa_driver.wave_in_volume = NULL; /* FIXME */ |
495 |
|
496 |
alsa_driver.need_byteswap_on_be = 0; |
497 |
alsa_driver.need_resampling = 0; |
498 |
|
499 |
if (options) |
500 |
{ |
501 |
pcm_name = xstrdup(options); |
502 |
} |
503 |
else |
504 |
{ |
505 |
pcm_name = DEFAULTDEVICE; |
506 |
} |
507 |
|
508 |
return &alsa_driver; |
509 |
} |