directsoundsrc: Implement volume and mute
authorVictor Toso <victortoso@redhat.com>
Tue, 31 Mar 2015 09:53:55 +0000 (10:53 +0100)
committerJulien Isorce <j.isorce@samsung.com>
Tue, 31 Mar 2015 09:53:55 +0000 (10:53 +0100)
Using the MixerAPI as IDirectSoundCaptureBuffer doesn't implement volume
control.

https://bugzilla.gnome.org/show_bug.cgi?id=744383

configure.ac
sys/directsound/Makefile.am
sys/directsound/gstdirectsoundsrc.c
sys/directsound/gstdirectsoundsrc.h

index f466a3e..ae51373 100644 (file)
@@ -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 <windows.h>
 #include <dxerr9.h>
 #include <dsound.h>
+#include <mmsystem.h>
 ]], [[
   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)
index 18b0980..cfc769f 100644 (file)
@@ -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)
 
index 420a70d..0bd77e8 100644 (file)
 
 #include <windows.h>
 #include <dsound.h>
+#include <mmsystem.h>
 
 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 *
@@ -223,6 +253,12 @@ gst_directsound_src_set_property (GObject * object, guint prop_id,
       }
 
       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);
       break;
@@ -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;
+}
index 4c65372..a2ac6ff 100644 (file)
@@ -52,6 +52,7 @@
 #include <gst/audio/gstaudiosrc.h>\r
 #include <windows.h>\r
 #include <dsound.h>\r
+#include <mmsystem.h>
 \r
 /* add here some headers if needed */\r
 \r
@@ -91,6 +92,15 @@ struct _GstDirectSoundSrc
   guint buffer_time;\r
   guint latency_time;\r
 \r
+  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;
 \r
   GUID *device_guid;\r
   char *device_name;\r