Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Backports:SLE-15-SP1:Update
xfce4-volumed
xfce4-mixer-alsa.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File xfce4-mixer-alsa.patch of Package xfce4-volumed
--- configure.ac | 21 - src/Makefile.am | 18 src/main.c | 1 src/xfce4-mixer-alsa.c | 975 +++++++++++++++++++++++++++++++++++++++++++++++++ src/xfce4-mixer-alsa.h | 192 +++++++++ src/xvd_data_types.h | 5 src/xvd_mixer.c | 43 +- src/xvd_mixer.h | 5 8 files changed, 1244 insertions(+), 16 deletions(-) --- a/configure.ac +++ b/configure.ac @@ -15,13 +15,24 @@ PKG_CHECK_MODULES(XFCONF, [libxfconf-0]) AC_SUBST(XFCONF_CFLAGS) AC_SUBST(XFCONF_LIBS) -PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10]) +AC_MSG_CHECKING(for audio engine) +AC_ARG_WITH(audio, + AS_HELP_STRING([--with-audio], + [audio engine, either gstmixer or alsa (default)]), + audio="$withval", audio="alsa") + +if test "$audio" = "gstmixer"; then + AC_MSG_RESULT(gstreamer-0.10 native mixer) + PKG_CHECK_MODULES([GSTREAMER], [gstreamer-plugins-base-0.10]) +else + AC_MSG_RESULT(gstreamer-1.0 ALSA mixer) + PKG_CHECK_MODULES([GSTREAMER], [gstreamer-1.0]) + PKG_CHECK_MODULES([ALSA], [alsa]) + AC_DEFINE([XFCE4_MIXER_ALSA], 1, [Built-in ALSA-based gstreamer mixer i/f]) +fi AC_SUBST(GSTREAMER_CFLAGS) AC_SUBST(GSTREAMER_LIBS) - -PKG_CHECK_MODULES(GSTREAMER_AUDIO, [gstreamer-audio-0.10]) -AC_SUBST(GSTREAMER_AUDIO_CFLAGS) -AC_SUBST(GSTREAMER_AUDIO_LIBS) +AM_CONDITIONAL([XFCE4_MIXER_ALSA], [test x"$audio" != x"gstmixer"]) PKG_CHECK_MODULES(LIBKEYBINDER, [keybinder]) AC_SUBST(LIBKEYBINDER_CFLAGS) --- a/src/Makefile.am +++ b/src/Makefile.am @@ -12,19 +12,33 @@ xfce4_volumed_SOURCES = \ xvd_xfconf.c \ xvd_xfconf.h +if XFCE4_MIXER_ALSA +xfce4_volumed_SOURCES += \ + xfce4-mixer-alsa.h \ + xfce4-mixer-alsa.c +endif + INCLUDES = \ @GLIB_CFLAGS@ \ @XFCONF_CFLAGS@ \ @GSTREAMER_CFLAGS@ \ - @GSTREAMER_AUDIO_CFLAGS@ \ @LIBKEYBINDER_CFLAGS@ \ @LIBNOTIFY_CFLAGS@ +if XFCE4_MIXER_ALSA +INCLUDES += \ + $(ALSA_CFLAGS) +endif + xfce4_volumed_LDADD = \ @GLIB_LIBS@ \ @XFCONF_LIBS@ \ @GSTREAMER_LIBS@ \ - @GSTREAMER_AUDIO_LIBS@ \ @LIBKEYBINDER_LIBS@ \ @LIBNOTIFY_LIBS@ +if XFCE4_MIXER_ALSA +xfce4_volumed_LDADD += \ + $(ALSA_LIBS) +endif + --- /dev/null +++ b/src/xfce4-mixer-alsa.c @@ -0,0 +1,975 @@ +/* + * Simple alternative GstMixer implementation with ALSA-native API + */ + +#include "config.h" +#include <gst/gst.h> +#include "xfce4-mixer-alsa.h" + +#include <alsa/asoundlib.h> + +#define GST_MIXER_MESSAGE_NAME "gst-mixer-message" + +/* + * GstMixer + */ + +G_DEFINE_TYPE (GstMixer, gst_mixer, GST_TYPE_ELEMENT); + +static void gst_mixer_init (GstMixer *mixer) +{ +} + +static void gst_mixer_dispose (GObject * object) +{ + GstMixer *mixer = GST_MIXER (object); + + if (mixer->src) { + g_source_destroy (mixer->src); + mixer->src = NULL; + } + + if (mixer->handle) { + snd_mixer_close (mixer->handle); + mixer->handle = NULL; + } + + g_list_free_full (mixer->tracklist, g_object_unref); + mixer->tracklist = NULL; + + g_free ((gpointer *) mixer->name); + mixer->name = NULL; + + g_free ((gpointer *) mixer->card_name); + mixer->card_name = NULL; + + G_OBJECT_CLASS (gst_mixer_parent_class)->dispose (object); +} + +static void gst_mixer_class_init (GstMixerClass *klass) +{ + GstElementClass *element_klass = GST_ELEMENT_CLASS (klass); + GObjectClass *object_klass = G_OBJECT_CLASS (klass); + + gst_element_class_set_static_metadata (element_klass, + "ALSA mixer", "Generic/Audio", + "Control audio mixer via ALSA API", + "Takashi Iwai <tiwai@suse.de>"); + + object_klass->dispose = gst_mixer_dispose; +} + +/* + * GstMixerTrack + */ + +G_DEFINE_TYPE (GstMixerTrack, gst_mixer_track, G_TYPE_OBJECT); + +static void gst_mixer_track_init (GstMixerTrack *track) +{ +} + +static void notify_mute_change (GstMixer *mixer, GstMixerTrack *track, + gboolean mute) +{ + GstStructure *s; + GstMessage *m; + + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "mute-toggled", + "track", GST_TYPE_MIXER_TRACK, track, + "mute", G_TYPE_BOOLEAN, mute, + NULL); + m = gst_message_new_element (GST_OBJECT (mixer), s); + gst_element_post_message (GST_ELEMENT (mixer), m); +} + +static void update_mute (GstMixer *mixer, GstMixerTrack *track, gboolean mute) +{ + int old_flag = track->flags & GST_MIXER_TRACK_MUTE; + + if (mute) { + track->flags |= GST_MIXER_TRACK_MUTE; + if (track->shared_mute) + track->shared_mute->flags |= GST_MIXER_TRACK_MUTE; + } else { + track->flags &= ~GST_MIXER_TRACK_MUTE; + if (track->shared_mute) + track->shared_mute->flags &= ~GST_MIXER_TRACK_MUTE; + } + + if ((track->flags & GST_MIXER_TRACK_MUTE) != old_flag) + notify_mute_change (mixer, track, mute); +} + +static void notify_recording_change (GstMixer *mixer, GstMixerTrack *track, + gboolean recording) +{ + GstStructure *s; + GstMessage *m; + + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "record-toggled", + "track", GST_TYPE_MIXER_TRACK, track, + "record", G_TYPE_BOOLEAN, recording, + NULL); + m = gst_message_new_element (GST_OBJECT (mixer), s); + gst_element_post_message (GST_ELEMENT (mixer), m); +} + +static void update_recording (GstMixer *mixer, GstMixerTrack *track, + gboolean recording) +{ + int old_flag = track->flags & GST_MIXER_TRACK_RECORD; + + if (recording) + track->flags |= GST_MIXER_TRACK_RECORD; + else + track->flags &= ~GST_MIXER_TRACK_RECORD; + + if ((track->flags & GST_MIXER_TRACK_RECORD) != old_flag) + notify_recording_change (mixer, track, recording); +} + +static void notify_volume_change (GstMixer *mixer, GstMixerTrack *track) +{ + GstStructure *s; + GstMessage *m; + GValue l = { 0, }; + GValue v = { 0, }; + int i; + + s = gst_structure_new (GST_MIXER_MESSAGE_NAME, + "type", G_TYPE_STRING, "volume-changed", + "track", GST_TYPE_MIXER_TRACK, track, + NULL); + g_value_init (&l, GST_TYPE_ARRAY); + g_value_init (&v, G_TYPE_INT); + + for (i = 0; i < track->num_channels; i++) { + g_value_set_int (&v, track->volumes[i]); + gst_value_array_append_value (&l, &v); + } + + gst_structure_set_value (s, "volumes", &l); + g_value_unset (&v); + g_value_unset (&l); + + m = gst_message_new_element (GST_OBJECT (mixer), s); + gst_element_post_message (GST_ELEMENT (mixer), m); +} + +static void track_update (GstMixer *mixer, GstMixerTrack *track) +{ + gboolean vol_changed = FALSE; + int i; + + if (track->flags & GST_MIXER_TRACK_OUTPUT) { + int audible = 0; + if (track->has_switch) { + for (i = 0; i < track->num_channels; i++) { + int v = 0; + snd_mixer_selem_get_playback_switch (track->element, i, &v); + if (v) + audible = 1; + } + } + + if (track->has_volume) { + for (i = 0; i < track->num_channels; i++) { + long vol = 0; + snd_mixer_selem_get_playback_volume (track->element, i, &vol); + if (track->volumes[i] != vol) + vol_changed = TRUE; + track->volumes[i] = vol; + if (!track->has_switch && + vol > track->min_volume) + audible = 1; + } + } + + update_mute (mixer, track, !audible); + } + + if (track->flags & GST_MIXER_TRACK_INPUT) { + int recording = 0; + if (track->has_switch) { + for (i = 0; i < track->num_channels; i++) { + int v = 0; + snd_mixer_selem_get_capture_switch (track->element, i, &v); + if (v) + recording = 1; + } + } + + if (track->has_volume) { + for (i = 0; i < track->num_channels; i++) { + long vol = 0; + snd_mixer_selem_get_capture_volume (track->element, i, &vol); + if (track->volumes[i] != vol) + vol_changed = TRUE; + track->volumes[i] = vol; + if (!track->has_switch && + vol > track->min_volume) + recording = 1; + } + } + + update_recording (mixer, track, recording); + } + + if (vol_changed) + notify_volume_change (mixer, track); +} + +static GstMixerTrack *track_new (snd_mixer_elem_t *element, int num, + int flags, gboolean append_capture) +{ + GstMixerTrack *track; + const char *name; + + track = (GstMixerTrack *) g_object_new (GST_TYPE_MIXER_TRACK, NULL); + track->index = snd_mixer_selem_get_index (element); + track->element = element; + track->flags = flags; + + if (flags & GST_MIXER_TRACK_OUTPUT) { + while (snd_mixer_selem_has_playback_channel (element, + track->num_channels)) + track->num_channels++; + } else if (flags & GST_MIXER_TRACK_INPUT) { + while (snd_mixer_selem_has_capture_channel (element, + track->num_channels)) + track->num_channels++; + } + + track->volumes = g_new (gint, track->num_channels); + + name = snd_mixer_selem_get_name (element); + track->untranslated_label = g_strdup (name); + + if (!num) + track->label = g_strdup_printf ("%s%s", name, + append_capture ? " Capture" : ""); + else + track->label = g_strdup_printf ("%s%s %d", name, + append_capture ? " Capture" : "", + num); + + return track; +} + +enum { + ARG_0, + ARG_LABEL, + ARG_UNTRANSLATED_LABEL, + ARG_INDEX, +}; + +static void gst_mixer_track_get_property (GObject *object, guint prop_id, + GValue *value, GParamSpec *pspec) +{ + GstMixerTrack *track = GST_MIXER_TRACK (object); + + switch (prop_id) { + case ARG_LABEL: + g_value_set_string (value, track->label); + break; + case ARG_UNTRANSLATED_LABEL: + g_value_set_string (value, track->untranslated_label); + break; + case ARG_INDEX: + g_value_set_uint (value, track->index); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_mixer_track_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec * pspec) +{ + GstMixerTrack *track; + + track = GST_MIXER_TRACK (object); + + switch (prop_id) { + case ARG_LABEL: + g_free (track->label); + track->label = g_value_dup_string (value); + break; + case ARG_UNTRANSLATED_LABEL: + g_free (track->untranslated_label); + track->untranslated_label = g_value_dup_string (value); + break; + case ARG_INDEX: + track->index = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void gst_mixer_track_dispose (GObject * object) +{ + GstMixerTrack *track = GST_MIXER_TRACK (object); + + if (track->label) { + g_free (track->label); + track->label = NULL; + } + + if (track->untranslated_label) { + g_free (track->untranslated_label); + track->untranslated_label = NULL; + } + + G_OBJECT_CLASS (gst_mixer_track_parent_class)->dispose (object); +} + +static void gst_mixer_track_class_init (GstMixerTrackClass * klass) +{ + GObjectClass *object_klass = G_OBJECT_CLASS (klass); + + object_klass->get_property = gst_mixer_track_get_property; + object_klass->set_property = gst_mixer_track_set_property; + + g_object_class_install_property (object_klass, ARG_UNTRANSLATED_LABEL, + g_param_spec_string ("untranslated-label", + "Untranslated track label", + "The untranslated label assigned to the track (since 0.10.13)", + NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_klass, ARG_LABEL, + g_param_spec_string ("label", "Track label", + "The label assigned to the track (may be translated)", + NULL, + G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (object_klass, ARG_INDEX, + g_param_spec_uint ("index", "Index", + "Track index", + 0, G_MAXUINT, 0, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); + + object_klass->dispose = gst_mixer_track_dispose; +} + +static void get_playback_min_max (GstMixerTrack *track) +{ + if (track->has_volume) { + long min = 0, max = 0; + snd_mixer_selem_get_playback_volume_range (track->element, &min, &max); + track->min_volume = min; + track->max_volume = max; + } +} + +static void get_capture_min_max (GstMixerTrack *track) +{ + if (track->has_volume) { + long min = 0, max = 0; + snd_mixer_selem_get_capture_volume_range (track->element, &min, &max); + track->min_volume = min; + track->max_volume = max; + } +} + +static GstMixerTrack *get_named_playback_track (GstMixer *mixer, + const char *name) +{ + GList *item; + GstMixerTrack *track; + + for (item = mixer->tracklist; item; item = item->next) { + track = GST_MIXER_TRACK (item->data); + if (! (track->flags & GST_MIXER_TRACK_OUTPUT)) + continue; + if (!strcmp (track->label, name)) + return track; + } + return NULL; +} + +static void mark_master_track (GstMixer *mixer) +{ + GList *item; + GstMixerTrack *track; + + if ((track = get_named_playback_track (mixer, "Master")) || + (track = get_named_playback_track (mixer, "Front")) || + (track = get_named_playback_track (mixer, "PCM")) || + (track = get_named_playback_track (mixer, "Speaker"))) + goto found; + + /* If not found, take a mono track with both volume and switch */ + for (item = mixer->tracklist; item; item = item->next) { + track = GST_MIXER_TRACK (item->data); + if (! (track->flags & GST_MIXER_TRACK_OUTPUT)) + continue; + if (track->has_volume && track->has_switch && + track->num_channels == 1) + goto found; + } + + /* If not found, take any track with both volume and switch */ + for (item = mixer->tracklist; item; item = item->next) { + track = GST_MIXER_TRACK (item->data); + if (! (track->flags & GST_MIXER_TRACK_OUTPUT)) + continue; + if (track->has_volume && track->has_switch) + goto found; + } + + /* If not found, take any track with volume */ + for (item = mixer->tracklist; item; item = item->next) { + track = GST_MIXER_TRACK (item->data); + if (! (track->flags & GST_MIXER_TRACK_OUTPUT)) + continue; + if (track->has_volume) + goto found; + } + + return; + + found: + track->flags |= GST_MIXER_TRACK_MASTER; + return; +} + +static int mixer_elem_callback (snd_mixer_elem_t *elem, unsigned int mask) +{ + GstMixer *mixer = snd_mixer_elem_get_callback_private (elem); + GList *item; + + for (item = mixer->tracklist; item; item = item->next) { + GstMixerTrack *track = GST_MIXER_TRACK (item->data); + if (track->element == elem) + track_update (mixer, track); + } + + return 0; +} + +static int mixer_callback (snd_mixer_t *ctl, unsigned int mask, + snd_mixer_elem_t *elem) +{ + GstMixer *mixer = snd_mixer_get_callback_private (ctl); + + snd_mixer_handle_events (mixer->handle); + return 0; +} + +const GList *gst_mixer_list_tracks (GstMixer *mixer) +{ + return mixer->tracklist; +} + +static gboolean same_volumes (gint num_channels, const gint *volumes) +{ + gint i; + + for (i = 1; i < num_channels; i++) { + if (volumes[0] != volumes[i]) + return FALSE; + } + + return TRUE; +} + +void gst_mixer_set_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes) +{ + gint i; + + track_update (mixer, track); + + if (!track->has_volume) + return; + + for (i = 0; i < track->num_channels; i++) + track->volumes[i] = volumes[i]; + + if (track->flags & GST_MIXER_TRACK_OUTPUT) { + if (!track->has_switch && (track->flags & GST_MIXER_TRACK_MUTE)) + return; + if (same_volumes (track->num_channels, volumes)) { + snd_mixer_selem_set_playback_volume_all (track->element, + volumes[0]); + } else { + for (i = 0; i < track->num_channels; i++) + snd_mixer_selem_set_playback_volume (track->element, i, + volumes[i]); + } + } else { + if (!track->has_switch && ! (track->flags & GST_MIXER_TRACK_RECORD)) + return; + if (same_volumes (track->num_channels, volumes)) { + snd_mixer_selem_set_capture_volume_all (track->element, + volumes[0]); + } else { + for (i = 0; i < track->num_channels; i++) + snd_mixer_selem_set_capture_volume (track->element, i, + volumes[i]); + } + } +} + +void gst_mixer_get_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes) +{ + int i; + + if (!track->has_volume) + return; + + track_update (mixer, track); + for (i = 0; i < track->num_channels; i++) + volumes[i] = track->volumes[i]; +} + +void gst_mixer_set_mute (GstMixer *mixer, GstMixerTrack *track, gboolean mute) +{ + int i; + + if (track->flags & GST_MIXER_TRACK_INPUT) { + if (track->shared_mute) + track = track->shared_mute; + else + return; + } + + track_update (mixer, track); + + mute = !!mute; + if (mute == !! (track->flags & GST_MIXER_TRACK_MUTE)) + return; + + update_mute (mixer, track, mute); + + if (track->has_switch) { + snd_mixer_selem_set_playback_switch_all (track->element, !mute); + } else { + for (i = 0; i < track->num_channels; i++) { + long vol = mute ? track->min_volume : track->volumes[i]; + snd_mixer_selem_set_playback_volume (track->element, i, vol); + } + } +} + +void gst_mixer_set_record (GstMixer * mixer, GstMixerTrack *track, gboolean record) +{ + int i; + + if (! (track->flags & GST_MIXER_TRACK_INPUT)) + return; + + track_update (mixer, track); + + record = !!record; + if (record == !! (track->flags & GST_MIXER_TRACK_RECORD)) + return; + + if (record) + track->flags |= GST_MIXER_TRACK_RECORD; + else + track->flags &= ~GST_MIXER_TRACK_RECORD; + + if (track->has_switch) { + snd_mixer_selem_set_capture_switch_all (track->element, record); + } else { + for (i = 0; i < track->num_channels; i++) { + long vol = record ? track->volumes[i] : track->min_volume; + snd_mixer_selem_set_capture_volume (track->element, i, vol); + } + } +} + +GstMixerMessageType +gst_mixer_message_get_type (GstMessage * message) +{ + const GstStructure *s; + const gchar *m_type; + + s = gst_message_get_structure (message); + m_type = gst_structure_get_string (s, "type"); + if (!m_type) + return GST_MIXER_MESSAGE_INVALID; + + if (g_str_equal (m_type, "mute-toggled")) + return GST_MIXER_MESSAGE_MUTE_TOGGLED; + else if (g_str_equal (m_type, "record-toggled")) + return GST_MIXER_MESSAGE_RECORD_TOGGLED; + else if (g_str_equal (m_type, "volume-changed")) + return GST_MIXER_MESSAGE_VOLUME_CHANGED; + else if (g_str_equal (m_type, "option-changed")) + return GST_MIXER_MESSAGE_OPTION_CHANGED; + else if (g_str_equal (m_type, "options-list-changed")) + return GST_MIXER_MESSAGE_OPTIONS_LIST_CHANGED; + else if (g_str_equal (m_type, "mixer-changed")) + return GST_MIXER_MESSAGE_MIXER_CHANGED; + + return GST_MIXER_MESSAGE_INVALID; +} + +static void message_parse_track (const GstStructure *s, GstMixerTrack **track) +{ + if (track) { + const GValue *v = gst_structure_get_value (s, "track"); + *track = (GstMixerTrack *)g_value_get_object (v); + } +} + +void gst_mixer_message_parse_mute_toggled (GstMessage *message, + GstMixerTrack **track, + gboolean *mute) +{ + const GstStructure *s = gst_message_get_structure (message); + + message_parse_track (s, track); + if (mute) + gst_structure_get_boolean (s, "mute", mute); +} + +void gst_mixer_message_parse_record_toggled (GstMessage *message, + GstMixerTrack **track, + gboolean *record) +{ + const GstStructure *s = gst_message_get_structure (message); + + message_parse_track (s, track); + if (record) + gst_structure_get_boolean (s, "record", record); +} + +void gst_mixer_message_parse_volume_changed (GstMessage *message, + GstMixerTrack **track, + gint **volumes, + gint *num_channels) +{ + const GstStructure *s = gst_message_get_structure (message); + + message_parse_track (s, track); + if (volumes || num_channels) { + gint n_chans, i; + const GValue *v = gst_structure_get_value (s, "volumes"); + + n_chans = gst_value_array_get_size (v); + if (num_channels) + *num_channels = n_chans; + + if (volumes) { + *volumes = g_new (gint, n_chans); + for (i = 0; i < n_chans; i++) { + const GValue *e = gst_value_array_get_value (v, i); + + (*volumes)[i] = g_value_get_int (e); + } + } + } +} + +/* + * GstMixerOptions + */ + +G_DEFINE_TYPE (GstMixerOptions, gst_mixer_options, GST_TYPE_MIXER_TRACK); + +static GstMixerOptions * +mixer_options_new (snd_mixer_elem_t *element, int num) +{ + GstMixerOptions *opt; + GstMixerTrack *track; + const char *label; + int i; + + label = snd_mixer_selem_get_name (element); + opt = g_object_new (GST_TYPE_MIXER_OPTIONS, + "untranslated-label", label, + "index", snd_mixer_selem_get_index (element), + NULL); + track = GST_MIXER_TRACK (opt); + track->element = element; + if (!num) + track->label = g_strdup (label); + else + track->label = g_strdup_printf ("%s %d", label, num); + + num = snd_mixer_selem_get_enum_items (element); + for (i = 0; i < num; i++) { + char str[256]; + if (snd_mixer_selem_get_enum_item_name (element, i, sizeof(str), str) < 0) + break; + opt->values = g_list_append (opt->values, g_strdup (str)); + } + + return opt; +} + +static void gst_mixer_options_dispose (GObject * object) +{ + GstMixerOptions *opt = GST_MIXER_OPTIONS (object); + + g_list_free_full (opt->values, g_free); + opt->values = NULL; + + G_OBJECT_CLASS (gst_mixer_options_parent_class)->dispose (object); +} + +static void gst_mixer_options_init (GstMixerOptions *opt) +{ +} + +static void gst_mixer_options_class_init (GstMixerOptionsClass * klass) +{ + GObjectClass *object_klass = G_OBJECT_CLASS (klass); + + object_klass->dispose = gst_mixer_options_dispose; +} + +const gchar *gst_mixer_get_option (GstMixer *mixer, GstMixerOptions *opt) +{ + unsigned int idx; + + if (snd_mixer_selem_get_enum_item (opt->parent.element, 0, &idx) < 0) + return "error"; + return g_list_nth_data (opt->values, idx); +} + +void gst_mixer_set_option (GstMixer *mixer, GstMixerOptions *opt, + gchar *value) +{ + int n = 0; + GList *item; + + for (item = opt->values; item; item = item->next, n++) { + if (!strcmp (item->data, value)) { + snd_mixer_selem_set_enum_item (opt->parent.element, 0, n); + break; + } + } +} + +GList *gst_mixer_options_get_values (GstMixerOptions *opt) +{ + return opt->values; +} + +static void message_parse_options (const GstStructure *s, + GstMixerOptions ** options) +{ + if (options) { + const GValue *v = gst_structure_get_value (s, "options"); + *options = (GstMixerOptions *) g_value_get_object (v); + } +} + +void gst_mixer_message_parse_option_changed (GstMessage *message, + GstMixerOptions ** options, + const gchar **value) +{ + const GstStructure *s = gst_message_get_structure (message); + + message_parse_options (s, options); + if (value) + *value = gst_structure_get_string (s, "value"); +} + +void gst_mixer_message_parse_options_list_changed (GstMessage *message, + GstMixerOptions **options) +{ + const GstStructure *s = gst_message_get_structure (message); + + message_parse_options (s, options); +} + +/* + */ + +static void create_track_list (GstMixer *mixer) +{ + snd_mixer_elem_t *element, *temp; + GList *item; + + if (mixer->tracklist) + return; + + for (element = snd_mixer_first_elem (mixer->handle); element; + element = snd_mixer_elem_next (element)) { + GstMixerTrack *play_track = NULL; + GstMixerTrack *cap_track = NULL; + const gchar *name = snd_mixer_selem_get_name (element); + int index = 0; + int has_volume, has_switch; + + for (item = mixer->tracklist; item; item = item->next) { + temp = GST_MIXER_TRACK (item->data)->element; + if (strcmp (name, snd_mixer_selem_get_name (temp)) == 0) + index++; + } + + has_volume = snd_mixer_selem_has_playback_volume (element); + has_switch = snd_mixer_selem_has_playback_switch (element); + if (has_volume || has_switch) { + play_track = track_new (element, index, + GST_MIXER_TRACK_OUTPUT, FALSE); + play_track->has_volume = has_volume; + play_track->has_switch = has_switch; + get_playback_min_max (play_track); + } + + has_volume = snd_mixer_selem_has_capture_volume (element); + has_switch = snd_mixer_selem_has_capture_switch (element); + if (play_track && snd_mixer_selem_has_common_volume (element)) + has_volume = 0; + if (play_track && snd_mixer_selem_has_common_switch (element)) + has_switch = 0; + if (has_volume || has_switch) { + cap_track = track_new (element, index, + GST_MIXER_TRACK_INPUT, + play_track != NULL); + cap_track->has_volume = has_volume; + cap_track->has_switch = has_switch; + get_capture_min_max (cap_track); + } + + if (play_track && cap_track) { + play_track->shared_mute = cap_track; + cap_track->shared_mute = play_track; + } + + if (play_track) { + track_update (mixer, play_track); + mixer->tracklist = g_list_append (mixer->tracklist, play_track); + } + + if (cap_track) { + track_update (mixer, cap_track); + mixer->tracklist = g_list_append (mixer->tracklist, cap_track); + } + + if (snd_mixer_selem_is_enumerated (element)) { + mixer->tracklist = g_list_append (mixer->tracklist, + mixer_options_new (element, index)); + } + + snd_mixer_elem_set_callback_private (element, mixer); + snd_mixer_elem_set_callback (element, mixer_elem_callback); + } + + mark_master_track (mixer); +} + +static gboolean mixer_src_callback (gpointer user_data) +{ + GstMixer *mixer = (GstMixer *)user_data; + + snd_mixer_handle_events (mixer->handle); + return TRUE; +} + +static gboolean mixer_src_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + return callback (user_data); +} + +static void mixer_src_attach (GstMixer *mixer) +{ + static GSourceFuncs func = { + .dispatch = mixer_src_dispatch, + }; + struct pollfd pfd; + + if (snd_mixer_poll_descriptors (mixer->handle, &pfd, 1) != 1) + return; + + mixer->src = g_source_new (&func, sizeof (*mixer->src)); + g_source_add_unix_fd (mixer->src, pfd.fd, G_IO_IN | G_IO_ERR); + g_source_set_callback (mixer->src, mixer_src_callback, mixer, NULL); + g_source_attach (mixer->src, g_main_context_default ()); +} + +/* + * These are new functions that didn't exist in the original gstreamer API; + * instead of lengthy probing using factory, just provide a simpler method + */ + +int gst_mixer_new (const char *name, GstMixer **mixer_ret) +{ + GstMixer *mixer; + snd_hctl_t *hctl; + int err; + + mixer = (GstMixer *) g_object_new (GST_TYPE_MIXER, NULL); + mixer->name = g_strdup (name); + + err = snd_mixer_open ((snd_mixer_t **) &mixer->handle, 0); + if (err < 0) + return err; + + err = snd_mixer_attach (mixer->handle, name); + if (err < 0) + goto error; + + err = snd_mixer_selem_register (mixer->handle, NULL, NULL); + if (err < 0) + goto error; + + err = snd_mixer_load (mixer->handle); + if (err < 0) + goto error; + + snd_mixer_get_hctl (mixer->handle, name, &hctl); + { + snd_ctl_card_info_t *info; + + snd_ctl_card_info_alloca (&info); + snd_ctl_card_info (snd_hctl_ctl (hctl), info); + mixer->card_name = g_strdup_printf ("%s (Alsa mixer)", + snd_ctl_card_info_get_name (info)); + } + + snd_mixer_set_callback_private (mixer->handle, mixer); + snd_mixer_set_callback (mixer->handle, mixer_callback); + + create_track_list (mixer); + + mixer_src_attach (mixer); + + *mixer_ret = mixer; + return 0; + + error: + gst_object_unref (mixer); + return err; +} + +GList *gst_mixer_probe_devices (void) +{ + int card = -1; + GList *card_list = NULL; + + while (snd_card_next(&card) >= 0 && card >= 0) { + GstMixer *mixer; + char name [16]; + int err; + + sprintf (name, "hw:%d", card); + err = gst_mixer_new (name, &mixer); + if (err < 0) + continue; + card_list = g_list_append (card_list, mixer); + } + + return card_list; +} + +const gchar *gst_mixer_get_card_name (GstMixer *mixer) +{ + return mixer->card_name; +} --- /dev/null +++ b/src/xfce4-mixer-alsa.h @@ -0,0 +1,192 @@ +/* + * Simple alternative GstMixer implementation with ALSA-native API + */ + +#ifndef __XFCE4_MIXER_ALSA_H +#define __XFCE4_MIXER_ALSA_H + +G_BEGIN_DECLS + +/* + * GstMixer + */ + +GType gst_mixer_get_type (void); + +#define GST_TYPE_MIXER \ + (gst_mixer_get_type ()) +#define GST_MIXER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MIXER, GstMixer)) +#define GST_MIXER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MIXER, GstMixerClass)) +#define GST_IS_MIXER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MIXER)) +#define GST_IS_MIXER_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MIXER)) +#define GST_MIXER_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_MIXER, GstMixerClass)) + +typedef struct _GstMixer GstMixer; +typedef struct _GstMixerClass GstMixerClass; + +typedef enum { + GST_MIXER_FLAG_NONE = 0, + GST_MIXER_FLAG_AUTO_NOTIFICATIONS = (1<<0), + GST_MIXER_FLAG_HAS_WHITELIST = (1<<1), + GST_MIXER_FLAG_GROUPING = (1<<2), +} GstMixerFlags; + +typedef enum { + GST_MIXER_MESSAGE_INVALID, + GST_MIXER_MESSAGE_MUTE_TOGGLED, + GST_MIXER_MESSAGE_RECORD_TOGGLED, + GST_MIXER_MESSAGE_VOLUME_CHANGED, + GST_MIXER_MESSAGE_OPTION_CHANGED, + GST_MIXER_MESSAGE_OPTIONS_LIST_CHANGED, + GST_MIXER_MESSAGE_MIXER_CHANGED +} GstMixerMessageType; + +struct _GstMixer { + GstElement element; + GList *tracklist; + void *handle; /* snd_mixer_t */ + const char *name; + const gchar *card_name; + GSource *src; +}; + +struct _GstMixerClass { + GstElementClass parent_class; +}; + +const GList *gst_mixer_list_tracks (GstMixer *mixer); + +static inline GstMixerFlags +gst_mixer_get_mixer_flags (GstMixer * mixer) +{ + return GST_MIXER_FLAG_AUTO_NOTIFICATIONS; +} + +GstMixerMessageType gst_mixer_message_get_type (GstMessage *message); + +int gst_mixer_new (const char *name, GstMixer **mixer_ret); +GList *gst_mixer_probe_devices (void); +const gchar *gst_mixer_get_card_name (GstMixer *mixer); + +/* + * GstMixerTrack + */ + +GType gst_mixer_track_get_type (void); + +#define GST_TYPE_MIXER_TRACK \ + (gst_mixer_track_get_type ()) +#define GST_MIXER_TRACK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MIXER_TRACK, \ + GstMixerTrack)) +#define GST_MIXER_TRACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MIXER_TRACK, \ + GstMixerTrackClass)) +#define GST_IS_MIXER_TRACK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MIXER_TRACK)) +#define GST_IS_MIXER_TRACK_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MIXER_TRACK)) + +typedef struct _GstMixerTrack GstMixerTrack; +typedef struct _GstMixerTrackClass GstMixerTrackClass; + +typedef enum { + GST_MIXER_TRACK_INPUT = (1<<0), + GST_MIXER_TRACK_OUTPUT = (1<<1), + GST_MIXER_TRACK_MUTE = (1<<2), + GST_MIXER_TRACK_RECORD = (1<<3), + GST_MIXER_TRACK_MASTER = (1<<4), + GST_MIXER_TRACK_SOFTWARE = (1<<5), + GST_MIXER_TRACK_NO_RECORD = (1<<6), + GST_MIXER_TRACK_NO_MUTE = (1<<7), + GST_MIXER_TRACK_WHITELIST = (1<<8), + GST_MIXER_TRACK_READONLY = (1<<9), + GST_MIXER_TRACK_WRITEONLY = (1<<10) +} GstMixerTrackFlags; + +struct _GstMixerTrack { + GObject parent; + void *element; + gchar *label; + gchar *untranslated_label; + guint index; + GstMixerTrackFlags flags; + gint num_channels; + gint *volumes; + gint min_volume; + gint max_volume; + GstMixerTrack *shared_mute; + gboolean has_volume; + gboolean has_switch; +}; + +struct _GstMixerTrackClass { + GObjectClass parent; +}; + +#define GST_MIXER_TRACK_HAS_FLAG(track, flag) ((track)->flags & (flag)) + +void gst_mixer_get_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes); +void gst_mixer_set_volume (GstMixer *mixer, GstMixerTrack *track, gint *volumes); +void gst_mixer_set_mute (GstMixer *mixer, GstMixerTrack *track, gboolean mute); +void gst_mixer_set_record (GstMixer *mixer, GstMixerTrack *track, gboolean record); + +void gst_mixer_message_parse_mute_toggled (GstMessage *message, + GstMixerTrack **track, + gboolean *mute); +void gst_mixer_message_parse_record_toggled (GstMessage *message, + GstMixerTrack **track, + gboolean *record); +void gst_mixer_message_parse_volume_changed (GstMessage *message, + GstMixerTrack **track, + gint **volumes, + gint *num_channels); + +/* + * GstMixerOptions + */ + +GType gst_mixer_options_get_type (void); + +#define GST_TYPE_MIXER_OPTIONS \ + (gst_mixer_options_get_type ()) +#define GST_MIXER_OPTIONS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_MIXER_OPTIONS, GstMixerOptions)) +#define GST_MIXER_OPTIONS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_MIXER_OPTIONS, GstMixerOptionsClass)) +#define GST_IS_MIXER_OPTIONS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_MIXER_OPTIONS)) +#define GST_IS_MIXER_OPTIONS_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_MIXER_OPTIONS)) +#define GST_MIXER_OPTIONS_GET_CLASS(inst) \ + (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GST_TYPE_MIXER_OPTIONS, GstMixerOptionsClass)) + +typedef struct _GstMixerOptions GstMixerOptions; +typedef struct _GstMixerOptionsClass GstMixerOptionsClass; + +struct _GstMixerOptions { + GstMixerTrack parent; + GList *values; +}; + +struct _GstMixerOptionsClass { + GstMixerTrackClass parent; +}; + +void gst_mixer_set_option (GstMixer * mixer, GstMixerOptions * opts, gchar * value); +const gchar * gst_mixer_get_option (GstMixer * mixer, GstMixerOptions * opts); +GList * gst_mixer_options_get_values (GstMixerOptions *mixer_options); +void gst_mixer_message_parse_option_changed (GstMessage *message, + GstMixerOptions ** options, + const gchar **value); +void gst_mixer_message_parse_options_list_changed (GstMessage *message, + GstMixerOptions **options); + +G_END_DECLS + +#endif /* __XFCE4_MIXER_ALSA_H */ --- a/src/xvd_mixer.c +++ b/src/xvd_mixer.c @@ -30,6 +30,9 @@ #include "xvd_notify.h" #endif +static void set_mixer_name (GstMixer *mixer, const gchar *name); + +#ifndef XFCE4_MIXER_ALSA static gboolean _xvd_mixer_filter_mixer (GstMixer *tmp_mixer, gpointer user_data) @@ -37,10 +40,7 @@ _xvd_mixer_filter_mixer (GstMixer *tmp_m GstElementFactory *factory; const gchar *long_name; gchar *device_name; - gchar *internal_name; gchar *name; - gchar *p; - gint length; gint *counter = user_data; /* Get long name of the mixer element */ @@ -61,6 +61,20 @@ _xvd_mixer_filter_mixer (GstMixer *tmp_m /* Free device name */ g_free (device_name); + set_mixer_name (mixer, name); + + g_free (name); + + return TRUE; +} +#endif /* !XFCE4_MIXER_ALSA */ + +static void set_mixer_name (GstMixer *mixer, const gchar *name) +{ + gint length; + gchar *internal_name; + const gchar *p; + /* Count alpha-numeric characters in the name */ for (length = 0, p = name; *p != '\0'; ++p) if (g_ascii_isalnum (*p)) @@ -74,12 +88,9 @@ _xvd_mixer_filter_mixer (GstMixer *tmp_m internal_name[length] = '\0'; /* Remember name for use by xfce4-mixer */ - g_object_set_data_full (G_OBJECT (tmp_mixer), "xfce-mixer-internal-name", internal_name, (GDestroyNotify) g_free); - - g_free (name); - - return TRUE; -} + g_object_set_data_full (G_OBJECT (mixer), "xfce-mixer-internal-name", + internal_name, (GDestroyNotify) g_free); +} #ifdef HAVE_LIBNOTIFY static void @@ -133,11 +144,25 @@ _xvd_mixer_bus_message (GstBus *bus, Gst } #endif +#ifdef XFCE4_MIXER_ALSA +static void init_mixer (gpointer data, gpointer user_data) +{ + GstMixer *card = GST_MIXER (data); + + set_mixer_name (card, gst_mixer_get_card_name (card)); +} +#endif + void xvd_mixer_init(XvdInstance *Inst) { /* Get list of all available mixer devices */ +#ifdef XFCE4_MIXER_ALSA + Inst->mixers = gst_mixer_probe_devices (); + g_list_foreach (Inst->mixers, (GFunc) init_mixer, NULL); +#else Inst->mixers = gst_audio_default_registry_mixer_filter (_xvd_mixer_filter_mixer, FALSE, &(Inst->nameless_cards_count)); +#endif } #ifdef HAVE_LIBNOTIFY --- a/src/xvd_mixer.h +++ b/src/xvd_mixer.h @@ -24,7 +24,12 @@ #ifndef _XVD_MIXER_H #define _XVD_MIXER_H +#ifdef XFCE4_MIXER_ALSA +#include <gst/gst.h> +#include "xfce4-mixer-alsa.h" +#else #include <gst/audio/mixerutils.h> +#endif #include "xvd_data_types.h" --- a/src/xvd_data_types.h +++ b/src/xvd_data_types.h @@ -38,7 +38,12 @@ #include <xfconf/xfconf.h> +#ifdef XFCE4_MIXER_ALSA +#include <gst/gst.h> +#include "xfce4-mixer-alsa.h" +#else #include <gst/audio/mixerutils.h> +#endif #include <keybinder.h> #ifdef HAVE_LIBNOTIFY --- a/src/main.c +++ b/src/main.c @@ -21,6 +21,7 @@ #include "config.h" #endif +#include <gtk/gtk.h> #include "xvd_keys.h" #include "xvd_data_types.h" #include "xvd_mixer.h"
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor