[Spice-devel] [vdagent-linux PATCH v3 1/2] audio: add functions to set volume/mute with alsa

Victor Toso victortoso at redhat.com
Fri Mar 27 07:09:48 PDT 2015


This patch includes the vdagentd-audio.[ch] files in order to
communicate with backend audio server.

The two functions provide a way to set volume and mute in the guest
by connecting to default mixer control in alsa which is 'Master' for
playback and 'Capture' for record.
---
 Makefile.am          |   6 +-
 configure.ac         |   1 +
 src/vdagentd-audio.c | 168 +++++++++++++++++++++++++++++++++++++++++++++++++++
 src/vdagentd-audio.h |  27 +++++++++
 4 files changed, 200 insertions(+), 2 deletions(-)
 create mode 100644 src/vdagentd-audio.c
 create mode 100644 src/vdagentd-audio.h

diff --git a/Makefile.am b/Makefile.am
index 510f460..d789cc1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,11 +9,12 @@ src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS)
 src_spice_vdagent_SOURCES = src/vdagent.c src/vdagent-x11.c src/vdagent-x11-randr.c src/vdagent-file-xfers.c src/udscs.c
 
 src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(LIBSYSTEMD_LOGIN_CFLAGS) \
-  $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS)
+  $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS) $(ALSA_CFLAGS)
 src_spice_vdagentd_LDADD = $(DBUS_LIBS) $(LIBSYSTEMD_LOGIN_LIBS) \
-  $(PCIACCESS_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(PIE_LDFLAGS)
+  $(PCIACCESS_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(PIE_LDFLAGS) $(ALSA_LIBS)
 src_spice_vdagentd_SOURCES = src/vdagentd.c \
                              src/vdagentd-uinput.c \
+                             src/vdagentd-audio.c \
                              src/vdagentd-xorg-conf.c \
                              src/vdagent-virtio-port.c \
                              src/udscs.c
@@ -37,6 +38,7 @@ noinst_HEADERS = src/glib-compat.h \
                  src/vdagentd-proto.h \
                  src/vdagentd-proto-strings.h \
                  src/vdagentd-uinput.h \
+                 src/vdagentd-audio.h \
                  src/vdagentd-xorg-conf.h
 
 xdgautostartdir = $(sysconfdir)/xdg/autostart
diff --git a/configure.ac b/configure.ac
index 79905a8..b011358 100644
--- a/configure.ac
+++ b/configure.ac
@@ -79,6 +79,7 @@ AC_ARG_ENABLE([static-uinput],
 PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.12])
 PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11])
 PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.5])
+PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.22])
 
 if test "$with_session_info" = "auto" || test "$with_session_info" = "systemd"; then
     PKG_CHECK_MODULES([LIBSYSTEMD_LOGIN],
diff --git a/src/vdagentd-audio.c b/src/vdagentd-audio.c
new file mode 100644
index 0000000..c3bfe09
--- /dev/null
+++ b/src/vdagentd-audio.c
@@ -0,0 +1,168 @@
+/*  vdagentd-audio.c vdagentd audio handling code
+
+    Copyright 2015 Red Hat, Inc.
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <syslog.h>
+#include <stdbool.h>
+#include <alsa/asoundlib.h>
+#include <alsa/mixer.h>
+#include <alsa/error.h>
+#include "vdagentd-audio.h"
+
+#define ALSA_MUTE   0
+#define ALSA_UNMUTE 1
+
+static snd_mixer_elem_t *
+alsa_get_default_mixer_by_name(snd_mixer_t **handle, const char *name)
+{
+    snd_mixer_selem_id_t *sid;
+    int err = 0;
+
+    if ((err = snd_mixer_open(handle, 0)) < 0)
+        goto fail;
+
+    if ((err = snd_mixer_attach(*handle, "default")) < 0)
+        goto fail;
+
+    if ((err = snd_mixer_selem_register(*handle, NULL, NULL)) < 0)
+        goto fail;
+
+    if ((err = snd_mixer_load(*handle)) < 0)
+        goto fail;
+
+    snd_mixer_selem_id_alloca(&sid);
+    snd_mixer_selem_id_set_index(sid, 0);
+    snd_mixer_selem_id_set_name(sid, name);
+    return snd_mixer_find_selem(*handle, sid);
+
+fail:
+    syslog(LOG_WARNING, "%s fail: %s", __func__, snd_strerror(err));
+    return NULL;
+}
+
+static bool alsa_set_capture(uint8_t mute, uint8_t nchannels, uint16_t *volume)
+{
+    snd_mixer_t *handle = NULL;
+    snd_mixer_elem_t *e;
+    long min, max, vol;
+    bool ret = true;
+    int alsa_mute;
+
+    e = alsa_get_default_mixer_by_name (&handle, "Capture");
+    if (e == NULL) {
+        syslog(LOG_WARNING, "vdagentd-audio: can't get default alsa mixer");
+        ret = false;
+        goto end;
+    }
+
+    alsa_mute = (mute) ? ALSA_MUTE : ALSA_UNMUTE;
+    snd_mixer_selem_set_capture_switch_all(e, alsa_mute);
+
+    snd_mixer_selem_get_capture_volume_range(e, &min, &max);
+    switch (nchannels) {
+    case 1: /* MONO */
+        vol = CLAMP(volume[0], min, max);
+        snd_mixer_selem_set_capture_volume(e, SND_MIXER_SCHN_MONO, vol);
+        syslog(LOG_DEBUG, "vdagentd-audio: (capture-mono) %lu (%%%0.2f)",
+               vol, (float) (100*vol/max));
+        break;
+    case 2: /* LEFT-RIGHT */
+        vol = CLAMP(volume[0], min, max);
+        snd_mixer_selem_set_capture_volume(e, SND_MIXER_SCHN_FRONT_LEFT, vol);
+        syslog(LOG_DEBUG, "vdagentd-audio: (capture-left) %lu (%%%0.2f)",
+               vol, (float) (100*vol/max));
+        vol = CLAMP(volume[1], min, max);
+        snd_mixer_selem_set_capture_volume(e, SND_MIXER_SCHN_FRONT_RIGHT, vol);
+        syslog(LOG_DEBUG, "vdagentd-audio: (capture-right) %lu (%%%0.2f)",
+               vol, (float) (100*vol/max));
+        break;
+    default:
+        syslog(LOG_WARNING, "vdagentd-audio: number of channels not supported");
+        ret = false;
+    }
+end:
+    if (handle != NULL)
+        snd_mixer_close(handle);
+    return ret;
+}
+
+static bool alsa_set_playback (uint8_t mute, uint8_t nchannels, uint16_t *volume)
+{
+    snd_mixer_t *handle = NULL;
+    snd_mixer_elem_t* e;
+    long min, max, vol;
+    bool ret = true;
+    int alsa_mute;
+
+    e = alsa_get_default_mixer_by_name (&handle, "Master");
+    if (e == NULL) {
+        syslog(LOG_WARNING, "vdagentd-audio: can't get default alsa mixer");
+        ret = false;
+        goto end;
+    }
+
+    alsa_mute = (mute) ? ALSA_MUTE : ALSA_UNMUTE;
+    snd_mixer_selem_set_playback_switch_all(e, alsa_mute);
+
+    snd_mixer_selem_get_playback_volume_range(e, &min, &max);
+    switch (nchannels) {
+    case 1: /* MONO */
+        vol = CLAMP(volume[0], min, max);
+        snd_mixer_selem_set_playback_volume(e, SND_MIXER_SCHN_MONO, vol);
+        syslog(LOG_DEBUG, "vdagentd-audio: (playback-mono) %lu (%%%0.2f)",
+               vol, (float) (100*vol/max));
+        break;
+    case 2: /* LEFT-RIGHT */
+        vol = CLAMP(volume[0], min, max);
+        snd_mixer_selem_set_playback_volume(e, SND_MIXER_SCHN_FRONT_LEFT, vol);
+        syslog(LOG_DEBUG, "vdagentd-audio: (playback-left) %lu (%%%0.2f)",
+               vol, (float) (100*vol/max));
+        vol = CLAMP(volume[1], min, max);
+        snd_mixer_selem_set_playback_volume(e, SND_MIXER_SCHN_FRONT_RIGHT, vol);
+        syslog(LOG_DEBUG, "vdagentd-audio: (playback-right) %lu (%%%0.2f)",
+               vol, (float) (100*vol/max));
+        break;
+    default:
+        syslog(LOG_WARNING, "vdagentd-audio: number of channels not supported");
+        ret = false;
+    }
+end:
+    if (handle != NULL)
+        snd_mixer_close(handle);
+    return ret;
+}
+
+void vdagent_audio_playback_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume)
+{
+    syslog(LOG_DEBUG, "%s mute=%s nchannels=%u",
+           __func__, (mute) ? "yes" : "no", nchannels);
+    if (alsa_set_playback (mute, nchannels, volume) == false)
+        syslog(LOG_WARNING, "Fail to sync playback volume");
+}
+
+void vdagent_audio_record_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume)
+{
+    syslog(LOG_DEBUG, "%s mute=%s nchannels=%u",
+           __func__, (mute) ? "yes" : "no", nchannels);
+    if (alsa_set_capture (mute, nchannels, volume) == false)
+        syslog(LOG_WARNING, "Fail to sync record volume");
+}
diff --git a/src/vdagentd-audio.h b/src/vdagentd-audio.h
new file mode 100644
index 0000000..950827a
--- /dev/null
+++ b/src/vdagentd-audio.h
@@ -0,0 +1,27 @@
+/*  vdagentd-audio.h vdagentd audio handling header
+
+    Copyright 2015 Red Hat, Inc.
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+#ifndef __VDAGENTD_AUDIO_H
+#define __VDAGENTD_AUDIO_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+void vdagent_audio_playback_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume);
+void vdagent_audio_record_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume);
+
+#endif
-- 
2.1.0



More information about the Spice-devel mailing list