/[rdesktop]/sourceforge.net/trunk/rdesktop/rdpsnd_alsa.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 /sourceforge.net/trunk/rdesktop/rdpsnd_alsa.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1346 - (show annotations)
Thu Dec 7 15:23:45 2006 UTC (17 years, 5 months ago) by ossman_
File MIME type: text/plain
File size: 8242 byte(s)
Abstract select() handling in rdpsnd so that backends can do their thing
more correctly.

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
5 Copyright (C) GuoJunBo guojunbo@ict.ac.cn 2003
6 Copyright (C) Michael Gernoth mike@zerfleddert.de 2006
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 #include "rdesktop.h"
24 #include "rdpsnd.h"
25 #include "rdpsnd_dsp.h"
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <errno.h>
29 #include <alsa/asoundlib.h>
30 #include <sys/time.h>
31
32 #define DEFAULTDEVICE "default"
33 #define MAX_FRAMES 32
34
35 static struct pollfd pfds[32];
36 static int num_fds;
37
38 static snd_pcm_t *pcm_handle = NULL;
39 static snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
40 static BOOL reopened;
41 static short samplewidth;
42 static int audiochannels;
43 static unsigned int rate;
44 static char *pcm_name;
45
46 void alsa_play(void);
47
48 void
49 alsa_add_fds(int *n, fd_set * rfds, fd_set * wfds, struct timeval *tv)
50 {
51 int err;
52 struct pollfd *f;
53
54 if (!pcm_handle)
55 return;
56
57 if (rdpsnd_queue_empty())
58 return;
59
60 num_fds = snd_pcm_poll_descriptors_count(pcm_handle);
61
62 if (num_fds > sizeof(pfds) / sizeof(*pfds))
63 return;
64
65 err = snd_pcm_poll_descriptors(pcm_handle, pfds, num_fds);
66 if (err < 0)
67 return;
68
69 for (f = pfds; f < &pfds[num_fds]; f++)
70 {
71 if (f->events & POLLIN)
72 FD_SET(f->fd, rfds);
73 if (f->events & POLLOUT)
74 FD_SET(f->fd, wfds);
75 if (f->fd > *n && (f->events & (POLLIN | POLLOUT)))
76 *n = f->fd;
77 }
78 }
79
80 void
81 alsa_check_fds(fd_set * rfds, fd_set * wfds)
82 {
83 struct pollfd *f;
84 int err;
85 unsigned short revents;
86
87 if (!pcm_handle)
88 return;
89
90 for (f = pfds; f < &pfds[num_fds]; f++)
91 {
92 f->revents = 0;
93 if (f->fd != -1)
94 {
95 /* Fixme: This doesn't properly deal with things like POLLHUP */
96 if (FD_ISSET(f->fd, rfds))
97 f->revents |= POLLIN;
98 if (FD_ISSET(f->fd, wfds))
99 f->revents |= POLLOUT;
100 }
101 }
102
103 err = snd_pcm_poll_descriptors_revents(pcm_handle, pfds, num_fds, &revents);
104 if (err < 0)
105 return;
106
107 if (revents & POLLOUT)
108 alsa_play();
109 }
110
111 BOOL
112 alsa_open(void)
113 {
114 int err;
115
116 if ((err = snd_pcm_open(&pcm_handle, pcm_name, stream, 0)) < 0)
117 {
118 error("snd_pcm_open: %s\n", snd_strerror(err));
119 return False;
120 }
121
122 reopened = True;
123
124 return True;
125 }
126
127 void
128 alsa_close(void)
129 {
130 /* Ack all remaining packets */
131 while (!rdpsnd_queue_empty())
132 rdpsnd_queue_next(0);
133
134 if (pcm_handle)
135 {
136 snd_pcm_close(pcm_handle);
137 pcm_handle = NULL;
138 }
139 }
140
141 BOOL
142 alsa_format_supported(WAVEFORMATEX * pwfx)
143 {
144 #if 0
145 int err;
146 snd_pcm_hw_params_t *hwparams = NULL;
147
148 if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0)
149 {
150 error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err));
151 return False;
152 }
153
154 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
155 {
156 error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err));
157 return False;
158 }
159 snd_pcm_hw_params_free(hwparams);
160 #endif
161
162 if (pwfx->wFormatTag != WAVE_FORMAT_PCM)
163 return False;
164 if ((pwfx->nChannels != 1) && (pwfx->nChannels != 2))
165 return False;
166 if ((pwfx->wBitsPerSample != 8) && (pwfx->wBitsPerSample != 16))
167 return False;
168 if ((pwfx->nSamplesPerSec != 44100) && (pwfx->nSamplesPerSec != 22050))
169 return False;
170
171 return True;
172 }
173
174 BOOL
175 alsa_set_format(WAVEFORMATEX * pwfx)
176 {
177 snd_pcm_hw_params_t *hwparams = NULL;
178 int err;
179 unsigned int buffertime;
180
181 samplewidth = pwfx->wBitsPerSample / 8;
182
183 if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0)
184 {
185 error("snd_pcm_hw_params_malloc: %s\n", snd_strerror(err));
186 return False;
187 }
188
189 if ((err = snd_pcm_hw_params_any(pcm_handle, hwparams)) < 0)
190 {
191 error("snd_pcm_hw_params_any: %s\n", snd_strerror(err));
192 return False;
193 }
194
195 if ((err =
196 snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
197 {
198 error("snd_pcm_hw_params_set_access: %s\n", snd_strerror(err));
199 return False;
200 }
201
202 if (pwfx->wBitsPerSample == 16)
203 {
204 if ((err =
205 snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
206 {
207 error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err));
208 return False;
209 }
210 }
211 else
212 {
213 if ((err =
214 snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S8)) < 0)
215 {
216 error("snd_pcm_hw_params_set_format: %s\n", snd_strerror(err));
217 return False;
218 }
219 }
220
221 #if 0
222 if ((err = snd_pcm_hw_params_set_rate_resample(pcm_handle, hwparams, 1)) < 0)
223 {
224 error("snd_pcm_hw_params_set_rate_resample: %s\n", snd_strerror(err));
225 return False;
226 }
227 #endif
228
229 rate = pwfx->nSamplesPerSec;
230 if ((err = snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &rate, 0)) < 0)
231 {
232 error("snd_pcm_hw_params_set_rate_near: %s\n", snd_strerror(err));
233 return False;
234 }
235
236 audiochannels = pwfx->nChannels;
237 if ((err = snd_pcm_hw_params_set_channels(pcm_handle, hwparams, pwfx->nChannels)) < 0)
238 {
239 error("snd_pcm_hw_params_set_channels: %s\n", snd_strerror(err));
240 return False;
241 }
242
243
244 buffertime = 500000; /* microseconds */
245 if ((err =
246 snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams, &buffertime, 0)) < 0)
247 {
248 error("snd_pcm_hw_params_set_buffer_time_near: %s\n", snd_strerror(err));
249 return False;
250 }
251
252 if ((err = snd_pcm_hw_params(pcm_handle, hwparams)) < 0)
253 {
254 error("snd_pcm_hw_params: %s\n", snd_strerror(err));
255 return False;
256 }
257
258 snd_pcm_hw_params_free(hwparams);
259
260 if ((err = snd_pcm_prepare(pcm_handle)) < 0)
261 {
262 error("snd_pcm_prepare: %s\n", snd_strerror(err));
263 return False;
264 }
265
266 reopened = True;
267
268 return True;
269 }
270
271 void
272 alsa_play(void)
273 {
274 struct audio_packet *packet;
275 STREAM out;
276 int len;
277 static long prev_s, prev_us;
278 unsigned int duration;
279 struct timeval tv;
280 int next_tick;
281
282 if (reopened)
283 {
284 reopened = False;
285 gettimeofday(&tv, NULL);
286 prev_s = tv.tv_sec;
287 prev_us = tv.tv_usec;
288 }
289
290 /* We shouldn't be called if the queue is empty, but still */
291 if (rdpsnd_queue_empty())
292 return;
293
294 packet = rdpsnd_queue_current_packet();
295 out = &packet->s;
296
297 next_tick = rdpsnd_queue_next_tick();
298
299 len = (out->end - out->p) / (samplewidth * audiochannels);
300 if ((len = snd_pcm_writei(pcm_handle, out->p, ((MAX_FRAMES < len) ? MAX_FRAMES : len))) < 0)
301 {
302 snd_pcm_prepare(pcm_handle);
303 len = 0;
304 }
305 out->p += (len * samplewidth * audiochannels);
306
307 gettimeofday(&tv, NULL);
308
309 duration = ((tv.tv_sec - prev_s) * 1000000 + (tv.tv_usec - prev_us)) / 1000;
310
311 if (packet->tick > next_tick)
312 next_tick += 65536;
313
314 if ((out->p == out->end) || duration > next_tick - packet->tick + 500)
315 {
316 snd_pcm_sframes_t delay_frames;
317 unsigned long delay_us;
318
319 prev_s = tv.tv_sec;
320 prev_us = tv.tv_usec;
321
322 if (abs((next_tick - packet->tick) - duration) > 20)
323 {
324 DEBUG(("duration: %d, calc: %d, ", duration, next_tick - packet->tick));
325 DEBUG(("last: %d, is: %d, should: %d\n", packet->tick,
326 (packet->tick + duration) % 65536, next_tick % 65536));
327 }
328
329 if (snd_pcm_delay(pcm_handle, &delay_frames) < 0)
330 delay_frames = out->size / (samplewidth * audiochannels);
331 if (delay_frames < 0)
332 delay_frames = 0;
333
334 delay_us = delay_frames * (1000000 / rate);
335
336 rdpsnd_queue_next(delay_us);
337 }
338 }
339
340 static struct audio_driver alsa_driver = {
341 .name = "alsa",
342 .description = "ALSA output driver, default device: " DEFAULTDEVICE,
343
344 .add_fds = alsa_add_fds,
345 .check_fds = alsa_check_fds,
346
347 .wave_out_open = alsa_open,
348 .wave_out_close = alsa_close,
349 .wave_out_format_supported = alsa_format_supported,
350 .wave_out_set_format = alsa_set_format,
351 .wave_out_volume = rdpsnd_dsp_softvol_set,
352
353 .need_byteswap_on_be = 0,
354 .need_resampling = 0,
355 };
356
357 struct audio_driver *
358 alsa_register(char *options)
359 {
360 if (options)
361 {
362 pcm_name = xstrdup(options);
363 }
364 else
365 {
366 pcm_name = xstrdup(DEFAULTDEVICE);
367 }
368
369 return &alsa_driver;
370 }

  ViewVC Help
Powered by ViewVC 1.1.26