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