0
|
1 /* XMMS - ALSA output plugin
|
|
2 * Copyright (C) 2001-2003 Matthieu Sozeau <mattam@altern.org>
|
|
3 * Copyright (C) 1998-2003 Peter Alm, Mikael Alm, Olle Hallnas,
|
|
4 * Thomas Nilsson and 4Front Technologies
|
|
5 * Copyright (C) 1999-2004 Haavard Kvaalen
|
|
6 *
|
|
7 * This program is free software; you can redistribute it and/or modify
|
|
8 * it under the terms of the GNU General Public License as published by
|
|
9 * the Free Software Foundation; either version 2 of the License, or
|
|
10 * (at your option) any later version.
|
|
11 *
|
|
12 * This program is distributed in the hope that it will be useful,
|
|
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
15 * GNU General Public License for more details.
|
|
16 *
|
|
17 * You should have received a copy of the GNU General Public License
|
|
18 * along with this program; if not, write to the Free Software
|
|
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
20 *
|
|
21 *
|
|
22 * CHANGES
|
|
23 *
|
|
24 * 2005.01.05 Takashi Iwai <tiwai@suse.de>
|
|
25 * Impelemented the multi-threaded mode with an audio-thread.
|
|
26 * Many fixes and cleanups.
|
|
27 */
|
|
28
|
|
29 #include "alsa.h"
|
|
30 #include <ctype.h>
|
|
31 #include <glib.h>
|
|
32 #include <libaudacious/xconvert.h>
|
|
33
|
|
34 static snd_pcm_t *alsa_pcm = NULL;
|
|
35
|
|
36 static snd_output_t *logs = NULL;
|
|
37
|
|
38 static guint64 alsa_total_written = 0; /* input bytes */
|
|
39 static guint64 alsa_hw_written = 0; /* output bytes */
|
|
40 static gint output_time_offset = 0;
|
|
41
|
|
42 /* device buffer/period sizes in bytes */
|
|
43 static int hw_buffer_size, hw_period_size; /* in output bytes */
|
|
44 static int hw_buffer_size_in, hw_period_size_in; /* in input bytes */
|
|
45
|
|
46 /* Set/Get volume */
|
|
47 static snd_mixer_elem_t *pcm_element = NULL;
|
|
48 static snd_mixer_t *mixer = NULL;
|
|
49
|
|
50 static gboolean mmap, going = FALSE, paused, multi_thread, mixer_start = TRUE;;
|
|
51
|
|
52 static gboolean alsa_can_pause;
|
|
53
|
|
54 static guint mixer_timeout;
|
|
55
|
|
56 /* for audio thread */
|
|
57 static GThread *audio_thread; /* audio loop thread */
|
|
58 static int thread_buffer_size; /* size of intermediate buffer in bytes */
|
|
59 static char *thread_buffer; /* audio intermediate buffer */
|
|
60 static int rd_index, wr_index; /* current read/write position in int-buffer */
|
|
61 static gboolean pause_request; /* pause status currently requested */
|
|
62 static gint flush_request; /* flush status (time) currently requested */
|
|
63
|
|
64 struct snd_format {
|
|
65 unsigned int rate;
|
|
66 unsigned int channels;
|
|
67 snd_pcm_format_t format;
|
|
68 AFormat xmms_format;
|
|
69 int sample_bits;
|
|
70 int bps;
|
|
71 };
|
|
72
|
|
73 static struct snd_format *inputf = NULL;
|
|
74 static struct snd_format *effectf = NULL;
|
|
75 static struct snd_format *outputf = NULL;
|
|
76
|
|
77 static int alsa_setup(struct snd_format *f);
|
|
78 static void alsa_mmap_audio(char *data, int length);
|
|
79 static void alsa_write_audio(char *data, int length);
|
|
80
|
|
81 static struct snd_format *snd_format_from_xmms(AFormat fmt, int rate,
|
|
82 int channels);
|
|
83
|
|
84 static struct xmms_convert_buffers *convertb;
|
|
85
|
|
86 static convert_func_t alsa_convert_func;
|
|
87 static convert_channel_func_t alsa_stereo_convert_func;
|
|
88 static convert_freq_func_t alsa_frequency_convert_func;
|
|
89
|
|
90 static const struct {
|
|
91 AFormat xmms;
|
|
92 snd_pcm_format_t alsa;
|
|
93 } format_table[] = {
|
|
94 {FMT_S16_LE, SND_PCM_FORMAT_S16_LE},
|
|
95 {FMT_S16_BE, SND_PCM_FORMAT_S16_BE},
|
|
96 {FMT_S16_NE, SND_PCM_FORMAT_S16},
|
|
97 {FMT_U16_LE, SND_PCM_FORMAT_U16_LE},
|
|
98 {FMT_U16_BE, SND_PCM_FORMAT_U16_BE},
|
|
99 {FMT_U16_NE, SND_PCM_FORMAT_U16},
|
|
100 {FMT_U8, SND_PCM_FORMAT_U8},
|
|
101 {FMT_S8, SND_PCM_FORMAT_S8},
|
|
102 };
|
|
103
|
|
104
|
|
105 static void
|
|
106 debug(char *str, ...)
|
|
107 G_GNUC_PRINTF(1, 2);
|
|
108
|
|
109 static void debug(char *str, ...)
|
|
110 {
|
|
111 va_list args;
|
|
112
|
|
113 if (alsa_cfg.debug) {
|
|
114 va_start(args, str);
|
|
115 g_logv(NULL, G_LOG_LEVEL_MESSAGE, str, args);
|
|
116 va_end(args);
|
|
117 }
|
|
118 }
|
|
119
|
|
120 /*
|
|
121 * mixer stuff
|
|
122 */
|
|
123 static void
|
|
124 parse_mixer_name(char *str, char **name, int *index)
|
|
125 {
|
|
126 char *end;
|
|
127
|
|
128 while (isspace(*str))
|
|
129 str++;
|
|
130
|
|
131 if ((end = strchr(str, ',')) != NULL) {
|
|
132 *name = g_strndup(str, end - str);
|
|
133 end++;
|
|
134 *index = atoi(end);
|
|
135 }
|
|
136 else {
|
|
137 *name = g_strdup(str);
|
|
138 *index = 0;
|
|
139 }
|
|
140 }
|
|
141
|
|
142 int
|
|
143 alsa_get_mixer(snd_mixer_t ** mixer, int card)
|
|
144 {
|
|
145 char *dev;
|
|
146 int err;
|
|
147
|
|
148 debug("alsa_get_mixer");
|
|
149
|
|
150 dev = g_strdup_printf("hw:%i", card);
|
|
151
|
|
152 if ((err = snd_mixer_open(mixer, 0)) < 0) {
|
|
153 g_warning("alsa_get_mixer(): Failed to open empty mixer: %s",
|
|
154 snd_strerror(-err));
|
|
155 mixer = NULL;
|
|
156 return -1;
|
|
157 }
|
|
158 if ((err = snd_mixer_attach(*mixer, dev)) < 0) {
|
|
159 g_warning("alsa_get_mixer(): Attaching to mixer %s failed: %s",
|
|
160 dev, snd_strerror(-err));
|
|
161 return -1;
|
|
162 }
|
|
163 if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0) {
|
|
164 g_warning("alsa_get_mixer(): Failed to register mixer: %s",
|
|
165 snd_strerror(-err));
|
|
166 return -1;
|
|
167 }
|
|
168 if ((err = snd_mixer_load(*mixer)) < 0) {
|
|
169 g_warning("alsa_get_mixer(): Failed to load mixer: %s",
|
|
170 snd_strerror(-err));
|
|
171 return -1;
|
|
172 }
|
|
173
|
|
174 g_free(dev);
|
|
175
|
|
176 return (*mixer != NULL);
|
|
177 }
|
|
178
|
|
179
|
|
180 static snd_mixer_elem_t* alsa_get_mixer_elem(snd_mixer_t *mixer, char *name, int index)
|
|
181 {
|
|
182 snd_mixer_selem_id_t *selem_id;
|
|
183 snd_mixer_elem_t *elem;
|
|
184 snd_mixer_selem_id_alloca(&selem_id);
|
|
185
|
|
186 if (index != -1)
|
|
187 snd_mixer_selem_id_set_index(selem_id, index);
|
|
188 if (name != NULL)
|
|
189 snd_mixer_selem_id_set_name(selem_id, name);
|
|
190
|
|
191 elem = snd_mixer_find_selem(mixer, selem_id);
|
|
192
|
|
193 return elem;
|
|
194 }
|
|
195
|
|
196 static int
|
|
197 alsa_setup_mixer(void)
|
|
198 {
|
|
199 char *name;
|
|
200 long int a, b;
|
|
201 long alsa_min_vol, alsa_max_vol;
|
|
202 int err, index;
|
|
203
|
|
204 debug("alsa_setup_mixer");
|
|
205
|
|
206 if ((err = alsa_get_mixer(&mixer, alsa_cfg.mixer_card)) < 0)
|
|
207 return err;
|
|
208
|
|
209 parse_mixer_name(alsa_cfg.mixer_device, &name, &index);
|
|
210
|
|
211 pcm_element = alsa_get_mixer_elem(mixer, name, index);
|
|
212
|
|
213 g_free(name);
|
|
214
|
|
215 if (!pcm_element) {
|
|
216 g_warning("alsa_setup_mixer(): Failed to find mixer element: %s",
|
|
217 alsa_cfg.mixer_device);
|
|
218 return -1;
|
|
219 }
|
|
220
|
|
221 /*
|
|
222 * Work around a bug in alsa-lib up to 1.0.0rc2 where the
|
|
223 * new range don't take effect until the volume is changed.
|
|
224 * This hack should be removed once we depend on Alsa 1.0.0.
|
|
225 */
|
|
226 snd_mixer_selem_get_playback_volume(pcm_element,
|
|
227 SND_MIXER_SCHN_FRONT_LEFT, &a);
|
|
228 snd_mixer_selem_get_playback_volume(pcm_element,
|
|
229 SND_MIXER_SCHN_FRONT_RIGHT, &b);
|
|
230
|
|
231 snd_mixer_selem_get_playback_volume_range(pcm_element,
|
|
232 &alsa_min_vol, &alsa_max_vol);
|
|
233 snd_mixer_selem_set_playback_volume_range(pcm_element, 0, 100);
|
|
234
|
|
235 if (alsa_max_vol == 0) {
|
|
236 pcm_element = NULL;
|
|
237 return -1;
|
|
238 }
|
|
239
|
|
240 if (!alsa_cfg.soft_volume)
|
|
241 alsa_set_volume(a * 100 / alsa_max_vol, b * 100 / alsa_max_vol);
|
|
242
|
|
243 debug("alsa_setup_mixer: end");
|
|
244
|
|
245 return 0;
|
|
246 }
|
|
247
|
|
248 static int
|
|
249 alsa_mixer_timeout(void *data)
|
|
250 {
|
|
251 if (mixer) {
|
|
252 snd_mixer_close(mixer);
|
|
253 mixer = NULL;
|
|
254 pcm_element = NULL;
|
|
255 }
|
|
256 mixer_timeout = 0;
|
|
257 mixer_start = TRUE;
|
|
258
|
|
259 g_message("alsa mixer timed out");
|
|
260 return FALSE;
|
|
261 }
|
|
262
|
|
263
|
|
264 static void alsa_cleanup_mixer(void)
|
|
265 {
|
|
266 pcm_element = NULL;
|
|
267 if (mixer) {
|
|
268 snd_mixer_close(mixer);
|
|
269 mixer = NULL;
|
|
270 }
|
|
271 }
|
|
272
|
|
273
|
|
274 void
|
|
275 alsa_get_volume(int *l, int *r)
|
|
276 {
|
|
277 long ll = *l, lr = *r;
|
|
278
|
|
279 if (mixer_start) {
|
|
280 alsa_setup_mixer();
|
|
281 mixer_start = FALSE;
|
|
282 }
|
|
283
|
|
284 if (!pcm_element)
|
|
285 return;
|
|
286
|
|
287 snd_mixer_handle_events(mixer);
|
|
288
|
|
289 if (alsa_cfg.soft_volume) {
|
|
290 *l = alsa_cfg.vol.left;
|
|
291 *r = alsa_cfg.vol.right;
|
|
292 }
|
|
293 else {
|
|
294 snd_mixer_selem_get_playback_volume(pcm_element,
|
|
295 SND_MIXER_SCHN_FRONT_LEFT, &ll);
|
|
296 snd_mixer_selem_get_playback_volume(pcm_element,
|
|
297 SND_MIXER_SCHN_FRONT_RIGHT, &lr);
|
|
298 *l = ll;
|
|
299 *r = lr;
|
|
300 }
|
|
301 if (mixer_timeout)
|
|
302 gtk_timeout_remove(mixer_timeout);
|
|
303 mixer_timeout = gtk_timeout_add(5000, alsa_mixer_timeout, NULL);
|
|
304 }
|
|
305
|
|
306
|
|
307 void
|
|
308 alsa_set_volume(int l, int r)
|
|
309 {
|
|
310 if (!pcm_element)
|
|
311 return;
|
|
312
|
|
313 if (alsa_cfg.soft_volume) {
|
|
314 alsa_cfg.vol.left = l;
|
|
315 alsa_cfg.vol.right = r;
|
|
316 }
|
|
317 else {
|
|
318 snd_mixer_selem_set_playback_volume(pcm_element,
|
|
319 SND_MIXER_SCHN_FRONT_LEFT, l);
|
|
320 snd_mixer_selem_set_playback_volume(pcm_element,
|
|
321 SND_MIXER_SCHN_FRONT_RIGHT, r);
|
|
322 }
|
|
323 }
|
|
324
|
|
325 /*
|
|
326 * audio stuff
|
|
327 */
|
|
328
|
|
329 int alsa_playing(void)
|
|
330 {
|
|
331 if (!going || paused || alsa_pcm == NULL)
|
|
332 return FALSE;
|
|
333
|
|
334 return(snd_pcm_state(alsa_pcm) == SND_PCM_STATE_RUNNING);
|
|
335 }
|
|
336
|
|
337
|
|
338 /* handle generic errors */
|
|
339 static int alsa_handle_error(int err)
|
|
340 {
|
|
341 switch (err) {
|
|
342 case -EPIPE: /* XRUN */
|
|
343 if (alsa_cfg.debug) {
|
|
344 snd_pcm_status_t *alsa_status;
|
|
345 snd_pcm_status_alloca(&alsa_status);
|
|
346 if (snd_pcm_status(alsa_pcm, alsa_status) < 0)
|
|
347 g_warning("xrun_recover(): snd_pcm_status() failed");
|
|
348 else {
|
|
349 printf("Status:\n");
|
|
350 snd_pcm_status_dump(alsa_status, logs);
|
|
351 }
|
|
352 }
|
|
353 return snd_pcm_prepare(alsa_pcm);
|
|
354
|
|
355 case -ESTRPIPE: /* suspend */
|
|
356 while ((err = snd_pcm_resume(alsa_pcm)) == -EAGAIN)
|
|
357 sleep(1); /* wait until suspend flag is released */
|
|
358 if (err < 0) {
|
|
359 g_warning("suspend_recover(): snd_pcm_resume() failed.");
|
|
360 return snd_pcm_prepare(alsa_pcm);
|
|
361 }
|
|
362 break;
|
|
363 }
|
|
364
|
|
365 return err;
|
|
366 }
|
|
367
|
|
368 /* update and get the available space on h/w buffer (in frames) */
|
|
369 static snd_pcm_sframes_t alsa_get_avail(void)
|
|
370 {
|
|
371 snd_pcm_sframes_t ret;
|
|
372
|
|
373 if (alsa_pcm == NULL)
|
|
374 return 0;
|
|
375
|
|
376 while ((ret = snd_pcm_avail_update(alsa_pcm)) < 0) {
|
|
377 ret = alsa_handle_error(ret);
|
|
378 if (ret < 0) {
|
|
379 g_warning("alsa_get_avail(): snd_pcm_avail_update() failed: %s",
|
|
380 snd_strerror(-ret));
|
|
381 return 0;
|
|
382 }
|
|
383 }
|
|
384 return ret;
|
|
385 }
|
|
386
|
|
387 /* do pause operation */
|
|
388 static void alsa_do_pause(gboolean p)
|
|
389 {
|
|
390 if (paused == p)
|
|
391 return;
|
|
392
|
|
393 if (alsa_pcm) {
|
|
394 if (alsa_can_pause) {
|
|
395 snd_pcm_pause(alsa_pcm, p);
|
|
396 } else if (p) {
|
|
397 snd_pcm_drop(alsa_pcm);
|
|
398 snd_pcm_prepare(alsa_pcm);
|
|
399 }
|
|
400 }
|
|
401 paused = p;
|
|
402 }
|
|
403
|
|
404 void alsa_pause(short p)
|
|
405 {
|
|
406 debug("alsa_pause");
|
|
407 if (multi_thread)
|
|
408 pause_request = p;
|
|
409 else
|
|
410 alsa_do_pause(p);
|
|
411 }
|
|
412
|
|
413 /* close PCM and release associated resources */
|
|
414 static void alsa_close_pcm(void)
|
|
415 {
|
|
416 if (alsa_pcm) {
|
|
417 int err;
|
|
418 snd_pcm_drop(alsa_pcm);
|
|
419 if ((err = snd_pcm_close(alsa_pcm)) < 0)
|
|
420 g_warning("alsa_pcm_close() failed: %s",
|
|
421 snd_strerror(-err));
|
|
422 alsa_pcm = NULL;
|
|
423 }
|
|
424 }
|
|
425
|
|
426 /* reopen ALSA PCM */
|
|
427 static int alsa_reopen(struct snd_format *f)
|
|
428 {
|
|
429 /* remember the current position */
|
|
430 output_time_offset += (alsa_hw_written * 1000) / outputf->bps;
|
|
431 alsa_hw_written = 0;
|
|
432
|
|
433 alsa_close_pcm();
|
|
434
|
|
435 return alsa_setup(f);
|
|
436 }
|
|
437
|
|
438 /* do flush (drop) operation */
|
|
439 static void alsa_do_flush(int time)
|
|
440 {
|
|
441 if (alsa_pcm) {
|
|
442 snd_pcm_drop(alsa_pcm);
|
|
443 snd_pcm_prepare(alsa_pcm);
|
|
444 }
|
|
445 /* correct the offset */
|
|
446 output_time_offset = time;
|
|
447 alsa_total_written = (guint64) time * inputf->bps / 1000;
|
|
448 rd_index = wr_index = alsa_hw_written = 0;
|
|
449 }
|
|
450
|
|
451 void alsa_flush(int time)
|
|
452 {
|
|
453 if (multi_thread) {
|
|
454 flush_request = time;
|
|
455 while (flush_request != -1)
|
|
456 xmms_usleep(10000);
|
|
457 } else
|
|
458 alsa_do_flush(time);
|
|
459 }
|
|
460
|
|
461 void alsa_close(void)
|
|
462 {
|
|
463 if (! going)
|
|
464 return;
|
|
465
|
|
466 debug("Closing device");
|
|
467
|
|
468 going = 0;
|
|
469
|
|
470 if (multi_thread)
|
|
471 g_thread_join(audio_thread);
|
|
472 else
|
|
473 alsa_close_pcm();
|
|
474
|
|
475 alsa_cleanup_mixer();
|
|
476
|
|
477 xmms_convert_buffers_destroy(convertb);
|
|
478 convertb = NULL;
|
|
479 g_free(inputf);
|
|
480 inputf = NULL;
|
|
481 g_free(effectf);
|
|
482 effectf = NULL;
|
|
483 g_free(outputf);
|
|
484 outputf = NULL;
|
|
485
|
|
486 alsa_save_config();
|
|
487
|
|
488 if (alsa_cfg.debug)
|
|
489 snd_output_close(logs);
|
|
490 debug("Device closed");
|
|
491 }
|
|
492
|
|
493 /* return the size of audio data filled in the audio thread buffer */
|
|
494 static int get_thread_buffer_filled(void)
|
|
495 {
|
|
496 if (wr_index >= rd_index)
|
|
497 return wr_index - rd_index;
|
|
498 return thread_buffer_size - (rd_index - wr_index);
|
|
499 }
|
|
500
|
|
501 /* get the free space on buffer */
|
|
502 int alsa_free(void)
|
|
503 {
|
|
504 int result = 0;
|
|
505 if (multi_thread)
|
|
506 result = thread_buffer_size - get_thread_buffer_filled() - 1;
|
|
507 else if (! paused && alsa_pcm)
|
|
508 result = snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail());
|
|
509 return result;
|
|
510 }
|
|
511
|
|
512
|
|
513 int
|
|
514 alsa_get_output_time(void)
|
|
515 {
|
|
516 snd_pcm_sframes_t delay;
|
|
517 guint64 bytes = 0;
|
|
518
|
|
519 if (!going || alsa_pcm == NULL)
|
|
520 return 0;
|
|
521
|
|
522 if (!snd_pcm_delay(alsa_pcm, &delay)) {
|
|
523 bytes = snd_pcm_frames_to_bytes(alsa_pcm, delay);
|
|
524 if (alsa_hw_written < bytes)
|
|
525 bytes = 0;
|
|
526 else
|
|
527 bytes = alsa_hw_written - bytes;
|
|
528 }
|
|
529 return output_time_offset + (bytes * 1000) / outputf->bps;
|
|
530 }
|
|
531
|
|
532 int
|
|
533 alsa_get_written_time(void)
|
|
534 {
|
|
535 if (!going)
|
|
536 return 0;
|
|
537 return (alsa_total_written * 1000) / inputf->bps;
|
|
538 }
|
|
539
|
|
540 #define STEREO_ADJUST(type, type2, endian) \
|
|
541 do { \
|
|
542 type *ptr = data; \
|
|
543 for (i = 0; i < length; i += 4) \
|
|
544 { \
|
|
545 *ptr = type2##_TO_##endian(type2##_FROM_## endian(*ptr) * \
|
|
546 alsa_cfg.vol.left / 100); \
|
|
547 ptr++; \
|
|
548 *ptr = type2##_TO_##endian(type2##_FROM_##endian(*ptr) * \
|
|
549 alsa_cfg.vol.right / 100); \
|
|
550 ptr++; \
|
|
551 } \
|
|
552 } while (0)
|
|
553
|
|
554 #define MONO_ADJUST(type, type2, endian) \
|
|
555 do { \
|
|
556 type *ptr = data; \
|
|
557 for (i = 0; i < length; i += 4) \
|
|
558 { \
|
|
559 *ptr = type2##_TO_##endian(type2##_FROM_## endian(*ptr) * \
|
|
560 vol / 100); \
|
|
561 ptr++; \
|
|
562 } \
|
|
563 } while (0)
|
|
564
|
|
565 #define VOLUME_ADJUST(type, type2, endian) \
|
|
566 do { \
|
|
567 if (channels == 2) \
|
|
568 STEREO_ADJUST(type, type2, endian); \
|
|
569 else \
|
|
570 MONO_ADJUST(type, type2, endian); \
|
|
571 } while (0)
|
|
572
|
|
573 #define STEREO_ADJUST8(type) \
|
|
574 do { \
|
|
575 type *ptr = data; \
|
|
576 for (i = 0; i < length; i += 2) \
|
|
577 { \
|
|
578 *ptr = *ptr * alsa_cfg.vol.left / 100; \
|
|
579 ptr++; \
|
|
580 *ptr = *ptr * alsa_cfg.vol.right / 100; \
|
|
581 ptr++; \
|
|
582 } \
|
|
583 } while (0)
|
|
584
|
|
585 #define MONO_ADJUST8(type) \
|
|
586 do { \
|
|
587 type *ptr = data; \
|
|
588 for (i = 0; i < length; i += 4) \
|
|
589 { \
|
|
590 *ptr = *ptr * vol / 100; \
|
|
591 ptr++; \
|
|
592 } \
|
|
593 } while (0)
|
|
594
|
|
595 #define VOLUME_ADJUST8(type) \
|
|
596 do { \
|
|
597 if (channels == 2) \
|
|
598 STEREO_ADJUST8(type); \
|
|
599 else \
|
|
600 MONO_ADJUST8(type); \
|
|
601 } while (0)
|
|
602
|
|
603
|
|
604 static void
|
|
605 volume_adjust(void *data, int length, AFormat fmt, int channels)
|
|
606 {
|
|
607 int i, vol;
|
|
608
|
|
609 if ((alsa_cfg.vol.left == 100 && alsa_cfg.vol.right == 100) ||
|
|
610 (channels == 1 &&
|
|
611 (alsa_cfg.vol.left == 100 || alsa_cfg.vol.right == 100)))
|
|
612 return;
|
|
613
|
|
614 vol = MAX(alsa_cfg.vol.left, alsa_cfg.vol.right);
|
|
615
|
|
616 switch (fmt) {
|
|
617 case FMT_S16_LE:
|
|
618 VOLUME_ADJUST(gint16, GINT16, LE);
|
|
619 break;
|
|
620 case FMT_U16_LE:
|
|
621 VOLUME_ADJUST(guint16, GUINT16, LE);
|
|
622 break;
|
|
623 case FMT_S16_BE:
|
|
624 VOLUME_ADJUST(gint16, GINT16, BE);
|
|
625 break;
|
|
626 case FMT_U16_BE:
|
|
627 VOLUME_ADJUST(guint16, GUINT16, BE);
|
|
628 break;
|
|
629 case FMT_S8:
|
|
630 VOLUME_ADJUST8(gint8);
|
|
631 break;
|
|
632 case FMT_U8:
|
|
633 VOLUME_ADJUST8(guint8);
|
|
634 break;
|
|
635 default:
|
|
636 g_warning("volume_adjust(): unhandled format: %d", fmt);
|
|
637 break;
|
|
638 }
|
|
639 }
|
|
640
|
|
641
|
|
642 /* transfer data to audio h/w; length is given in bytes
|
|
643 *
|
|
644 * data can be modified via effect plugin, rate conversion or
|
|
645 * software volume before passed to audio h/w
|
|
646 */
|
|
647 static void alsa_do_write(gpointer data, int length)
|
|
648 {
|
|
649 EffectPlugin *ep = NULL;
|
|
650 int new_freq;
|
|
651 int new_chn;
|
|
652 AFormat f;
|
|
653
|
|
654 if (paused)
|
|
655 return;
|
|
656
|
|
657 new_freq = inputf->rate;
|
|
658 new_chn = inputf->channels;
|
|
659 f = inputf->xmms_format;
|
|
660
|
|
661 if (effects_enabled() && (ep = get_current_effect_plugin()) &&
|
|
662 ep->query_format)
|
|
663 ep->query_format(&f, &new_freq, &new_chn);
|
|
664
|
|
665 if (f != effectf->xmms_format || new_freq != effectf->rate ||
|
|
666 new_chn != effectf->channels) {
|
|
667 debug("Changing audio format for effect plugin");
|
|
668 g_free(effectf);
|
|
669 effectf = snd_format_from_xmms(f, new_freq, new_chn);
|
|
670 if (alsa_reopen(effectf) < 0) {
|
|
671 /* fatal error... */
|
|
672 alsa_close();
|
|
673 return;
|
|
674 }
|
|
675 }
|
|
676
|
|
677 if (ep) {
|
|
678 length = ep->mod_samples(&data, length,
|
|
679 inputf->xmms_format,
|
|
680 inputf->rate, inputf->channels);
|
|
681 }
|
|
682
|
|
683 if (alsa_convert_func != NULL)
|
|
684 length = alsa_convert_func(convertb, &data, length);
|
|
685 if (alsa_stereo_convert_func != NULL)
|
|
686 length = alsa_stereo_convert_func(convertb, &data, length);
|
|
687 if (alsa_frequency_convert_func != NULL)
|
|
688 length = alsa_frequency_convert_func(convertb, &data, length,
|
|
689 effectf->rate, outputf->rate);
|
|
690
|
|
691 if (alsa_cfg.soft_volume)
|
|
692 volume_adjust(data, length, outputf->xmms_format, outputf->channels);
|
|
693
|
|
694 if (mmap)
|
|
695 alsa_mmap_audio(data, length);
|
|
696 else
|
|
697 alsa_write_audio(data, length);
|
|
698 }
|
|
699
|
|
700 /* write callback */
|
|
701 void alsa_write(gpointer data, int length)
|
|
702 {
|
|
703 if (multi_thread) {
|
|
704 int cnt;
|
|
705 char *src = (char *)data;
|
|
706
|
|
707 alsa_total_written += length;
|
|
708 while (length > 0) {
|
|
709 int wr;
|
|
710 cnt = MIN(length, thread_buffer_size - wr_index);
|
|
711 memcpy(thread_buffer + wr_index, src, cnt);
|
|
712 wr = (wr_index + cnt) % thread_buffer_size;
|
|
713 wr_index = wr;
|
|
714 length -= cnt;
|
|
715 src += cnt;
|
|
716 }
|
|
717 } else {
|
|
718 alsa_do_write(data, length);
|
|
719 alsa_total_written += length;
|
|
720 }
|
|
721 }
|
|
722
|
|
723 /* transfer data to audio h/w via normal write */
|
|
724 static void alsa_write_audio(char *data, int length)
|
|
725 {
|
|
726 snd_pcm_sframes_t written_frames;
|
|
727
|
|
728 while (length > 0) {
|
|
729 int frames = snd_pcm_bytes_to_frames(alsa_pcm, length);
|
|
730 written_frames = snd_pcm_writei(alsa_pcm, data, frames);
|
|
731
|
|
732 if (written_frames > 0) {
|
|
733 int written = snd_pcm_frames_to_bytes(alsa_pcm,
|
|
734 written_frames);
|
|
735 length -= written;
|
|
736 data += written;
|
|
737 alsa_hw_written += written;
|
|
738 }
|
|
739 else {
|
|
740 int err = alsa_handle_error((int)written_frames);
|
|
741 if (err < 0) {
|
|
742 g_warning("alsa_write_audio(): write error: %s",
|
|
743 snd_strerror(-err));
|
|
744 break;
|
|
745 }
|
|
746 }
|
|
747 }
|
|
748 }
|
|
749
|
|
750 /* transfer data to audio h/w via mmap
|
|
751 *
|
|
752 * basically, it makes sense only in the single thread mode.
|
|
753 * also, don't expect too much efficiency over mmap...
|
|
754 */
|
|
755 static void
|
|
756 alsa_mmap_audio(char *data, int length)
|
|
757 {
|
|
758 int cnt, err;
|
|
759 snd_pcm_uframes_t offset, frames;
|
|
760 const snd_pcm_channel_area_t *chan_areas;
|
|
761 snd_pcm_channel_area_t src_area;
|
|
762 int ch, channels, sample_bits;
|
|
763
|
|
764 if (snd_pcm_state(alsa_pcm) == SND_PCM_STATE_XRUN)
|
|
765 alsa_handle_error(-EPIPE);
|
|
766
|
|
767 /* need to call this before snd_pcm_mmap_begin() */
|
|
768 alsa_get_avail();
|
|
769
|
|
770 channels = outputf->channels;
|
|
771 sample_bits = outputf->sample_bits;
|
|
772 while (length > 0) {
|
|
773 frames = snd_pcm_bytes_to_frames(alsa_pcm, length);
|
|
774 if ((err = snd_pcm_mmap_begin(alsa_pcm, &chan_areas, &offset, &frames) < 0)) {
|
|
775 g_warning("alsa_mmap_audio(): snd_pcm_mmap_begin() " "failed: %s",
|
|
776 snd_strerror(-err));
|
|
777 break;
|
|
778 }
|
|
779
|
|
780 cnt = snd_pcm_frames_to_bytes(alsa_pcm, frames);
|
|
781
|
|
782 src_area.addr = data;
|
|
783 src_area.first = 0;
|
|
784 src_area.step = channels * sample_bits;
|
|
785 for (ch = 0; ch < channels; ch++) {
|
|
786 snd_pcm_area_copy(&chan_areas[ch], offset,
|
|
787 &src_area, 0, frames, outputf->format);
|
|
788 src_area.first += sample_bits;
|
|
789 }
|
|
790
|
|
791 err = snd_pcm_mmap_commit(alsa_pcm, offset, frames);
|
|
792 if (err < 0) {
|
|
793 err = alsa_handle_error(err);
|
|
794 if (err < 0)
|
|
795 g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit() "
|
|
796 "failed: %s", snd_strerror(-err));
|
|
797 }
|
|
798 else {
|
|
799 if (err != frames)
|
|
800 g_warning("alsa_mmap_audio(): snd_pcm_mmap_commit "
|
|
801 "returned %d, expected %d", err, (int)frames);
|
|
802 data += cnt;
|
|
803 length -= cnt;
|
|
804 alsa_hw_written += cnt;
|
|
805 }
|
|
806 }
|
|
807
|
|
808 /* PCM isn't started automatically in the case of mmap mode, so
|
|
809 * we need to trigger manually
|
|
810 */
|
|
811 if (snd_pcm_state(alsa_pcm) == SND_PCM_STATE_PREPARED) {
|
|
812 if (alsa_hw_written >= hw_period_size)
|
|
813 snd_pcm_start(alsa_pcm);
|
|
814 }
|
|
815 }
|
|
816
|
|
817 /* transfer audio data from thread buffer to h/w */
|
|
818 static void alsa_write_out_thread_data(void)
|
|
819 {
|
|
820 gint length, cnt, avail;
|
|
821 int err;
|
|
822
|
|
823 length = MIN(hw_period_size_in, get_thread_buffer_filled());
|
|
824 avail = snd_pcm_frames_to_bytes(alsa_pcm, alsa_get_avail());
|
|
825 length = MIN(length, avail);
|
|
826 while (length > 0) {
|
|
827 int rd;
|
|
828 cnt = MIN(length, thread_buffer_size - rd_index);
|
|
829 alsa_do_write(thread_buffer + rd_index, cnt);
|
|
830 rd = (rd_index + cnt) % thread_buffer_size;
|
|
831 rd_index = rd;
|
|
832 length -= cnt;
|
|
833
|
|
834 if (length > 0 && snd_pcm_state(alsa_pcm) == SND_PCM_STATE_PREPARED) {
|
|
835 if ((err = snd_pcm_start(alsa_pcm)) < 0)
|
|
836 g_warning("alsa_mmap_audio(): snd_pcm_start() "
|
|
837 "failed: %s", snd_strerror(-err));
|
|
838 }
|
|
839 }
|
|
840 }
|
|
841
|
|
842 /* audio thread loop */
|
|
843 /* FIXME: proper lock? */
|
|
844 static void *alsa_loop(void *arg)
|
|
845 {
|
|
846 int npfds = snd_pcm_poll_descriptors_count(alsa_pcm);
|
|
847 struct pollfd *pfds;
|
|
848 unsigned short *revents;
|
|
849
|
|
850 if (npfds <= 0)
|
|
851 goto _error;
|
|
852 pfds = alloca(sizeof(*pfds) * npfds);
|
|
853 revents = alloca(sizeof(*revents) * npfds);
|
|
854 while (going && alsa_pcm) {
|
|
855 if (! paused && get_thread_buffer_filled() > hw_period_size_in) {
|
|
856 snd_pcm_poll_descriptors(alsa_pcm, pfds, npfds);
|
|
857 if (poll(pfds, npfds, 10) > 0) {
|
|
858 /* need to check revents. poll() with dmix returns
|
|
859 * a postive value even if no data is available
|
|
860 */
|
|
861 int i;
|
|
862 snd_pcm_poll_descriptors_revents(alsa_pcm, pfds, npfds, revents);
|
|
863 for (i = 0; i < npfds; i++)
|
|
864 if (revents[i] & POLLOUT) {
|
|
865 alsa_write_out_thread_data();
|
|
866 break;
|
|
867 }
|
|
868 }
|
|
869 } else
|
|
870 xmms_usleep(10000);
|
|
871
|
|
872 if (pause_request != paused)
|
|
873 alsa_do_pause(pause_request);
|
|
874
|
|
875 if (flush_request != -1) {
|
|
876 alsa_do_flush(flush_request);
|
|
877 flush_request = -1;
|
|
878 }
|
|
879 }
|
|
880
|
|
881 _error:
|
|
882 alsa_close_pcm();
|
|
883 g_free(thread_buffer);
|
|
884 thread_buffer = NULL;
|
|
885
|
|
886 g_thread_exit(NULL);
|
|
887
|
|
888 /* shut GCC up */
|
|
889 return NULL;
|
|
890 }
|
|
891
|
|
892 /* open callback */
|
|
893 int
|
|
894 alsa_open(AFormat fmt, int rate, int nch)
|
|
895 {
|
|
896 debug("Opening device");
|
|
897 inputf = snd_format_from_xmms(fmt, rate, nch);
|
|
898 effectf = snd_format_from_xmms(fmt, rate, nch);
|
|
899
|
|
900 if (alsa_cfg.debug)
|
|
901 snd_output_stdio_attach(&logs, stdout, 0);
|
|
902
|
|
903 mmap = alsa_cfg.mmap;
|
|
904
|
|
905 if (alsa_setup(inputf) < 0) {
|
|
906 alsa_close();
|
|
907 return 0;
|
|
908 }
|
|
909
|
|
910 if (!mixer)
|
|
911 alsa_setup_mixer();
|
|
912
|
|
913 convertb = xmms_convert_buffers_new();
|
|
914
|
|
915 output_time_offset = 0;
|
|
916 alsa_total_written = alsa_hw_written = 0;
|
|
917 going = TRUE;
|
|
918 paused = FALSE;
|
|
919
|
|
920 multi_thread = alsa_cfg.multi_thread;
|
|
921 debug("ALSA: multi_thread = %d\n", multi_thread);
|
|
922
|
|
923 if (multi_thread) {
|
|
924 thread_buffer_size = (guint64)alsa_cfg.thread_buffer_time * inputf->bps / 1000;
|
|
925 if (thread_buffer_size < hw_buffer_size)
|
|
926 thread_buffer_size = hw_buffer_size * 2;
|
|
927 if (thread_buffer_size < 8192)
|
|
928 thread_buffer_size = 8192;
|
|
929 thread_buffer_size += hw_buffer_size;
|
|
930 thread_buffer_size -= thread_buffer_size % hw_period_size;
|
|
931 thread_buffer = g_malloc0(thread_buffer_size);
|
|
932 wr_index = rd_index = 0;
|
|
933 pause_request = FALSE;
|
|
934 flush_request = -1;
|
|
935
|
|
936 audio_thread = g_thread_create(alsa_loop, NULL, TRUE, NULL);
|
|
937 }
|
|
938
|
|
939 return 1;
|
|
940 }
|
|
941
|
|
942 static struct snd_format *
|
|
943 snd_format_from_xmms(AFormat fmt, int rate, int channels)
|
|
944 {
|
|
945 struct snd_format *f = g_malloc(sizeof(struct snd_format));
|
|
946 int i;
|
|
947
|
|
948 f->xmms_format = fmt;
|
|
949 f->format = SND_PCM_FORMAT_UNKNOWN;
|
|
950
|
|
951 for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++)
|
|
952 if (format_table[i].xmms == fmt) {
|
|
953 f->format = format_table[i].alsa;
|
|
954 break;
|
|
955 }
|
|
956
|
|
957 /* Get rid of _NE */
|
|
958 for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++)
|
|
959 if (format_table[i].alsa == f->format) {
|
|
960 f->xmms_format = format_table[i].xmms;
|
|
961 break;
|
|
962 }
|
|
963
|
|
964
|
|
965 f->rate = rate;
|
|
966 f->channels = channels;
|
|
967 f->sample_bits = snd_pcm_format_physical_width(f->format);
|
|
968 f->bps = (rate * f->sample_bits * channels) >> 3;
|
|
969
|
|
970 return f;
|
|
971 }
|
|
972
|
|
973 static int
|
|
974 format_from_alsa(snd_pcm_format_t fmt)
|
|
975 {
|
|
976 int i;
|
|
977 for (i = 0; i < sizeof(format_table) / sizeof(format_table[0]); i++)
|
|
978 if (format_table[i].alsa == fmt)
|
|
979 return format_table[i].xmms;
|
|
980 g_warning("Unsupported format: %s", snd_pcm_format_name(fmt));
|
|
981 return -1;
|
|
982 }
|
|
983
|
|
984 static int
|
|
985 alsa_setup(struct snd_format *f)
|
|
986 {
|
|
987 int err;
|
|
988 snd_pcm_hw_params_t *hwparams;
|
|
989 snd_pcm_sw_params_t *swparams;
|
|
990 int alsa_buffer_time;
|
|
991 unsigned int alsa_period_time;
|
|
992 snd_pcm_uframes_t alsa_buffer_size, alsa_period_size;
|
|
993
|
|
994 debug("alsa_setup");
|
|
995
|
|
996 alsa_convert_func = NULL;
|
|
997 alsa_stereo_convert_func = NULL;
|
|
998 alsa_frequency_convert_func = NULL;
|
|
999
|
|
1000 g_free(outputf);
|
|
1001 outputf = snd_format_from_xmms(f->xmms_format, f->rate, f->channels);
|
|
1002
|
|
1003 debug("Opening device: %s", alsa_cfg.pcm_device);
|
|
1004 /* FIXME: Can snd_pcm_open() return EAGAIN? */
|
|
1005 if ((err = snd_pcm_open(&alsa_pcm, alsa_cfg.pcm_device,
|
|
1006 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) < 0) {
|
|
1007 g_warning("alsa_setup(): Failed to open pcm device (%s): %s",
|
|
1008 alsa_cfg.pcm_device, snd_strerror(-err));
|
|
1009 alsa_pcm = NULL;
|
|
1010 g_free(outputf);
|
|
1011 outputf = NULL;
|
|
1012 return -1;
|
|
1013 }
|
|
1014
|
|
1015 /* doesn't care about non-blocking */
|
|
1016 /* snd_pcm_nonblock(alsa_pcm, 0); */
|
|
1017
|
|
1018 if (alsa_cfg.debug) {
|
|
1019 snd_pcm_info_t *info;
|
|
1020 int alsa_card, alsa_device, alsa_subdevice;
|
|
1021
|
|
1022 snd_pcm_info_alloca(&info);
|
|
1023 snd_pcm_info(alsa_pcm, info);
|
|
1024 alsa_card = snd_pcm_info_get_card(info);
|
|
1025 alsa_device = snd_pcm_info_get_device(info);
|
|
1026 alsa_subdevice = snd_pcm_info_get_subdevice(info);
|
|
1027 printf("Card %i, Device %i, Subdevice %i\n",
|
|
1028 alsa_card, alsa_device, alsa_subdevice);
|
|
1029 }
|
|
1030
|
|
1031 snd_pcm_hw_params_alloca(&hwparams);
|
|
1032
|
|
1033 if ((err = snd_pcm_hw_params_any(alsa_pcm, hwparams)) < 0) {
|
|
1034 g_warning("alsa_setup(): No configuration available for "
|
|
1035 "playback: %s", snd_strerror(-err));
|
|
1036 return -1;
|
|
1037 }
|
|
1038
|
|
1039 if (mmap &&
|
|
1040 (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams,
|
|
1041 SND_PCM_ACCESS_MMAP_INTERLEAVED))
|
|
1042 < 0) {
|
|
1043 g_message("alsa_setup(): Cannot set mmap'ed mode: %s. "
|
|
1044 "falling back to direct write", snd_strerror(-err));
|
|
1045 mmap = 0;
|
|
1046 }
|
|
1047
|
|
1048 if (!mmap &&
|
|
1049 (err = snd_pcm_hw_params_set_access(alsa_pcm, hwparams,
|
|
1050 SND_PCM_ACCESS_RW_INTERLEAVED)) <
|
|
1051 0) {
|
|
1052 g_warning("alsa_setup(): Cannot set direct write mode: %s",
|
|
1053 snd_strerror(-err));
|
|
1054 return -1;
|
|
1055 }
|
|
1056
|
|
1057 if ((err =
|
|
1058 snd_pcm_hw_params_set_format(alsa_pcm, hwparams,
|
|
1059 outputf->format)) < 0) {
|
|
1060 /*
|
|
1061 * Try if one of these format work (one of them should work
|
|
1062 * on almost all soundcards)
|
|
1063 */
|
|
1064 snd_pcm_format_t formats[] = { SND_PCM_FORMAT_S16_LE,
|
|
1065 SND_PCM_FORMAT_S16_BE,
|
|
1066 SND_PCM_FORMAT_U8
|
|
1067 };
|
|
1068 int i;
|
|
1069
|
|
1070 for (i = 0; i < sizeof(formats) / sizeof(formats[0]); i++) {
|
|
1071 if (snd_pcm_hw_params_set_format(alsa_pcm, hwparams,
|
|
1072 formats[i]) == 0) {
|
|
1073 outputf->format = formats[i];
|
|
1074 break;
|
|
1075 }
|
|
1076 }
|
|
1077 if (outputf->format != f->format) {
|
|
1078 outputf->xmms_format = format_from_alsa(outputf->format);
|
|
1079 debug("Converting format from %d to %d",
|
|
1080 f->xmms_format, outputf->xmms_format);
|
|
1081 if (outputf->xmms_format < 0)
|
|
1082 return -1;
|
|
1083 alsa_convert_func =
|
|
1084 xmms_convert_get_func(outputf->xmms_format,
|
|
1085 f->xmms_format);
|
|
1086 if (alsa_convert_func == NULL)
|
|
1087 return -1;
|
|
1088 }
|
|
1089 else {
|
|
1090 g_warning("alsa_setup(): Sample format not "
|
|
1091 "available for playback: %s", snd_strerror(-err));
|
|
1092 return -1;
|
|
1093 }
|
|
1094 }
|
|
1095
|
|
1096 snd_pcm_hw_params_set_channels_near(alsa_pcm, hwparams,
|
|
1097 &outputf->channels);
|
|
1098 if (outputf->channels != f->channels) {
|
|
1099 debug("Converting channels from %d to %d",
|
|
1100 f->channels, outputf->channels);
|
|
1101 alsa_stereo_convert_func =
|
|
1102 xmms_convert_get_channel_func(outputf->xmms_format,
|
|
1103 outputf->channels,
|
|
1104 f->channels);
|
|
1105 if (alsa_stereo_convert_func == NULL)
|
|
1106 return -1;
|
|
1107 }
|
|
1108
|
|
1109 snd_pcm_hw_params_set_rate_near(alsa_pcm, hwparams, &outputf->rate, 0);
|
|
1110 if (outputf->rate == 0) {
|
|
1111 g_warning("alsa_setup(): No usable samplerate available.");
|
|
1112 return -1;
|
|
1113 }
|
|
1114 if (outputf->rate != f->rate) {
|
|
1115 debug("Converting samplerate from %d to %d",
|
|
1116 f->rate, outputf->rate);
|
|
1117 alsa_frequency_convert_func =
|
|
1118 xmms_convert_get_frequency_func(outputf->xmms_format,
|
|
1119 outputf->channels);
|
|
1120 if (alsa_frequency_convert_func == NULL)
|
|
1121 return -1;
|
|
1122 }
|
|
1123
|
|
1124 outputf->sample_bits = snd_pcm_format_physical_width(outputf->format);
|
|
1125 outputf->bps = (outputf->rate * outputf->sample_bits * outputf->channels) >> 3;
|
|
1126
|
|
1127 alsa_buffer_time = alsa_cfg.buffer_time * 1000;
|
|
1128 if ((err = snd_pcm_hw_params_set_buffer_time_near(alsa_pcm, hwparams,
|
|
1129 &alsa_buffer_time,
|
|
1130 0)) < 0) {
|
|
1131 g_warning("alsa_setup(): Set buffer time failed: %s.",
|
|
1132 snd_strerror(-err));
|
|
1133 return -1;
|
|
1134 }
|
|
1135
|
|
1136 alsa_period_time = alsa_cfg.period_time * 1000;
|
|
1137 if ((err = snd_pcm_hw_params_set_period_time_near(alsa_pcm, hwparams,
|
|
1138 &alsa_period_time,
|
|
1139 0)) < 0) {
|
|
1140 g_warning("alsa_setup(): Set period time failed: %s.",
|
|
1141 snd_strerror(-err));
|
|
1142 return -1;
|
|
1143 }
|
|
1144
|
|
1145 if (snd_pcm_hw_params(alsa_pcm, hwparams) < 0) {
|
|
1146 if (alsa_cfg.debug)
|
|
1147 snd_pcm_hw_params_dump(hwparams, logs);
|
|
1148 g_warning("alsa_setup(): Unable to install hw params");
|
|
1149 return -1;
|
|
1150 }
|
|
1151
|
|
1152 if ((err =
|
|
1153 snd_pcm_hw_params_get_buffer_size(hwparams,
|
|
1154 &alsa_buffer_size)) < 0) {
|
|
1155 g_warning("alsa_setup(): snd_pcm_hw_params_get_buffer_size() "
|
|
1156 "failed: %s", snd_strerror(-err));
|
|
1157 return -1;
|
|
1158 }
|
|
1159
|
|
1160 if ((err =
|
|
1161 snd_pcm_hw_params_get_period_size(hwparams, &alsa_period_size,
|
|
1162 0)) < 0) {
|
|
1163 g_warning("alsa_setup(): snd_pcm_hw_params_get_period_size() "
|
|
1164 "failed: %s", snd_strerror(-err));
|
|
1165 return -1;
|
|
1166 }
|
|
1167
|
|
1168 alsa_can_pause = snd_pcm_hw_params_can_pause(hwparams);
|
|
1169
|
|
1170 snd_pcm_sw_params_alloca(&swparams);
|
|
1171 snd_pcm_sw_params_current(alsa_pcm, swparams);
|
|
1172
|
|
1173 /* This has effect for non-mmap only */
|
|
1174 if ((err = snd_pcm_sw_params_set_start_threshold(alsa_pcm,
|
|
1175 swparams,
|
|
1176 alsa_buffer_size -
|
|
1177 alsa_period_size) < 0))
|
|
1178 g_warning("alsa_setup(): setting start " "threshold failed: %s",
|
|
1179 snd_strerror(-err));
|
|
1180 if (snd_pcm_sw_params(alsa_pcm, swparams) < 0) {
|
|
1181 g_warning("alsa_setup(): Unable to install sw params");
|
|
1182 return -1;
|
|
1183 }
|
|
1184
|
|
1185 if (alsa_cfg.debug) {
|
|
1186 snd_pcm_sw_params_dump(swparams, logs);
|
|
1187 snd_pcm_dump(alsa_pcm, logs);
|
|
1188 }
|
|
1189
|
|
1190 hw_buffer_size = snd_pcm_frames_to_bytes(alsa_pcm, alsa_buffer_size);
|
|
1191 hw_period_size = snd_pcm_frames_to_bytes(alsa_pcm, alsa_period_size);
|
|
1192 if (inputf->bps != outputf->bps) {
|
|
1193 hw_buffer_size_in = ((guint64)hw_buffer_size * inputf->bps +
|
|
1194 outputf->bps/2) / outputf->bps;
|
|
1195 hw_period_size_in = ((guint64)hw_period_size * inputf->bps +
|
|
1196 outputf->bps/2) / outputf->bps;
|
|
1197 } else {
|
|
1198 hw_buffer_size_in = hw_buffer_size;
|
|
1199 hw_period_size_in = hw_period_size;
|
|
1200 }
|
|
1201
|
|
1202 debug("Device setup: buffer time: %i, size: %i.", alsa_buffer_time,
|
|
1203 hw_buffer_size);
|
|
1204 debug("Device setup: period time: %i, size: %i.", alsa_period_time,
|
|
1205 hw_period_size);
|
|
1206 debug("bits per sample: %i; frame size: %i; Bps: %i",
|
|
1207 snd_pcm_format_physical_width(outputf->format),
|
|
1208 snd_pcm_frames_to_bytes(alsa_pcm, 1), outputf->bps);
|
|
1209
|
|
1210 return 0;
|
|
1211 }
|