From 6fdb8262a770dabeb6d4322d55da18848182d3b7 Mon Sep 17 00:00:00 2001 From: Viktor Peters Date: Tue, 29 Aug 2006 11:50:51 +0000 Subject: [PATCH] ext/alsa/: Improve and fix mixer track handling, in particular better handling of alsa's pvolume/pswitch/cvolume/cswi... Original commit message from CVS: Patch by: Viktor Peters * ext/alsa/gstalsamixer.c: (gst_alsa_mixer_ensure_track_list), (gst_alsa_mixer_update), (gst_alsa_mixer_get_volume), (gst_alsa_mixer_set_volume), (gst_alsa_mixer_set_mute), (gst_alsa_mixer_set_record): * ext/alsa/gstalsamixertrack.c: (gst_alsa_mixer_track_update_alsa_capabilities), (alsa_track_has_cap), (gst_alsa_mixer_track_new), (gst_alsa_mixer_track_update): * ext/alsa/gstalsamixertrack.h: Improve and fix mixer track handling, in particular better handling of alsa's pvolume/pswitch/cvolume/cswitch capabilities; create separate track objects for tracks that have both capture and playback volume (and label them differently as well so they're not mistakenly assumed to be duplicates); classify mixer tracks that only affect the audible volume of something (rather than the capture volume) as playback tracks. Redefine/fix meaning of RECORD and MUTE flags for capture tracks to correspond to alsa-pswitch alsa-cswitch (following the meaning documented in the mixer interface header file); add support for alsa's exclusive cswitch groups; update/sync state/flags better if mixer settings are changed by another application. Fixes #336075. --- ChangeLog | 26 +++ ext/alsa/gstalsamixer.c | 295 ++++++++++++++++++++++------------- ext/alsa/gstalsamixertrack.c | 241 ++++++++++++++++++++++------ ext/alsa/gstalsamixertrack.h | 26 ++- 4 files changed, 431 insertions(+), 157 deletions(-) diff --git a/ChangeLog b/ChangeLog index e80c453e13..8ee752ccd5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,29 @@ +2006-08-29 Tim-Philipp Müller + + Patch by: Viktor Peters + + * ext/alsa/gstalsamixer.c: (gst_alsa_mixer_ensure_track_list), + (gst_alsa_mixer_update), (gst_alsa_mixer_get_volume), + (gst_alsa_mixer_set_volume), (gst_alsa_mixer_set_mute), + (gst_alsa_mixer_set_record): + * ext/alsa/gstalsamixertrack.c: + (gst_alsa_mixer_track_update_alsa_capabilities), + (alsa_track_has_cap), (gst_alsa_mixer_track_new), + (gst_alsa_mixer_track_update): + * ext/alsa/gstalsamixertrack.h: + Improve and fix mixer track handling, in particular better handling + of alsa's pvolume/pswitch/cvolume/cswitch capabilities; create separate + track objects for tracks that have both capture and playback volume + (and label them differently as well so they're not mistakenly + assumed to be duplicates); classify mixer tracks that only affect + the audible volume of something (rather than the capture volume) + as playback tracks. Redefine/fix meaning of RECORD and MUTE flags + for capture tracks to correspond to alsa-pswitch alsa-cswitch + (following the meaning documented in the mixer interface header + file); add support for alsa's exclusive cswitch groups; update/sync + state/flags better if mixer settings are changed by another + application. Fixes #336075. + 2006-08-29 Tim-Philipp Müller * gst/playback/gstplaybin.c: diff --git a/ext/alsa/gstalsamixer.c b/ext/alsa/gstalsamixer.c index a7d7798d18..5b8f0d60e8 100644 --- a/ext/alsa/gstalsamixer.c +++ b/ext/alsa/gstalsamixer.c @@ -123,8 +123,6 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) { gint i, count; snd_mixer_elem_t *element; - GstMixerTrack *track; - GstMixerOptions *opts; gboolean first = TRUE; g_return_if_fail (mixer->handle != NULL); @@ -135,21 +133,20 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) count = snd_mixer_get_count (mixer->handle); element = snd_mixer_first_elem (mixer->handle); - /* build track list */ - for (i = 0; i < count; i++) { - GList *item; - gint channels = 0, samename = 0; - gint flags = GST_MIXER_TRACK_OUTPUT; - gboolean got_it = FALSE; + /* build track list + * + * Some ALSA tracks may have playback and capture capabilities. + * Here we model them as two separate GStreamer tracks. + */ - if (snd_mixer_selem_has_capture_switch (element)) { - if (!(mixer->dir & GST_ALSA_MIXER_CAPTURE)) - goto next; - flags = GST_MIXER_TRACK_INPUT; - } else { - if (!(mixer->dir & GST_ALSA_MIXER_PLAYBACK)) - goto next; - } + for (i = 0; i < count; i++) { + GstMixerTrack *play_track = NULL; + GstMixerTrack *cap_track = NULL; + const gchar *name; + GList *item; + gint samename = 0; + + name = snd_mixer_selem_get_name (element); /* prevent dup names */ for (item = mixer->tracklist; item != NULL; item = item->next) { @@ -160,54 +157,82 @@ gst_alsa_mixer_ensure_track_list (GstAlsaMixer * mixer) else temp = GST_ALSA_MIXER_TRACK (item->data)->element; - if (!strcmp (snd_mixer_selem_get_name (element), - snd_mixer_selem_get_name (temp))) + if (strcmp (name, snd_mixer_selem_get_name (temp)) == 0) samename++; } - if (snd_mixer_selem_has_capture_volume (element)) { - while (snd_mixer_selem_has_capture_channel (element, channels)) - channels++; - track = gst_alsa_mixer_track_new (element, samename, - i, channels, flags, GST_ALSA_MIXER_TRACK_CAPTURE); - mixer->tracklist = g_list_append (mixer->tracklist, track); - got_it = TRUE; + GST_LOG ("[%s] probing element #%u, mixer->dir=%u", name, i, mixer->dir); - /* there might be another volume slider; make that playback */ - flags &= ~GST_MIXER_TRACK_INPUT; - flags |= GST_MIXER_TRACK_OUTPUT; - } + if (mixer->dir & GST_ALSA_MIXER_PLAYBACK) { + gboolean has_playback_switch, has_playback_volume; - if (snd_mixer_selem_has_playback_volume (element)) { - while (snd_mixer_selem_has_playback_channel (element, channels)) - channels++; - if (first) { - first = FALSE; - flags |= GST_MIXER_TRACK_MASTER; - } - track = gst_alsa_mixer_track_new (element, samename, - i, channels, flags, GST_ALSA_MIXER_TRACK_PLAYBACK); - mixer->tracklist = g_list_append (mixer->tracklist, track); - got_it = TRUE; - } + has_playback_switch = snd_mixer_selem_has_playback_switch (element); + has_playback_volume = snd_mixer_selem_has_playback_volume (element); - if (snd_mixer_selem_is_enumerated (element)) { - opts = gst_alsa_mixer_options_new (element, i); - mixer->tracklist = g_list_append (mixer->tracklist, opts); - got_it = TRUE; - } + GST_LOG ("[%s] PLAYBACK: has_playback_volume=%d, has_playback_switch=%d", + name, has_playback_volume, has_playback_switch); - if (!got_it) { - if (flags == GST_MIXER_TRACK_OUTPUT && - snd_mixer_selem_has_playback_switch (element)) { + if (has_playback_volume) { + gint flags = GST_MIXER_TRACK_OUTPUT; + + if (first) { + first = FALSE; + flags |= GST_MIXER_TRACK_MASTER; + } + play_track = gst_alsa_mixer_track_new (element, samename, i, + flags, FALSE, NULL, FALSE); + + } else if (has_playback_switch) { /* simple mute switch */ - track = gst_alsa_mixer_track_new (element, samename, - i, 0, flags, GST_ALSA_MIXER_TRACK_PLAYBACK); - mixer->tracklist = g_list_append (mixer->tracklist, track); + play_track = gst_alsa_mixer_track_new (element, samename, i, + GST_MIXER_TRACK_OUTPUT, TRUE, NULL, FALSE); + } + + if (snd_mixer_selem_is_enumerated (element)) { + GstMixerOptions *opts = gst_alsa_mixer_options_new (element, i); + + GST_LOG ("[%s] is enumerated (%d)", name, i); + mixer->tracklist = g_list_append (mixer->tracklist, opts); } } - next: + if (mixer->dir & GST_ALSA_MIXER_CAPTURE) { + gboolean has_capture_switch, has_common_switch; + gboolean has_capture_volume, has_common_volume; + + has_capture_switch = snd_mixer_selem_has_capture_switch (element); + has_common_switch = snd_mixer_selem_has_common_switch (element); + has_capture_volume = snd_mixer_selem_has_capture_volume (element); + has_common_volume = snd_mixer_selem_has_common_volume (element); + + GST_LOG ("[%s] CAPTURE: has_capture_volume=%d, has_common_volume=%d, " + "has_capture_switch=%d, has_common_switch=%d, play_track=%p", name, + has_capture_volume, has_common_volume, has_capture_switch, + has_common_switch, play_track); + + if (has_capture_volume && !(play_track && has_common_volume)) { + cap_track = gst_alsa_mixer_track_new (element, samename, i, + GST_MIXER_TRACK_INPUT, FALSE, NULL, play_track != NULL); + } else if (has_capture_switch && !(play_track && has_common_switch)) { + cap_track = gst_alsa_mixer_track_new (element, samename, i, + GST_MIXER_TRACK_INPUT, TRUE, NULL, play_track != NULL); + } + } + + + if (play_track && cap_track) { + GST_ALSA_MIXER_TRACK (play_track)->shared_mute = + GST_ALSA_MIXER_TRACK (cap_track); + GST_ALSA_MIXER_TRACK (cap_track)->shared_mute = + GST_ALSA_MIXER_TRACK (play_track); + } + + if (play_track) + mixer->tracklist = g_list_append (mixer->tracklist, play_track); + + if (cap_track) + mixer->tracklist = g_list_append (mixer->tracklist, cap_track); + element = snd_mixer_elem_next (element); } } @@ -282,30 +307,13 @@ gst_alsa_mixer_list_tracks (GstAlsaMixer * mixer) static void gst_alsa_mixer_update (GstAlsaMixer * mixer, GstAlsaMixerTrack * alsa_track) { - GstMixerTrack *track = (GstMixerTrack *) alsa_track; - int v = 0; - snd_mixer_handle_events (mixer->handle); - if (!alsa_track) - return; - /* Any updates in flags? */ - if (snd_mixer_selem_has_playback_switch (alsa_track->element)) { - snd_mixer_selem_get_playback_switch (alsa_track->element, 0, &v); - if (v) - track->flags &= ~GST_MIXER_TRACK_MUTE; - else - track->flags |= GST_MIXER_TRACK_MUTE; - } - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CAPTURE) { - snd_mixer_selem_get_capture_switch (alsa_track->element, 0, &v); - if (!v) - track->flags &= ~GST_MIXER_TRACK_RECORD; - else - track->flags |= GST_MIXER_TRACK_RECORD; - } + if (alsa_track) + gst_alsa_mixer_track_update (alsa_track); } + void gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track, gint * volumes) @@ -317,21 +325,36 @@ gst_alsa_mixer_get_volume (GstAlsaMixer * mixer, GstMixerTrack * track, gst_alsa_mixer_update (mixer, alsa_track); - if (track->flags & GST_MIXER_TRACK_MUTE && - !snd_mixer_selem_has_playback_switch (alsa_track->element)) { - for (i = 0; i < track->num_channels; i++) - volumes[i] = alsa_track->volumes[i]; - } else { - for (i = 0; i < track->num_channels; i++) { - long tmp = 0; + if (track->flags & GST_MIXER_TRACK_OUTPUT) { /* return playback volume */ + + /* Is emulated mute flag activated? */ + if (track->flags & GST_MIXER_TRACK_MUTE && + !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) { + for (i = 0; i < track->num_channels; i++) + volumes[i] = alsa_track->volumes[i]; + } else { + for (i = 0; i < track->num_channels; i++) { + long tmp = 0; - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PLAYBACK) { snd_mixer_selem_get_playback_volume (alsa_track->element, i, &tmp); - } else if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CAPTURE) { - snd_mixer_selem_get_capture_volume (alsa_track->element, i, &tmp); + alsa_track->volumes[i] = volumes[i] = (gint) tmp; } + } - alsa_track->volumes[i] = volumes[i] = (gint) tmp; + } else if (track->flags & GST_MIXER_TRACK_INPUT) { /* return capture volume */ + + /* Is emulated record flag activated? */ + if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH || + track->flags & GST_MIXER_TRACK_RECORD) { + for (i = 0; i < track->num_channels; i++) { + long tmp = 0; + + snd_mixer_selem_get_capture_volume (alsa_track->element, i, &tmp); + alsa_track->volumes[i] = volumes[i] = (gint) tmp; + } + } else { + for (i = 0; i < track->num_channels; i++) + volumes[i] = alsa_track->volumes[i]; } } } @@ -340,27 +363,41 @@ void gst_alsa_mixer_set_volume (GstAlsaMixer * mixer, GstMixerTrack * track, gint * volumes) { - gint i; GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track); + gint i; g_return_if_fail (mixer->handle != NULL); gst_alsa_mixer_update (mixer, alsa_track); - /* only set the volume with ALSA lib if the track isn't muted. */ - for (i = 0; i < track->num_channels; i++) { - alsa_track->volumes[i] = volumes[i]; + if (track->flags & GST_MIXER_TRACK_OUTPUT) { - if (!(track->flags & GST_MIXER_TRACK_MUTE) || - snd_mixer_selem_has_playback_switch (alsa_track->element)) { - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PLAYBACK) { + /* Is emulated mute flag activated? */ + if (track->flags & GST_MIXER_TRACK_MUTE && + !(alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH)) { + for (i = 0; i < track->num_channels; i++) + alsa_track->volumes[i] = volumes[i]; + } else { + for (i = 0; i < track->num_channels; i++) { + alsa_track->volumes[i] = volumes[i]; snd_mixer_selem_set_playback_volume (alsa_track->element, i, - (long) volumes[i]); - } else if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CAPTURE) { - snd_mixer_selem_set_capture_volume (alsa_track->element, i, - (long) volumes[i]); + volumes[i]); } } + + } else if (track->flags & GST_MIXER_TRACK_INPUT) { + + /* Is emulated record flag activated? */ + if (track->flags & GST_MIXER_TRACK_RECORD || + alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) { + for (i = 0; i < track->num_channels; i++) { + alsa_track->volumes[i] = volumes[i]; + snd_mixer_selem_set_capture_volume (alsa_track->element, i, volumes[i]); + } + } else { + for (i = 0; i < track->num_channels; i++) + alsa_track->volumes[i] = volumes[i]; + } } } @@ -368,30 +405,46 @@ void gst_alsa_mixer_set_mute (GstAlsaMixer * mixer, GstMixerTrack * track, gboolean mute) { - gint i; GstAlsaMixerTrack *alsa_track = GST_ALSA_MIXER_TRACK (track); g_return_if_fail (mixer->handle != NULL); gst_alsa_mixer_update (mixer, alsa_track); + if (!!(mute) == !!(track->flags & GST_MIXER_TRACK_MUTE)) + return; + if (mute) { track->flags |= GST_MIXER_TRACK_MUTE; + + if (alsa_track->shared_mute) + ((GstMixerTrack *) (alsa_track->shared_mute))->flags |= + GST_MIXER_TRACK_MUTE; } else { track->flags &= ~GST_MIXER_TRACK_MUTE; + + if (alsa_track->shared_mute) + ((GstMixerTrack *) (alsa_track->shared_mute))->flags &= + ~GST_MIXER_TRACK_MUTE; } - if (snd_mixer_selem_has_playback_switch (alsa_track->element)) { + if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PSWITCH) { snd_mixer_selem_set_playback_switch_all (alsa_track->element, mute ? 0 : 1); } else { - for (i = 0; i < track->num_channels; i++) { - long vol = mute ? 0 : alsa_track->volumes[i]; + gint i; + GstAlsaMixerTrack *ctrl_track; - if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CAPTURE) { - snd_mixer_selem_set_capture_volume (alsa_track->element, i, vol); - } else if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_PLAYBACK) { - snd_mixer_selem_set_playback_volume (alsa_track->element, i, vol); - } + if ((track->flags & GST_MIXER_TRACK_INPUT) + && alsa_track->shared_mute != NULL) + ctrl_track = alsa_track->shared_mute; + else + ctrl_track = alsa_track; + + for (i = 0; i < ((GstMixerTrack *) ctrl_track)->num_channels; i++) { + long vol = + mute ? ((GstMixerTrack *) ctrl_track)->min_volume : ctrl_track-> + volumes[i]; + snd_mixer_selem_set_playback_volume (ctrl_track->element, i, vol); } } } @@ -406,13 +459,45 @@ gst_alsa_mixer_set_record (GstAlsaMixer * mixer, gst_alsa_mixer_update (mixer, alsa_track); + if (!!(record) == !!(track->flags & GST_MIXER_TRACK_RECORD)) + return; + if (record) { track->flags |= GST_MIXER_TRACK_RECORD; } else { track->flags &= ~GST_MIXER_TRACK_RECORD; } - snd_mixer_selem_set_capture_switch_all (alsa_track->element, record ? 1 : 0); + if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH) { + snd_mixer_selem_set_capture_switch_all (alsa_track->element, + record ? 1 : 0); + + /* update all tracks in same exlusive cswitch group */ + if (alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL) { + GList *item; + + for (item = mixer->tracklist; item != NULL; item = item->next) { + + if (GST_IS_ALSA_MIXER_TRACK (item->data)) { + GstAlsaMixerTrack *item_alsa_track = + GST_ALSA_MIXER_TRACK (item->data); + + if (item_alsa_track->alsa_flags & GST_ALSA_MIXER_TRACK_CSWITCH_EXCL && + item_alsa_track->capture_group == alsa_track->capture_group) { + gst_alsa_mixer_update (mixer, item_alsa_track); + } + } + } + } + } else { + gint i; + + for (i = 0; i < track->num_channels; i++) { + long vol = record ? alsa_track->volumes[i] : track->min_volume; + + snd_mixer_selem_set_capture_volume (alsa_track->element, i, vol); + } + } } void diff --git a/ext/alsa/gstalsamixertrack.c b/ext/alsa/gstalsamixertrack.c index 8546521354..849e36ea3d 100644 --- a/ext/alsa/gstalsamixertrack.c +++ b/ext/alsa/gstalsamixertrack.c @@ -68,10 +68,57 @@ gst_alsa_mixer_track_init (GstAlsaMixerTrack * alsa_track) { } +static void +gst_alsa_mixer_track_update_alsa_capabilities (GstAlsaMixerTrack * alsa_track) +{ + alsa_track->alsa_flags = 0; + alsa_track->capture_group = -1; + + if (snd_mixer_selem_has_common_volume (alsa_track->element)) + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_VOLUME; + + if (snd_mixer_selem_has_playback_volume (alsa_track->element)) + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PVOLUME; + + if (snd_mixer_selem_has_capture_volume (alsa_track->element)) + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CVOLUME; + + if (snd_mixer_selem_has_common_switch (alsa_track->element)) + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_SWITCH; + + if (snd_mixer_selem_has_playback_switch (alsa_track->element)) + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PSWITCH; + + if (snd_mixer_selem_has_capture_switch (alsa_track->element)) { + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH; + + if (snd_mixer_selem_has_capture_switch_exclusive (alsa_track->element)) { + alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH_EXCL; + alsa_track->capture_group = + snd_mixer_selem_get_capture_group (alsa_track->element); + } + } + + GST_LOG ("[%s] alsa_flags=0x%08x, capture_group=%d", + snd_mixer_selem_get_name (alsa_track->element), + alsa_track->alsa_flags, alsa_track->capture_group); +} + +inline static gboolean +alsa_track_has_cap (GstAlsaMixerTrack * alsa_track, guint32 flag) +{ + return ((alsa_track->alsa_flags & flag) != 0); +} + GstMixerTrack * gst_alsa_mixer_track_new (snd_mixer_elem_t * element, - gint num, gint track_num, gint channels, gint flags, gint alsa_flags) + gint num, gint track_num, gint flags, gboolean sw, + GstAlsaMixerTrack * shared_mute_track, gboolean append_capture) { + GstAlsaMixerTrack *alsa_track; + GstMixerTrack *track; + const gchar *name; + const gchar *label; gint i; long min = 0, max = 0; const struct @@ -93,71 +140,177 @@ gst_alsa_mixer_track_new (snd_mixer_elem_t * element, "Capture", N_("Capture")} }; - GstMixerTrack *track = g_object_new (GST_ALSA_MIXER_TRACK_TYPE, NULL); - GstAlsaMixerTrack *alsa_track = (GstAlsaMixerTrack *) track; + name = snd_mixer_selem_get_name (element); - /* set basic information */ - if (num == 0) - track->label = g_strdup (snd_mixer_selem_get_name (element)); + GST_LOG ("[%s] num=%d,track_num=%d,flags=0x%08x,sw=%s,shared_mute_track=%p", + name, num, track_num, flags, (sw) ? "true" : "false", shared_mute_track); + + track = (GstMixerTrack *) g_object_new (GST_ALSA_MIXER_TRACK_TYPE, NULL); + alsa_track = (GstAlsaMixerTrack *) track; + + GST_LOG ("[%s] created new mixer track %p", name, track); + + /* This reflects the assumptions used for GstAlsaMixerTrack */ + if (!(!!(flags & GST_MIXER_TRACK_OUTPUT) ^ !!(flags & GST_MIXER_TRACK_INPUT))) { + GST_ERROR ("Mixer track must be either output or input!"); + g_return_val_if_reached (NULL); + } + + track->flags = flags; + alsa_track->element = element; + alsa_track->shared_mute = shared_mute_track; + alsa_track->track_num = track_num; + alsa_track->alsa_channels = 0; + + gst_alsa_mixer_track_update_alsa_capabilities (alsa_track); + + if (flags & GST_MIXER_TRACK_OUTPUT) { + while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS && + snd_mixer_selem_has_playback_channel (element, + alsa_track->alsa_channels)) { + alsa_track->alsa_channels++; + } + GST_LOG ("[%s] %d output channels", name, alsa_track->alsa_channels); + } else if (flags & GST_MIXER_TRACK_INPUT) { + while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS && + snd_mixer_selem_has_capture_channel (element, + alsa_track->alsa_channels)) { + alsa_track->alsa_channels++; + } + GST_LOG ("[%s] %d input channels", name, alsa_track->alsa_channels); + } else { + g_assert_not_reached (); + } + + if (sw) + track->num_channels = 0; else - track->label = g_strdup_printf ("%s %d", - snd_mixer_selem_get_name (element), num + 1); + track->num_channels = alsa_track->alsa_channels; + /* translate the name if we can */ + label = name; for (i = 0; i < G_N_ELEMENTS (alsa_track_labels); ++i) { - if (!g_utf8_collate (snd_mixer_selem_get_name (element), - alsa_track_labels[i].orig)) { - g_free (track->label); - if (num == 0) - track->label = g_strdup (_(alsa_track_labels[i].trans)); - else - track->label = g_strdup_printf ("%s %d", - _(alsa_track_labels[i].trans), num); + if (g_utf8_collate (label, alsa_track_labels[i].orig) == 0) { + label = _(alsa_track_labels[i].trans); break; } } - track->num_channels = channels; - track->flags = flags; - alsa_track->element = element; - alsa_track->alsa_flags = alsa_flags; - alsa_track->track_num = track_num; + + if (num == 0) { + track->label = g_strdup_printf ("%s%s%s", label, + append_capture ? " " : "", append_capture ? _("Capture") : ""); + } else { + track->label = g_strdup_printf ("%s%s%s %d", label, + append_capture ? " " : "", append_capture ? _("Capture") : "", num); + } /* set volume information */ - if (channels) { - if (alsa_flags & GST_ALSA_MIXER_TRACK_PLAYBACK) { + if (track->num_channels > 0) { + if ((flags & GST_MIXER_TRACK_OUTPUT)) snd_mixer_selem_get_playback_volume_range (element, &min, &max); - } else if (alsa_flags & GST_ALSA_MIXER_TRACK_CAPTURE) { + else snd_mixer_selem_get_capture_volume_range (element, &min, &max); - } } track->min_volume = (gint) min; track->max_volume = (gint) max; - for (i = 0; i < channels; i++) { + for (i = 0; i < track->num_channels; i++) { long tmp = 0; - if (alsa_flags & GST_ALSA_MIXER_TRACK_PLAYBACK) { + if (flags & GST_MIXER_TRACK_OUTPUT) snd_mixer_selem_get_playback_volume (element, i, &tmp); - } else if (alsa_flags & GST_ALSA_MIXER_TRACK_CAPTURE) { + else snd_mixer_selem_get_capture_volume (element, i, &tmp); - } + alsa_track->volumes[i] = (gint) tmp; } - if (snd_mixer_selem_has_playback_switch (element)) { - int val = 1; - - snd_mixer_selem_get_playback_switch (element, 0, &val); - if (!val) - track->flags |= GST_MIXER_TRACK_MUTE; - } - - if (flags & GST_MIXER_TRACK_INPUT) { - int val = 0; - - snd_mixer_selem_get_capture_switch (element, 0, &val); - if (val) - track->flags |= GST_MIXER_TRACK_RECORD; - } + gst_alsa_mixer_track_update (alsa_track); return track; } + +void +gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track) +{ + GstMixerTrack *track = (GstMixerTrack *) alsa_track; + gint i; + gint audible = !(track->flags & GST_MIXER_TRACK_MUTE); + + /* Any updates in flags? */ + if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PSWITCH)) { + int v = 0; + + audible = 0; + for (i = 0; i < alsa_track->alsa_channels; ++i) { + snd_mixer_selem_get_playback_switch (alsa_track->element, i, &v); + audible += v; + } + + } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME) && + track->flags & GST_MIXER_TRACK_MUTE) { + /* check if user has raised volume with a parallel running application */ + + for (i = 0; i < track->num_channels; i++) { + long vol = 0; + + snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol); + + if (vol > track->min_volume) { + audible = 1; + break; + } + } + } + + if (!!(audible) != !(track->flags & GST_MIXER_TRACK_MUTE)) { + if (audible) { + track->flags &= ~GST_MIXER_TRACK_MUTE; + + if (alsa_track->shared_mute) + ((GstMixerTrack *) (alsa_track->shared_mute))->flags &= + ~GST_MIXER_TRACK_MUTE; + } else { + track->flags |= GST_MIXER_TRACK_MUTE; + + if (alsa_track->shared_mute) + ((GstMixerTrack *) (alsa_track->shared_mute))->flags |= + GST_MIXER_TRACK_MUTE; + } + } + + if (track->flags & GST_MIXER_TRACK_INPUT) { + gint recording = track->flags & GST_MIXER_TRACK_RECORD; + + if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CSWITCH)) { + int v = 0; + + recording = 0; + for (i = 0; i < alsa_track->alsa_channels; ++i) { + snd_mixer_selem_get_capture_switch (alsa_track->element, i, &v); + recording += v; + } + + } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME) && + !(track->flags & GST_MIXER_TRACK_RECORD)) { + /* check if user has raised volume with a parallel running application */ + + for (i = 0; i < track->num_channels; i++) { + long vol = 0; + + snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol); + + if (vol > track->min_volume) { + recording = 1; + break; + } + } + } + + if (recording) + track->flags |= GST_MIXER_TRACK_RECORD; + else + track->flags &= ~GST_MIXER_TRACK_RECORD; + } + +} diff --git a/ext/alsa/gstalsamixertrack.h b/ext/alsa/gstalsamixertrack.h index de1974b86f..acc64cb3f1 100644 --- a/ext/alsa/gstalsamixertrack.h +++ b/ext/alsa/gstalsamixertrack.h @@ -38,15 +38,24 @@ G_BEGIN_DECLS typedef struct _GstAlsaMixerTrack GstAlsaMixerTrack; typedef struct _GstAlsaMixerTrackClass GstAlsaMixerTrackClass; -#define GST_ALSA_MIXER_TRACK_CAPTURE (1<<0) -#define GST_ALSA_MIXER_TRACK_PLAYBACK (1<<1) +#define GST_ALSA_MIXER_TRACK_VOLUME (1<<0) /* common volume */ +#define GST_ALSA_MIXER_TRACK_PVOLUME (1<<1) +#define GST_ALSA_MIXER_TRACK_CVOLUME (1<<2) +#define GST_ALSA_MIXER_TRACK_SWITCH (1<<3) /* common switch */ +#define GST_ALSA_MIXER_TRACK_PSWITCH (1<<4) +#define GST_ALSA_MIXER_TRACK_CSWITCH (1<<5) +#define GST_ALSA_MIXER_TRACK_CSWITCH_EXCL (1<<6) + +#define GST_ALSA_MAX_CHANNELS (SND_MIXER_SCHN_LAST+1) -#define GST_ALSA_MAX_CHANNELS 32 /* tracks can have up to 32 channels */ struct _GstAlsaMixerTrack { GstMixerTrack parent; - snd_mixer_elem_t *element; /* the ALSA mixer element for this track */ + snd_mixer_elem_t *element; /* the ALSA mixer element for this track */ + GstAlsaMixerTrack *shared_mute; gint track_num; - gint alsa_flags; + guint32 alsa_flags; /* alsa track capabilities */ + gint alsa_channels; + gint capture_group; gint volumes[GST_ALSA_MAX_CHANNELS]; }; @@ -58,10 +67,11 @@ GType gst_alsa_mixer_track_get_type (void); GstMixerTrack * gst_alsa_mixer_track_new (snd_mixer_elem_t * element, gint num, gint track_num, - gint channels, gint flags, - gint alsa_flags); - + gboolean sw, /* is simple switch? */ + GstAlsaMixerTrack * shared_mute_track, + gboolean label_append_capture); +void gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track); G_END_DECLS