1 /*
2 * ALSA Output Plugin for Audacious
3 * Copyright 2009-2014 John Lindgren
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions, and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions, and the following disclaimer in the documentation
13 * provided with the distribution.
14 *
15 * This software is provided "as is" and without any warranty, express or
16 * implied. In no event shall the authors be liable for any damages arising from
17 * the use of this software.
18 */
19
20 /*
21 * Because ALSA is not thread-safe (despite claims to the contrary) we use non-
22 * blocking output in the pump thread with the mutex locked, then unlock the
23 * mutex and wait for more room in the buffer with poll() while other threads
24 * lock the mutex and read the output time. We poll a pipe of our own as well
25 * as the ALSA file descriptors so that we can wake up the pump thread when
26 * needed.
27 *
28 * When paused, or when it comes to the end of the data given it, the pump will
29 * wait on alsa_cond for the signal to continue. When it has more data waiting,
30 * however, it will be sitting in poll() waiting for ALSA's signal that more
31 * data can be written.
32 *
33 * * After adding more data to the buffer, and after resuming from pause,
34 * signal on alsa_cond to wake the pump. (There is no need to signal when
35 * entering pause.)
36 * * After setting the pump_quit flag, signal on alsa_cond AND the poll_pipe
37 * before joining the thread.
38 */
39
40 #include <assert.h>
41 #include <errno.h>
42 #include <poll.h>
43 #include <pthread.h>
44 #include <stdint.h>
45 #include <time.h>
46 #include <unistd.h>
47
48 #include <alsa/asoundlib.h>
49 #include <libaudcore/ringbuf.h>
50
51 #include "alsa.h"
52
53 EXPORT ALSAPlugin aud_plugin_instance;
54
55 #define CHECK_VAL_RECOVER(value, function, ...) \
56 do { \
57 (value) = function (__VA_ARGS__); \
58 if ((value) < 0) { \
59 CHECK (snd_pcm_recover, alsa_handle, (value), 0); \
60 CHECK_VAL ((value), function, __VA_ARGS__); \
61 } \
62 } while (0)
63
64 #define CHECK_RECOVER(function, ...) \
65 do { \
66 int CHECK_RECOVER_error; \
67 CHECK_VAL_RECOVER (CHECK_RECOVER_error, function, __VA_ARGS__); \
68 } while (0)
69
70 static snd_pcm_t * alsa_handle;
71 static pthread_mutex_t alsa_mutex = PTHREAD_MUTEX_INITIALIZER;
72 static pthread_cond_t alsa_cond = PTHREAD_COND_INITIALIZER;
73
74 static snd_pcm_format_t alsa_format;
75 static int alsa_channels, alsa_rate;
76
77 static RingBuf<char> alsa_buffer;
78 static int alsa_period; /* milliseconds */
79
80 static bool alsa_prebuffer, alsa_paused;
81 static int alsa_paused_delay; /* milliseconds */
82
83 static int poll_pipe[2];
84 static int poll_count;
85 static pollfd * poll_handles;
86
87 static bool pump_quit;
88 static pthread_t pump_thread;
89
90 static snd_mixer_t * alsa_mixer;
91 static snd_mixer_elem_t * alsa_mixer_element;
92
poll_setup()93 static bool poll_setup ()
94 {
95 if (pipe (poll_pipe))
96 {
97 AUDERR ("Failed to create pipe: %s.\n", strerror (errno));
98 return false;
99 }
100
101 if (fcntl (poll_pipe[0], F_SETFL, O_NONBLOCK))
102 {
103 AUDERR ("Failed to set O_NONBLOCK on pipe: %s.\n", strerror (errno));
104 close (poll_pipe[0]);
105 close (poll_pipe[1]);
106 return false;
107 }
108
109 poll_count = 1 + snd_pcm_poll_descriptors_count (alsa_handle);
110 poll_handles = new pollfd[poll_count];
111 poll_handles[0].fd = poll_pipe[0];
112 poll_handles[0].events = POLLIN;
113 poll_count = 1 + snd_pcm_poll_descriptors (alsa_handle, poll_handles + 1,
114 poll_count - 1);
115
116 return true;
117 }
118
poll_sleep()119 static void poll_sleep ()
120 {
121 if (poll (poll_handles, poll_count, -1) < 0)
122 {
123 AUDERR ("Failed to poll: %s.\n", strerror (errno));
124 return;
125 }
126
127 if (poll_handles[0].revents & POLLIN)
128 {
129 char c;
130 while (read (poll_pipe[0], & c, 1) == 1)
131 ;
132 }
133 }
134
poll_wake()135 static void poll_wake ()
136 {
137 const char c = 0;
138 if (write (poll_pipe[1], & c, 1) < 0)
139 AUDERR ("Failed to write to pipe: %s.\n", strerror (errno));
140 }
141
poll_cleanup()142 static void poll_cleanup ()
143 {
144 close (poll_pipe[0]);
145 close (poll_pipe[1]);
146 delete[] poll_handles;
147 }
148
pump(void *)149 static void * pump (void *)
150 {
151 pthread_mutex_lock (& alsa_mutex);
152
153 bool failed_once = false;
154 bool use_timed_wait = false;
155 int wakeups_since_write = 0;
156
157 while (! pump_quit)
158 {
159 int writable = snd_pcm_bytes_to_frames (alsa_handle, alsa_buffer.linear ());
160
161 if (alsa_prebuffer || alsa_paused || ! writable)
162 {
163 pthread_cond_wait (& alsa_cond, & alsa_mutex);
164 continue;
165 }
166
167 int avail;
168 CHECK_VAL_RECOVER (avail, snd_pcm_avail_update, alsa_handle);
169
170 if (avail)
171 {
172 wakeups_since_write = 0;
173
174 int written;
175 CHECK_VAL_RECOVER (written, snd_pcm_writei, alsa_handle,
176 & alsa_buffer[0], aud::min (writable, avail));
177
178 failed_once = false;
179
180 alsa_buffer.discard (snd_pcm_frames_to_bytes (alsa_handle, written));
181
182 pthread_cond_broadcast (& alsa_cond); /* signal write complete */
183
184 if (writable < avail)
185 continue;
186 }
187
188 pthread_mutex_unlock (& alsa_mutex);
189
190 if (wakeups_since_write > 4)
191 {
192 AUDDBG ("Activating timer workaround.\n");
193 use_timed_wait = true;
194 }
195
196 if (use_timed_wait && wakeups_since_write)
197 {
198 timespec delay = {0, 600000 * alsa_period};
199 nanosleep (& delay, nullptr);
200 }
201 else
202 {
203 poll_sleep ();
204 wakeups_since_write ++;
205 }
206
207 pthread_mutex_lock (& alsa_mutex);
208 continue;
209
210 FAILED:
211 if (failed_once)
212 break;
213
214 failed_once = true;
215 CHECK (snd_pcm_prepare, alsa_handle);
216 }
217
218 pthread_mutex_unlock (& alsa_mutex);
219 return nullptr;
220 }
221
pump_start()222 static void pump_start ()
223 {
224 AUDDBG ("Starting pump.\n");
225 pthread_create (& pump_thread, nullptr, pump, nullptr);
226 }
227
pump_stop()228 static void pump_stop ()
229 {
230 AUDDBG ("Stopping pump.\n");
231 pump_quit = true;
232 poll_wake ();
233 pthread_cond_broadcast (& alsa_cond);
234 pthread_mutex_unlock (& alsa_mutex);
235 pthread_join (pump_thread, nullptr);
236 pthread_mutex_lock (& alsa_mutex);
237 pump_quit = false;
238 }
239
start_playback()240 static void start_playback ()
241 {
242 AUDDBG ("Starting playback.\n");
243 CHECK (snd_pcm_prepare, alsa_handle);
244
245 FAILED:
246 alsa_prebuffer = false;
247 pthread_cond_broadcast (& alsa_cond);
248 }
249
get_delay_locked()250 static int get_delay_locked ()
251 {
252 snd_pcm_sframes_t delay = 0;
253 CHECK_RECOVER (snd_pcm_delay, alsa_handle, & delay);
254
255 FAILED:
256 return aud::rescale ((int) delay, alsa_rate, 1000);
257 }
258
init()259 bool ALSAPlugin::init ()
260 {
261 AUDDBG ("Initialize.\n");
262 init_config ();
263 open_mixer ();
264 return true;
265 }
266
cleanup()267 void ALSAPlugin::cleanup ()
268 {
269 AUDDBG ("Cleanup.\n");
270 close_mixer ();
271 }
272
convert_aud_format(int aud_format)273 static snd_pcm_format_t convert_aud_format (int aud_format)
274 {
275 static const struct
276 {
277 int aud_format;
278 snd_pcm_format_t format;
279 }
280 table[] =
281 {
282 {FMT_FLOAT, SND_PCM_FORMAT_FLOAT},
283 {FMT_S8, SND_PCM_FORMAT_S8},
284 {FMT_U8, SND_PCM_FORMAT_U8},
285 {FMT_S16_LE, SND_PCM_FORMAT_S16_LE},
286 {FMT_S16_BE, SND_PCM_FORMAT_S16_BE},
287 {FMT_U16_LE, SND_PCM_FORMAT_U16_LE},
288 {FMT_U16_BE, SND_PCM_FORMAT_U16_BE},
289 {FMT_S24_LE, SND_PCM_FORMAT_S24_LE},
290 {FMT_S24_BE, SND_PCM_FORMAT_S24_BE},
291 {FMT_U24_LE, SND_PCM_FORMAT_U24_LE},
292 {FMT_U24_BE, SND_PCM_FORMAT_U24_BE},
293 {FMT_S32_LE, SND_PCM_FORMAT_S32_LE},
294 {FMT_S32_BE, SND_PCM_FORMAT_S32_BE},
295 {FMT_U32_LE, SND_PCM_FORMAT_U32_LE},
296 {FMT_U32_BE, SND_PCM_FORMAT_U32_BE},
297 {FMT_S24_3LE, SND_PCM_FORMAT_S24_3LE},
298 {FMT_S24_3BE, SND_PCM_FORMAT_S24_3BE},
299 {FMT_U24_3LE, SND_PCM_FORMAT_U24_3LE},
300 {FMT_U24_3BE, SND_PCM_FORMAT_U24_3BE},
301 };
302
303 for (auto & conv : table)
304 {
305 if (conv.aud_format == aud_format)
306 return conv.format;
307 }
308
309 return SND_PCM_FORMAT_UNKNOWN;
310 }
311
open_audio(int aud_format,int rate,int channels,String & error)312 bool ALSAPlugin::open_audio (int aud_format, int rate, int channels, String & error)
313 {
314 int total_buffer, hard_buffer, soft_buffer, buffer_frames;
315 unsigned useconds;
316 int direction;
317
318 pthread_mutex_lock (& alsa_mutex);
319
320 assert (! alsa_handle);
321
322 String pcm = aud_get_str ("alsa", "pcm");
323 snd_pcm_format_t format = convert_aud_format (aud_format);
324
325 if (format == SND_PCM_FORMAT_UNKNOWN)
326 {
327 error = String ("Unsupported audio format");
328 goto FAILED;
329 }
330
331 AUDINFO ("Opening PCM device %s for %s, %d channels, %d Hz.\n",
332 (const char *) pcm, snd_pcm_format_name (format), channels, rate);
333 CHECK_STR (error, snd_pcm_open, & alsa_handle, pcm, SND_PCM_STREAM_PLAYBACK, 0);
334
335 snd_pcm_hw_params_t * params;
336 snd_pcm_hw_params_alloca (& params);
337 CHECK_STR (error, snd_pcm_hw_params_any, alsa_handle, params);
338 CHECK_STR (error, snd_pcm_hw_params_set_access, alsa_handle, params,
339 SND_PCM_ACCESS_RW_INTERLEAVED);
340
341 CHECK_STR (error, snd_pcm_hw_params_set_format, alsa_handle, params, format);
342 CHECK_STR (error, snd_pcm_hw_params_set_channels, alsa_handle, params, channels);
343 CHECK_STR (error, snd_pcm_hw_params_set_rate, alsa_handle, params, rate, 0);
344
345 alsa_format = format;
346 alsa_channels = channels;
347 alsa_rate = rate;
348
349 total_buffer = aud_get_int ("output_buffer_size");
350 useconds = 1000 * aud::min (1000, total_buffer / 2);
351 direction = 0;
352 CHECK_STR (error, snd_pcm_hw_params_set_buffer_time_near, alsa_handle,
353 params, & useconds, & direction);
354 hard_buffer = useconds / 1000;
355
356 useconds = 1000 * (hard_buffer / 4);
357 direction = 0;
358 CHECK_STR (error, snd_pcm_hw_params_set_period_time_near, alsa_handle,
359 params, & useconds, & direction);
360 alsa_period = useconds / 1000;
361
362 CHECK_STR (error, snd_pcm_hw_params, alsa_handle, params);
363
364 soft_buffer = aud::max (total_buffer / 2, total_buffer - hard_buffer);
365 AUDINFO ("Buffer: hardware %d ms, software %d ms, period %d ms.\n",
366 hard_buffer, soft_buffer, alsa_period);
367
368 buffer_frames = aud::rescale<int64_t> (soft_buffer, 1000, rate);
369 alsa_buffer.alloc (snd_pcm_frames_to_bytes (alsa_handle, buffer_frames));
370
371 alsa_prebuffer = true;
372 alsa_paused = false;
373 alsa_paused_delay = 0;
374
375 if (! poll_setup ())
376 goto FAILED;
377
378 pump_start ();
379
380 pthread_mutex_unlock (& alsa_mutex);
381 return true;
382
383 FAILED:
384 if (alsa_handle)
385 {
386 snd_pcm_close (alsa_handle);
387 alsa_handle = nullptr;
388 }
389
390 pthread_mutex_unlock (& alsa_mutex);
391 return false;
392 }
393
close_audio()394 void ALSAPlugin::close_audio ()
395 {
396 AUDDBG ("Closing audio.\n");
397 pthread_mutex_lock (& alsa_mutex);
398
399 assert (alsa_handle);
400
401 pump_stop ();
402 CHECK (snd_pcm_drop, alsa_handle);
403
404 FAILED:
405 alsa_buffer.destroy ();
406 poll_cleanup ();
407 snd_pcm_close (alsa_handle);
408 alsa_handle = nullptr;
409
410 pthread_mutex_unlock (& alsa_mutex);
411 }
412
write_audio(const void * data,int length)413 int ALSAPlugin::write_audio (const void * data, int length)
414 {
415 pthread_mutex_lock (& alsa_mutex);
416
417 length = aud::min (length, alsa_buffer.space ());
418 alsa_buffer.copy_in ((const char *) data, length);
419
420 AUDDBG ("Buffer fill levels: low = %d%%, high = %d%%.\n",
421 (alsa_buffer.len () - length) * 100 / alsa_buffer.size (),
422 alsa_buffer.len () * 100 / alsa_buffer.size ());
423
424 if (! alsa_prebuffer && ! alsa_paused)
425 pthread_cond_broadcast (& alsa_cond);
426
427 pthread_mutex_unlock (& alsa_mutex);
428 return length;
429 }
430
period_wait()431 void ALSAPlugin::period_wait ()
432 {
433 pthread_mutex_lock (& alsa_mutex);
434
435 while (! alsa_buffer.space ())
436 {
437 if (! alsa_paused)
438 {
439 if (alsa_prebuffer)
440 start_playback ();
441 else
442 pthread_cond_broadcast (& alsa_cond);
443 }
444
445 pthread_cond_wait (& alsa_cond, & alsa_mutex);
446 }
447
448 pthread_mutex_unlock (& alsa_mutex);
449 }
450
drain()451 void ALSAPlugin::drain ()
452 {
453 AUDDBG ("Drain.\n");
454 pthread_mutex_lock (& alsa_mutex);
455
456 assert (! alsa_paused);
457
458 if (alsa_prebuffer)
459 start_playback ();
460
461 while (snd_pcm_bytes_to_frames (alsa_handle, alsa_buffer.len ()))
462 pthread_cond_wait (& alsa_cond, & alsa_mutex);
463
464 if (! alsa_prebuffer)
465 {
466 timespec ts {};
467 clock_gettime (CLOCK_REALTIME, & ts);
468
469 int d = get_delay_locked ();
470 ts.tv_sec += d / 1000;
471 ts.tv_nsec += d % 1000 * 1000000;
472
473 if (ts.tv_nsec >= 1000000000)
474 {
475 ts.tv_sec ++;
476 ts.tv_nsec -= 1000000000;
477 }
478
479 /* Avoid calling snd_pcm_delay() after this point, otherwise it
480 * prints a warning that underrun has occurred */
481 alsa_prebuffer = true;
482 alsa_paused_delay = 0;
483
484 poll_wake (); /* wake pump so it's ready */
485 pthread_cond_timedwait (& alsa_cond, & alsa_mutex, & ts);
486 }
487
488 pthread_mutex_unlock (& alsa_mutex);
489 }
490
get_delay()491 int ALSAPlugin::get_delay ()
492 {
493 pthread_mutex_lock (& alsa_mutex);
494
495 int buffered = snd_pcm_bytes_to_frames (alsa_handle, alsa_buffer.len ());
496 int delay = aud::rescale (buffered, alsa_rate, 1000);
497
498 if (alsa_prebuffer || alsa_paused)
499 delay += alsa_paused_delay;
500 else
501 delay += get_delay_locked ();
502
503 pthread_mutex_unlock (& alsa_mutex);
504 return delay;
505 }
506
flush()507 void ALSAPlugin::flush ()
508 {
509 AUDDBG ("Seek requested; discarding buffer.\n");
510 pthread_mutex_lock (& alsa_mutex);
511
512 CHECK (snd_pcm_drop, alsa_handle);
513
514 FAILED:
515 alsa_buffer.discard ();
516
517 alsa_prebuffer = true;
518 alsa_paused_delay = 0;
519
520 poll_wake (); /* wake pump so it's ready */
521 pthread_cond_broadcast (& alsa_cond); /* interrupt period wait */
522 pthread_mutex_unlock (& alsa_mutex);
523 }
524
pause(bool pause)525 void ALSAPlugin::pause (bool pause)
526 {
527 AUDDBG ("%sause.\n", pause ? "P" : "Unp");
528 pthread_mutex_lock (& alsa_mutex);
529
530 alsa_paused = pause;
531
532 if (! alsa_prebuffer)
533 {
534 if (pause)
535 alsa_paused_delay = get_delay_locked ();
536
537 CHECK (snd_pcm_pause, alsa_handle, pause);
538 }
539
540 DONE:
541 if (! alsa_prebuffer && ! pause)
542 pthread_cond_broadcast (& alsa_cond);
543
544 pthread_mutex_unlock (& alsa_mutex);
545 return;
546
547 FAILED:
548 AUDDBG ("Trying to work around broken pause.\n");
549
550 if (pause)
551 snd_pcm_drop (alsa_handle);
552 else
553 snd_pcm_prepare (alsa_handle);
554
555 goto DONE;
556 }
557
open_mixer()558 void ALSAPlugin::open_mixer ()
559 {
560 alsa_mixer = nullptr;
561
562 String mixer = aud_get_str ("alsa", "mixer");
563 String mixer_element = aud_get_str ("alsa", "mixer-element");
564
565 if (! mixer_element[0])
566 goto FAILED;
567
568 AUDDBG ("Opening mixer card %s.\n", (const char *) mixer);
569 CHECK (snd_mixer_open, & alsa_mixer, 0);
570 CHECK (snd_mixer_attach, alsa_mixer, mixer);
571 CHECK (snd_mixer_selem_register, alsa_mixer, nullptr, nullptr);
572 CHECK (snd_mixer_load, alsa_mixer);
573
574 snd_mixer_selem_id_t * selem_id;
575 snd_mixer_selem_id_alloca (& selem_id);
576 snd_mixer_selem_id_set_name (selem_id, mixer_element);
577 alsa_mixer_element = snd_mixer_find_selem (alsa_mixer, selem_id);
578
579 if (! alsa_mixer_element)
580 {
581 AUDERR ("snd_mixer_find_selem failed.\n");
582 goto FAILED;
583 }
584
585 CHECK (snd_mixer_selem_set_playback_volume_range, alsa_mixer_element, 0, 100);
586 return;
587
588 FAILED:
589 if (alsa_mixer)
590 {
591 snd_mixer_close (alsa_mixer);
592 alsa_mixer = nullptr;
593 }
594 }
595
close_mixer()596 void ALSAPlugin::close_mixer ()
597 {
598 if (alsa_mixer)
599 snd_mixer_close (alsa_mixer);
600 }
601
get_volume()602 StereoVolume ALSAPlugin::get_volume ()
603 {
604 pthread_mutex_lock (& alsa_mutex);
605
606 long left = 0, right = 0;
607
608 if (! alsa_mixer)
609 goto FAILED;
610
611 CHECK (snd_mixer_handle_events, alsa_mixer);
612
613 if (snd_mixer_selem_is_playback_mono (alsa_mixer_element))
614 {
615 CHECK (snd_mixer_selem_get_playback_volume, alsa_mixer_element,
616 SND_MIXER_SCHN_MONO, & left);
617 right = left;
618
619 if (snd_mixer_selem_has_playback_switch (alsa_mixer_element))
620 {
621 int on = 0;
622 CHECK (snd_mixer_selem_get_playback_switch, alsa_mixer_element,
623 SND_MIXER_SCHN_MONO, & on);
624
625 if (! on)
626 left = right = 0;
627 }
628 }
629 else
630 {
631 CHECK (snd_mixer_selem_get_playback_volume, alsa_mixer_element,
632 SND_MIXER_SCHN_FRONT_LEFT, & left);
633 CHECK (snd_mixer_selem_get_playback_volume, alsa_mixer_element,
634 SND_MIXER_SCHN_FRONT_RIGHT, & right);
635
636 if (snd_mixer_selem_has_playback_switch (alsa_mixer_element))
637 {
638 int left_on = 0, right_on = 0;
639 CHECK (snd_mixer_selem_get_playback_switch, alsa_mixer_element,
640 SND_MIXER_SCHN_FRONT_LEFT, & left_on);
641 CHECK (snd_mixer_selem_get_playback_switch, alsa_mixer_element,
642 SND_MIXER_SCHN_FRONT_RIGHT, & right_on);
643
644 if (! left_on)
645 left = 0;
646 if (! right_on)
647 right = 0;
648 }
649 }
650
651 FAILED:
652 pthread_mutex_unlock (& alsa_mutex);
653 return {(int) left, (int) right};
654 }
655
set_volume(StereoVolume v)656 void ALSAPlugin::set_volume (StereoVolume v)
657 {
658 pthread_mutex_lock (& alsa_mutex);
659
660 if (! alsa_mixer)
661 goto FAILED;
662
663 if (snd_mixer_selem_is_playback_mono (alsa_mixer_element))
664 {
665 CHECK (snd_mixer_selem_set_playback_volume, alsa_mixer_element,
666 SND_MIXER_SCHN_MONO, aud::max (v.left, v.right));
667
668 if (snd_mixer_selem_has_playback_switch (alsa_mixer_element))
669 CHECK (snd_mixer_selem_set_playback_switch, alsa_mixer_element,
670 SND_MIXER_SCHN_MONO, aud::max (v.left, v.right) != 0);
671 }
672 else
673 {
674 CHECK (snd_mixer_selem_set_playback_volume, alsa_mixer_element,
675 SND_MIXER_SCHN_FRONT_LEFT, v.left);
676 CHECK (snd_mixer_selem_set_playback_volume, alsa_mixer_element,
677 SND_MIXER_SCHN_FRONT_RIGHT, v.right);
678
679 if (snd_mixer_selem_has_playback_switch (alsa_mixer_element))
680 {
681 if (snd_mixer_selem_has_playback_switch_joined (alsa_mixer_element))
682 CHECK (snd_mixer_selem_set_playback_switch, alsa_mixer_element,
683 SND_MIXER_SCHN_FRONT_LEFT, aud::max (v.left, v.right) != 0);
684 else
685 {
686 CHECK (snd_mixer_selem_set_playback_switch, alsa_mixer_element,
687 SND_MIXER_SCHN_FRONT_LEFT, v.left != 0);
688 CHECK (snd_mixer_selem_set_playback_switch, alsa_mixer_element,
689 SND_MIXER_SCHN_FRONT_RIGHT, v.right != 0);
690 }
691 }
692 }
693
694 CHECK (snd_mixer_handle_events, alsa_mixer);
695
696 FAILED:
697 pthread_mutex_unlock (& alsa_mutex);
698 }
699