ext/alsa/: Improve and fix mixer track handling, in particular better handling of...
authorViktor Peters <viktor.peters@gmail.com>
Tue, 29 Aug 2006 11:50:51 +0000 (11:50 +0000)
committerTim-Philipp Müller <tim@centricular.net>
Tue, 29 Aug 2006 11:50:51 +0000 (11:50 +0000)
Original commit message from CVS:
Patch by: Viktor Peters  <viktor dot peters at gmail dot com>
* 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
ext/alsa/gstalsamixer.c
ext/alsa/gstalsamixertrack.c
ext/alsa/gstalsamixertrack.h

index e80c453..8ee752c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,31 @@
 2006-08-29  Tim-Philipp Müller  <tim at centricular dot net>
 
+       Patch by: Viktor Peters  <viktor dot peters at gmail dot com>
+
+       * 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  <tim at centricular dot net>
+
        * gst/playback/gstplaybin.c:
          Improve docs: add section about BUFFERING messages sent by playbin.
 
index a7d7798..5b8f0d6 100644 (file)
@@ -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 */
+  /* build track list
+   *
+   * Some ALSA tracks may have playback and capture capabilities.
+   * Here we model them as two separate GStreamer tracks.
+   */
+
   for (i = 0; i < count; i++) {
+    GstMixerTrack *play_track = NULL;
+    GstMixerTrack *cap_track = NULL;
+    const gchar *name;
     GList *item;
-    gint channels = 0, samename = 0;
-    gint flags = GST_MIXER_TRACK_OUTPUT;
-    gboolean got_it = FALSE;
-
-    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;
-    }
+    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;
-
-      /* there might be another volume slider; make that playback */
-      flags &= ~GST_MIXER_TRACK_INPUT;
-      flags |= GST_MIXER_TRACK_OUTPUT;
-    }
+    GST_LOG ("[%s] probing element #%u, mixer->dir=%u", name, i, mixer->dir);
+
+    if (mixer->dir & GST_ALSA_MIXER_PLAYBACK) {
+      gboolean has_playback_switch, has_playback_volume;
+
+      has_playback_switch = snd_mixer_selem_has_playback_switch (element);
+      has_playback_volume = snd_mixer_selem_has_playback_volume (element);
+
+      GST_LOG ("[%s] PLAYBACK: has_playback_volume=%d, has_playback_switch=%d",
+          name, has_playback_volume, has_playback_switch);
+
+      if (has_playback_volume) {
+        gint flags = GST_MIXER_TRACK_OUTPUT;
 
-    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;
+        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 */
+        play_track = gst_alsa_mixer_track_new (element, samename, i,
+            GST_MIXER_TRACK_OUTPUT, TRUE, NULL, FALSE);
       }
-      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;
-    }
 
-    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;
+      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);
+      }
     }
 
-    if (!got_it) {
-      if (flags == GST_MIXER_TRACK_OUTPUT &&
-          snd_mixer_selem_has_playback_switch (element)) {
-        /* 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);
+    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);
       }
     }
 
-  next:
+
+    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;
       }
+    }
+
+  } 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;
 
-      alsa_track->volumes[i] = volumes[i] = (gint) tmp;
+        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
index 8546521..849e36e 100644 (file)
@@ -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;
+  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;
 
-    snd_mixer_selem_get_playback_switch (element, 0, &val);
-    if (!val)
+      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 (flags & GST_MIXER_TRACK_INPUT) {
-    int val = 0;
+  if (track->flags & GST_MIXER_TRACK_INPUT) {
+    gint recording = track->flags & GST_MIXER_TRACK_RECORD;
 
-    snd_mixer_selem_get_capture_switch (element, 0, &val);
-    if (val)
+    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;
   }
 
-  return track;
 }
index de1974b..acc64cb 100644 (file)
@@ -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