1 /*
2 * Copyright (C) 2000-2020 the xine project
3 *
4 * This file is part of xine, a free video player.
5 *
6 * xine is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * xine is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19 *
20 * Credits go
21 * - for the SPDIF A/52 sync part
22 * - frame size calculation added (16-08-2001)
23 * (c) 2001 Andy Lo A Foe <andy@alsaplayer.org>
24 * for initial ALSA 0.9.x support.
25 * adding MONO/STEREO/4CHANNEL/5CHANNEL/5.1CHANNEL analogue support.
26 * (c) 2001 James Courtier-Dutton <James@superbug.demon.co.uk>
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdarg.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <stdlib.h>
39 #include <fcntl.h>
40 #include <math.h>
41 #ifdef HAVE_ALLOCA_H
42 #include <alloca.h>
43 #endif
44
45 #define ALSA_PCM_NEW_HW_PARAMS_API
46 #define ALSA_PCM_NEW_SW_PARAMS_API
47 #include <alsa/asoundlib.h>
48
49 #include <sys/ioctl.h>
50 #include <inttypes.h>
51 #include <pthread.h>
52
53 #include <xine/xine_internal.h>
54 #include <xine/xineutils.h>
55 #include <xine/compat.h>
56 #include <xine/audio_out.h>
57
58 #include "speakers.h"
59
60 /*
61 #define ALSA_LOG
62 #define ALSA_LOG_BUFFERS
63 */
64 /*
65 #define LOG_DEBUG
66 */
67
68 #define AO_OUT_ALSA_IFACE_VERSION 9
69
70 #define BUFFER_TIME 1000*1000
71 #define GAP_TOLERANCE 5000
72
73 #define MIXER_MASK_LEFT 0x0001
74 #define MIXER_MASK_RIGHT 0x0002
75 #define MIXER_MASK_MUTE 0x0004
76 #define MIXER_MASK_STEREO 0x0008
77 #define MIXER_HAS_MUTE_SWITCH 0x0010
78
79 typedef struct {
80 audio_driver_class_t driver_class;
81
82 xine_t *xine;
83 } alsa_class_t;
84
85 typedef struct alsa_driver_s {
86
87 ao_driver_t ao_driver;
88
89 alsa_class_t *class;
90
91 snd_pcm_t *audio_fd;
92 int open_mode;
93 int has_pause_resume;
94 int is_paused;
95
96 int32_t output_sample_rate, input_sample_rate;
97 double sample_rate_factor;
98 uint32_t num_channels;
99 uint32_t bits_per_sample;
100 uint32_t bytes_per_frame;
101 uint32_t bytes_in_buffer; /* number of bytes writen to audio hardware */
102 snd_pcm_uframes_t buffer_size;
103 int32_t mmap;
104 uint32_t supported_channels; /* field of 1 << num_channels */
105 uint32_t capabilities;
106
107 struct _alsa_dev_info_s {
108 struct alsa_driver_s *this;
109 const char *type;
110 const char *config_key;
111 char *name;
112 uint32_t supported_channels; /* field of 1 << num_channels */
113 uint32_t capabilities;
114 } devs[5];
115
116 const char *bits_names[4]; /* 8, 16, 24, 32 */
117
118 struct {
119 pthread_t thread;
120 int thread_created;
121 pthread_mutex_t mutex;
122 char *name;
123 snd_mixer_t *handle;
124 snd_mixer_elem_t *elem;
125 long min;
126 long max;
127 long left_vol;
128 long right_vol;
129 int mute;
130 int running;
131 } mixer;
132
133 /* avoid *_alloca () */
134 snd_pcm_hw_params_t *hw_params;
135 snd_pcm_sw_params_t *sw_params;
136 snd_pcm_access_mask_t *ac_mask;
137 snd_ctl_card_info_t *card_info;
138 snd_pcm_status_t *pcm_status;
139 } alsa_driver_t;
140
141 static snd_output_t *jcd_out;
142
143 /*
144 * Get and convert volume to percent value
145 */
ao_alsa_get_percent_from_volume(long val,long min,long max)146 static int ao_alsa_get_percent_from_volume(long val, long min, long max) {
147 int range = max - min;
148 return (range == 0) ? 0 : ((val - min) * 100.0 / range + .5);
149 }
150
151 /* Stolen from alsa-lib */
my_snd_mixer_wait(snd_mixer_t * mixer,int timeout)152 static int my_snd_mixer_wait(snd_mixer_t *mixer, int timeout) {
153 struct pollfd spfds[16];
154 struct pollfd *pfds = spfds;
155 int err, count;
156 void *freeme = NULL;
157
158 count = snd_mixer_poll_descriptors(mixer, pfds, sizeof(spfds) / sizeof(spfds[0]));
159
160 if (count < 0)
161 return count;
162
163 if ((unsigned int) count > sizeof(spfds) / sizeof(spfds[0])) {
164 freeme = pfds = calloc(count, sizeof(*pfds));
165
166 if (!pfds)
167 return -ENOMEM;
168
169 err = snd_mixer_poll_descriptors(mixer, pfds, (unsigned int) count);
170 // XXX is this assert correct ? if triggered, it would be alsa bug, not xine bug ?
171 assert(err == count);
172 if (err < 0) {
173 free(freeme);
174 return err;
175 }
176 }
177
178 err = poll(pfds, (unsigned int) count, timeout);
179
180 free(freeme);
181
182 if (err < 0)
183 return -errno;
184
185 return err;
186 }
187
188 /*
189 * Wait (non blocking) till a mixer event happen
190 */
ao_alsa_handle_event_thread(void * data)191 static void *ao_alsa_handle_event_thread(void *data) {
192 alsa_driver_t *this = (alsa_driver_t *) data;
193
194 do {
195
196 if(my_snd_mixer_wait(this->mixer.handle, 333) > 0) {
197 int err, mute = 0, swl = 0, swr = 0;
198 long right_vol, left_vol;
199 int old_mute;
200
201 pthread_mutex_lock(&this->mixer.mutex);
202
203 old_mute = (this->mixer.mute & MIXER_MASK_MUTE) ? 1 : 0;
204
205 if((err = snd_mixer_handle_events(this->mixer.handle)) < 0) {
206 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
207 "audio_alsa_out: snd_mixer_handle_events(): %s\n", snd_strerror(err));
208 pthread_mutex_unlock(&this->mixer.mutex);
209 continue;
210 }
211
212 if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &left_vol)) < 0) {
213 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
214 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
215 pthread_mutex_unlock(&this->mixer.mutex);
216 continue;
217 }
218
219 if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, &right_vol)) < 0) {
220 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
221 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
222 pthread_mutex_unlock(&this->mixer.mutex);
223 continue;
224 }
225
226 if(this->mixer.mute & MIXER_HAS_MUTE_SWITCH) {
227
228 if(this->mixer.mute & MIXER_MASK_STEREO) {
229 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &swl);
230 mute = (swl) ? 0 : 1;
231 }
232 else {
233
234 if (this->mixer.mute & MIXER_MASK_LEFT)
235 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &swl);
236
237 if ((SND_MIXER_SCHN_FRONT_RIGHT != SND_MIXER_SCHN_UNKNOWN) && (this->mixer.mute & MIXER_MASK_RIGHT))
238 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, &swr);
239
240 mute = (swl || swr) ? 0 : 1;
241 }
242 }
243
244 if((this->mixer.right_vol != right_vol) || (this->mixer.left_vol != left_vol) || (old_mute != mute)) {
245 xine_event_t event;
246 xine_audio_level_data_t data;
247 xine_stream_t *stream;
248 xine_list_iterator_t ite;
249
250 this->mixer.right_vol = right_vol;
251 this->mixer.left_vol = left_vol;
252 if(mute)
253 this->mixer.mute |= MIXER_MASK_MUTE;
254 else
255 this->mixer.mute &= ~MIXER_MASK_MUTE;
256
257 data.right = ao_alsa_get_percent_from_volume(this->mixer.right_vol,
258 this->mixer.min, this->mixer.max);
259 data.left = ao_alsa_get_percent_from_volume(this->mixer.left_vol,
260 this->mixer.min, this->mixer.max);
261 data.mute = (this->mixer.mute & MIXER_MASK_MUTE) ? 1 : 0;
262
263 event.type = XINE_EVENT_AUDIO_LEVEL;
264 event.data = &data;
265 event.data_length = sizeof(data);
266
267 pthread_mutex_lock(&this->class->xine->streams_lock);
268 ite = NULL;
269 while ((stream = xine_list_next_value (this->class->xine->streams, &ite))) {
270 event.stream = stream;
271 xine_event_send(stream, &event);
272 }
273 pthread_mutex_unlock(&this->class->xine->streams_lock);
274 }
275
276 pthread_mutex_unlock(&this->mixer.mutex);
277 }
278
279 } while(this->mixer.running);
280
281 pthread_exit(NULL);
282 }
283
284 /*
285 * Convert percent value to volume and set
286 */
ao_alsa_get_volume_from_percent(int val,long min,long max)287 static long ao_alsa_get_volume_from_percent(int val, long min, long max) {
288 int range = max - min;
289 return (range == 0) ? min : (val * range / 100.0 + min + .5);
290 }
291
292 /*
293 * Error callback, we need to control this,
294 * error message should be printed only in DEBUG mode.
295 * XINE_FORMAT_PRINTF(5, 6) is true but useless here,
296 * as alsa delivers "fmt" at runtime only.
297 */
error_callback(const char * file,int line,const char * function,int err,const char * fmt,...)298 static void error_callback(const char *file, int line,
299 const char *function, int err, const char *fmt, ...) {
300 #ifdef DEBUG
301 va_list args;
302 char *buf = NULL;
303 int result;
304
305 va_start(args, fmt);
306 result = vasprintf(&buf, fmt, args);
307 va_end(args);
308
309 if (result >= 0) {
310 printf("%s: %s() %s.\n", file, function, buf);
311 free(buf);
312 }
313 #else
314 (void)file;
315 (void)function;
316 (void)fmt;
317 #endif
318 (void)err;
319 (void)line;
320 }
321
322 /*
323 * open the audio device for writing to
324 */
ao_alsa_open(ao_driver_t * this_gen,uint32_t bits,uint32_t rate,int mode)325 static int ao_alsa_open(ao_driver_t *this_gen, uint32_t bits, uint32_t rate, int mode) {
326 alsa_driver_t *this = (alsa_driver_t *) this_gen;
327 char *pcm_device;
328 int err;
329
330 err = snd_output_stdio_attach(&jcd_out, stdout, 0);
331
332 switch (mode) {
333 case AO_CAP_MODE_MONO:
334 this->num_channels = 1;
335 pcm_device = this->devs[0].name;
336 break;
337 case AO_CAP_MODE_STEREO:
338 this->num_channels = 2;
339 pcm_device = this->devs[1].name;
340 break;
341 case AO_CAP_MODE_4CHANNEL:
342 this->num_channels = 4;
343 pcm_device = this->devs[2].name;
344 break;
345 case AO_CAP_MODE_4_1CHANNEL:
346 case AO_CAP_MODE_5CHANNEL:
347 case AO_CAP_MODE_5_1CHANNEL:
348 this->num_channels = 6;
349 pcm_device = this->devs[3].name;
350 break;
351 case AO_CAP_MODE_A52:
352 case AO_CAP_MODE_AC5:
353 this->num_channels = 2;
354 pcm_device = this->devs[4].name;
355 break;
356 default:
357 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
358 "audio_alsa_out: ALSA Driver does not support the requested mode: 0x%X\n",mode);
359 return 0;
360 }
361
362 #ifdef ALSA_LOG
363 printf("audio_alsa_out: Audio Device name = %s\n",pcm_device);
364 printf("audio_alsa_out: Number of channels = %d\n",this->num_channels);
365 #endif
366
367 if (this->audio_fd) {
368 xine_log (this->class->xine, XINE_LOG_MSG, _("audio_alsa_out:Already open...WHY!"));
369 snd_pcm_close (this->audio_fd);
370 this->audio_fd = NULL;
371 }
372
373 this->open_mode = mode;
374 this->input_sample_rate = rate;
375 this->bits_per_sample = bits;
376 this->bytes_in_buffer = 0;
377
378 /*
379 * open audio device
380 * When switching to surround, dmix blocks the device some time, so we just keep trying for 0.8sec.
381 */
382 {
383 struct timeval start_time, end_time;
384 gettimeofday (&start_time, NULL);
385 while (1) {
386 err = snd_pcm_open (&this->audio_fd, pcm_device, SND_PCM_STREAM_PLAYBACK, /* NONBLOCK */ 1);
387 if (err != -EBUSY)
388 break;
389 gettimeofday (&end_time, NULL);
390 if ((end_time.tv_sec - start_time.tv_sec) * 1000000 + end_time.tv_usec - start_time.tv_usec > 800000)
391 break;
392 usleep (10000);
393 }
394 }
395 if (err < 0) {
396 xprintf (this->class->xine, XINE_VERBOSITY_LOG,
397 _("audio_alsa_out: snd_pcm_open() of %s failed: %s\n"), pcm_device, snd_strerror (err));
398 if (err == -EBUSY) {
399 xprintf (this->class->xine, XINE_VERBOSITY_LOG,
400 _("audio_alsa_out: >>> check if another program already uses PCM <<<\n"));
401 }
402 return 0;
403 }
404 /* printf ("audio_alsa_out: snd_pcm_open() opened %s\n", pcm_device); */
405 /* We wanted non blocking open but now put it back to normal */
406 //snd_pcm_nonblock(this->audio_fd, 0);
407 snd_pcm_nonblock(this->audio_fd, 1);
408
409 do {
410 /* configure audio device */
411 err = snd_pcm_hw_params_any (this->audio_fd, this->hw_params);
412 if (err < 0) {
413 xprintf (this->class->xine, XINE_VERBOSITY_LOG,
414 _("audio_alsa_out: broken configuration for this PCM: no configurations available: %s\n"),
415 snd_strerror (err));
416 break;
417 }
418
419 /* set interleaved access */
420 do {
421 if (this->mmap != 0) {
422 snd_pcm_access_mask_none (this->ac_mask);
423 snd_pcm_access_mask_set (this->ac_mask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
424 snd_pcm_access_mask_set (this->ac_mask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
425 snd_pcm_access_mask_set (this->ac_mask, SND_PCM_ACCESS_MMAP_COMPLEX);
426 err = snd_pcm_hw_params_set_access_mask (this->audio_fd, this->hw_params, this->ac_mask);
427 if (err >= 0)
428 break;
429 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
430 "audio_alsa_out: mmap not available, falling back to compatiblity mode\n");
431 this->mmap = 0;
432 }
433 err = snd_pcm_hw_params_set_access (this->audio_fd, this->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
434 } while (0);
435 if (err < 0) {
436 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
437 "audio_alsa_out: access type not available: %s\n", snd_strerror (err));
438 break;
439 }
440
441 /* set the sample format ([SU]{8,16,24,FLOAT}) */
442 /* ALSA automatically appends _LE or _BE depending on the CPU */
443 if ((bits < 8) || (bits > 32)) {
444 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
445 "audio_alsa_out: pcm format bits=%d unknown, trying 16.\n", (int)bits);
446 bits = 16;
447 }
448 {
449 static const snd_pcm_format_t fmts[5] = {
450 0,
451 SND_PCM_FORMAT_U8,
452 SND_PCM_FORMAT_S16,
453 #ifdef WORDS_BIGENDIAN
454 SND_PCM_FORMAT_S24_3BE, /* 24 bit samples taking 3 bytes. */
455 #else
456 SND_PCM_FORMAT_S24_3LE,
457 #endif
458 SND_PCM_FORMAT_FLOAT
459 };
460 snd_pcm_format_t format = fmts[bits >> 3];
461 err = snd_pcm_hw_params_set_format (this->audio_fd, this->hw_params, format);
462 if (err < 0) {
463 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
464 "audio_alsa_out: sample format not available: %s\n", snd_strerror (err));
465 break;
466 }
467 }
468
469 /* set the number of channels */
470 err = snd_pcm_hw_params_set_channels (this->audio_fd, this->hw_params, this->num_channels);
471 if (err < 0) {
472 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
473 "audio_alsa_out: Cannot set number of channels to %d (err=%d:%s)\n",
474 this->num_channels, err, snd_strerror (err));
475 break;
476 }
477
478 {
479 snd_pcm_uframes_t period_size, period_size_min, period_size_max;
480 snd_pcm_uframes_t buffer_size_min, buffer_size_max;
481 uint32_t buffer_time = BUFFER_TIME;
482 snd_pcm_uframes_t buffer_time_to_size;
483 int dir;
484 #if 0
485 uint32_t periods;
486 #endif
487
488 #if 0
489 /* Restrict a configuration space to contain only real hardware rates */
490 err = snd_pcm_hw_params_set_rate_resample (this->audio_fd, this->hw_params, 0);
491 #endif
492 /* set the stream rate [Hz] */
493 dir = 0;
494 err = snd_pcm_hw_params_set_rate_near (this->audio_fd, this->hw_params, &rate, &dir);
495 if (err < 0) {
496 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
497 "audio_alsa_out: rate not available: %s\n", snd_strerror (err));
498 break;
499 }
500 this->output_sample_rate = (uint32_t)rate;
501 if (this->input_sample_rate != this->output_sample_rate) {
502 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
503 "audio_alsa_out: audio rate : %d requested, %d provided by device/sec\n",
504 this->input_sample_rate, this->output_sample_rate);
505 }
506
507 buffer_time_to_size = ((uint64_t)buffer_time * rate) / 1000000;
508 snd_pcm_hw_params_get_buffer_size_min (this->hw_params, &buffer_size_min);
509 snd_pcm_hw_params_get_buffer_size_max (this->hw_params, &buffer_size_max);
510 dir = 0;
511 snd_pcm_hw_params_get_period_size_min (this->hw_params, &period_size_min, &dir);
512 dir = 0;
513 snd_pcm_hw_params_get_period_size_max (this->hw_params, &period_size_max, &dir);
514 #ifdef ALSA_LOG_BUFFERS
515 printf ("Buffer size range from %lu to %lu\n", buffer_size_min, buffer_size_max);
516 printf ("Period size range from %lu to %lu\n", period_size_min, period_size_max);
517 printf ("Buffer time size %lu\n", buffer_time_to_size);
518 #endif
519 this->buffer_size = buffer_time_to_size;
520 if (buffer_size_max < this->buffer_size)
521 this->buffer_size = buffer_size_max;
522 if (buffer_size_min > this->buffer_size)
523 this->buffer_size = buffer_size_min;
524 period_size = this->buffer_size / 8;
525 this->buffer_size = period_size * 8;
526 #ifdef ALSA_LOG_BUFFERS
527 printf ("To choose buffer_size = %ld\n", this->buffer_size);
528 printf ("To choose period_size = %ld\n", period_size);
529 #endif
530
531 #if 0
532 /* Set period to buffer size ratios at 8 periods to 1 buffer */
533 dir = -1;
534 periods = 8;
535 err = snd_pcm_hw_params_set_periods_near (this->audio_fd, this->hw_params, &periods, &dir);
536 if (err < 0) {
537 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
538 "audio_alsa_out: unable to set any periods: %s\n", snd_strerror (err));
539 break;
540 }
541 /* set the ring-buffer time [us] (large enough for x us|y samples ...) */
542 dir = 0;
543 err = snd_pcm_hw_params_set_buffer_time_near (this->audio_fd, this->hw_params, &buffer_time, &dir);
544 if (err < 0) {
545 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
546 "audio_alsa_out: buffer time not available: %s\n", snd_strerror (err));
547 break;
548 }
549 #endif
550
551 #if 1
552 /* set the period time [us] (interrupt every x us|y samples ...) */
553 dir = 0;
554 err = snd_pcm_hw_params_set_period_size_near (this->audio_fd, this->hw_params, &period_size, &dir);
555 if (err < 0) {
556 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
557 "audio_alsa_out: period time not available: %s\n", snd_strerror (err));
558 break;
559 }
560 #endif
561 dir = 0;
562 snd_pcm_hw_params_get_period_size (this->hw_params, &period_size, &dir);
563
564 dir = 0;
565 err = snd_pcm_hw_params_set_buffer_size_near (this->audio_fd, this->hw_params, &this->buffer_size);
566 if (err < 0) {
567 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
568 "audio_alsa_out: buffer time not available: %s\n", snd_strerror (err));
569 break;
570 }
571 snd_pcm_hw_params_get_buffer_size (this->hw_params, &(this->buffer_size));
572 #ifdef ALSA_LOG_BUFFERS
573 printf ("was set period_size = %ld\n", period_size);
574 printf ("was set buffer_size = %ld\n", this->buffer_size);
575 #endif
576 if (2 * period_size > this->buffer_size) {
577 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
578 "audio_alsa_out: buffer too small, could not use\n");
579 break;
580 }
581
582 /* write the parameters to device */
583 err = snd_pcm_hw_params (this->audio_fd, this->hw_params);
584 if (err < 0) {
585 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
586 "audio_alsa_out: pcm hw_params failed: %s\n", snd_strerror (err));
587 break;
588 }
589
590 /* Check for pause/resume support */
591 this->has_pause_resume = (snd_pcm_hw_params_can_pause (this->hw_params)
592 && snd_pcm_hw_params_can_resume (this->hw_params));
593 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
594 "audio_alsa_out:open pause_resume=%d\n", this->has_pause_resume);
595 if (this->has_pause_resume)
596 this->capabilities &= ~AO_CAP_NO_UNPAUSE;
597 else
598 this->capabilities |= AO_CAP_NO_UNPAUSE;
599
600 this->sample_rate_factor = (double) this->output_sample_rate / (double) this->input_sample_rate;
601 this->bytes_per_frame = snd_pcm_frames_to_bytes (this->audio_fd, 1);
602
603 /* audio buffer size handling */
604 /* Copy current parameters into swparams */
605 err = snd_pcm_sw_params_current (this->audio_fd, this->sw_params);
606 if (err < 0) {
607 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
608 "audio_alsa_out: Unable to determine current swparams: %s\n", snd_strerror (err));
609 break;
610 }
611
612 #if defined(SND_LIB_VERSION) && SND_LIB_VERSION >= 0x010016
613 /* snd_pcm_sw_params_set_xfer_align() is deprecated, alignment is always 1 */
614 #else
615 /* align all transfers to 1 sample */
616 err = snd_pcm_sw_params_set_xfer_align (this->audio_fd, this->sw_params, 1);
617 if (err < 0) {
618 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
619 "audio_alsa_out: Unable to set transfer alignment: %s\n", snd_strerror (err));
620 break;
621 }
622 #endif
623
624 /* allow the transfer when at least period_size samples can be processed */
625 err = snd_pcm_sw_params_set_avail_min (this->audio_fd, this->sw_params, period_size);
626 if (err < 0) {
627 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
628 "audio_alsa_out: Unable to set available min: %s\n", snd_strerror (err));
629 break;
630 }
631
632 /* start the transfer when the buffer contains at least period_size samples */
633 err = snd_pcm_sw_params_set_start_threshold (this->audio_fd, this->sw_params, period_size);
634 if (err < 0) {
635 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
636 "audio_alsa_out: Unable to set start threshold: %s\n", snd_strerror (err));
637 break;
638 }
639 }
640
641 /* never stop the transfer, even on xruns */
642 err = snd_pcm_sw_params_set_stop_threshold (this->audio_fd, this->sw_params, this->buffer_size);
643 if (err < 0) {
644 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
645 "audio_alsa_out: Unable to set stop threshold: %s\n", snd_strerror (err));
646 break;
647 }
648
649 /* Install swparams into current parameters */
650 err = snd_pcm_sw_params (this->audio_fd, this->sw_params);
651 if (err < 0) {
652 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
653 "audio_alsa_out: Unable to set swparams: %s\n", snd_strerror (err));
654 break;
655 }
656
657 #ifdef ALSA_LOG
658 snd_pcm_dump_setup (this->audio_fd, jcd_out);
659 snd_pcm_sw_params_dump (this->sw_params, jcd_out);
660 #endif
661
662 return this->output_sample_rate;
663 } while (0);
664
665 snd_pcm_close (this->audio_fd);
666 this->audio_fd = NULL;
667 return 0;
668 }
669
670 /*
671 * Return the number of audio channels
672 */
ao_alsa_num_channels(ao_driver_t * this_gen)673 static int ao_alsa_num_channels(ao_driver_t *this_gen) {
674 alsa_driver_t *this = (alsa_driver_t *) this_gen;
675 return this->num_channels;
676 }
677
678 /*
679 * Return the number of bytes per frame
680 */
ao_alsa_bytes_per_frame(ao_driver_t * this_gen)681 static int ao_alsa_bytes_per_frame(ao_driver_t *this_gen) {
682 alsa_driver_t *this = (alsa_driver_t *) this_gen;
683 return this->bytes_per_frame;
684 }
685
686 /*
687 * Return gap tolerance (in pts)
688 */
ao_alsa_get_gap_tolerance(ao_driver_t * this_gen)689 static int ao_alsa_get_gap_tolerance (ao_driver_t *this_gen) {
690 (void)this_gen;
691 return GAP_TOLERANCE;
692 }
693
694 /*
695 * Return the delay. is frames measured by looking at pending samples
696 */
697 /* FIXME: delay returns invalid data if status is not RUNNING.
698 * e.g When there is an XRUN or we are in PREPARED mode.
699 */
ao_alsa_delay(ao_driver_t * this_gen)700 static int ao_alsa_delay (ao_driver_t *this_gen) {
701 snd_pcm_sframes_t delay = 0;
702 int err = 0;
703 alsa_driver_t *this = (alsa_driver_t *) this_gen;
704 #ifdef LOG_DEBUG
705 struct timeval now;
706 printf("audio_alsa_out:delay:ENTERED\n");
707 #endif
708 err = snd_pcm_delay( this->audio_fd, &delay );
709
710 #ifdef LOG_DEBUG
711 printf("audio_alsa_out:delay:delay all=%ld err=%d\n",delay, err);
712 gettimeofday(&now, 0);
713 printf("audio_alsa_out:delay: Time = %ld.%ld\n", now.tv_sec, now.tv_usec);
714 printf("audio_alsa_out:delay:FINISHED\n");
715 #endif
716
717 /*
718 * try to recover from errors and recalculate delay
719 */
720 if(err) {
721 #ifdef LOG_DEBUG
722 printf("gap audio_alsa_out:delay: recovery\n");
723 #endif
724 err = snd_pcm_recover( this->audio_fd, err, 1 );
725 err = snd_pcm_delay( this->audio_fd, &delay );
726 }
727
728 /*
729 * if we have a negative delay try to forward within the buffer
730 */
731 if(!err && (delay < 0)) {
732 #ifdef LOG_DEBUG
733 printf("gap audio_alsa_out:delay: forwarding frames: %d\n", (int)-delay);
734 #endif
735 err = snd_pcm_forward( this->audio_fd, -delay );
736 if(err >= 0) {
737 err = snd_pcm_delay( this->audio_fd, &delay );
738 }
739 }
740
741 /*
742 * on error or (still) negative delays ensure delay
743 * is not negative
744 */
745 if (err || (delay < 0))
746 delay = 0;
747
748 return delay;
749 }
750
751 #if 0
752 /*
753 * Handle over/under-run
754 */
755 static void xrun(alsa_driver_t *this)
756 {
757 int res;
758
759 /*
760 if ((res = snd_pcm_status (this->audio_fd, this->pcm_status)) < 0) {
761 printf ("audio_alsa_out: status error: %s\n", snd_strerror(res));
762 return;
763 }
764 snd_pcm_status_dump (this->pcm_status, jcd_out);
765 */
766 if (snd_pcm_state(this->audio_fd) == SND_PCM_STATE_XRUN) {
767 /*
768 struct timeval now, diff, tstamp;
769 gettimeofday(&now, 0);
770 snd_pcm_status_get_trigger_tstamp (this->pcm_status, &tstamp);
771 timersub(&now, &tstamp, &diff);
772 printf ("audio_alsa_out: xrun!!! (at least %.3f ms long)\n", diff.tv_sec * 1000 + diff.tv_usec / 1000.0);
773 */
774 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "audio_alsa_out: XRUN!!!\n");
775 if ((res = snd_pcm_prepare(this->audio_fd))<0) {
776 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "audio_alsa_out: xrun: prepare error: %s", snd_strerror(res));
777 return;
778 }
779 return; /* ok, data should be accepted again */
780 }
781 }
782 #endif
783
784 /*
785 * resume from suspend
786 */
resume(snd_pcm_t * pcm)787 static int resume(snd_pcm_t *pcm)
788 {
789 int res;
790 while ((res = snd_pcm_resume(pcm)) == -EAGAIN)
791 sleep(1);
792 if (! res)
793 return 0;
794 return snd_pcm_prepare(pcm);
795 }
796
797 /*
798 * Write audio data to output buffer (blocking using snd_pcm_wait)
799 */
ao_alsa_write(ao_driver_t * this_gen,int16_t * data,uint32_t count)800 static int ao_alsa_write(ao_driver_t *this_gen, int16_t *data, uint32_t count) {
801 snd_pcm_sframes_t result;
802 snd_pcm_state_t state;
803 #ifdef LOG_DEBUG
804 struct timeval now;
805 #endif
806 int wait_result;
807 int res;
808 uint8_t *buffer=(uint8_t *)data;
809 snd_pcm_uframes_t number_of_frames = (snd_pcm_uframes_t) count;
810 alsa_driver_t *this = (alsa_driver_t *) this_gen;
811
812 #ifdef LOG_DEBUG
813 printf("audio_alsa_out:write:ENTERED\n");
814 gettimeofday(&now, 0);
815 printf("audio_alsa_out:write: Time = %ld.%ld\n", now.tv_sec, now.tv_usec);
816 printf("audio_alsa_out:write:count=%u\n",count);
817 #endif
818 state = snd_pcm_state(this->audio_fd);
819 if (state == SND_PCM_STATE_SUSPENDED) {
820 res = resume(this->audio_fd);
821 if (res < 0)
822 return 0;
823 state = snd_pcm_state(this->audio_fd);
824 } else if (state == SND_PCM_STATE_DISCONNECTED) {
825 /* the device is gone. audio_out.c handles it if we return something < 0 */
826 return -1;
827 }
828 if (state == SND_PCM_STATE_XRUN) {
829 #ifdef LOG_DEBUG
830 printf("audio_alsa_out:write:XRUN before\n");
831 snd_pcm_status (this->audio_fd, this->pcm_status);
832 snd_pcm_status_dump (this->pcm_status, jcd_out);
833 #endif
834 if ((res = snd_pcm_prepare(this->audio_fd))<0) {
835 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
836 "audio_alsa_out: xrun: prepare error: %s", snd_strerror(res));
837 return 0;
838 }
839 state = snd_pcm_state(this->audio_fd);
840 #ifdef LOG_DEBUG
841 printf("audio_alsa_out:write:XRUN after\n");
842 #endif
843 }
844 if ( (state != SND_PCM_STATE_PREPARED) &&
845 (state != SND_PCM_STATE_RUNNING) &&
846 (state != SND_PCM_STATE_DRAINING) ) {
847 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
848 "audio_alsa_out:write:BAD STATE, state = %d\n",state);
849 }
850
851 while( number_of_frames > 0) {
852 if ( state == SND_PCM_STATE_RUNNING ) {
853 #ifdef LOG_DEBUG
854 printf("audio_alsa_out:write:loop:waiting for Godot\n");
855 #endif
856 snd_pcm_status (this->audio_fd, this->pcm_status);
857 if (snd_pcm_status_get_avail (this->pcm_status) < number_of_frames) {
858 wait_result = snd_pcm_wait(this->audio_fd, 1000);
859 #ifdef LOG_DEBUG
860 printf("audio_alsa_out:write:loop:wait_result=%d\n",wait_result);
861 #endif
862 if (wait_result <= 0) return 0;
863 }
864 }
865 if (this->mmap != 0) {
866 result = snd_pcm_mmap_writei(this->audio_fd, buffer, number_of_frames);
867 } else {
868 result = snd_pcm_writei(this->audio_fd, buffer, number_of_frames);
869 }
870
871 if (result < 0) {
872 #ifdef LOG_DEBUG
873 printf("audio_alsa_out:write:result=%ld:%s\n",result, snd_strerror(result));
874 #endif
875 state = snd_pcm_state(this->audio_fd);
876 if (state == SND_PCM_STATE_SUSPENDED) {
877 res = resume(this->audio_fd);
878 if (res < 0)
879 return 0;
880 continue;
881 }
882 if (state == SND_PCM_STATE_DISCONNECTED) {
883 /* the device is gone. audio_out.c handles it if we return something < 0 */
884 return -1;
885 } else if ( (state != SND_PCM_STATE_PREPARED) &&
886 (state != SND_PCM_STATE_RUNNING) &&
887 (state != SND_PCM_STATE_DRAINING) ) {
888 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
889 "audio_alsa_out:write:BAD STATE2, state = %d, going to try XRUN\n",state);
890 if ((res = snd_pcm_prepare(this->audio_fd))<0) {
891 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
892 "audio_alsa_out: xrun: prepare error: %s", snd_strerror(res));
893 return 0;
894 }
895 }
896 }
897 if (result > 0) {
898 number_of_frames -= result;
899 buffer += result * this->bytes_per_frame;
900 }
901 }
902 #if 0
903 if ( (state == SND_PCM_STATE_RUNNING) ) {
904 #ifdef LOG_DEBUG
905 printf("audio_alsa_out:write:loop:waiting for Godot2\n");
906 #endif
907 wait_result = snd_pcm_wait(this->audio_fd, 1000000);
908 #ifdef LOG_DEBUG
909 printf("audio_alsa_out:write:loop:wait_result=%d\n",wait_result);
910 #endif
911 if (wait_result < 0) return 0;
912 }
913 #endif
914 #ifdef LOG_DEBUG
915 gettimeofday(&now, 0);
916 printf("audio_alsa_out:write: Time = %ld.%ld\n", now.tv_sec, now.tv_usec);
917 printf("audio_alsa_out:write:FINISHED\n");
918 #endif
919 return 1; /* audio samples were processed ok */
920 }
921
922 /*
923 * This is called when the decoder no longer uses the audio
924 */
ao_alsa_close(ao_driver_t * this_gen)925 static void ao_alsa_close(ao_driver_t *this_gen) {
926 alsa_driver_t *this = (alsa_driver_t *) this_gen;
927
928 if(this->audio_fd) {
929 snd_pcm_nonblock(this->audio_fd, 0);
930 snd_pcm_drain(this->audio_fd);
931 snd_pcm_close(this->audio_fd);
932 }
933 this->audio_fd = NULL;
934 this->has_pause_resume = 0; /* This is set at open time */
935 }
936
937 /*
938 * Find out what output modes + capatilities are supported
939 */
ao_alsa_get_capabilities(ao_driver_t * this_gen)940 static uint32_t ao_alsa_get_capabilities (ao_driver_t *this_gen) {
941 alsa_driver_t *this = (alsa_driver_t *) this_gen;
942 return this->capabilities;
943 }
944
945 /*
946 * Shut down audio output driver plugin and free all resources allocated
947 */
ao_alsa_exit(ao_driver_t * this_gen)948 static void ao_alsa_exit(ao_driver_t *this_gen) {
949 alsa_driver_t *this = (alsa_driver_t *) this_gen;
950
951 this->class->xine->config->unregister_callbacks (this->class->xine->config,
952 NULL, NULL, this, sizeof (*this));
953
954 /*
955 * Destroy the mixer thread and cleanup the mixer, so that
956 * any child processes (such as xscreensaver) cannot inherit
957 * the mixer's handle and keep it open.
958 * By rejoining the mixer thread, we remove a race condition
959 * between closing the handle and spawning the child process
960 * (i.e. xscreensaver).
961 */
962
963 if(this->mixer.handle && this->mixer.thread_created) {
964 this->mixer.running = 0;
965 pthread_join(this->mixer.thread, NULL);
966 snd_mixer_close(this->mixer.handle);
967 this->mixer.handle=0;
968 }
969 pthread_mutex_destroy(&this->mixer.mutex);
970
971 if (this->audio_fd) snd_pcm_close(this->audio_fd);
972 this->audio_fd=NULL;
973
974 xine_config_free_string(this->class->xine, &this->mixer.name);
975
976 {
977 uint32_t u;
978 for (u = 0; u < sizeof (this->devs) / sizeof (this->devs[0]); u++) {
979 _x_freep (&this->devs[u].name);
980 }
981 }
982
983 free (this);
984 }
985
986 /*
987 * Get a property of audio driver
988 */
ao_alsa_get_property(ao_driver_t * this_gen,int property)989 static int ao_alsa_get_property (ao_driver_t *this_gen, int property) {
990 alsa_driver_t *this = (alsa_driver_t *) this_gen;
991 int err;
992
993 switch(property) {
994 case AO_PROP_MIXER_VOL:
995 case AO_PROP_PCM_VOL:
996 if(this->mixer.elem) {
997 int vol;
998
999 pthread_mutex_lock(&this->mixer.mutex);
1000
1001 if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT,
1002 &this->mixer.left_vol)) < 0) {
1003 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1004 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
1005 goto done;
1006 }
1007
1008 if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT,
1009 &this->mixer.right_vol)) < 0) {
1010 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1011 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
1012 goto done;
1013 }
1014
1015 done:
1016 vol = (((ao_alsa_get_percent_from_volume(this->mixer.left_vol, this->mixer.min, this->mixer.max)) +
1017 (ao_alsa_get_percent_from_volume(this->mixer.right_vol, this->mixer.min, this->mixer.max))) /2);
1018 pthread_mutex_unlock(&this->mixer.mutex);
1019
1020 return vol;
1021 }
1022 break;
1023
1024 case AO_PROP_MUTE_VOL:
1025 {
1026 int mute;
1027
1028 pthread_mutex_lock(&this->mixer.mutex);
1029 mute = ((this->mixer.mute & MIXER_HAS_MUTE_SWITCH) && (this->mixer.mute & MIXER_MASK_MUTE)) ? 1 : 0;
1030 pthread_mutex_unlock(&this->mixer.mutex);
1031
1032 return mute;
1033 }
1034 break;
1035 }
1036
1037 return 0;
1038 }
1039
1040 /*
1041 * Set a property of audio driver
1042 */
ao_alsa_set_property(ao_driver_t * this_gen,int property,int value)1043 static int ao_alsa_set_property (ao_driver_t *this_gen, int property, int value) {
1044 alsa_driver_t *this = (alsa_driver_t *) this_gen;
1045 int err;
1046
1047 switch(property) {
1048 case AO_PROP_MIXER_VOL:
1049 case AO_PROP_PCM_VOL:
1050 if(this->mixer.elem) {
1051
1052 pthread_mutex_lock(&this->mixer.mutex);
1053
1054 this->mixer.left_vol = this->mixer.right_vol = ao_alsa_get_volume_from_percent(value, this->mixer.min, this->mixer.max);
1055
1056 if((err = snd_mixer_selem_set_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT,
1057 this->mixer.left_vol)) < 0) {
1058 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1059 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
1060 pthread_mutex_unlock(&this->mixer.mutex);
1061 return ~value;
1062 }
1063
1064 if((err = snd_mixer_selem_set_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT,
1065 this->mixer.right_vol)) < 0) {
1066 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1067 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
1068 pthread_mutex_unlock(&this->mixer.mutex);
1069 return ~value;
1070 }
1071 pthread_mutex_unlock(&this->mixer.mutex);
1072 return value;
1073 }
1074 break;
1075
1076 case AO_PROP_MUTE_VOL:
1077 if(this->mixer.elem) {
1078
1079 if(this->mixer.mute & MIXER_HAS_MUTE_SWITCH) {
1080 int swl = 0, swr = 0;
1081 int old_mute;
1082
1083 pthread_mutex_lock(&this->mixer.mutex);
1084
1085 old_mute = this->mixer.mute;
1086 if(value)
1087 this->mixer.mute |= MIXER_MASK_MUTE;
1088 else
1089 this->mixer.mute &= ~MIXER_MASK_MUTE;
1090
1091 if ((this->mixer.mute & MIXER_MASK_MUTE) != (old_mute & MIXER_MASK_MUTE)) {
1092 if(this->mixer.mute & MIXER_MASK_STEREO) {
1093 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &swl);
1094 snd_mixer_selem_set_playback_switch_all(this->mixer.elem, !swl);
1095 }
1096 else {
1097 if (this->mixer.mute & MIXER_MASK_LEFT) {
1098 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &swl);
1099 snd_mixer_selem_set_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, !swl);
1100 }
1101 if (SND_MIXER_SCHN_FRONT_RIGHT != SND_MIXER_SCHN_UNKNOWN && (this->mixer.mute & MIXER_MASK_RIGHT)) {
1102 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, &swr);
1103 snd_mixer_selem_set_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, !swr);
1104 }
1105 }
1106 }
1107
1108 pthread_mutex_unlock(&this->mixer.mutex);
1109 }
1110 return value;
1111 }
1112 return ~value;
1113 break;
1114 }
1115
1116 return ~value;
1117 }
1118
1119 /*
1120 * Misc control operations
1121 */
ao_alsa_ctrl(ao_driver_t * this_gen,int cmd,...)1122 static int ao_alsa_ctrl(ao_driver_t *this_gen, int cmd, ...) {
1123 alsa_driver_t *this = (alsa_driver_t *) this_gen;
1124 int err;
1125
1126 /* Alsa 0.9.x pause and resume is not stable enough at the moment.
1127 * Use snd_pcm_drop and restart instead.
1128 */
1129 switch (cmd) {
1130
1131 case AO_CTRL_PLAY_PAUSE:
1132 if (this->audio_fd) {
1133 if (this->has_pause_resume) {
1134 if ((err=snd_pcm_pause(this->audio_fd, 1)) < 0) {
1135 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1136 "audio_alsa_out: Pause call failed. (err=%d:%s)\n",err, snd_strerror(err));
1137 this->has_pause_resume = 0;
1138 ao_alsa_ctrl(this_gen, AO_CTRL_PLAY_PAUSE, NULL);
1139 } else {
1140 this->is_paused = 1;
1141 }
1142 } else {
1143 if ((err=snd_pcm_reset(this->audio_fd)) < 0) {
1144 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1145 "audio_alsa_out: Reset call failed. (err=%d:%s)\n",err, snd_strerror(err));
1146 }
1147 if ((err=snd_pcm_drain(this->audio_fd)) < 0) {
1148 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1149 "audio_alsa_out: Drain call failed. (err=%d:%s)\n",err, snd_strerror(err));
1150 }
1151 if ((err=snd_pcm_prepare(this->audio_fd)) < 0) {
1152 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1153 "audio_alsa_out: Prepare call failed. (err=%d:%s)\n",err, snd_strerror(err));
1154 }
1155 }
1156 }
1157 break;
1158
1159 case AO_CTRL_PLAY_RESUME:
1160 if (this->audio_fd) {
1161 if (this->has_pause_resume && this->is_paused) {
1162 if ((err=snd_pcm_pause(this->audio_fd, 0)) < 0) {
1163 if (err == -77) {
1164 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1165 "audio_alsa_out: Warning: How am I supposed to RESUME, if I am not PAUSED. "
1166 "audio_out.c, please don't call me!\n");
1167 break;
1168 }
1169 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1170 "audio_alsa_out: Resume call failed. (err=%d:%s)\n",err, snd_strerror(err));
1171 this->has_pause_resume = 0;
1172 } else {
1173 this->is_paused = 0;
1174 }
1175 }
1176 }
1177 break;
1178
1179 case AO_CTRL_FLUSH_BUFFERS:
1180 if (this->audio_fd) {
1181 if ((err=snd_pcm_drop(this->audio_fd)) < 0) {
1182 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1183 "audio_alsa_out: Drop call failed. (err=%d:%s)\n",err, snd_strerror(err));
1184 }
1185 if ((err=snd_pcm_prepare(this->audio_fd)) < 0) {
1186 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1187 "audio_alsa_out: Prepare call failed. (err=%d:%s)\n",err, snd_strerror(err));
1188 }
1189 }
1190 break;
1191 }
1192
1193 return 0;
1194 }
1195
1196 /*
1197 * Initialize mixer
1198 */
ao_alsa_mixer_init(ao_driver_t * this_gen)1199 static void ao_alsa_mixer_init(ao_driver_t *this_gen) {
1200 alsa_driver_t *this = (alsa_driver_t *) this_gen;
1201 config_values_t *config = this->class->xine->config;
1202 const char *pcm_device;
1203 snd_ctl_t *ctl_handle;
1204 int err;
1205 void *mixer_sid;
1206 snd_mixer_elem_t *elem;
1207 int mixer_n_selems = 0;
1208 snd_mixer_selem_id_t *sid;
1209 int loop = 0;
1210 int found;
1211 int swl = 0, swr = 0, send_events;
1212
1213 this->mixer.elem = 0;
1214 pcm_device = this->devs[0].name;
1215 err = snd_ctl_open (&ctl_handle, pcm_device, 0);
1216 if (err < 0) {
1217 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "audio_alsa_out: snd_ctl_open(): %s\n", snd_strerror(err));
1218 return;
1219 }
1220
1221 if ((err = snd_ctl_card_info (ctl_handle, this->card_info)) < 0) {
1222 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1223 "audio_alsa_out: snd_ctl_card_info(): %s\n", snd_strerror(err));
1224 snd_ctl_close(ctl_handle);
1225 return;
1226 }
1227
1228 snd_ctl_close (ctl_handle);
1229
1230 /*
1231 * Open mixer device
1232 */
1233 if ((err = snd_mixer_open (&this->mixer.handle, 0)) < 0) {
1234 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1235 "audio_alsa_out: snd_mixer_open(): %s\n", snd_strerror(err));
1236 this->mixer.handle=0;
1237 return;
1238 }
1239
1240 if ((err = snd_mixer_attach (this->mixer.handle, pcm_device)) < 0) {
1241 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1242 "audio_alsa_out: snd_mixer_attach(): %s\n", snd_strerror(err));
1243 snd_mixer_close(this->mixer.handle);
1244 this->mixer.handle=0;
1245 return;
1246 }
1247
1248 if ((err = snd_mixer_selem_register (this->mixer.handle, NULL, NULL)) < 0) {
1249 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1250 "audio_alsa_out: snd_mixer_selem_register(): %s\n", snd_strerror(err));
1251 snd_mixer_close(this->mixer.handle);
1252 this->mixer.handle=0;
1253 return;
1254 }
1255
1256 if ((err = snd_mixer_load (this->mixer.handle)) < 0) {
1257 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1258 "audio_alsa_out: snd_mixer_load(): %s\n", snd_strerror(err));
1259 snd_mixer_close(this->mixer.handle);
1260 this->mixer.handle=0;
1261 return;
1262 }
1263
1264 mixer_sid = calloc (1, snd_mixer_selem_id_sizeof () * snd_mixer_get_count (this->mixer.handle));
1265 if (mixer_sid == NULL) {
1266 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1267 "audio_alsa_out: malloc () failed: %s\n", strerror (errno));
1268 snd_mixer_close(this->mixer.handle);
1269 this->mixer.handle=0;
1270 return;
1271 }
1272
1273 again:
1274
1275 found = 0;
1276 mixer_n_selems = 0;
1277 for (elem = snd_mixer_first_elem(this->mixer.handle); elem; elem = snd_mixer_elem_next(elem)) {
1278 sid = (snd_mixer_selem_id_t *)(((char *)mixer_sid) + snd_mixer_selem_id_sizeof() * mixer_n_selems);
1279
1280 if ((snd_mixer_elem_get_type(elem) != SND_MIXER_ELEM_SIMPLE) ||
1281 !snd_mixer_selem_is_active(elem))
1282 continue;
1283
1284 snd_mixer_selem_get_id(elem, sid);
1285 mixer_n_selems++;
1286
1287 if(!strcmp((snd_mixer_selem_get_name(elem)), this->mixer.name)) {
1288 /* printf("found %s\n", snd_mixer_selem_get_name(elem)); */
1289
1290 this->mixer.elem = elem;
1291
1292 snd_mixer_selem_get_playback_volume_range(this->mixer.elem,
1293 &this->mixer.min, &this->mixer.max);
1294 if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT,
1295 &this->mixer.left_vol)) < 0) {
1296 xprintf(this->class->xine, XINE_VERBOSITY_DEBUG,
1297 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
1298 this->mixer.elem = NULL;
1299 continue;
1300 }
1301
1302 if((err = snd_mixer_selem_get_playback_volume(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT,
1303 &this->mixer.right_vol)) < 0) {
1304 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1305 "audio_alsa_out: snd_mixer_selem_get_playback_volume(): %s\n", snd_strerror(err));
1306 this->mixer.elem = NULL;
1307 continue;
1308 }
1309
1310 /* Channels mute */
1311 this->mixer.mute = 0;
1312 if(snd_mixer_selem_has_playback_switch(this->mixer.elem)) {
1313 this->mixer.mute |= MIXER_HAS_MUTE_SWITCH;
1314
1315 if (snd_mixer_selem_has_playback_switch_joined(this->mixer.elem)) {
1316 this->mixer.mute |= MIXER_MASK_STEREO;
1317 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &swl);
1318 }
1319 else {
1320 this->mixer.mute |= MIXER_MASK_LEFT;
1321 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_LEFT, &swl);
1322
1323 if (SND_MIXER_SCHN_FRONT_RIGHT != SND_MIXER_SCHN_UNKNOWN) {
1324 this->mixer.mute |= MIXER_MASK_RIGHT;
1325 snd_mixer_selem_get_playback_switch(this->mixer.elem, SND_MIXER_SCHN_FRONT_RIGHT, &swr);
1326 }
1327
1328 if(!swl || !swr)
1329 this->mixer.mute |= MIXER_MASK_MUTE;
1330 }
1331
1332 this->capabilities |= AO_CAP_MUTE_VOL;
1333 }
1334
1335 found++;
1336
1337 goto mixer_found;
1338 }
1339 }
1340
1341 if(loop)
1342 goto mixer_found; /* Yes, untrue but... ;-) */
1343
1344 if(!strcmp(this->mixer.name, "PCM")) {
1345 config->update_string(config, "audio.device.alsa_mixer_name", "Master");
1346 loop++;
1347 }
1348 else {
1349 config->update_string(config, "audio.device.alsa_mixer_name", "PCM");
1350 }
1351
1352 config->free_string(config, &this->mixer.name);
1353 this->mixer.name = config->lookup_string(config, "audio.device.alsa_mixer_name");
1354
1355 goto again;
1356
1357 mixer_found:
1358
1359 free (mixer_sid);
1360
1361 /*
1362 * Ugly: yes[*] no[ ]
1363 */
1364 if(found) {
1365 if(!strcmp(this->mixer.name, "Master"))
1366 this->capabilities |= AO_CAP_MIXER_VOL;
1367 else
1368 this->capabilities |= AO_CAP_PCM_VOL;
1369 } else {
1370 if (this->mixer.handle) {
1371 snd_mixer_close(this->mixer.handle);
1372 this->mixer.handle=0;
1373 }
1374 return;
1375 }
1376
1377 /* Create a thread which wait/handle mixer events */
1378 send_events = config->register_bool(config, "audio.alsa_hw_mixer", 1,
1379 _("notify changes to the hardware mixer"),
1380 _("When the hardware mixer changes, your application will receive "
1381 "a notification so that it can update its graphical representation "
1382 "of the mixer settings on the fly."),
1383 10, NULL, NULL);
1384
1385 if (send_events && found) {
1386 pthread_attr_t pth_attrs;
1387 struct sched_param pth_params;
1388
1389 this->mixer.running = 1;
1390
1391 pthread_attr_init(&pth_attrs);
1392
1393 pthread_attr_getschedparam(&pth_attrs, &pth_params);
1394 pth_params.sched_priority = sched_get_priority_min(SCHED_OTHER);
1395 pthread_attr_setschedparam(&pth_attrs, &pth_params);
1396 if (pthread_create(&this->mixer.thread, &pth_attrs, ao_alsa_handle_event_thread, (void *) this)) {
1397 xprintf (this->class->xine, XINE_VERBOSITY_LOG, LOG_MODULE ": pthread_create() failed\n");
1398 } else {
1399 this->mixer.thread_created = 1;
1400 }
1401 pthread_attr_destroy(&pth_attrs);
1402 }
1403 }
1404
alsa_apply_speaker_arrangement(alsa_driver_t * this,int speakers)1405 static void alsa_apply_speaker_arrangement (alsa_driver_t *this, int speakers) {
1406 char logbuf[2048], *q, *logend = logbuf + sizeof (logbuf);
1407
1408 q = logbuf;
1409 q += strlcpy (q, _("audio_alsa_out : supported modes are"), logend - q);
1410 if (q >= logend)
1411 q = logend;
1412
1413 if (this->capabilities & AO_CAP_8BITS) {
1414 q += strlcpy (q, this->bits_names[0], logend - q);
1415 if (q >= logend)
1416 q = logend;
1417 }
1418 if (this->capabilities & AO_CAP_16BITS) {
1419 q += strlcpy (q, this->bits_names[1], logend - q);
1420 if (q >= logend)
1421 q = logend;
1422 }
1423 if (this->capabilities & AO_CAP_24BITS) {
1424 q += strlcpy (q, this->bits_names[2], logend - q);
1425 if (q >= logend)
1426 q = logend;
1427 }
1428 if (this->capabilities & AO_CAP_FLOAT32) {
1429 q += strlcpy (q, this->bits_names[3], logend - q);
1430 if (q >= logend)
1431 q = logend;
1432 }
1433
1434 this->capabilities &= ~(AO_CAP_MODE_MONO |
1435 AO_CAP_MODE_STEREO |
1436 AO_CAP_MODE_4CHANNEL |
1437 AO_CAP_MODE_4_1CHANNEL |
1438 AO_CAP_MODE_5CHANNEL |
1439 AO_CAP_MODE_5_1CHANNEL |
1440 AO_CAP_MODE_A52 |
1441 AO_CAP_MODE_AC5);
1442
1443 /* always set these. */
1444 if (this->supported_channels & (1 << 1)) {
1445 this->capabilities |= AO_CAP_MODE_MONO;
1446 q += strlcpy (q, this->devs[0].type, logend - q);
1447 if (q >= logend)
1448 q = logend;
1449 }
1450 if (this->supported_channels & (1 << 2)) {
1451 this->capabilities |= AO_CAP_MODE_STEREO;
1452 q += strlcpy (q, this->devs[1].type, logend - q);
1453 if (q >= logend)
1454 q = logend;
1455 }
1456
1457 if (this->supported_channels & (1 << 4)) {
1458 const char *t;
1459 if (speakers == SURROUND4) {
1460 this->capabilities |= AO_CAP_MODE_4CHANNEL;
1461 t = this->devs[2].type;
1462 } else {
1463 t = _(" (4-channel not enabled in xine config)");
1464 }
1465 q += strlcpy (q, t, logend - q);
1466 if (q >= logend)
1467 q = logend;
1468 }
1469 if (this->supported_channels & (1 << 6)) {
1470 const char *t;
1471 if (speakers == SURROUND41) {
1472 this->capabilities |= AO_CAP_MODE_4_1CHANNEL;
1473 t = _(" 4.1-channel");
1474 } else {
1475 t = _(" (4.1-channel not enabled in xine config)");
1476 }
1477 q += strlcpy (q, t, logend - q);
1478 if (q >= logend)
1479 q = logend;
1480 if (speakers == SURROUND5) {
1481 this->capabilities |= AO_CAP_MODE_5CHANNEL;
1482 t = _(" 5-channel");
1483 } else {
1484 t = _(" (5-channel not enabled in xine config)");
1485 }
1486 q += strlcpy (q, t, logend - q);
1487 if (q >= logend)
1488 q = logend;
1489 /* NOTE: That ">=" covers both the high channel counts and passthrough mode.
1490 * This is more or less a BUG when a traditional SPDIF link is used.
1491 * However, if alsa_surround51_device refers to the same HDMI port
1492 * as alsa_passthrough_device, then we have a HACK that routes software
1493 * decoded 5.1 where it belongs :-) */
1494 if (speakers >= SURROUND51) {
1495 this->capabilities |= AO_CAP_MODE_5_1CHANNEL;
1496 t = this->devs[3].type;
1497 } else {
1498 t = _(" (5.1-channel not enabled in xine config)");
1499 }
1500 q += strlcpy (q, t, logend - q);
1501 if (q >= logend)
1502 q = logend;
1503 }
1504
1505 {
1506 const char *t;
1507 if (speakers == A52_PASSTHRU) {
1508 this->capabilities |= AO_CAP_MODE_A52 | AO_CAP_MODE_AC5;
1509 t = this->devs[4].type;
1510 } else {
1511 t = _(" (a/52 and DTS pass-through not enabled in xine config)");
1512 }
1513 q += strlcpy (q, t, logend - q);
1514 /* if (q >= logend)
1515 q = logend; */
1516 }
1517
1518 xprintf (this->class->xine, XINE_VERBOSITY_LOG, "%s.\n", logbuf);
1519 }
1520
_alsa_mmap_cb(void * user_data,xine_cfg_entry_t * entry)1521 static void _alsa_mmap_cb (void *user_data, xine_cfg_entry_t *entry) {
1522 alsa_driver_t *this = (alsa_driver_t *)user_data;
1523 this->mmap = entry->num_value;
1524 }
1525
_alsa_speaker_arrangement_cb(void * user_data,xine_cfg_entry_t * entry)1526 static void _alsa_speaker_arrangement_cb (void *user_data, xine_cfg_entry_t *entry) {
1527 alsa_driver_t *this = (alsa_driver_t *)user_data;
1528 alsa_apply_speaker_arrangement (this, entry->num_value);
1529 }
1530
_alsa_safe_strdup(const char * s)1531 static char *_alsa_safe_strdup (const char *s) {
1532 return s ? strdup (s) : NULL;
1533 }
1534
_alsa_dev_name_cb(void * user_data,xine_cfg_entry_t * entry)1535 static void _alsa_dev_name_cb (void *user_data, xine_cfg_entry_t *entry) {
1536 struct _alsa_dev_info_s *info = (struct _alsa_dev_info_s *)user_data;
1537 free (info->name);
1538 info->name = _alsa_safe_strdup (entry->str_value);
1539 }
1540
_alsa_query_dev(alsa_driver_t * this,uint32_t index)1541 static int _alsa_query_dev (alsa_driver_t *this, uint32_t index) {
1542 char logbuf[2048], *q = logbuf, *logend = logbuf + sizeof (logbuf);
1543 struct _alsa_dev_info_s *info = &this->devs[index];
1544 const char *name;
1545 uint32_t u;
1546 int err;
1547
1548 name = info->name ? info->name : "default";
1549 if (1) {
1550 /* skip doubles */
1551 for (u = 0; u < sizeof (this->devs) / sizeof (this->devs[0]); u++) {
1552 if (u != index) {
1553 const char *alias = this->devs[u].name ? this->devs[u].name : "default";
1554 if (!strcmp (name, alias) && this->devs[u].supported_channels) {
1555 info->capabilities = this->devs[u].capabilities;
1556 info->supported_channels = this->devs[u].supported_channels;
1557 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1558 "audio_alsa_out: already probed \"%s\" for%s.\n", name, info->type);
1559 return 2;
1560 }
1561 }
1562 }
1563 }
1564 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1565 "audio_alsa_out: probing \"%s\" for%s ...\n", name, info->type);
1566 q += strlcpy (q, "audio_alsa_out: ", logend - q);
1567 if (q > logend)
1568 q = logend;
1569 q += strlcpy (q, name, logend - q);
1570 if (q > logend)
1571 q = logend;
1572 q += strlcpy (q, ": ", logend - q);
1573 if (q > logend)
1574 q = logend;
1575
1576 err = snd_pcm_open (&this->audio_fd, name, SND_PCM_STREAM_PLAYBACK, 1); /* NON-BLOCK mode */
1577 if (err < 0) {
1578 xine_log (this->class->xine, XINE_LOG_MSG,
1579 _("snd_pcm_open() failed:%d:%s\n"), err, snd_strerror (err));
1580 xine_log (this->class->xine, XINE_LOG_MSG,
1581 _(">>> Check if another program already uses PCM <<<\n"));
1582 return 0;
1583 }
1584
1585 err = snd_pcm_hw_params_any (this->audio_fd, this->hw_params);
1586 if (err < 0) {
1587 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1588 "audio_alsa_out: broken configuration for this PCM: no configurations available.\n");
1589 snd_pcm_close (this->audio_fd);
1590 this->audio_fd = NULL;
1591 return 0;
1592 }
1593
1594 info->capabilities &= ~(AO_CAP_8BITS | AO_CAP_16BITS | AO_CAP_24BITS | AO_CAP_FLOAT32);
1595 if (!(snd_pcm_hw_params_test_format (this->audio_fd, this->hw_params, SND_PCM_FORMAT_U8))) {
1596 info->capabilities |= AO_CAP_8BITS;
1597 q += strlcpy (q, this->bits_names[0], logend - q);
1598 if (q > logend)
1599 q = logend;
1600 }
1601 if (!(snd_pcm_hw_params_test_format (this->audio_fd, this->hw_params, SND_PCM_FORMAT_S16))) {
1602 info->capabilities |= AO_CAP_16BITS;
1603 q += strlcpy (q, this->bits_names[1], logend - q);
1604 if (q > logend)
1605 q = logend;
1606 }
1607 if (!(snd_pcm_hw_params_test_format (this->audio_fd, this->hw_params, SND_PCM_FORMAT_S24))) {
1608 info->capabilities |= AO_CAP_24BITS;
1609 q += strlcpy (q, this->bits_names[2], logend - q);
1610 if (q > logend)
1611 q = logend;
1612 }
1613 if (!(snd_pcm_hw_params_test_format (this->audio_fd, this->hw_params, SND_PCM_FORMAT_FLOAT))) {
1614 info->capabilities |= AO_CAP_FLOAT32;
1615 q += strlcpy (q, this->bits_names[3], logend - q);
1616 if (q > logend)
1617 q = logend;
1618 }
1619 if (0 == (info->capabilities & (AO_CAP_FLOAT32 | AO_CAP_24BITS | AO_CAP_16BITS | AO_CAP_8BITS))) {
1620 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "audio_alsa_out: no supported PCM format found.\n");
1621 snd_pcm_close (this->audio_fd);
1622 this->audio_fd = NULL;
1623 return 0;
1624 }
1625
1626 info->supported_channels = 0;
1627 if (!(snd_pcm_hw_params_test_channels (this->audio_fd, this->hw_params, 1))) {
1628 info->supported_channels |= 1 << 1;
1629 q += strlcpy (q, this->devs[0].type, logend - q);
1630 if (q > logend)
1631 q = logend;
1632 }
1633 if (!(snd_pcm_hw_params_test_channels (this->audio_fd, this->hw_params, 2))) {
1634 info->supported_channels |= 1 << 2;
1635 q += strlcpy (q, this->devs[1].type, logend - q);
1636 if (q > logend)
1637 q = logend;
1638 }
1639 if (!(snd_pcm_hw_params_test_channels (this->audio_fd, this->hw_params, 4))) {
1640 info->supported_channels |= 1 << 4;
1641 q += strlcpy (q, this->devs[2].type, logend - q);
1642 if (q > logend)
1643 q = logend;
1644 }
1645 if (!(snd_pcm_hw_params_test_channels (this->audio_fd, this->hw_params, 6))) {
1646 info->supported_channels |= 1 << 6;
1647 q += strlcpy (q, this->devs[3].type, logend - q);
1648 if (q > logend)
1649 q = logend;
1650 }
1651
1652 err = snd_pcm_hw_params_set_access (this->audio_fd, this->hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
1653 if (err < 0) {
1654 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG,
1655 "audio_alsa_out: interleaved access not available.");
1656 snd_pcm_close (this->audio_fd);
1657 this->audio_fd = NULL;
1658 return 0;
1659 }
1660
1661 if (index == 4) {
1662 info->capabilities |= AO_CAP_MODE_A52 | AO_CAP_MODE_AC5;
1663 q += strlcpy (q, this->devs[4].type, logend - q);
1664 if (q > logend)
1665 q = logend;
1666 }
1667
1668 xprintf (this->class->xine, XINE_VERBOSITY_DEBUG, "%s.\n", logbuf);
1669
1670 this->supported_channels = 0;
1671 for (u = 0; u < sizeof (this->devs) / sizeof (this->devs[0]); u++) {
1672 this->supported_channels |= this->devs[u].supported_channels;
1673 if (u != index) {
1674 const char *alias = this->devs[u].name ? this->devs[u].name : "default";
1675 if (!strcmp (name, alias)) {
1676 this->devs[u].capabilities = info->capabilities;
1677 this->devs[u].supported_channels = info->supported_channels;
1678 }
1679 }
1680 }
1681
1682 snd_pcm_close (this->audio_fd);
1683 this->audio_fd = NULL;
1684 return 1;
1685 }
1686
1687 /*
1688 * Initialize plugin
1689 */
1690
open_plugin(audio_driver_class_t * class_gen,const void * data)1691 static ao_driver_t *open_plugin (audio_driver_class_t *class_gen, const void *data) {
1692 alsa_class_t *class = (alsa_class_t *) class_gen;
1693 config_values_t *config = class->xine->config;
1694 alsa_driver_t *this;
1695
1696 (void)data;
1697 {
1698 uint32_t s_this = (sizeof (*this) + 15) & ~15u;
1699 uint32_t s_hw_params = (snd_pcm_hw_params_sizeof () + 15) & ~15u;
1700 uint32_t s_sw_params = (snd_pcm_sw_params_sizeof () + 15) & ~15u;
1701 uint32_t s_ac_mask = (snd_pcm_access_mask_sizeof () + 15) & ~15u;
1702 uint32_t s_card_info = (snd_ctl_card_info_sizeof () + 15) & ~15u;
1703 uint32_t s_status = (snd_pcm_status_sizeof () + 15) & ~15u;
1704 uint8_t *m = calloc (1, s_this + s_hw_params + s_sw_params + s_ac_mask + s_card_info + s_status);
1705 if (!m)
1706 return NULL;
1707 this = (alsa_driver_t *)m;
1708 m += s_this;
1709 this->hw_params = (snd_pcm_hw_params_t *)m;
1710 m += s_hw_params;
1711 this->sw_params = (snd_pcm_sw_params_t *)m;
1712 m += s_sw_params;
1713 this->ac_mask = (snd_pcm_access_mask_t *)m;
1714 m += s_ac_mask;
1715 this->card_info = (snd_ctl_card_info_t *)m;
1716 m += s_card_info;
1717 this->pcm_status = (snd_pcm_status_t *)m;
1718 }
1719 #ifndef HAVE_ZERO_SAFE_MEM
1720 this->devs[0].supported_channels = 0;
1721 this->devs[0].capabilities = 0;
1722 this->devs[1].supported_channels = 0;
1723 this->devs[1].capabilities = 0;
1724 this->devs[2].supported_channels = 0;
1725 this->devs[2].capabilities = 0;
1726 this->devs[3].supported_channels = 0;
1727 this->devs[3].capabilities = 0;
1728 this->devs[4].supported_channels = 0;
1729 this->devs[4].capabilities = 0;
1730 this->capabilities = 0;
1731 this->has_pause_resume = 0; /* This is checked at open time instead */
1732 this->is_paused = 0;
1733 this->output_sample_rate = 0;
1734 #endif
1735
1736 this->class = class;
1737
1738 this->bits_names[0] = _(" 8bit");
1739 this->bits_names[1] = _(" 16bit");
1740 this->bits_names[2] = _(" 24bit");
1741 this->bits_names[3] = _(" 32bit");
1742
1743 this->mmap = config->register_bool (config, "audio.device.alsa_mmap_enable", 0,
1744 _("sound card can do mmap"),
1745 _("Enable this, if your sound card and alsa driver support memory mapped IO.\n"
1746 "You can try enabling it and check, if everything works. If it does, this "
1747 "will increase performance."),
1748 10, _alsa_mmap_cb, this);
1749
1750 this->devs[0].this = this;
1751 this->devs[0].type = _(" mono");
1752 this->devs[0].config_key = "audio.device.alsa_default_device";
1753 this->devs[0].name = _alsa_safe_strdup (config->register_string (config,
1754 this->devs[0].config_key,
1755 "default",
1756 _("device used for mono output"),
1757 _("xine will use this alsa device to output mono sound.\n"
1758 "See the alsa documentation for information on alsa devices."),
1759 10, _alsa_dev_name_cb, &this->devs[0]));
1760
1761 this->devs[1].this = this;
1762 this->devs[1].type = _(" stereo");
1763 this->devs[1].config_key = "audio.device.alsa_front_device";
1764 this->devs[1].name = _alsa_safe_strdup (config->register_string (config,
1765 this->devs[1].config_key,
1766 "plug:front:default",
1767 _("device used for stereo output"),
1768 _("xine will use this alsa device to output stereo sound.\n"
1769 "See the alsa documentation for information on alsa devices."),
1770 10, _alsa_dev_name_cb, &this->devs[1]));
1771
1772 this->devs[2].this = this;
1773 this->devs[2].type = _(" 4-channel");
1774 this->devs[2].config_key = "audio.device.alsa_surround40_device";
1775 this->devs[2].name = _alsa_safe_strdup (config->register_string (config,
1776 this->devs[2].config_key,
1777 "plug:surround40:0",
1778 _("device used for 4-channel output"),
1779 _("xine will use this alsa device to output 4 channel (4.0) surround sound.\n"
1780 "See the alsa documentation for information on alsa devices."),
1781 10, _alsa_dev_name_cb, &this->devs[2]));
1782
1783 this->devs[3].this = this;
1784 this->devs[3].type = _(" 5.1-channel");
1785 this->devs[3].config_key = "audio.device.alsa_surround51_device";
1786 this->devs[3].name = _alsa_safe_strdup (config->register_string (config,
1787 this->devs[3].config_key,
1788 "plug:surround51:0",
1789 _("device used for 5.1-channel output"),
1790 _("xine will use this alsa device to output 5 channel plus LFE (5.1) surround sound.\n"
1791 "See the alsa documentation for information on alsa devices."),
1792 10, _alsa_dev_name_cb, &this->devs[3]));
1793
1794 this->devs[4].this = this;
1795 this->devs[4].type = _(" a/52 and DTS pass-through");
1796 this->devs[4].config_key = "audio.device.alsa_passthrough_device";
1797 this->devs[4].name = _alsa_safe_strdup (config->register_string (config,
1798 this->devs[4].config_key,
1799 "iec958:AES0=0x6,AES1=0x82,AES2=0x0,AES3=0x2",
1800 _("device used for a/52 and DTS pass-through"),
1801 _("xine will use this alsa device to output undecoded digital surround sound. "
1802 "This can be used be external surround decoders.\n"
1803 "See the alsa documentation for information on alsa devices."),
1804 10, _alsa_dev_name_cb, &this->devs[4]));
1805
1806 {
1807 int err = snd_lib_error_set_handler (error_callback);
1808 if (err < 0)
1809 xine_log (this->class->xine, XINE_LOG_MSG, _("snd_lib_error_set_handler() failed: %d"), err);
1810 }
1811
1812 {
1813 AUDIO_DEVICE_SPEAKER_ARRANGEMENT_TYPES;
1814 int speakers;
1815
1816 /* for usability reasons, keep this in sync with audio_oss_out.c */
1817 speakers = config->register_enum (config,
1818 "audio.output.speaker_arrangement",
1819 STEREO,
1820 (char **)speaker_arrangement,
1821 AUDIO_DEVICE_SPEAKER_ARRANGEMENT_HELP,
1822 0, _alsa_speaker_arrangement_cb, this);
1823
1824 /* query the devices we might need, and stop at stereo. */
1825 switch (speakers) {
1826 case A52_PASSTHRU:
1827 if (_alsa_query_dev (this, 4))
1828 this->capabilities |= this->devs[4].capabilities;
1829 /* fall through */
1830 case SURROUND71:
1831 case SURROUND61:
1832 case SURROUND6:
1833 case SURROUND51:
1834 case SURROUND5:
1835 case SURROUND41:
1836 if (_alsa_query_dev (this, 3)) {
1837 this->capabilities |= this->devs[3].capabilities;
1838 break;
1839 }
1840 /* fall through */
1841 case SURROUND4:
1842 if (_alsa_query_dev (this, 2))
1843 this->capabilities |= this->devs[2].capabilities;
1844 break;
1845 default: ;
1846 }
1847 if (_alsa_query_dev (this, 0))
1848 this->capabilities |= this->devs[0].capabilities;
1849 if (_alsa_query_dev (this, 1)) {
1850 this->capabilities |= this->devs[1].capabilities;
1851 } else {
1852 /* Fallback to "default" if device "front" does not exist.
1853 * Needed for some very basic sound cards. */
1854 config->update_string (config, this->devs[1].config_key, "default");
1855 if (_alsa_query_dev (this, 1))
1856 this->capabilities |= this->devs[1].capabilities;
1857 }
1858
1859 alsa_apply_speaker_arrangement (this, speakers);
1860 }
1861
1862 do {
1863 /* printf("audio_alsa_out: capabilities 0x%X\n",this->capabilities); */
1864 if (!this->capabilities)
1865 break;
1866
1867 config->register_string (config,
1868 "audio.device.alsa_mixer_name",
1869 "PCM",
1870 _("alsa mixer device"),
1871 _("xine will use this alsa mixer device to change the volume.\n"
1872 "See the alsa documentation for information on alsa devices."),
1873 10, NULL, NULL);
1874 this->mixer.name = config->lookup_string(config, "audio.device.alsa_mixer_name");
1875 if (!this->mixer.name)
1876 break;
1877
1878 pthread_mutex_init (&this->mixer.mutex, NULL);
1879 ao_alsa_mixer_init (&this->ao_driver);
1880
1881 this->ao_driver.get_capabilities = ao_alsa_get_capabilities;
1882 this->ao_driver.get_property = ao_alsa_get_property;
1883 this->ao_driver.set_property = ao_alsa_set_property;
1884 this->ao_driver.open = ao_alsa_open;
1885 this->ao_driver.num_channels = ao_alsa_num_channels;
1886 this->ao_driver.bytes_per_frame = ao_alsa_bytes_per_frame;
1887 this->ao_driver.delay = ao_alsa_delay;
1888 this->ao_driver.write = ao_alsa_write;
1889 this->ao_driver.close = ao_alsa_close;
1890 this->ao_driver.exit = ao_alsa_exit;
1891 this->ao_driver.get_gap_tolerance = ao_alsa_get_gap_tolerance;
1892 this->ao_driver.control = ao_alsa_ctrl;
1893
1894 return &this->ao_driver;
1895 } while (0);
1896
1897 config->unregister_callbacks (config, NULL, NULL, this, sizeof (*this));
1898 {
1899 uint32_t u;
1900 for (u = 0; u < sizeof (this->devs) / sizeof (this->devs[0]); u++) {
1901 _x_freep (&this->devs[u].name);
1902 }
1903 }
1904 free (this);
1905 return NULL;
1906 }
1907
1908
1909
1910 /*
1911 * class functions
1912 */
init_class(xine_t * xine,const void * data)1913 static void *init_class (xine_t *xine, const void *data) {
1914 alsa_class_t *this;
1915
1916 (void)data;
1917 this = calloc(1, sizeof (alsa_class_t));
1918 if (!this)
1919 return NULL;
1920
1921 this->driver_class.open_plugin = open_plugin;
1922 this->driver_class.identifier = "alsa";
1923 this->driver_class.description = N_("xine audio output plugin using alsa-compliant audio devices/drivers");
1924 this->driver_class.dispose = default_audio_driver_class_dispose;
1925
1926 /* this->config = xine->config; */
1927 this->xine = xine;
1928 return this;
1929 }
1930
1931 static ao_info_t ao_info_alsa = {
1932 .priority = 10,
1933 };
1934
1935 /*
1936 * exported plugin catalog entry
1937 */
1938
1939 const plugin_info_t xine_plugin_info[] EXPORTED = {
1940 /* type, API, "name", version, special_info, init_function */
1941 { PLUGIN_AUDIO_OUT, AO_OUT_ALSA_IFACE_VERSION, "alsa", XINE_VERSION_CODE, &ao_info_alsa, init_class },
1942 { PLUGIN_NONE, 0, NULL, 0, NULL, NULL }
1943 };
1944