cefbd0cea6ed5a9b846185cceb786761693261fc
[platform/upstream/gst-plugins-base.git] / ext / alsa / gstalsamixer.c
1 /* ALSA mixer implementation.
2  * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /**
21  * SECTION:element-alsamixer
22  * @see_also: alsasink, alsasrc
23  *
24  * This element controls various aspects such as the volume and balance
25  * of an audio device using the ALSA api.
26  *
27  * The application should query and use the interfaces provided by this 
28  * element to control the device.
29  *
30  * Last reviewed on 2006-03-01 (0.10.4)
31  */
32
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36
37 #include "gstalsamixer.h"
38
39 static void gst_alsa_mixer_update_option (GstAlsaMixer * mixer,
40     GstAlsaMixerOptions * alsa_opts);
41 static void gst_alsa_mixer_update_track (GstAlsaMixer * mixer,
42     GstAlsaMixerTrack * alsa_track);
43 static int gst_alsa_mixer_handle_callback (snd_mixer_t * handle,
44     unsigned int mask, snd_mixer_elem_t * elem);
45
46 /* First some utils, then the mixer implementation */
47 static gboolean
48 gst_alsa_mixer_open (GstAlsaMixer * mixer)
49 {
50   gint err;
51   snd_ctl_t *ctl;
52   snd_ctl_card_info_t *card_info;
53
54   g_return_val_if_fail (mixer->handle == NULL, FALSE);
55
56   /* open and initialize the mixer device */
57   err = snd_mixer_open (&mixer->handle, 0);
58   if (err < 0 || mixer->handle == NULL)
59     goto open_failed;
60
61   if ((err = snd_mixer_attach (mixer->handle, mixer->device)) < 0) {
62     GST_WARNING ("Cannot open mixer for sound device '%s': %s", mixer->device,
63         snd_strerror (err));
64     goto error;
65   }
66
67   if ((err = snd_mixer_selem_register (mixer->handle, NULL, NULL)) < 0) {
68     GST_WARNING ("Cannot register mixer elements: %s", snd_strerror (err));
69     goto error;
70   }
71
72   if ((err = snd_mixer_load (mixer->handle)) < 0) {
73     GST_WARNING ("Cannot load mixer settings: %s", snd_strerror (err));
74     goto error;
75   }
76
77   snd_mixer_set_callback_private (mixer->handle, mixer);
78   snd_mixer_set_callback (mixer->handle, gst_alsa_mixer_handle_callback);
79
80   /* now get the device name, any of this is not fatal */
81   g_free (mixer->cardname);
82   if ((err = snd_ctl_open (&ctl, mixer->device, 0)) < 0) {
83     GST_WARNING ("Cannot open CTL: %s", snd_strerror (err));
84     goto no_card_name;
85   }
86
87   snd_ctl_card_info_malloc (&card_info);
88   if ((err = snd_ctl_card_info (ctl, card_info)) < 0) {
89     GST_WARNING ("Cannot get card info: %s", snd_strerror (err));
90     snd_ctl_close (ctl);
91     goto no_card_name;
92   }
93
94   mixer->cardname = g_strdup (snd_ctl_card_info_get_name (card_info));
95   GST_DEBUG ("Card name = %s", GST_STR_NULL (mixer->cardname));
96   snd_ctl_card_info_free (card_info);
97   snd_ctl_close (ctl);
98
99 no_card_name:
100   if (mixer->cardname == NULL) {
101     mixer->cardname = g_strdup ("Unknown");
102     GST_DEBUG ("Cannot find card name");
103   }
104
105   GST_INFO ("Successfully opened mixer for device '%s'.", mixer->device);
106
107   return TRUE;
108
109   /* ERROR */
110 open_failed:
111   {
112     GST_WARNING ("Cannot open mixer: %s", snd_strerror (err));
113     mixer->handle = NULL;
114     return FALSE;
115   }
116 error:
117   {
118     snd_mixer_close (mixer->handle);
119     mixer->handle = NULL;
120     return FALSE;
121   }
122 }
123
124 static snd_mixer_elem_t *
125 gst_alsa_mixer_find_master_mixer (GstAlsaMixer * mixer, snd_mixer_t * handle)
126 {
127   snd_mixer_elem_t *element;
128   gint i, count;
129
130   count = snd_mixer_get_count (handle);
131
132   /* Check if we have a playback mixer labelled as 'Master' */
133   element = snd_mixer_first_elem (handle);
134   for (i = 0; i < count; i++) {
135     if (snd_mixer_selem_has_playback_volume (element) &&
136         strcmp (snd_mixer_selem_get_name (element), "Master") == 0) {
137       return element;
138     }
139     element = snd_mixer_elem_next (element);
140   }
141
142   /* If not, check if we have a playback mixer labelled as 'Front' */
143   element = snd_mixer_first_elem (handle);
144   for (i = 0; i < count; i++) {
145     if (snd_mixer_selem_has_playback_volume (element) &&
146         strcmp (snd_mixer_selem_get_name (element), "Front") == 0) {
147       return element;
148     }
149     element = snd_mixer_elem_next (element);
150   }
151
152   /* If not, check if we have a playback mixer labelled as 'PCM' */
153   element = snd_mixer_first_elem (handle);
154   for (i = 0; i < count; i++) {
155     if (snd_mixer_selem_has_playback_volume (element) &&
156         strcmp (snd_mixer_selem_get_name (element), "PCM") == 0) {
157       return element;
158     }
159     element = snd_mixer_elem_next (element);
160   }
161
162   /* If not, check if we have a playback mixer labelled as 'Speaker' */
163   element = snd_mixer_first_elem (handle);
164   for (i = 0; i < count; i++) {
165     if (snd_mixer_selem_has_playback_volume (element) &&
166         strcmp (snd_mixer_selem_get_name (element), "Speaker") == 0) {
167       return element;
168     }
169     element = snd_mixer_elem_next (element);
170   }
171
172   /* If not, check if we have a playback mixer with both volume and switch that
173    * is not mono */
174   element = snd_mixer_first_elem (handle);
175   for (i = 0; i < count; i++) {
176     if (snd_mixer_selem_has_playback_volume (element) &&
177         snd_mixer_selem_has_playback_switch (element) &&
178         !snd_mixer_selem_is_playback_mono (element)) {
179       return element;
180     }
181     element = snd_mixer_elem_next (element);
182   }
183
184   /* If not, check if we have any playback mixer with both volume and switch */
185   element = snd_mixer_first_elem (handle);
186   for (i = 0; i < count; i++) {
187     if (snd_mixer_selem_has_playback_volume (element) &&
188         snd_mixer_selem_has_playback_switch (element)) {
189       return element;
190     }
191     element = snd_mixer_elem_next (element);
192   }
193
194   /* If not, take any playback mixer with a volume control */
195   element = snd_mixer_first_elem (handle);
196   for (i = 0; i < count; i++) {
197     if (snd_mixer_selem_has_playback_volume (element)) {
198       return element;
199     }
200     element = snd_mixer_elem_next (element);
201   }
202
203   /* Looks like we're out of luck ... */
204   return NULL;
205 }
206
207 static void
208 gst_alsa_mixer_update (GstAlsaMixer * mixer, snd_mixer_elem_t * elem)
209 {
210   GList *item;
211
212   g_return_if_fail (mixer != NULL);
213
214   g_static_rec_mutex_lock (mixer->rec_mutex);
215
216   for (item = mixer->tracklist; item != NULL; item = item->next) {
217     if (GST_IS_ALSA_MIXER_TRACK (item->data)) {
218       if (elem && (GST_ALSA_MIXER_TRACK (item->data)->element != elem))
219         continue;
220
221       gst_alsa_mixer_update_track (mixer, GST_ALSA_MIXER_TRACK (item->data));
222     } else if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) {
223       if (elem && (GST_ALSA_MIXER_OPTIONS (item->data)->element != elem))
224         continue;
225
226       gst_alsa_mixer_update_option (mixer, GST_ALSA_MIXER_OPTIONS (item->data));
227     }
228   }
229
230   g_static_rec_mutex_unlock (mixer->rec_mutex);
231 }
232
233 static int
234 gst_alsa_mixer_elem_handle_callback (snd_mixer_elem_t * elem, unsigned int mask)
235 {
236   GstAlsaMixer *mixer =
237       (GstAlsaMixer *) snd_mixer_elem_get_callback_private (elem);
238
239   GST_LOG ("ALSA elem cb");
240
241   g_return_val_if_fail (mixer != NULL, 1);
242
243   gst_alsa_mixer_update (mixer, elem);
244
245   return 0;
246 }
247
248 static int
249 gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask,
250     snd_mixer_elem_t * elem)
251 {
252   GstAlsaMixer *mixer =
253       (GstAlsaMixer *) snd_mixer_get_callback_private (handle);
254
255   GST_LOG ("ALSA cb");
256
257   g_return_val_if_fail (mixer != NULL, 1);
258
259   /* Hopefully won't be call recursively and will handle pending elem events */
260   snd_mixer_handle_events (mixer->handle);
261
262   gst_alsa_mixer_update (mixer, elem);
263
264   return 0;
265 }
266
267 static void
268 gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer)
269 {
270   gint i, count;
271   snd_mixer_elem_t *element, *master;
272   GList *item;
273
274   g_return_if_fail (mixer->handle != NULL);
275
276   if (mixer->tracklist)
277     return;
278
279   g_static_rec_mutex_lock (mixer->rec_mutex);
280
281   master = gst_alsa_mixer_find_master_mixer (mixer, mixer->handle);
282
283   count = snd_mixer_get_count (mixer->handle);
284   element = snd_mixer_first_elem (mixer->handle);
285
286   /* build track list
287    *
288    * Some ALSA tracks may have playback and capture capabilities.
289    * Here we model them as two separate GStreamer tracks.
290    */
291
292   for (i = 0; i < count; i++) {
293     GstMixerTrack *play_track = NULL;
294     GstMixerTrack *cap_track = NULL;
295     const gchar *name;
296     GList *item;
297     gint samename = 0;
298
299     name = snd_mixer_selem_get_name (element);
300
301     /* prevent dup names */
302     for (item = mixer->tracklist; item != NULL; item = item->next) {
303       snd_mixer_elem_t *temp;
304
305       if (GST_IS_ALSA_MIXER_OPTIONS (item->data))
306         temp = GST_ALSA_MIXER_OPTIONS (item->data)->element;
307       else
308         temp = GST_ALSA_MIXER_TRACK (item->data)->element;
309
310       if (strcmp (name, snd_mixer_selem_get_name (temp)) == 0)
311         samename++;
312     }
313
314     GST_LOG ("[%s] probing element #%u, mixer->dir=%u", name, i, mixer->dir);
315
316     if (mixer->dir & GST_ALSA_MIXER_PLAYBACK) {
317       gboolean has_playback_switch, has_playback_volume;
318
319       has_playback_switch = snd_mixer_selem_has_playback_switch (element);
320       has_playback_volume = snd_mixer_selem_has_playback_volume (element);
321
322       GST_LOG ("[%s] PLAYBACK: has_playback_volume=%d, has_playback_switch=%d"
323           "%s", name, has_playback_volume, has_playback_switch,
324           (element == master) ? " MASTER" : "");
325
326       if (has_playback_volume) {
327         gint flags = GST_MIXER_TRACK_OUTPUT;
328
329         if (element == master)
330           flags |= GST_MIXER_TRACK_MASTER;
331
332         play_track = gst_alsa_mixer_track_new (element, samename, i,
333             flags, FALSE, NULL, FALSE);
334
335       } else if (has_playback_switch) {
336         /* simple mute switch */
337         play_track = gst_alsa_mixer_track_new (element, samename, i,
338             GST_MIXER_TRACK_OUTPUT, TRUE, NULL, FALSE);
339       }
340
341       if (snd_mixer_selem_is_enumerated (element)) {
342         GstMixerOptions *opts = gst_alsa_mixer_options_new (element, i);
343
344         GST_LOG ("[%s] is enumerated (%d)", name, i);
345         mixer->tracklist = g_list_append (mixer->tracklist, opts);
346       }
347     }
348
349     if (mixer->dir & GST_ALSA_MIXER_CAPTURE) {
350       gboolean has_capture_switch, has_common_switch;
351       gboolean has_capture_volume, has_common_volume;
352
353       has_capture_switch = snd_mixer_selem_has_capture_switch (element);
354       has_common_switch = snd_mixer_selem_has_common_switch (element);
355       has_capture_volume = snd_mixer_selem_has_capture_volume (element);
356       has_common_volume = snd_mixer_selem_has_common_volume (element);
357
358       GST_LOG ("[%s] CAPTURE: has_capture_volume=%d, has_common_volume=%d, "
359           "has_capture_switch=%d, has_common_switch=%d, play_track=%p", name,
360           has_capture_volume, has_common_volume, has_capture_switch,
361           has_common_switch, play_track);
362
363       if (has_capture_volume && !(play_track && has_common_volume)) {
364         cap_track = gst_alsa_mixer_track_new (element, samename, i,
365             GST_MIXER_TRACK_INPUT, FALSE, NULL, play_track != NULL);
366       } else if (has_capture_switch && !(play_track && has_common_switch)) {
367         cap_track = gst_alsa_mixer_track_new (element, samename, i,
368             GST_MIXER_TRACK_INPUT, TRUE, NULL, play_track != NULL);
369       }
370     }
371
372
373     if (play_track && cap_track) {
374       GST_ALSA_MIXER_TRACK (play_track)->shared_mute =
375           GST_ALSA_MIXER_TRACK (cap_track);
376       GST_ALSA_MIXER_TRACK (cap_track)->shared_mute =
377           GST_ALSA_MIXER_TRACK (play_track);
378     }
379
380     if (play_track)
381       mixer->tracklist = g_list_append (mixer->tracklist, play_track);
382
383     if (cap_track)
384       mixer->tracklist = g_list_append (mixer->tracklist, cap_track);
385
386     element = snd_mixer_elem_next (element);
387   }
388
389   for (item = mixer->tracklist; item != NULL; item = item->next) {
390     snd_mixer_elem_t *temp;
391
392     if (GST_IS_ALSA_MIXER_OPTIONS (item->data))
393       temp = GST_ALSA_MIXER_OPTIONS (item->data)->element;
394     else
395       temp = GST_ALSA_MIXER_TRACK (item->data)->element;
396
397     snd_mixer_elem_set_callback (temp, gst_alsa_mixer_elem_handle_callback);
398     snd_mixer_elem_set_callback_private (temp, mixer);
399   }
400
401   g_static_rec_mutex_unlock (mixer->rec_mutex);
402 }
403
404 static void
405 task_monitor_alsa (gpointer data)
406 {
407   struct pollfd *pfds;
408   unsigned int nfds, rnfds;
409   unsigned short revents;
410   GstAlsaMixer *mixer = (GstAlsaMixer *) data;
411
412   g_static_rec_mutex_lock (mixer->rec_mutex);
413
414   nfds = snd_mixer_poll_descriptors_count (mixer->handle);
415   if (nfds <= 0) {
416     GST_ERROR ("snd_mixer_poll_descriptors_count <= 0: %d", nfds);
417     /* FIXME: sleep ? stop monitoring ? */
418     return;
419   }
420
421   pfds = g_newa (struct pollfd, nfds + 1);
422   rnfds = snd_mixer_poll_descriptors (mixer->handle, pfds, nfds);
423   g_assert (rnfds <= nfds);
424
425   pfds[rnfds].fd = mixer->pfd[0];
426   pfds[rnfds].events = POLLIN | POLLPRI | POLLHUP | POLLERR;
427   pfds[rnfds].revents = 0;
428
429   g_static_rec_mutex_unlock (mixer->rec_mutex);
430
431   GST_LOG ("task loop");
432   poll (pfds, rnfds + 1, -1);
433
434   g_static_rec_mutex_lock (mixer->rec_mutex);
435
436   snd_mixer_poll_descriptors_revents (mixer->handle, pfds, nfds, &revents);
437   if (revents & POLLIN || revents & POLLPRI) {
438     GST_DEBUG ("Handling events");
439     snd_mixer_handle_events (mixer->handle);
440   }
441
442   g_static_rec_mutex_unlock (mixer->rec_mutex);
443 }
444
445 /* API */
446
447 GstAlsaMixer *
448 gst_alsa_mixer_new (const char *device, GstAlsaMixerDirection dir)
449 {
450   GstAlsaMixer *ret = NULL;
451
452   g_return_val_if_fail (device != NULL, NULL);
453
454   ret = g_new0 (GstAlsaMixer, 1);
455
456   if (pipe (ret->pfd) == -1)
457     goto error;
458
459   ret->rec_mutex = g_new (GStaticRecMutex, 1);
460   g_static_rec_mutex_init (ret->rec_mutex);
461
462   ret->task_mutex = g_new (GStaticRecMutex, 1);
463   g_static_rec_mutex_init (ret->task_mutex);
464
465   ret->task = gst_task_create (task_monitor_alsa, ret);
466   gst_task_set_lock (ret->task, ret->task_mutex);
467
468   ret->device = g_strdup (device);
469   ret->dir = dir;
470
471   if (!gst_alsa_mixer_open (ret))
472     goto error;
473
474   if (gst_task_start (ret->task) == FALSE) {
475     GST_WARNING ("Could not start alsamixer task");
476   }
477
478   return ret;
479
480   /* ERRORS */
481 error:
482   {
483     gst_alsa_mixer_free (ret);
484     return NULL;
485   }
486 }
487
488 void
489 gst_alsa_mixer_free (GstAlsaMixer * mixer)
490 {
491   g_return_if_fail (mixer != NULL);
492
493   if (mixer->task) {
494     if (write (mixer->pfd[1], "stop", 5) <= 0) {
495       GST_ERROR ("Cannot send " "stop" " to alsamixer task");
496       close (mixer->pfd[1]);
497       mixer->pfd[1] = -1;
498     }
499
500     if (gst_task_join (mixer->task) == FALSE) {
501       GST_ERROR ("Cannot join alsamixer task");
502     }
503
504     gst_object_unref (mixer->task);
505     mixer->task = NULL;
506   }
507
508   g_static_rec_mutex_free (mixer->task_mutex);
509   g_free (mixer->task_mutex);
510   mixer->task_mutex = NULL;
511
512   if (mixer->pfd[0] > 0) {
513     close (mixer->pfd[0]);
514     mixer->pfd[0] = -1;
515   }
516
517   if (mixer->pfd[1] > 0) {
518     close (mixer->pfd[1]);
519     mixer->pfd[1] = -1;
520   }
521
522   if (mixer->interface) {
523     g_object_unref (G_OBJECT (mixer->interface));
524     mixer->interface = NULL;
525   }
526
527   if (mixer->device) {
528     g_free (mixer->device);
529     mixer->device = NULL;
530   }
531
532   if (mixer->cardname) {
533     g_free (mixer->cardname);
534     mixer->cardname = NULL;
535   }
536
537   if (mixer->tracklist) {
538     g_list_foreach (mixer->tracklist, (GFunc) g_object_unref, NULL);
539     g_list_free (mixer->tracklist);
540     mixer->tracklist = NULL;
541   }
542
543   if (mixer->handle) {
544     snd_mixer_close (mixer->handle);
545     mixer->handle = NULL;
546   }
547
548   g_static_rec_mutex_free (mixer->rec_mutex);
549   g_free (mixer->rec_mutex);
550   mixer->rec_mutex = NULL;
551
552   g_free (mixer);
553 }
554
555 const GList *
556 gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer)
557 {
558   g_return_val_if_fail (mixer->handle != NULL, NULL);
559
560   gst_alsa_mixer_ensure_track_list (mixer);
561
562   return (const GList *) mixer->tracklist;
563 }
564
565 void
566 gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
567     gint * volumes)
568 {
569   gint i;
570   GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
571
572   g_return_if_fail (mixer->handle != NULL);
573
574   gst_alsa_mixer_track_update (alsa_track);
575
576   if (track->flags & GST_MIXER_TRACK_OUTPUT) {  /* return playback volume */
577
578     /* Is emulated mute flag activated? */
579     if (track->flags & GST_MIXER_TRACK_MUTE &&
580         !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) {
581       for (i = 0; i < track->num_channels; i++)
582         volumes[i] = alsa_track->volumes[i];
583     } else {
584       for (i = 0; i < track->num_channels; i++) {
585         long tmp = 0;
586
587         snd_mixer_selem_get_playback_volume (alsa_track->element, i, &tmp);
588         alsa_track->volumes[i] = volumes[i] = (gint) tmp;
589       }
590     }
591
592   } else if (track->flags & GST_MIXER_TRACK_INPUT) {    /* return capture volume */
593
594     /* Is emulated record flag activated? */
595     if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH ||
596         track->flags & GST_MIXER_TRACK_RECORD) {
597       for (i = 0; i < track->num_channels; i++) {
598         long tmp = 0;
599
600         snd_mixer_selem_get_capture_volume (alsa_track->element, i, &tmp);
601         alsa_track->volumes[i] = volumes[i] = (gint) tmp;
602       }
603     } else {
604       for (i = 0; i < track->num_channels; i++)
605         volumes[i] = alsa_track->volumes[i];
606     }
607   }
608 }
609
610 static gboolean
611 check_if_volumes_are_the_same (guint num_channels, gint * volumes)
612 {
613   guint i;
614
615   if (num_channels <= 1)
616     return TRUE;
617
618   for (i = 1; i < num_channels; i++) {
619     if (volumes[i] != volumes[0])
620       return FALSE;
621   }
622
623   return TRUE;
624 }
625
626 void
627 gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
628     gint * volumes)
629 {
630   GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
631   gint i;
632
633   g_return_if_fail (mixer->handle != NULL);
634
635   gst_alsa_mixer_track_update (alsa_track);
636
637   if (track->flags & GST_MIXER_TRACK_OUTPUT) {
638
639     /* Is emulated mute flag activated? */
640     if (track->flags & GST_MIXER_TRACK_MUTE &&
641         !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) {
642       for (i = 0; i < track->num_channels; i++)
643         alsa_track->volumes[i] = volumes[i];
644     } else {
645       if (check_if_volumes_are_the_same (track->num_channels, volumes)) {
646         snd_mixer_selem_set_playback_volume_all (alsa_track->element,
647             volumes[0]);
648         for (i = 0; i < track->num_channels; i++)
649           alsa_track->volumes[i] = volumes[0];
650       } else {
651         for (i = 0; i < track->num_channels; i++) {
652           alsa_track->volumes[i] = volumes[i];
653           snd_mixer_selem_set_playback_volume (alsa_track->element, i,
654               volumes[i]);
655         }
656       }
657     }
658
659   } else if (track->flags & GST_MIXER_TRACK_INPUT) {
660
661     /* Is emulated record flag activated? */
662     if (track->flags & GST_MIXER_TRACK_RECORD ||
663         alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) {
664       if (check_if_volumes_are_the_same (track->num_channels, volumes)) {
665         snd_mixer_selem_set_capture_volume_all (alsa_track->element,
666             volumes[0]);
667         for (i = 0; i < track->num_channels; i++)
668           alsa_track->volumes[i] = volumes[0];
669       } else {
670         for (i = 0; i < track->num_channels; i++) {
671           alsa_track->volumes[i] = volumes[i];
672           snd_mixer_selem_set_capture_volume (alsa_track->element, i,
673               volumes[i]);
674         }
675       }
676     } else {
677       for (i = 0; i < track->num_channels; i++)
678         alsa_track->volumes[i] = volumes[i];
679     }
680   }
681 }
682
683 void
684 gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, GstMixerTrack * track,
685     gboolean mute)
686 {
687   GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
688
689   g_return_if_fail (mixer->handle != NULL);
690
691   gst_alsa_mixer_track_update (alsa_track);
692
693   if (!!(mute) == !!(track->flags & GST_MIXER_TRACK_MUTE))
694     return;
695
696   if (mute) {
697     track->flags |= GST_MIXER_TRACK_MUTE;
698
699     if (alsa_track->shared_mute)
700       ((GstMixerTrack *) (alsa_track->shared_mute))->flags |=
701           GST_MIXER_TRACK_MUTE;
702   } else {
703     track->flags &= ~GST_MIXER_TRACK_MUTE;
704
705     if (alsa_track->shared_mute)
706       ((GstMixerTrack *) (alsa_track->shared_mute))->flags &=
707           ~GST_MIXER_TRACK_MUTE;
708   }
709
710   if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH) {
711     snd_mixer_selem_set_playback_switch_all (alsa_track->element, mute ? 0 : 1);
712   } else {
713     gint i;
714     GstAlsaMixerTrack *ctrl_track;
715
716     if ((track->flags & GST_MIXER_TRACK_INPUT)
717         && alsa_track->shared_mute != NULL)
718       ctrl_track = alsa_track->shared_mute;
719     else
720       ctrl_track = alsa_track;
721
722     for (i = 0; i < ((GstMixerTrack *) ctrl_track)->num_channels; i++) {
723       long vol =
724           mute ? ((GstMixerTrack *) ctrl_track)->min_volume : ctrl_track->
725           volumes[i];
726       snd_mixer_selem_set_playback_volume (ctrl_track->element, i, vol);
727     }
728   }
729 }
730
731 void
732 gst_alsa_mixer_set_record (GstAlsaMixer * mixer,
733     GstMixerTrack * track, gboolean record)
734 {
735   GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
736
737   g_return_if_fail (mixer->handle != NULL);
738
739   gst_alsa_mixer_track_update (alsa_track);
740
741   if (!!(record) == !!(track->flags & GST_MIXER_TRACK_RECORD))
742     return;
743
744   if (record) {
745     track->flags |= GST_MIXER_TRACK_RECORD;
746   } else {
747     track->flags &= ~GST_MIXER_TRACK_RECORD;
748   }
749
750   if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) {
751     snd_mixer_selem_set_capture_switch_all (alsa_track->element,
752         record ? 1 : 0);
753
754     /* update all tracks in same exlusive cswitch group */
755     if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL) {
756       GList *item;
757
758       for (item = mixer->tracklist; item != NULL; item = item->next) {
759
760         if (GST_IS_ALSA_MIXER_TRACK (item->data)) {
761           GstAlsaMixerTrack *item_alsa_track =
762               GST_ALSA_MIXER_TRACK (item->data);
763
764           if (item_alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL &&
765               item_alsa_track->capture_group == alsa_track->capture_group) {
766             gst_alsa_mixer_track_update (item_alsa_track);
767           }
768         }
769       }
770     }
771   } else {
772     gint i;
773
774     for (i = 0; i < track->num_channels; i++) {
775       long vol = record ? alsa_track->volumes[i] : track->min_volume;
776
777       snd_mixer_selem_set_capture_volume (alsa_track->element, i, vol);
778     }
779   }
780 }
781
782 void
783 gst_alsa_mixer_set_option (GstAlsaMixer * mixer,
784     GstMixerOptions * opts, gchar * value)
785 {
786   gint idx = -1, n = 0;
787   GList *item;
788   GstAlsaMixerOptions *alsa_opts = GST_ALSA_MIXER_OPTIONS (opts);
789
790   g_return_if_fail (mixer->handle != NULL);
791
792   for (item = opts->values; item != NULL; item = item->next, n++) {
793     if (!strcmp (item->data, value)) {
794       idx = n;
795       break;
796     }
797   }
798   if (idx == -1)
799     return;
800
801   snd_mixer_selem_set_enum_item (alsa_opts->element, 0, idx);
802 }
803
804 const gchar *
805 gst_alsa_mixer_get_option (GstAlsaMixer * mixer, GstMixerOptions * opts)
806 {
807   gint ret;
808   guint idx;
809   GstAlsaMixerOptions *alsa_opts = GST_ALSA_MIXER_OPTIONS (opts);
810
811   g_return_val_if_fail (mixer->handle != NULL, NULL);
812
813   ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx);
814   if (ret == 0)
815     return g_list_nth_data (opts->values, idx);
816   else
817     return snd_strerror (ret);  /* feeble attempt at error handling */
818 }
819
820 GstMixerFlags
821 gst_alsa_mixer_get_mixer_flags (GstAlsaMixer * mixer)
822 {
823   g_return_val_if_fail (mixer != NULL, GST_MIXER_FLAG_NONE);
824
825   return GST_MIXER_FLAG_AUTO_NOTIFICATIONS;
826 }
827
828 static void
829 gst_alsa_mixer_update_option (GstAlsaMixer * mixer,
830     GstAlsaMixerOptions * alsa_opts)
831 {
832   gint ret;
833   guint idx;
834   /* const */ gchar *option;
835
836   if (mixer->interface == NULL) {
837     GST_WARNING ("Cannot send update notifications, no GstMixer * given");
838     return;
839   }
840
841   ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx);
842   if (ret == 0) {
843     option = g_list_nth_data (GST_MIXER_OPTIONS (alsa_opts)->values, idx);
844     gst_mixer_option_changed (mixer->interface, GST_MIXER_OPTIONS (alsa_opts),
845         option);
846   }
847 }
848
849 static void
850 gst_alsa_mixer_update_track (GstAlsaMixer * mixer,
851     GstAlsaMixerTrack * alsa_track)
852 {
853   GstMixerTrack *track = (GstMixerTrack *) alsa_track;
854   gboolean old_mute;
855   gboolean old_record;
856   gint i, n_channels;
857   gint *old_volumes;
858
859   GST_DEBUG ("Updating track %" GST_PTR_FORMAT, alsa_track);
860
861   if (mixer->interface == NULL) {
862     GST_WARNING ("Cannot send update notifications, no GstMixer * given");
863     return;
864   }
865
866   old_mute = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE));
867   old_record = !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD));
868   old_volumes = g_new (gint, track->num_channels);
869   n_channels = track->num_channels;
870   memcpy (old_volumes, alsa_track->volumes,
871       sizeof (gint) * track->num_channels);
872
873   gst_alsa_mixer_track_update (alsa_track);
874
875   if (old_record !=
876       !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD))) {
877     gst_mixer_record_toggled (mixer->interface, track,
878         !!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD));
879   }
880   if (old_mute != !!(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))) {
881     gst_mixer_mute_toggled (mixer->interface, track,
882         !!GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE));
883   }
884
885   n_channels = MIN (n_channels, track->num_channels);
886   for (i = 0; i < n_channels; i++) {
887     if (old_volumes[i] != alsa_track->volumes[i]) {
888       gst_mixer_volume_changed (mixer->interface, track, alsa_track->volumes);
889       break;
890     }
891   }
892   g_free (old_volumes);
893 }
894
895 /* utility function for gstalsamixerelement to set the interface */
896 void
897 _gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, GstMixer * interface)
898 {
899   g_return_if_fail (mixer != NULL && mixer->interface == NULL);
900   g_return_if_fail (interface != NULL);
901
902   mixer->interface = g_object_ref (G_OBJECT (interface));
903 }