dea8ef0481a4d18267b439e0590a74dce0ba0b7e
[platform/upstream/gstreamer.git] / ext / alsa / gstalsamixertrack.c
1 /* ALSA mixer track 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 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <gst/gst-i18n-plugin.h>
25
26 #include "gstalsamixertrack.h"
27
28 static void gst_alsa_mixer_track_init (GstAlsaMixerTrack * alsa_track);
29 static void gst_alsa_mixer_track_class_init (gpointer g_class,
30     gpointer class_data);
31
32 static GstMixerTrackClass *parent_class = NULL;
33
34 GType
35 gst_alsa_mixer_track_get_type (void)
36 {
37   static GType track_type = 0;
38
39   if (!track_type) {
40     static const GTypeInfo track_info = {
41       sizeof (GstAlsaMixerTrackClass),
42       NULL,
43       NULL,
44       gst_alsa_mixer_track_class_init,
45       NULL,
46       NULL,
47       sizeof (GstAlsaMixerTrack),
48       0,
49       (GInstanceInitFunc) gst_alsa_mixer_track_init,
50       NULL
51     };
52
53     track_type =
54         g_type_register_static (GST_TYPE_MIXER_TRACK, "GstAlsaMixerTrack",
55         &track_info, 0);
56   }
57
58   return track_type;
59 }
60
61 static void
62 gst_alsa_mixer_track_class_init (gpointer g_class, gpointer class_data)
63 {
64   parent_class = g_type_class_peek_parent (g_class);
65 }
66
67 static void
68 gst_alsa_mixer_track_init (GstAlsaMixerTrack * alsa_track)
69 {
70 }
71
72 static void
73 gst_alsa_mixer_track_update_alsa_capabilities (GstAlsaMixerTrack * alsa_track)
74 {
75   alsa_track->alsa_flags = 0;
76   alsa_track->capture_group = -1;
77
78   /* common flags */
79   if (snd_mixer_selem_has_common_volume (alsa_track->element))
80     alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_VOLUME;
81
82   if (snd_mixer_selem_has_common_switch (alsa_track->element))
83     alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_SWITCH;
84
85   /* Since we create two separate mixer track objects for alsa elements that
86    * support both playback and capture, we're going to 'hide' the alsa flags
87    * that don't pertain to this mixer track from alsa_flags, otherwise
88    * gst_alsa_mixer_track_update() is going to do things we don't want */
89
90   /* playback flags */
91   if ((GST_MIXER_TRACK (alsa_track)->flags & GST_MIXER_TRACK_OUTPUT)) {
92     if (snd_mixer_selem_has_playback_volume (alsa_track->element))
93       alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PVOLUME;
94
95     if (snd_mixer_selem_has_playback_switch (alsa_track->element))
96       alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PSWITCH;
97   }
98
99   /* capture flags */
100   if ((GST_MIXER_TRACK (alsa_track)->flags & GST_MIXER_TRACK_INPUT)) {
101     if (snd_mixer_selem_has_capture_volume (alsa_track->element))
102       alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CVOLUME;
103
104     if (snd_mixer_selem_has_capture_switch (alsa_track->element)) {
105       alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH;
106
107       if (snd_mixer_selem_has_capture_switch_exclusive (alsa_track->element)) {
108         alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH_EXCL;
109         alsa_track->capture_group =
110             snd_mixer_selem_get_capture_group (alsa_track->element);
111       }
112     }
113   }
114
115   GST_LOG ("[%s] alsa_flags=0x%08x, capture_group=%d",
116       snd_mixer_selem_get_name (alsa_track->element),
117       alsa_track->alsa_flags, alsa_track->capture_group);
118 }
119
120 inline static gboolean
121 alsa_track_has_cap (GstAlsaMixerTrack * alsa_track, guint32 flag)
122 {
123   return ((alsa_track->alsa_flags & flag) != 0);
124 }
125
126 GstMixerTrack *
127 gst_alsa_mixer_track_new (snd_mixer_elem_t * element,
128     gint num, gint track_num, gint flags, gboolean sw,
129     GstAlsaMixerTrack * shared_mute_track, gboolean append_capture)
130 {
131   GstAlsaMixerTrack *alsa_track;
132   GstMixerTrack *track;
133   const gchar *name;
134   guint index;
135   const gchar *label;
136   gint i;
137   long min = 0, max = 0;
138   const struct
139   {
140     const gchar orig[12];
141     const gchar trans[12];
142   } alsa_track_labels[] = {
143     {
144     "Master", N_("Master")}, {
145     "Bass", N_("Bass")}, {
146     "Treble", N_("Treble")}, {
147     "PCM", N_("PCM")}, {
148     "Synth", N_("Synth")}, {
149     "Line", N_("Line-in")}, {
150     "CD", N_("CD")}, {
151     "Mic", N_("Microphone")}, {
152     "PC Speaker", N_("PC Speaker")}, {
153     "Playback", N_("Playback")}, {
154     "Capture", N_("Capture")}
155   };
156
157   name = snd_mixer_selem_get_name (element);
158   index = snd_mixer_selem_get_index (element);
159
160   GST_LOG
161       ("[%s,%u] num=%d,track_num=%d,flags=0x%08x,sw=%s,shared_mute_track=%p",
162       name, index, num, track_num, flags, (sw) ? "true" : "false",
163       shared_mute_track);
164
165   track = (GstMixerTrack *) g_object_new (GST_ALSA_MIXER_TRACK_TYPE,
166       "untranslated-label", name, "index", index, NULL);
167
168   alsa_track = (GstAlsaMixerTrack *) track;
169
170   GST_LOG ("[%s] created new mixer track %p", name, track);
171
172   /* This reflects the assumptions used for GstAlsaMixerTrack */
173   if (!(! !(flags & GST_MIXER_TRACK_OUTPUT) ^ ! !(flags &
174               GST_MIXER_TRACK_INPUT))) {
175     GST_ERROR ("Mixer track must be either output or input!");
176     g_return_val_if_reached (NULL);
177   }
178
179   track->flags = flags;
180   alsa_track->element = element;
181   alsa_track->shared_mute = shared_mute_track;
182   alsa_track->track_num = track_num;
183   alsa_track->alsa_channels = 0;
184
185   gst_alsa_mixer_track_update_alsa_capabilities (alsa_track);
186
187   if (flags & GST_MIXER_TRACK_OUTPUT) {
188     while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS &&
189         snd_mixer_selem_has_playback_channel (element,
190             alsa_track->alsa_channels)) {
191       alsa_track->alsa_channels++;
192     }
193     GST_LOG ("[%s] %d output channels", name, alsa_track->alsa_channels);
194   } else if (flags & GST_MIXER_TRACK_INPUT) {
195     while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS &&
196         snd_mixer_selem_has_capture_channel (element,
197             alsa_track->alsa_channels)) {
198       alsa_track->alsa_channels++;
199     }
200     GST_LOG ("[%s] %d input channels", name, alsa_track->alsa_channels);
201   } else {
202     g_assert_not_reached ();
203   }
204
205   if (sw)
206     track->num_channels = 0;
207   else
208     track->num_channels = alsa_track->alsa_channels;
209
210   /* translate the name if we can */
211   label = name;
212   for (i = 0; i < G_N_ELEMENTS (alsa_track_labels); ++i) {
213     if (g_utf8_collate (label, alsa_track_labels[i].orig) == 0) {
214       label = _(alsa_track_labels[i].trans);
215       break;
216     }
217   }
218
219   if (num == 0) {
220     track->label = g_strdup_printf ("%s%s%s", label,
221         append_capture ? " " : "", append_capture ? _("Capture") : "");
222   } else {
223     track->label = g_strdup_printf ("%s%s%s %d", label,
224         append_capture ? " " : "", append_capture ? _("Capture") : "", num);
225   }
226
227   /* set volume information */
228   if (track->num_channels > 0) {
229     if ((flags & GST_MIXER_TRACK_OUTPUT))
230       snd_mixer_selem_get_playback_volume_range (element, &min, &max);
231     else
232       snd_mixer_selem_get_capture_volume_range (element, &min, &max);
233   }
234   track->min_volume = (gint) min;
235   track->max_volume = (gint) max;
236
237   for (i = 0; i < track->num_channels; i++) {
238     long tmp = 0;
239
240     if (flags & GST_MIXER_TRACK_OUTPUT)
241       snd_mixer_selem_get_playback_volume (element, i, &tmp);
242     else
243       snd_mixer_selem_get_capture_volume (element, i, &tmp);
244
245     alsa_track->volumes[i] = (gint) tmp;
246   }
247
248   gst_alsa_mixer_track_update (alsa_track);
249
250   return track;
251 }
252
253 void
254 gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track)
255 {
256   GstMixerTrack *track = (GstMixerTrack *) alsa_track;
257   gint i;
258   gint audible = !(track->flags & GST_MIXER_TRACK_MUTE);
259
260   if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME)) {
261     /* update playback volume */
262     for (i = 0; i < track->num_channels; i++) {
263       long vol = 0;
264
265       snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol);
266       alsa_track->volumes[i] = (gint) vol;
267     }
268   }
269
270   if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME)) {
271     /* update capture volume */
272     for (i = 0; i < track->num_channels; i++) {
273       long vol = 0;
274
275       snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol);
276       alsa_track->volumes[i] = (gint) vol;
277     }
278   }
279
280   /* Any updates in flags? */
281   if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PSWITCH)) {
282     int v = 0;
283
284     audible = 0;
285     for (i = 0; i < alsa_track->alsa_channels; ++i) {
286       snd_mixer_selem_get_playback_switch (alsa_track->element, i, &v);
287       audible += v;
288     }
289
290   } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME) &&
291       track->flags & GST_MIXER_TRACK_MUTE) {
292     /* check if user has raised volume with a parallel running application */
293
294     for (i = 0; i < track->num_channels; i++) {
295       long vol = 0;
296
297       snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol);
298
299       if (vol > track->min_volume) {
300         audible = 1;
301         break;
302       }
303     }
304   }
305
306   if (! !(audible) != !(track->flags & GST_MIXER_TRACK_MUTE)) {
307     if (audible) {
308       track->flags &= ~GST_MIXER_TRACK_MUTE;
309
310       if (alsa_track->shared_mute)
311         ((GstMixerTrack *) (alsa_track->shared_mute))->flags &=
312             ~GST_MIXER_TRACK_MUTE;
313     } else {
314       track->flags |= GST_MIXER_TRACK_MUTE;
315
316       if (alsa_track->shared_mute)
317         ((GstMixerTrack *) (alsa_track->shared_mute))->flags |=
318             GST_MIXER_TRACK_MUTE;
319     }
320   }
321
322   if (track->flags & GST_MIXER_TRACK_INPUT) {
323     gint recording = track->flags & GST_MIXER_TRACK_RECORD;
324
325     if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CSWITCH)) {
326       int v = 0;
327
328       recording = 0;
329       for (i = 0; i < alsa_track->alsa_channels; ++i) {
330         snd_mixer_selem_get_capture_switch (alsa_track->element, i, &v);
331         recording += v;
332       }
333
334     } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME) &&
335         !(track->flags & GST_MIXER_TRACK_RECORD)) {
336       /* check if user has raised volume with a parallel running application */
337
338       for (i = 0; i < track->num_channels; i++) {
339         long vol = 0;
340
341         snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol);
342
343         if (vol > track->min_volume) {
344           recording = 1;
345           break;
346         }
347       }
348     }
349
350     if (recording)
351       track->flags |= GST_MIXER_TRACK_RECORD;
352     else
353       track->flags &= ~GST_MIXER_TRACK_RECORD;
354   }
355
356 }