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