From 1b564bfb8b0db43a24cf311dca24038558b80a68 Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Tue, 31 Mar 2015 10:53:55 +0100 Subject: [PATCH] directsoundsrc: Implement volume and mute Using the MixerAPI as IDirectSoundCaptureBuffer doesn't implement volume control. https://bugzilla.gnome.org/show_bug.cgi?id=744383 --- configure.ac | 5 +- sys/directsound/Makefile.am | 2 +- sys/directsound/gstdirectsoundsrc.c | 267 +++++++++++++++++++++++++++- sys/directsound/gstdirectsoundsrc.h | 10 ++ 4 files changed, 279 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index f466a3eec5..ae513734b9 100644 --- a/configure.ac +++ b/configure.ac @@ -1397,12 +1397,13 @@ AG_GST_CHECK_FEATURE(DIRECTSOUND, [DirectSound], directsoundsrc, [ save_LIBS="$LIBS" CFLAGS="$CFLAGS $DIRECTX_CFLAGS" LDFLAGS="$LDFLAGS $DIRECTX_LDFLAGS" - LIBS="$LIBS -ldsound -ldxerr9 -luser32" + LIBS="$LIBS -lwinmm -ldsound -ldxerr9 -luser32" AC_MSG_CHECKING(for DirectSound LDFLAGS) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include +#include ]], [[ DXGetErrorString9 (0); DirectSoundCreate(NULL, NULL, NULL); @@ -1416,7 +1417,7 @@ AG_GST_CHECK_FEATURE(DIRECTSOUND, [DirectSound], directsoundsrc, [ if test "x$HAVE_DIRECTSOUND" = "xyes"; then dnl this is much more than we want - DIRECTSOUND_LIBS="-ldsound -ldxerr9 -luser32" + DIRECTSOUND_LIBS="-lwinmm -ldsound -ldxerr9 -luser32" AC_SUBST(DIRECTX_CFLAGS) AC_SUBST(DIRECTX_LDFLAGS) AC_SUBST(DIRECTSOUND_LIBS) diff --git a/sys/directsound/Makefile.am b/sys/directsound/Makefile.am index 18b0980b91..cfc769f6ce 100644 --- a/sys/directsound/Makefile.am +++ b/sys/directsound/Makefile.am @@ -5,7 +5,7 @@ libgstdirectsoundsrc_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) \ $(GST_PLUGINS_BASE_CFLAGS) $(DIRECTX_CFLAGS) libgstdirectsoundsrc_la_LIBADD = \ $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgstaudio-$(GST_API_VERSION) \ - $(DIRECTX_LDFLAGS) -ldsound + $(DIRECTX_LDFLAGS) $(DIRECTSOUND_LIBS) libgstdirectsoundsrc_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstdirectsoundsrc_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) diff --git a/sys/directsound/gstdirectsoundsrc.c b/sys/directsound/gstdirectsoundsrc.c index 420a70d57c..0bd77e8294 100644 --- a/sys/directsound/gstdirectsoundsrc.c +++ b/sys/directsound/gstdirectsoundsrc.c @@ -73,18 +73,22 @@ #include #include +#include GST_DEBUG_CATEGORY_STATIC (directsoundsrc_debug); #define GST_CAT_DEFAULT directsoundsrc_debug /* defaults here */ #define DEFAULT_DEVICE 0 +#define DEFAULT_MUTE FALSE /* properties */ enum { PROP_0, - PROP_DEVICE_NAME + PROP_DEVICE_NAME, + PROP_VOLUME, + PROP_MUTE }; static HRESULT (WINAPI * pDSoundCaptureCreate) (LPGUID, @@ -114,6 +118,18 @@ static void gst_directsound_src_dispose (GObject * object); static guint gst_directsound_src_delay (GstAudioSrc * asrc); +static gboolean gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc, + MIXERCAPS * mixer_caps); +static void gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc); + +static gdouble gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc); +static void gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc, + gdouble volume); + +static gboolean gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc); +static void gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc, + gboolean mute); + static GstStaticPadTemplate directsound_src_src_factory = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, @@ -124,7 +140,9 @@ GST_STATIC_PAD_TEMPLATE ("src", "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, 2 ]")); #define gst_directsound_src_parent_class parent_class -G_DEFINE_TYPE (GstDirectSoundSrc, gst_directsound_src, GST_TYPE_AUDIO_SRC); +G_DEFINE_TYPE_WITH_CODE (GstDirectSoundSrc, gst_directsound_src, + GST_TYPE_AUDIO_SRC, G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL) + ); static void gst_directsound_src_dispose (GObject * object) @@ -193,6 +211,18 @@ gst_directsound_src_class_init (GstDirectSoundSrcClass * klass) (gobject_class, PROP_DEVICE_NAME, g_param_spec_string ("device-name", "Device name", "Human-readable name of the sound device", NULL, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, PROP_VOLUME, + g_param_spec_double ("volume", "Volume", + "Volume of this stream", 0.0, 1.0, 1.0, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property + (gobject_class, PROP_MUTE, + g_param_spec_boolean ("mute", "Mute", + "Mute state of this stream", DEFAULT_MUTE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); } static GstCaps * @@ -222,6 +252,12 @@ gst_directsound_src_set_property (GObject * object, guint prop_id, src->device_name = g_strdup (g_value_get_string (value)); } + break; + case PROP_VOLUME: + gst_directsound_src_set_volume (src, g_value_get_double (value)); + break; + case PROP_MUTE: + gst_directsound_src_set_mute (src, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -241,6 +277,12 @@ gst_directsound_src_get_property (GObject * object, guint prop_id, case PROP_DEVICE_NAME: g_value_set_string (value, src->device_name); break; + case PROP_VOLUME: + g_value_set_double (value, gst_directsound_src_get_volume (src)); + break; + case PROP_MUTE: + g_value_set_boolean (value, gst_directsound_src_get_mute (src)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -260,6 +302,11 @@ gst_directsound_src_init (GstDirectSoundSrc * src) g_mutex_init (&src->dsound_lock); src->device_guid = NULL; src->device_name = NULL; + src->mixer = NULL; + src->control_id_mute = -1; + src->control_id_volume = -1; + src->volume = 100; + src->mute = FALSE; } @@ -326,6 +373,7 @@ gst_directsound_src_open (GstAudioSrc * asrc) goto capture_object; } + gst_directsound_src_mixer_init (dsoundsrc); return TRUE; capture_function: @@ -374,6 +422,9 @@ gst_directsound_src_close (GstAudioSrc * asrc) /* Close library */ FreeLibrary (dsoundsrc->DSoundDLL); + if (dsoundsrc->mixer) + mixerClose (dsoundsrc->mixer); + return TRUE; } @@ -661,3 +712,215 @@ gst_directsound_src_reset (GstAudioSrc * asrc) GST_DSOUND_UNLOCK (dsoundsrc); } + +/* If the PROP_DEVICE_NAME is set, find the mixer related to device; + * otherwise we get the default input mixer. */ +static gboolean +gst_directsound_src_mixer_find (GstDirectSoundSrc * dsoundsrc, + MIXERCAPS * mixer_caps) +{ + MMRESULT mmres; + guint i, num_mixers; + + num_mixers = mixerGetNumDevs (); + for (i = 0; i < num_mixers; i++) { + mmres = mixerOpen (&dsoundsrc->mixer, i, 0L, 0L, + MIXER_OBJECTF_MIXER | MIXER_OBJECTF_WAVEIN); + + if (mmres != MMSYSERR_NOERROR) + continue; + + mmres = mixerGetDevCaps ((UINT) dsoundsrc->mixer, + mixer_caps, sizeof (MIXERCAPS)); + + if (mmres != MMSYSERR_NOERROR) { + mixerClose (dsoundsrc->mixer); + continue; + } + + /* Get default mixer */ + if (dsoundsrc->device_name == NULL) { + GST_DEBUG ("Got default input mixer: %s", mixer_caps->szPname); + return TRUE; + } + + if (g_strstr_len (dsoundsrc->device_name, -1, mixer_caps->szPname) != NULL) { + GST_DEBUG ("Got requested input mixer: %s", mixer_caps->szPname); + return TRUE; + } + + /* Wrong mixer */ + mixerClose (dsoundsrc->mixer); + } + + GST_DEBUG ("Can't find input mixer"); + return FALSE; +} + +static void +gst_directsound_src_mixer_init (GstDirectSoundSrc * dsoundsrc) +{ + gint i, k; + gboolean found_mic; + MMRESULT mmres; + MIXERCAPS mixer_caps; + MIXERLINE mixer_line; + MIXERLINECONTROLS ml_ctrl; + PMIXERCONTROL pamixer_ctrls; + + if (!gst_directsound_src_mixer_find (dsoundsrc, &mixer_caps)) + goto mixer_init_fail; + + /* Find the MIXERLINE related to MICROPHONE */ + found_mic = FALSE; + for (i = 0; i < mixer_caps.cDestinations && !found_mic; i++) { + gint j, num_connections; + + mixer_line.cbStruct = sizeof (mixer_line); + mixer_line.dwDestination = i; + mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer, + &mixer_line, MIXER_GETLINEINFOF_DESTINATION); + + if (mmres != MMSYSERR_NOERROR) + goto mixer_init_fail; + + num_connections = mixer_line.cConnections; + for (j = 0; j < num_connections && !found_mic; j++) { + mixer_line.cbStruct = sizeof (mixer_line); + mixer_line.dwDestination = i; + mixer_line.dwSource = j; + mmres = mixerGetLineInfo ((HMIXEROBJ) dsoundsrc->mixer, + &mixer_line, MIXER_GETLINEINFOF_SOURCE); + + if (mmres != MMSYSERR_NOERROR) + goto mixer_init_fail; + + if (mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE + || mixer_line.dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE) + found_mic = TRUE; + } + } + + if (found_mic == FALSE) { + GST_DEBUG ("Can't find mixer line related to input"); + goto mixer_init_fail; + } + + /* Get control associated with microphone audio line */ + pamixer_ctrls = g_malloc (sizeof (MIXERCONTROL) * mixer_line.cControls); + ml_ctrl.cbStruct = sizeof (ml_ctrl); + ml_ctrl.dwLineID = mixer_line.dwLineID; + ml_ctrl.cControls = mixer_line.cControls; + ml_ctrl.cbmxctrl = sizeof (MIXERCONTROL); + ml_ctrl.pamxctrl = pamixer_ctrls; + mmres = mixerGetLineControls ((HMIXEROBJ) dsoundsrc->mixer, + &ml_ctrl, MIXER_GETLINECONTROLSF_ALL); + + /* Find control associated with volume and mute */ + for (k = 0; k < mixer_line.cControls; k++) { + if (strstr (pamixer_ctrls[k].szName, "Volume") != NULL) { + dsoundsrc->control_id_volume = pamixer_ctrls[k].dwControlID; + dsoundsrc->dw_vol_max = pamixer_ctrls[k].Bounds.dwMaximum; + dsoundsrc->dw_vol_min = pamixer_ctrls[k].Bounds.dwMinimum; + } else if (strstr (pamixer_ctrls[k].szName, "Mute") != NULL) { + dsoundsrc->control_id_mute = pamixer_ctrls[k].dwControlID; + } else { + GST_DEBUG ("Control not handled: %s", pamixer_ctrls[k].szName); + } + } + g_free (pamixer_ctrls); + + if (dsoundsrc->control_id_volume < 0 && dsoundsrc->control_id_mute < 0) + goto mixer_init_fail; + + /* Save cChannels information to properly changes in volume */ + dsoundsrc->mixerline_cchannels = mixer_line.cChannels; + return; + +mixer_init_fail: + GST_WARNING ("Failed to get Volume and Mute controls"); + if (dsoundsrc->mixer != NULL) { + mixerClose (dsoundsrc->mixer); + dsoundsrc->mixer = NULL; + } +} + +static gdouble +gst_directsound_src_get_volume (GstDirectSoundSrc * dsoundsrc) +{ + return (gdouble) dsoundsrc->volume / 100; +} + +static gboolean +gst_directsound_src_get_mute (GstDirectSoundSrc * dsoundsrc) +{ + return dsoundsrc->mute; +} + +static void +gst_directsound_src_set_volume (GstDirectSoundSrc * dsoundsrc, gdouble volume) +{ + MMRESULT mmres; + MIXERCONTROLDETAILS details; + MIXERCONTROLDETAILS_UNSIGNED details_unsigned; + glong dwvolume; + + if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_volume < 0) { + GST_WARNING ("mixer not initialized"); + return; + } + + dwvolume = volume * dsoundsrc->dw_vol_max; + dwvolume = CLAMP (dwvolume, dsoundsrc->dw_vol_min, dsoundsrc->dw_vol_max); + + GST_DEBUG ("max volume %ld | min volume %ld", + dsoundsrc->dw_vol_max, dsoundsrc->dw_vol_min); + GST_DEBUG ("set volume to %f (%ld)", volume, dwvolume); + + details.cbStruct = sizeof (details); + details.dwControlID = dsoundsrc->control_id_volume; + details.cChannels = dsoundsrc->mixerline_cchannels; + details.cMultipleItems = 0; + + details_unsigned.dwValue = dwvolume; + details.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED); + details.paDetails = &details_unsigned; + + mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer, + &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); + + if (mmres != MMSYSERR_NOERROR) + GST_WARNING ("Failed to set volume"); + else + dsoundsrc->volume = volume * 100; +} + +static void +gst_directsound_src_set_mute (GstDirectSoundSrc * dsoundsrc, gboolean mute) +{ + MMRESULT mmres; + MIXERCONTROLDETAILS details; + MIXERCONTROLDETAILS_BOOLEAN details_boolean; + + if (dsoundsrc->mixer == NULL || dsoundsrc->control_id_mute < 0) { + GST_WARNING ("mixer not initialized"); + return; + } + + details.cbStruct = sizeof (details); + details.dwControlID = dsoundsrc->control_id_mute; + details.cChannels = dsoundsrc->mixerline_cchannels; + details.cMultipleItems = 0; + + details_boolean.fValue = mute; + details.cbDetails = sizeof (MIXERCONTROLDETAILS_BOOLEAN); + details.paDetails = &details_boolean; + + mmres = mixerSetControlDetails ((HMIXEROBJ) dsoundsrc->mixer, + &details, MIXER_OBJECTF_HMIXER | MIXER_SETCONTROLDETAILSF_VALUE); + + if (mmres != MMSYSERR_NOERROR) + GST_WARNING ("Failed to set mute"); + else + dsoundsrc->mute = mute; +} diff --git a/sys/directsound/gstdirectsoundsrc.h b/sys/directsound/gstdirectsoundsrc.h index 4c65372c97..a2ac6ffd36 100644 --- a/sys/directsound/gstdirectsoundsrc.h +++ b/sys/directsound/gstdirectsoundsrc.h @@ -52,6 +52,7 @@ #include #include #include +#include /* add here some headers if needed */ @@ -91,6 +92,15 @@ struct _GstDirectSoundSrc guint buffer_time; guint latency_time; + HMIXER mixer; + DWORD mixerline_cchannels; + gint control_id_volume; + gint control_id_mute; + glong dw_vol_max; + glong dw_vol_min; + + glong volume; + gboolean mute; GUID *device_guid; char *device_name;