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 if (snd_mixer_selem_has_common_volume (alsa_track->element)) |
|
79 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_VOLUME; |
|
80 |
|
81 if (snd_mixer_selem_has_playback_volume (alsa_track->element)) |
|
82 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PVOLUME; |
|
83 |
|
84 if (snd_mixer_selem_has_capture_volume (alsa_track->element)) |
|
85 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CVOLUME; |
|
86 |
|
87 if (snd_mixer_selem_has_common_switch (alsa_track->element)) |
|
88 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_SWITCH; |
|
89 |
|
90 if (snd_mixer_selem_has_playback_switch (alsa_track->element)) |
|
91 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_PSWITCH; |
|
92 |
|
93 if (snd_mixer_selem_has_capture_switch (alsa_track->element)) { |
|
94 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH; |
|
95 |
|
96 if (snd_mixer_selem_has_capture_switch_exclusive (alsa_track->element)) { |
|
97 alsa_track->alsa_flags |= GST_ALSA_MIXER_TRACK_CSWITCH_EXCL; |
|
98 alsa_track->capture_group = |
|
99 snd_mixer_selem_get_capture_group (alsa_track->element); |
|
100 } |
|
101 } |
|
102 |
|
103 GST_LOG ("[%s] alsa_flags=0x%08x, capture_group=%d", |
|
104 snd_mixer_selem_get_name (alsa_track->element), |
|
105 alsa_track->alsa_flags, alsa_track->capture_group); |
|
106 } |
|
107 |
|
108 inline static gboolean |
|
109 alsa_track_has_cap (GstAlsaMixerTrack * alsa_track, guint32 flag) |
|
110 { |
|
111 return ((alsa_track->alsa_flags & flag) != 0); |
|
112 } |
|
113 |
|
114 GstMixerTrack * |
|
115 gst_alsa_mixer_track_new (snd_mixer_elem_t * element, |
|
116 gint num, gint track_num, gint flags, gboolean sw, |
|
117 GstAlsaMixerTrack * shared_mute_track, gboolean append_capture) |
|
118 { |
|
119 GstAlsaMixerTrack *alsa_track; |
|
120 GstMixerTrack *track; |
|
121 const gchar *name; |
|
122 const gchar *label; |
|
123 gint i; |
|
124 long min = 0, max = 0; |
|
125 const struct |
|
126 { |
|
127 const gchar orig[12]; |
|
128 const gchar trans[12]; |
|
129 } alsa_track_labels[] = { |
|
130 { |
|
131 "Master", N_("Master")}, { |
|
132 "Bass", N_("Bass")}, { |
|
133 "Treble", N_("Treble")}, { |
|
134 "PCM", N_("PCM")}, { |
|
135 "Synth", N_("Synth")}, { |
|
136 "Line", N_("Line-in")}, { |
|
137 "CD", N_("CD")}, { |
|
138 "Mic", N_("Microphone")}, { |
|
139 "PC Speaker", N_("PC Speaker")}, { |
|
140 "Playback", N_("Playback")}, { |
|
141 "Capture", N_("Capture")} |
|
142 }; |
|
143 |
|
144 name = snd_mixer_selem_get_name (element); |
|
145 |
|
146 GST_LOG ("[%s] num=%d,track_num=%d,flags=0x%08x,sw=%s,shared_mute_track=%p", |
|
147 name, num, track_num, flags, (sw) ? "true" : "false", shared_mute_track); |
|
148 |
|
149 track = (GstMixerTrack *) g_object_new (GST_ALSA_MIXER_TRACK_TYPE, |
|
150 "untranslated-label", name, NULL); |
|
151 |
|
152 alsa_track = (GstAlsaMixerTrack *) track; |
|
153 |
|
154 GST_LOG ("[%s] created new mixer track %p", name, track); |
|
155 |
|
156 /* This reflects the assumptions used for GstAlsaMixerTrack */ |
|
157 if (!(!!(flags & GST_MIXER_TRACK_OUTPUT) ^ !!(flags & GST_MIXER_TRACK_INPUT))) { |
|
158 GST_ERROR ("Mixer track must be either output or input!"); |
|
159 g_return_val_if_reached (NULL); |
|
160 } |
|
161 |
|
162 track->flags = flags; |
|
163 alsa_track->element = element; |
|
164 alsa_track->shared_mute = shared_mute_track; |
|
165 alsa_track->track_num = track_num; |
|
166 alsa_track->alsa_channels = 0; |
|
167 |
|
168 gst_alsa_mixer_track_update_alsa_capabilities (alsa_track); |
|
169 |
|
170 if (flags & GST_MIXER_TRACK_OUTPUT) { |
|
171 while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS && |
|
172 snd_mixer_selem_has_playback_channel (element, |
|
173 alsa_track->alsa_channels)) { |
|
174 alsa_track->alsa_channels++; |
|
175 } |
|
176 GST_LOG ("[%s] %d output channels", name, alsa_track->alsa_channels); |
|
177 } else if (flags & GST_MIXER_TRACK_INPUT) { |
|
178 while (alsa_track->alsa_channels < GST_ALSA_MAX_CHANNELS && |
|
179 snd_mixer_selem_has_capture_channel (element, |
|
180 alsa_track->alsa_channels)) { |
|
181 alsa_track->alsa_channels++; |
|
182 } |
|
183 GST_LOG ("[%s] %d input channels", name, alsa_track->alsa_channels); |
|
184 } else { |
|
185 g_assert_not_reached (); |
|
186 } |
|
187 |
|
188 if (sw) |
|
189 track->num_channels = 0; |
|
190 else |
|
191 track->num_channels = alsa_track->alsa_channels; |
|
192 |
|
193 /* translate the name if we can */ |
|
194 label = name; |
|
195 for (i = 0; i < G_N_ELEMENTS (alsa_track_labels); ++i) { |
|
196 if (g_utf8_collate (label, alsa_track_labels[i].orig) == 0) { |
|
197 label = _(alsa_track_labels[i].trans); |
|
198 break; |
|
199 } |
|
200 } |
|
201 |
|
202 if (num == 0) { |
|
203 track->label = g_strdup_printf ("%s%s%s", label, |
|
204 append_capture ? " " : "", append_capture ? _("Capture") : ""); |
|
205 } else { |
|
206 track->label = g_strdup_printf ("%s%s%s %d", label, |
|
207 append_capture ? " " : "", append_capture ? _("Capture") : "", num); |
|
208 } |
|
209 |
|
210 /* set volume information */ |
|
211 if (track->num_channels > 0) { |
|
212 if ((flags & GST_MIXER_TRACK_OUTPUT)) |
|
213 snd_mixer_selem_get_playback_volume_range (element, &min, &max); |
|
214 else |
|
215 snd_mixer_selem_get_capture_volume_range (element, &min, &max); |
|
216 } |
|
217 track->min_volume = (gint) min; |
|
218 track->max_volume = (gint) max; |
|
219 |
|
220 for (i = 0; i < track->num_channels; i++) { |
|
221 long tmp = 0; |
|
222 |
|
223 if (flags & GST_MIXER_TRACK_OUTPUT) |
|
224 snd_mixer_selem_get_playback_volume (element, i, &tmp); |
|
225 else |
|
226 snd_mixer_selem_get_capture_volume (element, i, &tmp); |
|
227 |
|
228 alsa_track->volumes[i] = (gint) tmp; |
|
229 } |
|
230 |
|
231 gst_alsa_mixer_track_update (alsa_track); |
|
232 |
|
233 return track; |
|
234 } |
|
235 |
|
236 void |
|
237 gst_alsa_mixer_track_update (GstAlsaMixerTrack * alsa_track) |
|
238 { |
|
239 GstMixerTrack *track = (GstMixerTrack *) alsa_track; |
|
240 gint i; |
|
241 gint audible = !(track->flags & GST_MIXER_TRACK_MUTE); |
|
242 |
|
243 if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME)) { |
|
244 /* update playback volume */ |
|
245 for (i = 0; i < track->num_channels; i++) { |
|
246 long vol = 0; |
|
247 |
|
248 snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol); |
|
249 alsa_track->volumes[i] = (gint) vol; |
|
250 } |
|
251 } |
|
252 |
|
253 if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME)) { |
|
254 /* update capture volume */ |
|
255 for (i = 0; i < track->num_channels; i++) { |
|
256 long vol = 0; |
|
257 |
|
258 snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol); |
|
259 alsa_track->volumes[i] = (gint) vol; |
|
260 } |
|
261 } |
|
262 |
|
263 /* Any updates in flags? */ |
|
264 if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PSWITCH)) { |
|
265 int v = 0; |
|
266 |
|
267 audible = 0; |
|
268 for (i = 0; i < alsa_track->alsa_channels; ++i) { |
|
269 snd_mixer_selem_get_playback_switch (alsa_track->element, i, &v); |
|
270 audible += v; |
|
271 } |
|
272 |
|
273 } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_PVOLUME) && |
|
274 track->flags & GST_MIXER_TRACK_MUTE) { |
|
275 /* check if user has raised volume with a parallel running application */ |
|
276 |
|
277 for (i = 0; i < track->num_channels; i++) { |
|
278 long vol = 0; |
|
279 |
|
280 snd_mixer_selem_get_playback_volume (alsa_track->element, i, &vol); |
|
281 |
|
282 if (vol > track->min_volume) { |
|
283 audible = 1; |
|
284 break; |
|
285 } |
|
286 } |
|
287 } |
|
288 |
|
289 if (!!(audible) != !(track->flags & GST_MIXER_TRACK_MUTE)) { |
|
290 if (audible) { |
|
291 track->flags &= ~GST_MIXER_TRACK_MUTE; |
|
292 |
|
293 if (alsa_track->shared_mute) |
|
294 ((GstMixerTrack *) (alsa_track->shared_mute))->flags &= |
|
295 ~GST_MIXER_TRACK_MUTE; |
|
296 } else { |
|
297 track->flags |= GST_MIXER_TRACK_MUTE; |
|
298 |
|
299 if (alsa_track->shared_mute) |
|
300 ((GstMixerTrack *) (alsa_track->shared_mute))->flags |= |
|
301 GST_MIXER_TRACK_MUTE; |
|
302 } |
|
303 } |
|
304 |
|
305 if (track->flags & GST_MIXER_TRACK_INPUT) { |
|
306 gint recording = track->flags & GST_MIXER_TRACK_RECORD; |
|
307 |
|
308 if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CSWITCH)) { |
|
309 int v = 0; |
|
310 |
|
311 recording = 0; |
|
312 for (i = 0; i < alsa_track->alsa_channels; ++i) { |
|
313 snd_mixer_selem_get_capture_switch (alsa_track->element, i, &v); |
|
314 recording += v; |
|
315 } |
|
316 |
|
317 } else if (alsa_track_has_cap (alsa_track, GST_ALSA_MIXER_TRACK_CVOLUME) && |
|
318 !(track->flags & GST_MIXER_TRACK_RECORD)) { |
|
319 /* check if user has raised volume with a parallel running application */ |
|
320 |
|
321 for (i = 0; i < track->num_channels; i++) { |
|
322 long vol = 0; |
|
323 |
|
324 snd_mixer_selem_get_capture_volume (alsa_track->element, i, &vol); |
|
325 |
|
326 if (vol > track->min_volume) { |
|
327 recording = 1; |
|
328 break; |
|
329 } |
|
330 } |
|
331 } |
|
332 |
|
333 if (recording) |
|
334 track->flags |= GST_MIXER_TRACK_RECORD; |
|
335 else |
|
336 track->flags &= ~GST_MIXER_TRACK_RECORD; |
|
337 } |
|
338 |
|
339 } |
|