1 /* ALSA mixer implementation.
2 * Copyright (C) 2003 Leif Johnson <leif@ambient.2y.net>
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.
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.
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.
21 * SECTION:element-alsamixer
22 * @see_also: alsasink, alsasrc
24 * This element controls various aspects such as the volume and balance
25 * of an audio device using the ALSA api.
27 * The application should query and use the interfaces provided by this
28 * element to control the device.
30 * Last reviewed on 2006-03-01 (0.10.4)
37 /* FIXME 0.11: suppress warnings for deprecated API such as GStaticRecMutex
38 * with newer GLib versions (>= 2.31.0) */
39 #define GLIB_DISABLE_DEPRECATION_WARNINGS
41 #include "gstalsamixer.h"
42 #include "gst/glib-compat-private.h"
45 static void gst_alsa_mixer_update_option (GstAlsaMixer * mixer,
46 GstAlsaMixerOptions * alsa_opts);
47 static void gst_alsa_mixer_update_track (GstAlsaMixer * mixer,
48 GstAlsaMixerTrack * alsa_track);
49 static int gst_alsa_mixer_handle_callback (snd_mixer_t * handle,
50 unsigned int mask, snd_mixer_elem_t * elem);
52 /* First some utils, then the mixer implementation */
54 gst_alsa_mixer_open (GstAlsaMixer * mixer)
58 snd_ctl_card_info_t *card_info;
60 g_return_val_if_fail (mixer->handle == NULL, FALSE);
62 /* open and initialize the mixer device */
63 err = snd_mixer_open (&mixer->handle, 0);
64 if (err < 0 || mixer->handle == NULL)
67 if ((err = snd_mixer_attach (mixer->handle, mixer->device)) < 0) {
68 GST_WARNING ("Cannot open mixer for sound device '%s': %s", mixer->device,
73 if ((err = snd_mixer_selem_register (mixer->handle, NULL, NULL)) < 0) {
74 GST_WARNING ("Cannot register mixer elements: %s", snd_strerror (err));
78 if ((err = snd_mixer_load (mixer->handle)) < 0) {
79 GST_WARNING ("Cannot load mixer settings: %s", snd_strerror (err));
83 snd_mixer_set_callback_private (mixer->handle, mixer);
84 snd_mixer_set_callback (mixer->handle, gst_alsa_mixer_handle_callback);
86 /* now get the device name, any of this is not fatal */
87 g_free (mixer->cardname);
88 if ((err = snd_ctl_open (&ctl, mixer->device, 0)) < 0) {
89 GST_WARNING ("Cannot open CTL: %s", snd_strerror (err));
93 snd_ctl_card_info_malloc (&card_info);
94 if ((err = snd_ctl_card_info (ctl, card_info)) < 0) {
95 GST_WARNING ("Cannot get card info: %s", snd_strerror (err));
100 mixer->cardname = g_strdup (snd_ctl_card_info_get_name (card_info));
101 GST_DEBUG ("Card name = %s", GST_STR_NULL (mixer->cardname));
102 snd_ctl_card_info_free (card_info);
106 if (mixer->cardname == NULL) {
107 mixer->cardname = g_strdup ("Unknown");
108 GST_DEBUG ("Cannot find card name");
111 GST_INFO ("Successfully opened mixer for device '%s'.", mixer->device);
118 GST_WARNING ("Cannot open mixer: %s", snd_strerror (err));
119 mixer->handle = NULL;
124 snd_mixer_close (mixer->handle);
125 mixer->handle = NULL;
130 static snd_mixer_elem_t *
131 gst_alsa_mixer_find_master_mixer (GstAlsaMixer * mixer, snd_mixer_t * handle)
133 snd_mixer_elem_t *element;
136 count = snd_mixer_get_count (handle);
138 GST_ALSA_MIXER_LOCK (mixer);
140 /* Check if we have a playback mixer labelled as 'Master' */
141 element = snd_mixer_first_elem (handle);
142 for (i = 0; i < count; i++) {
143 if (snd_mixer_selem_has_playback_volume (element) &&
144 strcmp (snd_mixer_selem_get_name (element), "Master") == 0) {
145 GST_ALSA_MIXER_UNLOCK (mixer);
148 element = snd_mixer_elem_next (element);
151 /* If not, check if we have a playback mixer labelled as 'Front' */
152 element = snd_mixer_first_elem (handle);
153 for (i = 0; i < count; i++) {
154 if (snd_mixer_selem_has_playback_volume (element) &&
155 strcmp (snd_mixer_selem_get_name (element), "Front") == 0) {
156 GST_ALSA_MIXER_UNLOCK (mixer);
159 element = snd_mixer_elem_next (element);
162 /* If not, check if we have a playback mixer labelled as 'PCM' */
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), "PCM") == 0) {
167 GST_ALSA_MIXER_UNLOCK (mixer);
170 element = snd_mixer_elem_next (element);
173 /* If not, check if we have a playback mixer labelled as 'Speaker' */
174 element = snd_mixer_first_elem (handle);
175 for (i = 0; i < count; i++) {
176 if (snd_mixer_selem_has_playback_volume (element) &&
177 strcmp (snd_mixer_selem_get_name (element), "Speaker") == 0) {
178 GST_ALSA_MIXER_UNLOCK (mixer);
181 element = snd_mixer_elem_next (element);
184 /* If not, check if we have a playback mixer with both volume and switch that
186 element = snd_mixer_first_elem (handle);
187 for (i = 0; i < count; i++) {
188 if (snd_mixer_selem_has_playback_volume (element) &&
189 snd_mixer_selem_has_playback_switch (element) &&
190 !snd_mixer_selem_is_playback_mono (element)) {
191 GST_ALSA_MIXER_UNLOCK (mixer);
194 element = snd_mixer_elem_next (element);
197 /* If not, check if we have any playback mixer with both volume and switch */
198 element = snd_mixer_first_elem (handle);
199 for (i = 0; i < count; i++) {
200 if (snd_mixer_selem_has_playback_volume (element) &&
201 snd_mixer_selem_has_playback_switch (element)) {
202 GST_ALSA_MIXER_UNLOCK (mixer);
205 element = snd_mixer_elem_next (element);
208 /* If not, take any playback mixer with a volume control */
209 element = snd_mixer_first_elem (handle);
210 for (i = 0; i < count; i++) {
211 if (snd_mixer_selem_has_playback_volume (element)) {
212 GST_ALSA_MIXER_UNLOCK (mixer);
215 element = snd_mixer_elem_next (element);
218 GST_ALSA_MIXER_UNLOCK (mixer);
219 /* Looks like we're out of luck ... */
224 gst_alsa_mixer_update (GstAlsaMixer * mixer, snd_mixer_elem_t * elem)
228 g_return_if_fail (mixer != NULL);
230 GST_ALSA_MIXER_LOCK (mixer);
232 for (item = mixer->tracklist; item != NULL; item = item->next) {
233 if (GST_IS_ALSA_MIXER_TRACK (item->data)) {
234 if (elem && (GST_ALSA_MIXER_TRACK (item->data)->element != elem))
237 gst_alsa_mixer_update_track (mixer, GST_ALSA_MIXER_TRACK (item->data));
238 } else if (GST_IS_ALSA_MIXER_OPTIONS (item->data)) {
239 if (elem && (GST_ALSA_MIXER_OPTIONS (item->data)->element != elem))
242 gst_alsa_mixer_update_option (mixer, GST_ALSA_MIXER_OPTIONS (item->data));
246 GST_ALSA_MIXER_UNLOCK (mixer);
250 gst_alsa_mixer_elem_handle_callback (snd_mixer_elem_t * elem, unsigned int mask)
252 GstAlsaMixer *mixer =
253 (GstAlsaMixer *) snd_mixer_elem_get_callback_private (elem);
255 GST_LOG ("ALSA elem cb");
257 g_return_val_if_fail (mixer != NULL, 1);
259 gst_alsa_mixer_update (mixer, elem);
265 gst_alsa_mixer_handle_callback (snd_mixer_t * handle, unsigned int mask,
266 snd_mixer_elem_t * elem)
268 GstAlsaMixer *mixer =
269 (GstAlsaMixer *) snd_mixer_get_callback_private (handle);
273 g_return_val_if_fail (mixer != NULL, 1);
275 /* Hopefully won't be call recursively and will handle pending elem events */
276 snd_mixer_handle_events (mixer->handle);
278 gst_alsa_mixer_update (mixer, elem);
284 gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer)
287 snd_mixer_elem_t *element, *master;
290 g_return_if_fail (mixer->handle != NULL);
292 if (mixer->tracklist)
295 GST_ALSA_MIXER_LOCK (mixer);
297 master = gst_alsa_mixer_find_master_mixer (mixer, mixer->handle);
299 count = snd_mixer_get_count (mixer->handle);
300 element = snd_mixer_first_elem (mixer->handle);
304 * Some ALSA tracks may have playback and capture capabilities.
305 * Here we model them as two separate GStreamer tracks.
308 for (i = 0; i < count; i++) {
309 GstMixerTrack *play_track = NULL;
310 GstMixerTrack *cap_track = NULL;
315 name = snd_mixer_selem_get_name (element);
317 /* prevent dup names */
318 for (item = mixer->tracklist; item != NULL; item = item->next) {
319 snd_mixer_elem_t *temp;
321 if (GST_IS_ALSA_MIXER_OPTIONS (item->data))
322 temp = GST_ALSA_MIXER_OPTIONS (item->data)->element;
324 temp = GST_ALSA_MIXER_TRACK (item->data)->element;
326 if (strcmp (name, snd_mixer_selem_get_name (temp)) == 0)
330 GST_LOG ("[%s] probing element #%u, mixer->dir=%u", name, i, mixer->dir);
332 if (mixer->dir & GST_ALSA_MIXER_PLAYBACK) {
333 gboolean has_playback_switch, has_playback_volume;
335 has_playback_switch = snd_mixer_selem_has_playback_switch (element);
336 has_playback_volume = snd_mixer_selem_has_playback_volume (element);
338 GST_LOG ("[%s] PLAYBACK: has_playback_volume=%d, has_playback_switch=%d"
339 "%s", name, has_playback_volume, has_playback_switch,
340 (element == master) ? " MASTER" : "");
342 if (has_playback_volume) {
343 gint flags = GST_MIXER_TRACK_OUTPUT;
345 if (element == master)
346 flags |= GST_MIXER_TRACK_MASTER;
348 play_track = gst_alsa_mixer_track_new (element, samename, i,
349 flags, FALSE, NULL, FALSE);
351 } else if (has_playback_switch) {
352 /* simple mute switch */
353 play_track = gst_alsa_mixer_track_new (element, samename, i,
354 GST_MIXER_TRACK_OUTPUT, TRUE, NULL, FALSE);
357 if (snd_mixer_selem_is_enumerated (element)) {
358 GstMixerOptions *opts = gst_alsa_mixer_options_new (element, i);
360 GST_LOG ("[%s] is enumerated (%d)", name, i);
361 mixer->tracklist = g_list_append (mixer->tracklist, opts);
365 if (mixer->dir & GST_ALSA_MIXER_CAPTURE) {
366 gboolean has_capture_switch, has_common_switch;
367 gboolean has_capture_volume, has_common_volume;
369 has_capture_switch = snd_mixer_selem_has_capture_switch (element);
370 has_common_switch = snd_mixer_selem_has_common_switch (element);
371 has_capture_volume = snd_mixer_selem_has_capture_volume (element);
372 has_common_volume = snd_mixer_selem_has_common_volume (element);
374 GST_LOG ("[%s] CAPTURE: has_capture_volume=%d, has_common_volume=%d, "
375 "has_capture_switch=%d, has_common_switch=%d, play_track=%p", name,
376 has_capture_volume, has_common_volume, has_capture_switch,
377 has_common_switch, play_track);
379 if (has_capture_volume && !(play_track && has_common_volume)) {
380 cap_track = gst_alsa_mixer_track_new (element, samename, i,
381 GST_MIXER_TRACK_INPUT, FALSE, NULL, play_track != NULL);
382 } else if (has_capture_switch && !(play_track && has_common_switch)) {
383 cap_track = gst_alsa_mixer_track_new (element, samename, i,
384 GST_MIXER_TRACK_INPUT, TRUE, NULL, play_track != NULL);
389 if (play_track && cap_track) {
390 GST_ALSA_MIXER_TRACK (play_track)->shared_mute =
391 GST_ALSA_MIXER_TRACK (cap_track);
392 GST_ALSA_MIXER_TRACK (cap_track)->shared_mute =
393 GST_ALSA_MIXER_TRACK (play_track);
397 mixer->tracklist = g_list_append (mixer->tracklist, play_track);
400 mixer->tracklist = g_list_append (mixer->tracklist, cap_track);
402 element = snd_mixer_elem_next (element);
405 for (item = mixer->tracklist; item != NULL; item = item->next) {
406 snd_mixer_elem_t *temp;
408 if (GST_IS_ALSA_MIXER_OPTIONS (item->data))
409 temp = GST_ALSA_MIXER_OPTIONS (item->data)->element;
411 temp = GST_ALSA_MIXER_TRACK (item->data)->element;
413 snd_mixer_elem_set_callback (temp, gst_alsa_mixer_elem_handle_callback);
414 snd_mixer_elem_set_callback_private (temp, mixer);
417 GST_ALSA_MIXER_UNLOCK (mixer);
421 task_monitor_alsa (gpointer data)
425 unsigned short revents;
426 GstAlsaMixer *mixer = (GstAlsaMixer *) data;
429 GST_ALSA_MIXER_LOCK (mixer);
431 nfds = snd_mixer_poll_descriptors_count (mixer->handle);
433 GST_ERROR ("snd_mixer_poll_descriptors_count <= 0: %d", nfds);
434 /* FIXME: sleep ? stop monitoring ? */
435 GST_ALSA_MIXER_UNLOCK (mixer);
439 pfds = g_newa (struct pollfd, nfds + 1);
440 rnfds = snd_mixer_poll_descriptors (mixer->handle, pfds, nfds);
441 g_assert (rnfds <= nfds);
444 GST_ELEMENT_ERROR (mixer, RESOURCE, READ, (NULL), ("alsa error: %s",
445 snd_strerror (rnfds)));
446 gst_task_pause (mixer->task);
447 GST_ALSA_MIXER_UNLOCK (mixer);
451 pfds[rnfds].fd = mixer->pfd[0];
452 pfds[rnfds].events = POLLIN | POLLPRI | POLLHUP | POLLERR;
453 pfds[rnfds].revents = 0;
455 GST_ALSA_MIXER_UNLOCK (mixer);
457 GST_LOG ("task loop");
458 ret = poll (pfds, rnfds + 1, -1);
461 GST_ELEMENT_ERROR (mixer, RESOURCE, READ, (NULL), GST_ERROR_SYSTEM);
462 gst_task_pause (mixer->task);
466 GST_ALSA_MIXER_LOCK (mixer);
469 snd_mixer_poll_descriptors_revents (mixer->handle, pfds, nfds, &revents);
471 GST_ELEMENT_ERROR (mixer, RESOURCE, READ, (NULL), ("alsa error: %s",
472 snd_strerror (ret)));
473 gst_task_pause (mixer->task);
474 } else if (revents & (POLLIN | POLLPRI)) {
475 GST_DEBUG ("Handling events");
476 snd_mixer_handle_events (mixer->handle);
477 } else if (revents & (POLLERR | POLLNVAL | POLLHUP)) {
478 GST_ELEMENT_ERROR (mixer, RESOURCE, READ, (NULL), (NULL));
479 gst_task_pause (mixer->task);
482 GST_ALSA_MIXER_UNLOCK (mixer);
488 gst_alsa_mixer_new (const char *device, GstAlsaMixerDirection dir)
490 GstAlsaMixer *ret = NULL;
492 g_return_val_if_fail (device != NULL, NULL);
494 ret = g_new0 (GstAlsaMixer, 1);
496 if (pipe (ret->pfd) == -1)
499 #if !GLIB_CHECK_VERSION (2, 31, 0)
500 g_static_rec_mutex_init (&ret->rec_mutex);
502 g_rec_mutex_init (&ret->rec_mutex);
504 g_static_rec_mutex_init (&ret->task_mutex);
506 ret->task = gst_task_create (task_monitor_alsa, ret);
507 gst_task_set_lock (ret->task, &ret->task_mutex);
509 ret->device = g_strdup (device);
512 if (!gst_alsa_mixer_open (ret))
515 if (gst_task_start (ret->task) == FALSE) {
516 GST_WARNING ("Could not start alsamixer task");
524 gst_alsa_mixer_free (ret);
530 gst_alsa_mixer_free (GstAlsaMixer * mixer)
532 g_return_if_fail (mixer != NULL);
535 if (write (mixer->pfd[1], "stop", 5) <= 0) {
536 GST_ERROR ("Cannot send " "stop" " to alsamixer task");
537 close (mixer->pfd[1]);
541 if (gst_task_join (mixer->task) == FALSE) {
542 GST_ERROR ("Cannot join alsamixer task");
545 gst_object_unref (mixer->task);
549 g_static_rec_mutex_free (&mixer->task_mutex);
551 if (mixer->pfd[0] > 0) {
552 close (mixer->pfd[0]);
556 if (mixer->pfd[1] > 0) {
557 close (mixer->pfd[1]);
561 if (mixer->interface) {
562 g_object_unref (G_OBJECT (mixer->interface));
563 mixer->interface = NULL;
567 g_free (mixer->device);
568 mixer->device = NULL;
571 if (mixer->cardname) {
572 g_free (mixer->cardname);
573 mixer->cardname = NULL;
576 if (mixer->tracklist) {
577 g_list_foreach (mixer->tracklist, (GFunc) g_object_unref, NULL);
578 g_list_free (mixer->tracklist);
579 mixer->tracklist = NULL;
583 snd_mixer_close (mixer->handle);
584 mixer->handle = NULL;
586 #if !GLIB_CHECK_VERSION (2, 31, 0)
587 g_static_rec_mutex_free (&mixer->rec_mutex);
589 g_rec_mutex_clear (&mixer->rec_mutex);
596 gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer)
598 g_return_val_if_fail (mixer->handle != NULL, NULL);
600 gst_alsa_mixer_ensure_track_list (mixer);
602 return (const GList *) mixer->tracklist;
606 gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
610 GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
612 g_return_if_fail (mixer->handle != NULL);
614 GST_ALSA_MIXER_LOCK (mixer);
616 gst_alsa_mixer_track_update (alsa_track);
618 if (track->flags & GST_MIXER_TRACK_OUTPUT) { /* return playback volume */
620 /* Is emulated mute flag activated? */
621 if (track->flags & GST_MIXER_TRACK_MUTE &&
622 !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) {
623 for (i = 0; i < track->num_channels; i++)
624 volumes[i] = alsa_track->volumes[i];
626 for (i = 0; i < track->num_channels; i++) {
629 snd_mixer_selem_get_playback_volume (alsa_track->element, i, &tmp);
630 alsa_track->volumes[i] = volumes[i] = (gint) tmp;
634 } else if (track->flags & GST_MIXER_TRACK_INPUT) { /* return capture volume */
636 /* Is emulated record flag activated? */
637 if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH ||
638 track->flags & GST_MIXER_TRACK_RECORD) {
639 for (i = 0; i < track->num_channels; i++) {
642 snd_mixer_selem_get_capture_volume (alsa_track->element, i, &tmp);
643 alsa_track->volumes[i] = volumes[i] = (gint) tmp;
646 for (i = 0; i < track->num_channels; i++)
647 volumes[i] = alsa_track->volumes[i];
650 GST_ALSA_MIXER_UNLOCK (mixer);
654 check_if_volumes_are_the_same (guint num_channels, gint * volumes)
658 if (num_channels <= 1)
661 for (i = 1; i < num_channels; i++) {
662 if (volumes[i] != volumes[0])
670 gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, GstMixerTrack * track,
673 GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
676 g_return_if_fail (mixer->handle != NULL);
678 GST_ALSA_MIXER_LOCK (mixer);
680 gst_alsa_mixer_track_update (alsa_track);
682 if (track->flags & GST_MIXER_TRACK_OUTPUT) {
684 /* Is emulated mute flag activated? */
685 if (track->flags & GST_MIXER_TRACK_MUTE &&
686 !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) {
687 for (i = 0; i < track->num_channels; i++)
688 alsa_track->volumes[i] = volumes[i];
690 if (check_if_volumes_are_the_same (track->num_channels, volumes)) {
691 snd_mixer_selem_set_playback_volume_all (alsa_track->element,
693 for (i = 0; i < track->num_channels; i++)
694 alsa_track->volumes[i] = volumes[0];
696 for (i = 0; i < track->num_channels; i++) {
697 alsa_track->volumes[i] = volumes[i];
698 snd_mixer_selem_set_playback_volume (alsa_track->element, i,
704 } else if (track->flags & GST_MIXER_TRACK_INPUT) {
706 /* Is emulated record flag activated? */
707 if (track->flags & GST_MIXER_TRACK_RECORD ||
708 alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) {
709 if (check_if_volumes_are_the_same (track->num_channels, volumes)) {
710 snd_mixer_selem_set_capture_volume_all (alsa_track->element,
712 for (i = 0; i < track->num_channels; i++)
713 alsa_track->volumes[i] = volumes[0];
715 for (i = 0; i < track->num_channels; i++) {
716 alsa_track->volumes[i] = volumes[i];
717 snd_mixer_selem_set_capture_volume (alsa_track->element, i,
722 for (i = 0; i < track->num_channels; i++)
723 alsa_track->volumes[i] = volumes[i];
726 GST_ALSA_MIXER_UNLOCK (mixer);
730 gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, GstMixerTrack * track,
733 GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
735 g_return_if_fail (mixer->handle != NULL);
737 GST_ALSA_MIXER_LOCK (mixer);
739 gst_alsa_mixer_track_update (alsa_track);
741 if (! !(mute) == ! !(track->flags & GST_MIXER_TRACK_MUTE)) {
742 GST_ALSA_MIXER_UNLOCK (mixer);
746 track->flags |= GST_MIXER_TRACK_MUTE;
748 if (alsa_track->shared_mute)
749 ((GstMixerTrack *) (alsa_track->shared_mute))->flags |=
750 GST_MIXER_TRACK_MUTE;
752 track->flags &= ~GST_MIXER_TRACK_MUTE;
754 if (alsa_track->shared_mute)
755 ((GstMixerTrack *) (alsa_track->shared_mute))->flags &=
756 ~GST_MIXER_TRACK_MUTE;
759 if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH) {
760 snd_mixer_selem_set_playback_switch_all (alsa_track->element, mute ? 0 : 1);
763 GstAlsaMixerTrack *ctrl_track;
765 if ((track->flags & GST_MIXER_TRACK_INPUT)
766 && alsa_track->shared_mute != NULL)
767 ctrl_track = alsa_track->shared_mute;
769 ctrl_track = alsa_track;
771 for (i = 0; i < ((GstMixerTrack *) ctrl_track)->num_channels; i++) {
773 mute ? ((GstMixerTrack *) ctrl_track)->min_volume : ctrl_track->
775 snd_mixer_selem_set_playback_volume (ctrl_track->element, i, vol);
778 GST_ALSA_MIXER_UNLOCK (mixer);
782 gst_alsa_mixer_set_record (GstAlsaMixer * mixer,
783 GstMixerTrack * track, gboolean record)
785 GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track);
787 g_return_if_fail (mixer->handle != NULL);
789 GST_ALSA_MIXER_LOCK (mixer);
791 gst_alsa_mixer_track_update (alsa_track);
793 if (! !(record) == ! !(track->flags & GST_MIXER_TRACK_RECORD)) {
794 GST_ALSA_MIXER_UNLOCK (mixer);
799 track->flags |= GST_MIXER_TRACK_RECORD;
801 track->flags &= ~GST_MIXER_TRACK_RECORD;
804 if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) {
805 snd_mixer_selem_set_capture_switch_all (alsa_track->element,
808 /* update all tracks in same exclusive cswitch group */
809 if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL) {
812 for (item = mixer->tracklist; item != NULL; item = item->next) {
814 if (GST_IS_ALSA_MIXER_TRACK (item->data)) {
815 GstAlsaMixerTrack *item_alsa_track =
816 GST_ALSA_MIXER_TRACK (item->data);
818 if (item_alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL &&
819 item_alsa_track->capture_group == alsa_track->capture_group) {
820 gst_alsa_mixer_track_update (item_alsa_track);
828 for (i = 0; i < track->num_channels; i++) {
829 long vol = record ? alsa_track->volumes[i] : track->min_volume;
831 snd_mixer_selem_set_capture_volume (alsa_track->element, i, vol);
834 GST_ALSA_MIXER_UNLOCK (mixer);
838 gst_alsa_mixer_set_option (GstAlsaMixer * mixer,
839 GstMixerOptions * opts, gchar * value)
841 gint idx = -1, n = 0;
843 GstAlsaMixerOptions *alsa_opts = GST_ALSA_MIXER_OPTIONS (opts);
845 g_return_if_fail (mixer->handle != NULL);
847 for (item = opts->values; item != NULL; item = item->next, n++) {
848 if (!strcmp (item->data, value)) {
856 GST_ALSA_MIXER_LOCK (mixer);
857 snd_mixer_selem_set_enum_item (alsa_opts->element, 0, idx);
858 GST_ALSA_MIXER_UNLOCK (mixer);
862 gst_alsa_mixer_get_option (GstAlsaMixer * mixer, GstMixerOptions * opts)
866 GstAlsaMixerOptions *alsa_opts = GST_ALSA_MIXER_OPTIONS (opts);
868 g_return_val_if_fail (mixer->handle != NULL, NULL);
869 GST_ALSA_MIXER_LOCK (mixer);
870 ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx);
871 GST_ALSA_MIXER_UNLOCK (mixer);
873 return g_list_nth_data (opts->values, idx);
875 return snd_strerror (ret); /* feeble attempt at error handling */
879 gst_alsa_mixer_get_mixer_flags (GstAlsaMixer * mixer)
881 g_return_val_if_fail (mixer != NULL, GST_MIXER_FLAG_NONE);
883 return GST_MIXER_FLAG_AUTO_NOTIFICATIONS;
887 gst_alsa_mixer_update_option (GstAlsaMixer * mixer,
888 GstAlsaMixerOptions * alsa_opts)
892 /* const */ gchar *option;
894 if (mixer->interface == NULL) {
895 GST_WARNING ("Cannot send update notifications, no GstMixer * given");
898 GST_ALSA_MIXER_LOCK (mixer);
899 ret = snd_mixer_selem_get_enum_item (alsa_opts->element, 0, &idx);
900 GST_ALSA_MIXER_UNLOCK (mixer);
902 option = g_list_nth_data (GST_MIXER_OPTIONS (alsa_opts)->values, idx);
903 gst_mixer_option_changed (mixer->interface, GST_MIXER_OPTIONS (alsa_opts),
909 gst_alsa_mixer_update_track (GstAlsaMixer * mixer,
910 GstAlsaMixerTrack * alsa_track)
912 GstMixerTrack *track = (GstMixerTrack *) alsa_track;
918 GST_DEBUG ("Updating track %" GST_PTR_FORMAT, alsa_track);
920 if (mixer->interface == NULL) {
921 GST_WARNING ("Cannot send update notifications, no GstMixer * given");
925 old_mute = ! !(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE));
926 old_record = ! !(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD));
927 old_volumes = g_new (gint, track->num_channels);
928 n_channels = track->num_channels;
929 memcpy (old_volumes, alsa_track->volumes,
930 sizeof (gint) * track->num_channels);
932 gst_alsa_mixer_track_update (alsa_track);
935 ! !(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD))) {
936 gst_mixer_record_toggled (mixer->interface, track,
937 ! !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_RECORD));
939 if (old_mute != ! !(GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE))) {
940 gst_mixer_mute_toggled (mixer->interface, track,
941 ! !GST_MIXER_TRACK_HAS_FLAG (track, GST_MIXER_TRACK_MUTE));
944 n_channels = MIN (n_channels, track->num_channels);
945 for (i = 0; i < n_channels; i++) {
946 if (old_volumes[i] != alsa_track->volumes[i]) {
947 gst_mixer_volume_changed (mixer->interface, track, alsa_track->volumes);
951 g_free (old_volumes);
954 /* utility function for gstalsamixerelement to set the interface */
956 _gst_alsa_mixer_set_interface (GstAlsaMixer * mixer, GstMixer * interface)
958 g_return_if_fail (mixer != NULL && mixer->interface == NULL);
959 g_return_if_fail (interface != NULL);
961 mixer->interface = g_object_ref (G_OBJECT (interface));