1 /* GStreamer OSS4 mixer implementation
2 * Copyright (C) 2007-2008 Tim-Philipp Müller <tim centricular net>
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.
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.
14 * You should have received a copy of the GNU Library General Public
15 * License along with mixer library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * SECTION:element-oss4mixer
23 * This element lets you adjust sound input and output levels with the
24 * Open Sound System (OSS) version 4. It supports the GstMixer interface, which
25 * can be used to obtain a list of available mixer tracks. Set the mixer
26 * element to READY state before using the GstMixer interface on it.
29 * <title>Example pipelines</title>
31 * oss4mixer can't be used in a sensible way in gst-launch.
38 /* Note: ioctl calls on the same open mixer device are serialised via
39 * the object lock to make sure we don't do concurrent ioctls from two
40 * different threads (e.g. app thread and mixer watch thread), since that
41 * will probably confuse OSS. */
53 #include <sys/ioctl.h>
55 #include <gst/interfaces/mixer.h>
56 #include <gst/gst-i18n-plugin.h>
57 #include "gst/glib-compat-private.h"
59 #include <glib/gprintf.h>
61 #define NO_LEGACY_MIXER
63 #include "oss4-audio.h"
64 #include "oss4-mixer.h"
65 #include "oss4-mixer-enum.h"
66 #include "oss4-mixer-slider.h"
67 #include "oss4-mixer-switch.h"
68 #include "oss4-property-probe.h"
69 #include "oss4-soundcard.h"
71 #define GST_OSS4_MIXER_WATCH_INTERVAL 500 /* in millisecs, ie. 0.5s */
73 GST_DEBUG_CATEGORY_EXTERN (oss4mixer_debug);
74 #define GST_CAT_DEFAULT oss4mixer_debug
76 #define DEFAULT_DEVICE NULL
77 #define DEFAULT_DEVICE_NAME NULL
86 static void gst_oss4_mixer_init_interfaces (GType type);
88 GST_BOILERPLATE_FULL (GstOss4Mixer, gst_oss4_mixer, GstElement,
89 GST_TYPE_ELEMENT, gst_oss4_mixer_init_interfaces);
91 static GstStateChangeReturn gst_oss4_mixer_change_state (GstElement *
92 element, GstStateChange transition);
94 static void gst_oss4_mixer_set_property (GObject * object, guint prop_id,
95 const GValue * value, GParamSpec * pspec);
96 static void gst_oss4_mixer_get_property (GObject * object, guint prop_id,
97 GValue * value, GParamSpec * pspec);
98 static void gst_oss4_mixer_finalize (GObject * object);
100 static gboolean gst_oss4_mixer_open (GstOss4Mixer * mixer,
101 gboolean silent_errors);
102 static void gst_oss4_mixer_close (GstOss4Mixer * mixer);
104 static gboolean gst_oss4_mixer_enum_control_update_enum_list (GstOss4Mixer * m,
105 GstOss4MixerControl * mc);
107 #ifndef GST_DISABLE_GST_DEBUG
108 static const gchar *mixer_ext_type_get_name (gint type);
109 static const gchar *mixer_ext_flags_get_string (gint flags);
113 gst_oss4_mixer_base_init (gpointer klass)
115 gst_element_class_set_details_simple (GST_ELEMENT_CLASS (klass),
116 "OSS v4 Audio Mixer", "Generic/Audio",
117 "Control sound input and output levels with OSS4",
118 "Tim-Philipp Müller <tim centricular net>");
122 gst_oss4_mixer_class_init (GstOss4MixerClass * klass)
124 GstElementClass *element_class;
125 GObjectClass *gobject_class;
127 element_class = (GstElementClass *) klass;
128 gobject_class = (GObjectClass *) klass;
130 gobject_class->finalize = gst_oss4_mixer_finalize;
131 gobject_class->set_property = gst_oss4_mixer_set_property;
132 gobject_class->get_property = gst_oss4_mixer_get_property;
135 * GstOss4Mixer:device
137 * OSS4 mixer device (e.g. /dev/oss/hdaudio0/mix0 or /dev/mixerN)
140 g_object_class_install_property (gobject_class, PROP_DEVICE,
141 g_param_spec_string ("device", "Device",
142 "OSS mixer device (e.g. /dev/oss/hdaudio0/mix0 or /dev/mixerN) "
143 "(NULL = use first mixer device found)", DEFAULT_DEVICE,
144 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
147 * GstOss4Mixer:device-name
149 * Human-readable name of the sound device. May be NULL if the device is
150 * not open (ie. when the mixer is in NULL state)
153 g_object_class_install_property (gobject_class, PROP_DEVICE_NAME,
154 g_param_spec_string ("device-name", "Device name",
155 "Human-readable name of the sound device", DEFAULT_DEVICE_NAME,
156 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
158 element_class->change_state = GST_DEBUG_FUNCPTR (gst_oss4_mixer_change_state);
162 gst_oss4_mixer_finalize (GObject * obj)
164 GstOss4Mixer *mixer = GST_OSS4_MIXER (obj);
166 g_free (mixer->device);
168 G_OBJECT_CLASS (parent_class)->finalize (obj);
172 gst_oss4_mixer_reset (GstOss4Mixer * mixer)
175 mixer->need_update = TRUE;
176 memset (&mixer->last_mixext, 0, sizeof (oss_mixext));
180 gst_oss4_mixer_init (GstOss4Mixer * mixer, GstOss4MixerClass * g_class)
182 mixer->device = g_strdup (DEFAULT_DEVICE);
183 mixer->device_name = NULL;
185 gst_oss4_mixer_reset (mixer);
189 gst_oss4_mixer_set_property (GObject * object, guint prop_id,
190 const GValue * value, GParamSpec * pspec)
192 GstOss4Mixer *mixer = GST_OSS4_MIXER (object);
196 GST_OBJECT_LOCK (mixer);
197 if (!GST_OSS4_MIXER_IS_OPEN (mixer)) {
198 g_free (mixer->device);
199 mixer->device = g_value_dup_string (value);
200 /* unset any cached device-name */
201 g_free (mixer->device_name);
202 mixer->device_name = NULL;
204 g_warning ("%s: can't change \"device\" property while mixer is open",
205 GST_OBJECT_NAME (mixer));
207 GST_OBJECT_UNLOCK (mixer);
210 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
216 gst_oss4_mixer_get_property (GObject * object, guint prop_id,
217 GValue * value, GParamSpec * pspec)
219 GstOss4Mixer *mixer = GST_OSS4_MIXER (object);
223 GST_OBJECT_LOCK (mixer);
224 g_value_set_string (value, mixer->device);
225 GST_OBJECT_UNLOCK (mixer);
227 case PROP_DEVICE_NAME:
228 GST_OBJECT_LOCK (mixer);
229 /* If device is set, try to retrieve the name even if we're not open */
230 if (mixer->fd == -1 && mixer->device != NULL) {
231 if (gst_oss4_mixer_open (mixer, TRUE)) {
232 g_value_set_string (value, mixer->device_name);
233 gst_oss4_mixer_close (mixer);
235 g_value_set_string (value, mixer->device_name);
238 g_value_set_string (value, mixer->device_name);
240 GST_OBJECT_UNLOCK (mixer);
243 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
249 gst_oss4_mixer_open (GstOss4Mixer * mixer, gboolean silent_errors)
251 struct oss_mixerinfo mi = { 0, };
254 g_return_val_if_fail (!GST_OSS4_MIXER_IS_OPEN (mixer), FALSE);
257 device = g_strdup (mixer->device);
259 device = gst_oss4_audio_find_device (GST_OBJECT_CAST (mixer));
261 /* desperate times, desperate measures */
263 device = g_strdup ("/dev/mixer");
265 GST_INFO_OBJECT (mixer, "Trying to open OSS4 mixer device '%s'", device);
267 mixer->fd = open (device, O_RDWR, 0);
271 /* Make sure it's OSS4. If it's old OSS, let the old ossmixer handle it */
272 if (!gst_oss4_audio_check_version (GST_OBJECT (mixer), mixer->fd))
275 GST_INFO_OBJECT (mixer, "Opened mixer device '%s', which is mixer %d",
278 /* Get device name for currently open fd */
280 if (ioctl (mixer->fd, SNDCTL_MIXERINFO, &mi) == 0) {
281 mixer->modify_counter = mi.modify_counter;
282 if (mi.name[0] != '\0') {
283 mixer->device_name = g_strdup (mi.name);
286 mixer->modify_counter = 0;
289 if (mixer->device_name == NULL) {
290 mixer->device_name = g_strdup ("Unknown");
292 GST_INFO_OBJECT (mixer, "device name = '%s'", mixer->device_name);
294 mixer->open_device = device;
301 if (!silent_errors) {
302 GST_ELEMENT_ERROR (mixer, RESOURCE, OPEN_READ_WRITE,
303 (_("Could not open audio device for mixer control handling.")),
306 GST_DEBUG_OBJECT (mixer, "open failed: %s (ignoring errors)",
314 gst_oss4_mixer_close (mixer);
315 if (!silent_errors) {
316 GST_ELEMENT_ERROR (mixer, RESOURCE, OPEN_READ_WRITE,
317 (_("Could not open audio device for mixer control handling. "
318 "This version of the Open Sound System is not supported by this "
319 "element.")), ("Try the 'ossmixer' element instead"));
321 GST_DEBUG_OBJECT (mixer, "open failed: legacy oss (ignoring errors)");
329 gst_oss4_mixer_control_free (GstOss4MixerControl * mc)
331 g_list_free (mc->children);
332 g_list_free (mc->mute_group);
333 g_free (mc->enum_vals);
334 memset (mc, 0, sizeof (GstOss4MixerControl));
339 gst_oss4_mixer_free_tracks (GstOss4Mixer * mixer)
341 g_list_foreach (mixer->tracks, (GFunc) g_object_unref, NULL);
342 g_list_free (mixer->tracks);
343 mixer->tracks = NULL;
345 g_list_foreach (mixer->controls, (GFunc) gst_oss4_mixer_control_free, NULL);
346 g_list_free (mixer->controls);
347 mixer->controls = NULL;
351 gst_oss4_mixer_close (GstOss4Mixer * mixer)
353 g_free (mixer->device_name);
354 mixer->device_name = NULL;
356 g_free (mixer->open_device);
357 mixer->open_device = NULL;
359 gst_oss4_mixer_free_tracks (mixer);
361 if (mixer->fd != -1) {
366 gst_oss4_mixer_reset (mixer);
370 gst_oss4_mixer_watch_process_changes (GstOss4Mixer * mixer)
372 GList *c, *t, *tracks = NULL;
374 GST_INFO_OBJECT (mixer, "mixer interface or control changed");
376 /* this is all with the mixer object lock held */
378 /* we go through the list backwards so we can bail out faster when the entire
379 * interface needs to be rebuilt */
380 for (c = g_list_last (mixer->controls); c != NULL; c = c->prev) {
381 GstOss4MixerControl *mc = c->data;
382 oss_mixer_value ossval = { 0, };
385 mc->list_changed = FALSE;
387 /* not interested in controls we don't expose in the mixer interface */
391 /* don't try to read a value from controls that don't have one */
392 if (mc->mixext.type == MIXT_DEVROOT || mc->mixext.type == MIXT_GROUP)
395 /* is this an enum control whose list may change? */
396 if (mc->mixext.type == MIXT_ENUM && mc->enum_version != 0) {
397 if (gst_oss4_mixer_enum_control_update_enum_list (mixer, mc))
398 mc->list_changed = TRUE;
401 ossval.dev = mc->mixext.dev;
402 ossval.ctrl = mc->mixext.ctrl;
403 ossval.timestamp = mc->mixext.timestamp;
405 if (ioctl (mixer->fd, SNDCTL_MIX_READ, &ossval) == -1) {
406 if (errno == EIDRM || errno == EFAULT) {
407 GST_DEBUG ("%s has disappeared", mc->mixext.extname);
410 GST_WARNING_OBJECT (mixer, "MIX_READ failed: %s", g_strerror (errno));
411 /* just ignore, move on to next one */
415 if (ossval.value == mc->last_val) { /* no change */
416 /* GST_LOG_OBJECT (mixer, "%s hasn't changed", mc->mixext.extname); */
420 mc->last_val = ossval.value;
421 GST_LOG_OBJECT (mixer, "%s changed value to %u 0x%08x",
422 mc->mixext.extname, ossval.value, ossval.value);
426 /* copy list and take track refs, so we can safely drop the object lock,
427 * which we need to do to be able to post messages on the bus */
428 tracks = g_list_copy (mixer->tracks);
429 g_list_foreach (tracks, (GFunc) g_object_ref, NULL);
431 GST_OBJECT_UNLOCK (mixer);
433 /* since we don't know (or want to know exactly) which controls belong to
434 * which track, we just go through the tracks one-by-one now and make them
435 * check themselves if any of their controls have changed and which messages
436 * to post on the bus as a result */
437 for (t = tracks; t != NULL; t = t->next) {
438 GstMixerTrack *track = t->data;
440 if (GST_IS_OSS4_MIXER_SLIDER (track)) {
441 gst_oss4_mixer_slider_process_change_unlocked (track);
442 } else if (GST_IS_OSS4_MIXER_SWITCH (track)) {
443 gst_oss4_mixer_switch_process_change_unlocked (track);
444 } else if (GST_IS_OSS4_MIXER_ENUM (track)) {
445 gst_oss4_mixer_enum_process_change_unlocked (track);
448 g_object_unref (track);
450 g_list_free (tracks);
452 GST_OBJECT_LOCK (mixer);
457 GST_OBJECT_UNLOCK (mixer);
458 gst_mixer_mixer_changed (GST_MIXER (mixer));
459 GST_OBJECT_LOCK (mixer);
464 /* This thread watches the mixer for changes in a somewhat inefficient way
465 * (running an ioctl every half second or so). This is still better and
466 * cheaper than apps polling all tracks for changes a few times a second
467 * though. Needs more thought. There's probably (hopefully) a way to get
468 * notifications via the fd directly somehow. */
470 gst_oss4_mixer_watch_thread (gpointer thread_data)
472 GstOss4Mixer *mixer = GST_OSS4_MIXER_CAST (thread_data);
474 GST_DEBUG_OBJECT (mixer, "watch thread running");
476 GST_OBJECT_LOCK (mixer);
477 while (!mixer->watch_shutdown) {
478 oss_mixerinfo mi = { 0, };
482 if (ioctl (mixer->fd, SNDCTL_MIXERINFO, &mi) == 0) {
483 if (mixer->modify_counter != mi.modify_counter) {
484 /* GST_LOG ("processing changes"); */
485 gst_oss4_mixer_watch_process_changes (mixer);
486 mixer->modify_counter = mi.modify_counter;
488 /* GST_LOG ("no changes"); */
491 GST_WARNING_OBJECT (mixer, "MIXERINFO failed: %s", g_strerror (errno));
494 /* we could move the _get_current_time out of the loop and just do the
495 * add in ever iteration, which would be less exact, but who cares */
496 g_get_current_time (&tv);
497 g_time_val_add (&tv, GST_OSS4_MIXER_WATCH_INTERVAL * 1000);
498 (void) g_cond_timed_wait (mixer->watch_cond, GST_OBJECT_GET_LOCK (mixer),
501 GST_OBJECT_UNLOCK (mixer);
503 GST_DEBUG_OBJECT (mixer, "watch thread done");
504 gst_object_unref (mixer);
508 /* call with object lock held */
510 gst_oss4_mixer_wake_up_watch_task (GstOss4Mixer * mixer)
512 GST_LOG_OBJECT (mixer, "signalling watch thread to wake up");
513 g_cond_signal (mixer->watch_cond);
517 gst_oss4_mixer_stop_watch_task (GstOss4Mixer * mixer)
519 if (mixer->watch_thread) {
520 GST_OBJECT_LOCK (mixer);
521 mixer->watch_shutdown = TRUE;
522 GST_LOG_OBJECT (mixer, "signalling watch thread to stop");
523 g_cond_signal (mixer->watch_cond);
524 GST_OBJECT_UNLOCK (mixer);
525 GST_LOG_OBJECT (mixer, "waiting for watch thread to join");
526 g_thread_join (mixer->watch_thread);
527 GST_DEBUG_OBJECT (mixer, "watch thread stopped");
528 mixer->watch_thread = NULL;
531 if (mixer->watch_cond) {
532 g_cond_free (mixer->watch_cond);
533 mixer->watch_cond = NULL;
538 gst_oss4_mixer_start_watch_task (GstOss4Mixer * mixer)
542 mixer->watch_cond = g_cond_new ();
543 mixer->watch_shutdown = FALSE;
545 #if !GLIB_CHECK_VERSION (2, 31, 0)
546 mixer->watch_thread = g_thread_create (gst_oss4_mixer_watch_thread,
547 gst_object_ref (mixer), TRUE, &err);
549 mixer->watch_thread = g_thread_try_new ("oss4-mixer-thread",
550 gst_oss4_mixer_watch_thread, gst_object_ref (mixer), &err);
553 if (mixer->watch_thread == NULL) {
554 GST_ERROR_OBJECT (mixer, "Could not create watch thread: %s", err->message);
555 g_cond_free (mixer->watch_cond);
556 mixer->watch_cond = NULL;
561 static GstStateChangeReturn
562 gst_oss4_mixer_change_state (GstElement * element, GstStateChange transition)
564 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
565 GstOss4Mixer *mixer = GST_OSS4_MIXER (element);
567 switch (transition) {
568 case GST_STATE_CHANGE_NULL_TO_READY:
569 if (!gst_oss4_mixer_open (mixer, FALSE))
570 return GST_STATE_CHANGE_FAILURE;
571 gst_oss4_mixer_start_watch_task (mixer);
577 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
578 if (ret == GST_STATE_CHANGE_FAILURE)
581 switch (transition) {
582 case GST_STATE_CHANGE_READY_TO_NULL:
583 gst_oss4_mixer_stop_watch_task (mixer);
584 gst_oss4_mixer_close (mixer);
593 /* === GstMixer interface === */
595 static inline gboolean
596 gst_oss4_mixer_contains_track (GstMixer * mixer, GstMixerTrack * track)
598 return (g_list_find (GST_OSS4_MIXER (mixer)->tracks, track) != NULL);
601 static inline gboolean
602 gst_oss4_mixer_contains_options (GstMixer * mixer, GstMixerOptions * options)
604 return (g_list_find (GST_OSS4_MIXER (mixer)->tracks, options) != NULL);
608 gst_oss4_mixer_post_mixer_changed_msg (GstOss4Mixer * mixer)
610 /* only post mixer-changed message once */
611 if (!mixer->need_update) {
612 gst_mixer_mixer_changed (GST_MIXER (mixer));
613 mixer->need_update = TRUE;
617 /* call with mixer object lock held to serialise ioctl */
619 gst_oss4_mixer_get_control_val (GstOss4Mixer * mixer, GstOss4MixerControl * mc,
622 oss_mixer_value ossval = { 0, };
624 if (GST_OBJECT_TRYLOCK (mixer)) {
625 GST_ERROR ("must be called with mixer lock held");
626 GST_OBJECT_UNLOCK (mixer);
629 ossval.dev = mc->mixext.dev;
630 ossval.ctrl = mc->mixext.ctrl;
631 ossval.timestamp = mc->mixext.timestamp;
633 if (ioctl (mixer->fd, SNDCTL_MIX_READ, &ossval) == -1) {
634 if (errno == EIDRM) {
635 GST_DEBUG_OBJECT (mixer, "MIX_READ failed: mixer interface has changed");
636 gst_oss4_mixer_post_mixer_changed_msg (mixer);
638 GST_WARNING_OBJECT (mixer, "MIX_READ failed: %s", g_strerror (errno));
646 mc->last_val = ossval.value;
647 GST_LOG_OBJECT (mixer, "got value 0x%08x from %s)", *val, mc->mixext.extname);
651 /* call with mixer object lock held to serialise ioctl */
653 gst_oss4_mixer_set_control_val (GstOss4Mixer * mixer, GstOss4MixerControl * mc,
656 oss_mixer_value ossval = { 0, };
658 ossval.dev = mc->mixext.dev;
659 ossval.ctrl = mc->mixext.ctrl;
660 ossval.timestamp = mc->mixext.timestamp;
663 if (GST_OBJECT_TRYLOCK (mixer)) {
664 GST_ERROR ("must be called with mixer lock held");
665 GST_OBJECT_UNLOCK (mixer);
668 if (ioctl (mixer->fd, SNDCTL_MIX_WRITE, &ossval) == -1) {
669 if (errno == EIDRM) {
670 GST_LOG_OBJECT (mixer, "MIX_WRITE failed: mixer interface has changed");
671 gst_oss4_mixer_post_mixer_changed_msg (mixer);
673 GST_WARNING_OBJECT (mixer, "MIX_WRITE failed: %s", g_strerror (errno));
679 GST_LOG_OBJECT (mixer, "set value 0x%08x on %s", val, mc->mixext.extname);
685 gst_oss4_mixer_control_get_pretty_name (GstOss4MixerControl * mc)
689 const gchar *name, *u;
691 /* "The id field is the original name given by the driver when it called
692 * mixer_ext_create_control. This name can be used by fully featured GUI
693 * mixers. However this name should be downshifted and cut before the last
694 * underscore ("_") to get the proper name. For example mixer control name
695 * created as "MYDRV_MAINVOL" will become just "mainvol" after this
696 * transformation." */
697 name = mc->mixext.extname;
698 u = MAX (strrchr (name, '_'), strrchr (name, '.'));
702 /* maybe capitalize the first letter? */
703 return g_ascii_strdown (name, -1);
704 /* the .id thing doesn't really seem to work right, ie. for some sliders
705 * it's just '-' so you have to use the name of the parent control etc.
706 * let's not use it for now, much too painful. */
707 if (g_str_has_prefix (mc->mixext.extname, "misc."))
708 name = g_strdup (mc->mixext.extname + 5);
710 name = g_strdup (mc->mixext.extname);
711 /* chop off trailing digit (only one for now) */
712 if (strlen (name) > 0 && g_ascii_isdigit (name[strlen (name) - 1]))
713 name[strlen (name) - 1] = '\0';
714 g_strdelimit (name, ".", ' ');
719 /* these translations are a bit ad-hoc and horribly incomplete; it is not
720 * really going to work this way with all the different chipsets and drivers.
721 * We also use these for translating option values. */
724 const gchar oss_name[32];
728 "volume", N_("Volume")}, {
729 "master", N_("Master")}, {
730 "front", N_("Front")}, {
731 "rear", N_("Rear")}, {
732 "headphones", N_("Headphones")}, {
733 "center", N_("Center")}, {
735 "surround", N_("Surround")}, {
736 "side", N_("Side")}, {
737 "speaker", N_("Built-in Speaker")}, {
738 "aux1-out", N_("AUX 1 Out")}, {
739 "aux2-out", N_("AUX 2 Out")}, {
740 "aux-out", N_("AUX Out")}, {
741 "bass", N_("Bass")}, {
742 "treble", N_("Treble")}, {
743 "3d-depth", N_("3D Depth")}, {
744 "3d-center", N_("3D Center")}, {
745 "3d-enhance", N_("3D Enhance")}, {
746 "phone", N_("Telephone")}, {
747 "mic", N_("Microphone")}, {
748 "line-out", N_("Line Out")}, {
749 "line-in", N_("Line In")}, {
750 "linein", N_("Line In")}, {
751 "cd", N_("Internal CD")}, {
752 "video", N_("Video In")}, {
753 "aux1-in", N_("AUX 1 In")}, {
754 "aux2-in", N_("AUX 2 In")}, {
755 "aux-in", N_("AUX In")}, {
757 "record-gain", N_("Record Gain")}, {
758 "igain", N_("Record Gain")}, {
759 "ogain", N_("Output Gain")}, {
760 "micboost", N_("Microphone Boost")}, {
761 "loopback", N_("Loopback")}, {
762 "diag", N_("Diagnostic")}, {
763 "loudness", N_("Bass Boost")}, {
764 "outputs", N_("Playback Ports")}, {
765 "input", N_("Input")}, {
766 "inputs", N_("Record Source")}, {
767 "record-source", N_("Record Source")}, {
768 "monitor-source", N_("Monitor Source")}, {
769 "beep", N_("Keyboard Beep")}, {
770 "monitor-gain", N_("Monitor")}, {
771 "stereo-simulate", N_("Simulate Stereo")}, {
772 "stereo", N_("Stereo")}, {
773 "multich", N_("Surround Sound")}, {
774 "mic-gain", N_("Microphone Gain")}, {
775 "speaker-source", N_("Speaker Source")}, {
776 "mic-source", N_("Microphone Source")}, {
777 "jack", N_("Jack")}, {
778 "center/lfe", N_("Center / LFE")}, {
779 "stereo-mix", N_("Stereo Mix")}, {
780 "mono-mix", N_("Mono Mix")}, {
781 "input-mix", N_("Input Mix")}, {
782 "spdif-in", N_("SPDIF In")}, {
783 "spdif-out", N_("SPDIF Out")}, {
784 "mic1", N_("Microphone 1")}, {
785 "mic2", N_("Microphone 2")}, {
786 "digital-out", N_("Digital Out")}, {
787 "digital-in", N_("Digital In")}, {
788 "hdmi", N_("HDMI")}, {
789 "modem", N_("Modem")}, {
790 "handset", N_("Handset")}, {
791 "other", N_("Other")}, {
792 "stereo", N_("Stereo")}, {
793 "none", N_("None")}, {
796 "mute", N_("Mute")}, {
797 "fast", N_("Fast")}, {
798 /* TRANSLATORS: "Very Low" is a quality setting here */
799 "very-low", N_("Very Low")}, {
800 /* TRANSLATORS: "Low" is a quality setting here */
802 /* TRANSLATORS: "Medium" is a quality setting here */
803 "medium", N_("Medium")}, {
804 /* TRANSLATORS: "High" is a quality setting here */
805 "high", N_("High")}, {
806 /* TRANSLATORS: "Very High" is a quality setting here */
807 "very-high", N_("Very High")}, {
808 "high+", N_("Very High")}, {
809 /* TRANSLATORS: "Production" is a quality setting here */
810 "production", N_("Production")}, {
811 "fp-mic", N_("Front Panel Microphone")}, {
812 "fp-linein", N_("Front Panel Line In")}, {
813 "fp-headphones", N_("Front Panel Headphones")}, {
814 "fp-lineout", N_("Front Panel Line Out")}, {
815 "green", N_("Green Connector")}, {
816 "pink", N_("Pink Connector")}, {
817 "blue", N_("Blue Connector")}, {
818 "white", N_("White Connector")}, {
819 "black", N_("Black Connector")}, {
820 "gray", N_("Gray Connector")}, {
821 "orange", N_("Orange Connector")}, {
822 "red", N_("Red Connector")}, {
823 "yellow", N_("Yellow Connector")}, {
824 "fp-green", N_("Green Front Panel Connector")}, {
825 "fp-pink", N_("Pink Front Panel Connector")}, {
826 "fp-blue", N_("Blue Front Panel Connector")}, {
827 "fp-white", N_("White Front Panel Connector")}, {
828 "fp-black", N_("Black Front Panel Connector")}, {
829 "fp-gray", N_("Gray Front Panel Connector")}, {
830 "fp-orange", N_("Orange Front Panel Connector")}, {
831 "fp-red", N_("Red Front Panel Connector")}, {
832 "fp-yellow", N_("Yellow Front Panel Connector")}, {
833 "spread", N_("Spread Output")}, {
834 "downmix", N_("Downmix")},
835 /* FIXME translate Audigy NX USB labels) */
837 { "rec.src", N_("Record Source") },
838 { "output.mute", N_("Mute output") }
839 headph (Controller group)
840 headph.src (Enumeration control)
841 headph.mute (On/Off switch)
842 digital2 (Controller group)
843 digital2.src (Enumeration control)
844 digital2.mute (On/Off switch)
845 digital (Controller group)
846 digital.mute1 (On/Off switch)
847 digital.vol (Controller group)
848 digital.vol.front (Stereo slider (0-255))
849 digital.vol.surr (Stereo slider (0-255))
850 digital.vol.c/l (Stereo slider (0-255))
851 digital.vol.center (Stereo slider (0-255))
852 digital.mute2 (On/Off switch)
853 digital.vol (Stereo slider (0-255))
854 line (Controller group)
855 line.mute (On/Off switch)
856 line.vol (Stereo slider (0-255))
857 play-altset (Enumeration control)
858 rec-altset (Enumeration control)
862 /* Decent i18n is pretty much impossible with OSS's way of providing us with
863 * mixer labels (and the fact that they are pretty much random), but that
864 * doesn't mean we shouldn't at least try. */
866 gst_oss4_mixer_control_get_translated_name (GstOss4MixerControl * mc)
868 gchar name[128] = { 0, };
869 gchar vmix_str[32] = { '\0', };
873 gboolean function_suffix = FALSE;
875 /* main virtual mixer controls (we hide the stream volumes) */
876 if (sscanf (mc->mixext.extname, "vmix%d-%32c", &dummy, vmix_str) == 2) {
877 if (strcmp (vmix_str, "src") == 0)
878 return g_strdup (_("Virtual Mixer Input"));
879 else if (strcmp (vmix_str, "vol") == 0)
880 return g_strdup (_("Virtual Mixer Output"));
881 else if (strcmp (vmix_str, "channels") == 0)
882 return g_strdup (_("Virtual Mixer Channels"));
885 g_strlcpy (name, mc->mixext.extname, sizeof (name));
887 /* we deal with either "connector." or "jack." */
888 if ((g_str_has_prefix (name, "connector.")) ||
889 (g_str_has_prefix (name, "jack."))) {
890 ptr = strchr (mc->mixext.extname, '.');
892 g_strlcpy (name, ptr, sizeof (name));
895 /* special handling for jack retasking suffixes */
896 if (g_str_has_suffix (name, ".function") || g_str_has_suffix (name, ".mode")) {
897 function_suffix = TRUE;
898 ptr = strrchr (name, '.');
902 /* parse off trailing numbers */
904 while ((i > 0) && (g_ascii_isdigit (name[i - 1]))) {
907 /* the check catches the case where the control name is just a number */
908 if ((i > 0) && (name[i] != '\0')) {
909 num = atoi (name + i);
913 /* look for a match, progressively skipping '.' delimited prefixes as we go */
918 for (i = 0; i < G_N_ELEMENTS (labels); ++i) {
919 if (g_ascii_strcasecmp (ptr, labels[i].oss_name) == 0) {
920 g_strlcpy (name, _(labels[i].label), sizeof (name));
921 goto append_suffixes;
924 } while ((ptr = strchr (ptr, '.')) != NULL);
926 /* failing that, just replace periods with spaces */
927 g_strdelimit (name, ".", ' ');
931 if (function_suffix) {
932 /* TRANSLATORS: name + number of a volume mixer control */
933 return g_strdup_printf (_("%s %d Function"), name, num);
935 return g_strdup_printf ("%s %d", name, num);
938 if (function_suffix) {
939 /* TRANSLATORS: name of a volume mixer control */
940 return g_strdup_printf (_("%s Function"), name);
942 return g_strdup (name);
948 gst_oss4_mixer_control_get_translated_option (const gchar * name)
951 for (i = 0; i < G_N_ELEMENTS (labels); ++i) {
952 if (g_ascii_strcasecmp (name, labels[i].oss_name) == 0) {
953 return _(labels[i].label);
959 #ifndef GST_DISABLE_GST_DEBUG
961 mixer_ext_type_get_name (gint type)
965 return "Device root entry";
967 return "Controller group";
969 return "On/Off switch";
971 return "Enumeration control";
972 case MIXT_MONOSLIDER:
973 return "Mono slider (0-255)";
974 case MIXT_STEREOSLIDER:
975 return "Stereo slider (0-255)";
977 return "Textual message"; /* whatever this is */
979 return "Mono VU meter value";
981 return "Stereo VU meter value";
983 return "Mono VU meter peak value";
984 case MIXT_STEREOPEAK:
985 return "Stereo VU meter peak value";
986 case MIXT_RADIOGROUP:
987 return "Radio button group";
988 case MIXT_MARKER: /* Separator between normal and extension entries */
991 return "Decimal value entry";
993 return "Hex value entry";
995 return "Mono slider (31-bit value range)";
997 return "3D"; /* what's this? */
998 case MIXT_MONOSLIDER16:
999 return "Mono slider (0-32767)";
1000 case MIXT_STEREOSLIDER16:
1001 return "Stereo slider (0-32767)";
1003 return "Mute switch";
1009 #endif /* GST_DISABLE_GST_DEBUG */
1011 #ifndef GST_DISABLE_GST_DEBUG
1012 static const gchar *
1013 mixer_ext_flags_get_string (gint flags)
1020 /* first the important ones */
1022 MIXF_MAINVOL, "MAINVOL"}, {
1023 MIXF_PCMVOL, "PCMVOL"}, {
1024 MIXF_RECVOL, "RECVOL"}, {
1025 MIXF_MONVOL, "MONVOL"}, {
1026 MIXF_DESCR, "DESCR"},
1027 /* now the rest in the right order */
1029 MIXF_READABLE, "READABLE"}, {
1030 MIXF_WRITEABLE, "WRITABLE"}, {
1031 MIXF_POLL, "POLL"}, {
1033 MIXF_STRING, "STRING"}, {
1034 MIXF_DYNAMIC, "DYNAMIC"}, {
1035 MIXF_OKFAIL, "OKFAIL"}, {
1036 MIXF_FLAT, "FLAT"}, {
1037 MIXF_LEGACY, "LEGACY"}, {
1038 MIXF_CENTIBEL, "CENTIBEL"}, {
1039 MIXF_DECIBEL, "DECIBEL"}, {
1049 s = g_string_new ("");
1050 for (i = 0; i < G_N_ELEMENTS (all_flags); ++i) {
1051 if ((flags & all_flags[i].flag)) {
1053 g_string_append (s, " | ");
1054 g_string_append (s, all_flags[i].nick);
1055 flags &= ~all_flags[i].flag; /* unset */
1059 /* unknown flags? */
1062 g_string_append (s, " | ");
1063 g_string_append (s, "???");
1066 /* we'll just put it into the global quark table (cheeky, eh?) */
1067 q = g_quark_from_string (s->str);
1068 g_string_free (s, TRUE);
1070 return g_quark_to_string (q);
1072 #endif /* GST_DISABLE_GST_DEBUG */
1074 #ifndef GST_DISABLE_GST_DEBUG
1076 gst_oss4_mixer_control_dump_tree (GstOss4MixerControl * mc, gint depth)
1082 depth = MIN (sizeof (spaces) - 1, depth);
1083 for (i = 0; i < depth; ++i)
1087 GST_LOG ("%s%s (%s)", spaces, mc->mixext.extname,
1088 mixer_ext_type_get_name (mc->mixext.type));
1089 for (c = mc->children; c != NULL; c = c->next) {
1090 GstOss4MixerControl *child_mc = (GstOss4MixerControl *) c->data;
1092 gst_oss4_mixer_control_dump_tree (child_mc, depth + 2);
1095 #endif /* GST_DISABLE_GST_DEBUG */
1098 gst_oss4_mixer_get_controls (GstOss4Mixer * mixer)
1100 GstOss4MixerControl *root_mc = NULL;
1101 oss_mixerinfo mi = { 0, };
1102 GList *controls = NULL;
1106 /* Get info for currently open fd */
1108 if (ioctl (mixer->fd, SNDCTL_MIXERINFO, &mi) == -1)
1111 if (mi.nrext <= 0) {
1112 GST_DEBUG ("mixer %s has no controls", mi.id);
1116 GST_INFO ("Reading controls for mixer %s", mi.id);
1118 for (i = 0; i < mi.nrext; ++i) {
1119 GstOss4MixerControl *mc;
1120 oss_mixext mix_ext = { 0, };
1122 mix_ext.dev = mi.dev;
1125 if (ioctl (mixer->fd, SNDCTL_MIX_EXTINFO, &mix_ext) == -1) {
1126 GST_DEBUG ("SNDCTL_MIX_EXTINFO failed on mixer %s, control %d: %s",
1127 mi.id, i, g_strerror (errno));
1131 /* if this is the last one, save it for is-interface-up-to-date checking */
1133 mixer->last_mixext = mix_ext;
1135 mc = g_new0 (GstOss4MixerControl, 1);
1136 mc->mixext = mix_ext;
1138 /* both control_no and desc fields are pretty useless, ie. not always set,
1139 * if ever, so not listed here */
1140 GST_INFO ("Control %d", mix_ext.ctrl);
1141 GST_INFO (" name : %s", mix_ext.extname);
1142 GST_INFO (" type : %s (%d)", mixer_ext_type_get_name (mix_ext.type),
1144 GST_INFO (" flags : %s (0x%04x)",
1145 mixer_ext_flags_get_string (mix_ext.flags), mix_ext.flags);
1146 GST_INFO (" parent : %d", mix_ext.parent);
1148 if (!MIXEXT_IS_ROOT (mix_ext)) {
1149 /* find parent (we assume it comes in the list before the child) */
1150 for (l = controls; l != NULL; l = l->next) {
1151 GstOss4MixerControl *parent_mc = (GstOss4MixerControl *) l->data;
1153 if (parent_mc->mixext.ctrl == mix_ext.parent) {
1154 mc->parent = parent_mc;
1155 parent_mc->children = g_list_append (parent_mc->children, mc);
1159 if (mc->parent == NULL) {
1160 GST_ERROR_OBJECT (mixer, "couldn't find parent for control %d", i);
1164 } else if (root_mc == NULL) {
1167 GST_WARNING_OBJECT (mixer, "two root controls?!");
1170 controls = g_list_prepend (controls, mc);
1173 #ifndef GST_DISABLE_GST_DEBUG
1174 gst_oss4_mixer_control_dump_tree (root_mc, 0);
1177 return g_list_reverse (controls);
1182 GST_WARNING ("SNDCTL_MIXERINFO failed on mixer device %s: %s", mi.id,
1183 g_strerror (errno));
1189 gst_oss4_mixer_controls_guess_master (GstOss4Mixer * mixer,
1190 const GList * controls)
1192 GstOss4MixerControl *master_mc = NULL;
1195 for (l = controls; l != NULL; l = l->next) {
1196 GstOss4MixerControl *mc = (GstOss4MixerControl *) l->data;
1198 /* do we need to check if it's a slider type here? */
1199 if ((mc->mixext.flags & MIXF_PCMVOL)) {
1200 GST_INFO_OBJECT (mixer, "First PCM control: %s", mc->mixext.extname);
1205 if (((mc->mixext.flags & MIXF_MAINVOL)) && master_mc == NULL) {
1206 GST_INFO_OBJECT (mixer, "First main volume control: %s",
1207 mc->mixext.extname);
1212 if (master_mc != NULL)
1213 master_mc->is_master = TRUE;
1216 /* type: -1 for all types, otherwise just return siblings with requested type */
1218 gst_oss4_mixer_control_get_siblings (GstOss4MixerControl * mc, gint type)
1220 GList *s, *siblings = NULL;
1222 if (mc->parent == NULL)
1225 for (s = mc->parent->children; s != NULL; s = s->next) {
1226 GstOss4MixerControl *sibling = (GstOss4MixerControl *) s->data;
1228 if (mc != sibling && (type < 0 || sibling->mixext.type == type))
1229 siblings = g_list_append (siblings, sibling);
1236 gst_oss4_mixer_controls_find_sliders (GstOss4Mixer * mixer,
1237 const GList * controls)
1241 for (l = controls; l != NULL; l = l->next) {
1242 GstOss4MixerControl *mc = (GstOss4MixerControl *) l->data;
1243 GList *s, *siblings;
1245 if (!MIXEXT_IS_SLIDER (mc->mixext) || mc->parent == NULL || mc->used)
1248 mc->is_slider = TRUE;
1251 siblings = gst_oss4_mixer_control_get_siblings (mc, -1);
1253 /* Note: the names can be misleading and may not reflect the actual
1254 * hierarchy of the controls, e.g. it's possible that a slider is called
1255 * connector.green and the mute control then connector.green.mute, but
1256 * both controls are in fact siblings and both children of the group
1257 * 'green' instead of mute being a child of connector.green as the naming
1258 * would seem to suggest */
1259 GST_LOG ("Slider: %s, parent=%s, %d siblings", mc->mixext.extname,
1260 mc->parent->mixext.extname, g_list_length (siblings));
1262 for (s = siblings; s != NULL; s = s->next) {
1263 GstOss4MixerControl *sibling = (GstOss4MixerControl *) s->data;
1265 GST_LOG (" %s (%s)", sibling->mixext.extname,
1266 mixer_ext_type_get_name (sibling->mixext.type));
1268 if (sibling->mixext.type == MIXT_MUTE ||
1269 g_str_has_suffix (sibling->mixext.extname, ".mute")) {
1270 /* simple case: slider with single mute sibling. We assume the .mute
1271 * suffix in the name won't change - can't really do much else anyway */
1272 if (sibling->mixext.type == MIXT_ONOFF ||
1273 sibling->mixext.type == MIXT_MUTE) {
1274 GST_LOG (" mute control for %s is %s", mc->mixext.extname,
1275 sibling->mixext.extname);
1277 sibling->used = TRUE;
1279 /* a group of .mute controls. We assume they are all switches here */
1280 if (sibling->mixext.type == MIXT_GROUP) {
1283 for (m = sibling->children; m != NULL; m = m->next) {
1284 GstOss4MixerControl *grouped_sibling = m->data;
1286 if (grouped_sibling->mixext.type == MIXT_ONOFF ||
1287 grouped_sibling->mixext.type == MIXT_MUTE) {
1288 GST_LOG (" %s is grouped mute control for %s",
1289 grouped_sibling->mixext.extname, mc->mixext.extname);
1290 mc->mute_group = g_list_append (mc->mute_group, grouped_sibling);
1294 GST_LOG (" %s has a group of %d mute controls",
1295 mc->mixext.extname, g_list_length (mc->mute_group));
1297 /* we don't mark the individual mute controls as used, only the
1298 * group control, as we still want individual switches for the
1299 * individual controls */
1300 sibling->used = TRUE;
1304 g_list_free (siblings);
1308 /* should be called with the mixer object lock held because of the ioctl;
1309 * returns TRUE if the list was read the first time or modified */
1311 gst_oss4_mixer_enum_control_update_enum_list (GstOss4Mixer * mixer,
1312 GstOss4MixerControl * mc)
1314 oss_mixer_enuminfo ei = { 0, };
1315 guint num_existing = 0;
1318 /* count and existing values */
1319 while (mc->enum_vals != NULL && mc->enum_vals[num_existing] != 0)
1322 ei.dev = mc->mixext.dev;
1323 ei.ctrl = mc->mixext.ctrl;
1325 /* if we have create a generic list with numeric IDs already and the
1326 * number of values hasn't changed, then there's not much to do here */
1327 if (mc->no_list && mc->enum_vals != NULL &&
1328 num_existing == mc->mixext.maxvalue) {
1332 /* if we have a list and it doesn't change, there's nothing to do either */
1333 if (mc->enum_vals != NULL && mc->enum_version == 0)
1336 if (ioctl (mixer->fd, SNDCTL_MIX_ENUMINFO, &ei) == -1) {
1337 g_free (mc->enum_vals);
1338 mc->enum_vals = g_new0 (GQuark, mc->mixext.maxvalue + 1);
1340 GST_DEBUG ("no enum info available, creating numeric values from 0-%d",
1341 mc->mixext.maxvalue - 1);
1343 /* "It is possible that some enum controls don't have any name list
1344 * available. In this case the application should automatically generate
1345 * list of numbers (0 to N-1)" */
1346 for (i = 0; i < mc->mixext.maxvalue; ++i) {
1349 g_snprintf (num_str, sizeof (num_str), "%d", i);
1350 mc->enum_vals[i] = g_quark_from_string (num_str);
1352 mc->enum_version = 0; /* the only way to change is via maxvalue */
1354 /* old list same as current list? */
1355 if (mc->enum_vals != NULL && mc->enum_version == ei.version)
1358 /* no list yet, or the list has changed */
1359 GST_LOG ("%s", (mc->enum_vals) ? "enum list has changed" : "reading list");
1360 if (ei.nvalues != mc->mixext.maxvalue) {
1361 GST_WARNING_OBJECT (mixer, "Enum: %s, nvalues %d != maxvalue %d",
1362 mc->mixext.extname, ei.nvalues, mc->mixext.maxvalue);
1363 mc->mixext.maxvalue = MIN (ei.nvalues, mc->mixext.maxvalue);
1366 mc->mixext.maxvalue = MIN (mc->mixext.maxvalue, OSS_ENUM_MAXVALUE);
1368 g_free (mc->enum_vals);
1369 mc->enum_vals = g_new0 (GQuark, mc->mixext.maxvalue + 1);
1370 for (i = 0; i < mc->mixext.maxvalue; ++i) {
1371 GST_LOG (" %s", ei.strings + ei.strindex[i]);
1373 g_quark_from_string (gst_oss4_mixer_control_get_translated_option
1374 (ei.strings + ei.strindex[i]));
1382 gst_oss4_mixer_controls_find_enums (GstOss4Mixer * mixer,
1383 const GList * controls)
1387 for (l = controls; l != NULL; l = l->next) {
1388 GstOss4MixerControl *mc = (GstOss4MixerControl *) l->data;
1390 if (mc->mixext.type != MIXT_ENUM || mc->used)
1396 /* Note: enums are special: for most controls, the maxvalue is inclusive,
1397 * but for enum controls it's actually exclusive (boggle), so that
1398 * mixext.maxvalue = num_values */
1400 GST_LOG ("Enum: %s, parent=%s, num_enums=%d", mc->mixext.extname,
1401 mc->parent->mixext.extname, mc->mixext.maxvalue);
1403 gst_oss4_mixer_enum_control_update_enum_list (mixer, mc);
1408 gst_oss4_mixer_controls_find_switches (GstOss4Mixer * mixer,
1409 const GList * controls)
1413 for (l = controls; l != NULL; l = l->next) {
1414 GstOss4MixerControl *mc = (GstOss4MixerControl *) l->data;
1419 if (mc->mixext.type != MIXT_ONOFF && mc->mixext.type != MIXT_MUTE)
1422 mc->is_switch = TRUE;
1425 GST_LOG ("Switch: %s, parent=%s", mc->mixext.extname,
1426 mc->parent->mixext.extname);
1431 gst_oss4_mixer_controls_find_virtual (GstOss4Mixer * mixer,
1432 const GList * controls)
1436 for (l = controls; l != NULL; l = l->next) {
1437 GstOss4MixerControl *mc = (GstOss4MixerControl *) l->data;
1439 /* or sscanf (mc->mixext.extname, "vmix%d-out.", &n) == 1 ? */
1440 /* for now we just flag all virtual controls with managed labels, those
1441 * are really more appropriate for a pavucontrol-type control thing than
1442 * the (more hardware-oriented) mixer interface */
1443 if (mc->mixext.id[0] == '@') {
1444 mc->is_virtual = TRUE;
1445 GST_LOG ("%s is virtual control with managed label", mc->mixext.extname);
1451 gst_oss4_mixer_controls_dump_unused (GstOss4Mixer * mixer,
1452 const GList * controls)
1456 for (l = controls; l != NULL; l = l->next) {
1457 GstOss4MixerControl *mc = (GstOss4MixerControl *) l->data;
1462 switch (mc->mixext.type) {
1469 case MIXT_STEREOPEAK:
1471 continue; /* not interested in these types of controls */
1474 GST_DEBUG ("obsolete control type %d", mc->mixext.type);
1476 case MIXT_MONOSLIDER:
1477 case MIXT_STEREOSLIDER:
1479 case MIXT_MONOSLIDER16:
1480 case MIXT_STEREOSLIDER16:
1481 /* this shouldn't happen */
1482 GST_ERROR ("unused slider control?!");
1486 /* value entry, not sure what to do with that, skip for now */
1492 case MIXT_RADIOGROUP:
1493 GST_DEBUG ("FIXME: handle %s %s",
1494 mixer_ext_type_get_name (mc->mixext.type), mc->mixext.extname);
1497 GST_WARNING ("unknown control type %d", mc->mixext.type);
1504 gst_oss4_mixer_create_tracks (GstOss4Mixer * mixer, const GList * controls)
1507 GList *tracks = NULL;
1509 for (c = controls; c != NULL; c = c->next) {
1510 GstOss4MixerControl *mc = (GstOss4MixerControl *) c->data;
1511 GstMixerTrack *track = NULL;
1516 if (mc->is_slider) {
1517 track = gst_oss4_mixer_slider_new (mixer, mc);
1518 } else if (mc->is_enum) {
1519 track = gst_oss4_mixer_enum_new (mixer, mc);
1520 } else if (mc->is_switch) {
1521 track = gst_oss4_mixer_switch_new (mixer, mc);
1527 track->label = gst_oss4_mixer_control_get_translated_name (mc);
1530 GST_LOG ("translated label: %s [%s] = %s", track->label, mc->mixext.id,
1533 /* This whole 'a track is either INPUT or OUTPUT' model is just flawed,
1534 * esp. if a slider's role can be changed on the fly, like when you change
1535 * function of a connector. What should we do in that case? Change the flag
1536 * and make the app rebuild the interface? Ignore it? */
1537 if (mc->mixext.flags & (MIXF_MAINVOL | MIXF_PCMVOL)) {
1538 track->flags = GST_MIXER_TRACK_OUTPUT | GST_MIXER_TRACK_WHITELIST;
1540 } else if (mc->mixext.flags & MIXF_RECVOL) {
1541 /* record gain whitelisted by default */
1542 track->flags = GST_MIXER_TRACK_INPUT | GST_MIXER_TRACK_NO_RECORD |
1543 GST_MIXER_TRACK_WHITELIST;
1545 } else if (mc->mixext.flags & MIXF_MONVOL) {
1546 /* monitor sources not whitelisted by default */
1547 track->flags = GST_MIXER_TRACK_INPUT | GST_MIXER_TRACK_NO_RECORD;
1551 * The kernel may give us better clues about the scope of a control.
1552 * If so, try to honor it.
1554 switch (mc->mixext.desc & MIXEXT_SCOPE_MASK) {
1555 case MIXEXT_SCOPE_INPUT:
1556 case MIXEXT_SCOPE_RECSWITCH:
1557 track->flags |= GST_MIXER_TRACK_INPUT | GST_MIXER_TRACK_NO_RECORD |
1558 GST_MIXER_TRACK_WHITELIST;
1560 case MIXEXT_SCOPE_MONITOR:
1561 /* don't whitelist monitor tracks by default */
1562 track->flags |= GST_MIXER_TRACK_INPUT | GST_MIXER_TRACK_NO_RECORD;
1564 case MIXEXT_SCOPE_OUTPUT:
1565 track->flags = GST_MIXER_TRACK_OUTPUT | GST_MIXER_TRACK_WHITELIST;
1569 if (mc->is_master) {
1570 track->flags |= GST_MIXER_TRACK_OUTPUT;
1574 track->flags |= GST_MIXER_TRACK_MASTER;
1576 tracks = g_list_append (tracks, track);
1583 gst_oss4_mixer_update_tracks (GstOss4Mixer * mixer)
1585 GList *controls, *tracks;
1587 /* read and process controls */
1588 controls = gst_oss4_mixer_get_controls (mixer);
1590 gst_oss4_mixer_controls_guess_master (mixer, controls);
1592 gst_oss4_mixer_controls_find_sliders (mixer, controls);
1594 gst_oss4_mixer_controls_find_enums (mixer, controls);
1596 gst_oss4_mixer_controls_find_switches (mixer, controls);
1598 gst_oss4_mixer_controls_find_virtual (mixer, controls);
1600 gst_oss4_mixer_controls_dump_unused (mixer, controls);
1602 tracks = gst_oss4_mixer_create_tracks (mixer, controls);
1604 /* free old tracks and controls */
1605 gst_oss4_mixer_free_tracks (mixer);
1607 /* replace old with new */
1608 mixer->tracks = tracks;
1609 mixer->controls = controls;
1612 static const GList *
1613 gst_oss4_mixer_list_tracks (GstMixer * mixer_iface)
1615 GstOss4Mixer *mixer = GST_OSS4_MIXER (mixer_iface);
1617 g_return_val_if_fail (mixer != NULL, NULL);
1618 g_return_val_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer), NULL);
1620 GST_OBJECT_LOCK (mixer);
1622 /* Do a read on the last control to check if the interface has changed */
1623 if (!mixer->need_update && mixer->last_mixext.ctrl > 0) {
1624 GstOss4MixerControl mc = { {0,}
1629 mc.mixext = mixer->last_mixext;
1630 gst_oss4_mixer_get_control_val (mixer, &mc, &val);
1633 if (mixer->need_update || mixer->tracks == NULL) {
1634 gst_oss4_mixer_update_tracks (mixer);
1635 mixer->need_update = FALSE;
1638 GST_OBJECT_UNLOCK (mixer);
1640 return (const GList *) mixer->tracks;
1644 gst_oss4_mixer_set_volume (GstMixer * mixer, GstMixerTrack * track,
1649 g_return_if_fail (mixer != NULL);
1650 g_return_if_fail (GST_IS_OSS4_MIXER (mixer));
1651 g_return_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer));
1652 g_return_if_fail (gst_oss4_mixer_contains_track (mixer, track));
1653 g_return_if_fail (volumes != NULL);
1655 oss = GST_OSS4_MIXER (mixer);
1657 GST_OBJECT_LOCK (oss);
1659 if (GST_IS_OSS4_MIXER_SLIDER (track)) {
1660 gst_oss4_mixer_slider_set_volume (GST_OSS4_MIXER_SLIDER (track), volumes);
1663 GST_OBJECT_UNLOCK (oss);
1667 gst_oss4_mixer_get_volume (GstMixer * mixer, GstMixerTrack * track,
1672 g_return_if_fail (mixer != NULL);
1673 g_return_if_fail (GST_IS_OSS4_MIXER (mixer));
1674 g_return_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer));
1675 g_return_if_fail (gst_oss4_mixer_contains_track (mixer, track));
1676 g_return_if_fail (volumes != NULL);
1678 oss = GST_OSS4_MIXER (mixer);
1680 GST_OBJECT_LOCK (oss);
1682 memset (volumes, 0, track->num_channels * sizeof (gint));
1684 if (GST_IS_OSS4_MIXER_SWITCH (track)) {
1685 gboolean enabled = FALSE;
1686 gst_oss4_mixer_switch_get (GST_OSS4_MIXER_SWITCH (track), &enabled);
1688 if (GST_IS_OSS4_MIXER_SLIDER (track)) {
1689 gst_oss4_mixer_slider_get_volume (GST_OSS4_MIXER_SLIDER (track), volumes);
1692 GST_OBJECT_UNLOCK (oss);
1696 gst_oss4_mixer_set_record (GstMixer * mixer, GstMixerTrack * track,
1701 g_return_if_fail (mixer != NULL);
1702 g_return_if_fail (GST_IS_OSS4_MIXER (mixer));
1703 g_return_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer));
1704 g_return_if_fail (gst_oss4_mixer_contains_track (mixer, track));
1706 oss = GST_OSS4_MIXER (mixer);
1708 GST_OBJECT_LOCK (oss);
1710 if (GST_IS_OSS4_MIXER_SLIDER (track)) {
1711 gst_oss4_mixer_slider_set_record (GST_OSS4_MIXER_SLIDER (track), record);
1712 } else if (GST_IS_OSS4_MIXER_SWITCH (track)) {
1713 if ((track->flags & GST_MIXER_TRACK_INPUT)) {
1714 gst_oss4_mixer_switch_set (GST_OSS4_MIXER_SWITCH (track), record);
1716 GST_WARNING_OBJECT (track, "set_record called on non-INPUT track");
1720 GST_OBJECT_UNLOCK (oss);
1724 gst_oss4_mixer_set_mute (GstMixer * mixer, GstMixerTrack * track, gboolean mute)
1728 g_return_if_fail (mixer != NULL);
1729 g_return_if_fail (GST_IS_OSS4_MIXER (mixer));
1730 g_return_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer));
1731 g_return_if_fail (gst_oss4_mixer_contains_track (mixer, track));
1733 oss = GST_OSS4_MIXER (mixer);
1735 GST_OBJECT_LOCK (oss);
1737 if (GST_IS_OSS4_MIXER_SLIDER (track)) {
1738 gst_oss4_mixer_slider_set_mute (GST_OSS4_MIXER_SLIDER (track), mute);
1739 } else if (GST_IS_OSS4_MIXER_SWITCH (track)) {
1740 gst_oss4_mixer_switch_set (GST_OSS4_MIXER_SWITCH (track), mute);
1743 GST_OBJECT_UNLOCK (oss);
1747 gst_oss4_mixer_set_option (GstMixer * mixer, GstMixerOptions * options,
1752 g_return_if_fail (mixer != NULL);
1753 g_return_if_fail (value != NULL);
1754 g_return_if_fail (GST_IS_OSS4_MIXER (mixer));
1755 g_return_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer));
1756 g_return_if_fail (GST_IS_OSS4_MIXER_ENUM (options));
1757 g_return_if_fail (gst_oss4_mixer_contains_options (mixer, options));
1759 oss = GST_OSS4_MIXER (mixer);
1761 GST_OBJECT_LOCK (oss);
1763 if (!gst_oss4_mixer_enum_set_option (GST_OSS4_MIXER_ENUM (options), value)) {
1764 /* not much we can do here but wake up the watch thread early, so it
1765 * can do its thing and post messages if anything has changed */
1766 gst_oss4_mixer_wake_up_watch_task (oss);
1769 GST_OBJECT_UNLOCK (oss);
1772 static const gchar *
1773 gst_oss4_mixer_get_option (GstMixer * mixer, GstMixerOptions * options)
1776 const gchar *current_val;
1778 g_return_val_if_fail (mixer != NULL, NULL);
1779 g_return_val_if_fail (GST_IS_OSS4_MIXER (mixer), NULL);
1780 g_return_val_if_fail (GST_OSS4_MIXER_IS_OPEN (mixer), NULL);
1781 g_return_val_if_fail (GST_IS_OSS4_MIXER_ENUM (options), NULL);
1782 g_return_val_if_fail (gst_oss4_mixer_contains_options (mixer, options), NULL);
1784 oss = GST_OSS4_MIXER (mixer);
1786 GST_OBJECT_LOCK (oss);
1788 current_val = gst_oss4_mixer_enum_get_option (GST_OSS4_MIXER_ENUM (options));
1790 if (current_val == NULL) {
1791 /* not much we can do here but wake up the watch thread early, so it
1792 * can do its thing and post messages if anything has changed */
1793 gst_oss4_mixer_wake_up_watch_task (oss);
1796 GST_OBJECT_UNLOCK (oss);
1801 static GstMixerFlags
1802 gst_oss4_mixer_get_mixer_flags (GstMixer * mixer)
1804 return GST_MIXER_FLAG_AUTO_NOTIFICATIONS | GST_MIXER_FLAG_HAS_WHITELIST |
1805 GST_MIXER_FLAG_GROUPING;
1809 gst_oss4_mixer_interface_init (GstMixerClass * klass)
1811 GST_MIXER_TYPE (klass) = GST_MIXER_HARDWARE;
1813 klass->list_tracks = gst_oss4_mixer_list_tracks;
1814 klass->set_volume = gst_oss4_mixer_set_volume;
1815 klass->get_volume = gst_oss4_mixer_get_volume;
1816 klass->set_mute = gst_oss4_mixer_set_mute;
1817 klass->set_record = gst_oss4_mixer_set_record;
1818 klass->set_option = gst_oss4_mixer_set_option;
1819 klass->get_option = gst_oss4_mixer_get_option;
1820 klass->get_mixer_flags = gst_oss4_mixer_get_mixer_flags;
1823 /* Implement the horror that is GstImplementsInterface */
1826 gst_oss4_mixer_supported (GstImplementsInterface * iface, GType iface_type)
1828 GstOss4Mixer *mixer;
1830 g_return_val_if_fail (iface_type == GST_TYPE_MIXER, FALSE);
1832 mixer = GST_OSS4_MIXER (iface);
1834 return GST_OSS4_MIXER_IS_OPEN (mixer);
1838 gst_oss4_mixer_implements_interface_init (GstImplementsInterfaceClass * klass)
1840 klass->supported = gst_oss4_mixer_supported;
1844 gst_oss4_mixer_init_interfaces (GType type)
1846 static const GInterfaceInfo implements_iface_info = {
1847 (GInterfaceInitFunc) gst_oss4_mixer_implements_interface_init,
1851 static const GInterfaceInfo mixer_iface_info = {
1852 (GInterfaceInitFunc) gst_oss4_mixer_interface_init,
1857 g_type_add_interface_static (type, GST_TYPE_IMPLEMENTS_INTERFACE,
1858 &implements_iface_info);
1859 g_type_add_interface_static (type, GST_TYPE_MIXER, &mixer_iface_info);
1861 gst_oss4_add_property_probe_interface (type);