|
|
ad7ee3 |
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
|
ad7ee3 |
From: Victor Toso <victortoso@redhat.com>
|
|
|
ad7ee3 |
Date: Fri, 6 Mar 2015 18:14:21 +0100
|
|
|
ad7ee3 |
Subject: [PATCH] audio: add functions to set volume/mute with alsa
|
|
|
ad7ee3 |
|
|
|
ad7ee3 |
This patch includes the vdagent-audio.[ch] files in order to
|
|
|
ad7ee3 |
communicate with backend audio server.
|
|
|
ad7ee3 |
|
|
|
ad7ee3 |
The two functions provide a way to set volume and mute in the guest
|
|
|
ad7ee3 |
by connecting to default mixer control in alsa which is 'Master' for
|
|
|
ad7ee3 |
playback and 'Capture' for record.
|
|
|
ad7ee3 |
---
|
|
|
ad7ee3 |
Makefile.am | 12 +++-
|
|
|
ad7ee3 |
configure.ac | 1 +
|
|
|
ad7ee3 |
src/vdagent-audio.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
ad7ee3 |
src/vdagent-audio.h | 27 +++++++++
|
|
|
ad7ee3 |
4 files changed, 205 insertions(+), 3 deletions(-)
|
|
|
ad7ee3 |
create mode 100644 src/vdagent-audio.c
|
|
|
ad7ee3 |
create mode 100644 src/vdagent-audio.h
|
|
|
ad7ee3 |
|
|
|
ad7ee3 |
diff --git a/Makefile.am b/Makefile.am
|
|
|
ad7ee3 |
index a06a259..2effe4e 100644
|
|
|
ad7ee3 |
--- a/Makefile.am
|
|
|
ad7ee3 |
+++ b/Makefile.am
|
|
|
ad7ee3 |
@@ -4,9 +4,14 @@ NULL =
|
|
|
ad7ee3 |
bin_PROGRAMS = src/spice-vdagent
|
|
|
ad7ee3 |
sbin_PROGRAMS = src/spice-vdagentd
|
|
|
ad7ee3 |
|
|
|
ad7ee3 |
-src_spice_vdagent_CFLAGS = $(X_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS)
|
|
|
ad7ee3 |
-src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS)
|
|
|
ad7ee3 |
-src_spice_vdagent_SOURCES = src/vdagent.c src/vdagent-x11.c src/vdagent-x11-randr.c src/vdagent-file-xfers.c src/udscs.c
|
|
|
ad7ee3 |
+src_spice_vdagent_CFLAGS = $(X_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(ALSA_CFLAGS)
|
|
|
ad7ee3 |
+src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(ALSA_LIBS)
|
|
|
ad7ee3 |
+src_spice_vdagent_SOURCES = src/vdagent.c \
|
|
|
ad7ee3 |
+ src/vdagent-x11.c \
|
|
|
ad7ee3 |
+ src/vdagent-x11-randr.c \
|
|
|
ad7ee3 |
+ src/vdagent-file-xfers.c \
|
|
|
ad7ee3 |
+ src/vdagent-audio.c \
|
|
|
ad7ee3 |
+ src/udscs.c
|
|
|
ad7ee3 |
|
|
|
ad7ee3 |
src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(LIBSYSTEMD_LOGIN_CFLAGS) \
|
|
|
ad7ee3 |
$(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS)
|
|
|
ad7ee3 |
@@ -30,6 +35,7 @@ endif
|
|
|
ad7ee3 |
noinst_HEADERS = src/glib-compat.h \
|
|
|
ad7ee3 |
src/session-info.h \
|
|
|
ad7ee3 |
src/udscs.h \
|
|
|
ad7ee3 |
+ src/vdagent-audio.h \
|
|
|
ad7ee3 |
src/vdagent-file-xfers.h \
|
|
|
ad7ee3 |
src/vdagent-virtio-port.h \
|
|
|
ad7ee3 |
src/vdagent-x11.h \
|
|
|
ad7ee3 |
diff --git a/configure.ac b/configure.ac
|
|
|
ad7ee3 |
index a1ce6c0..377fc74 100644
|
|
|
ad7ee3 |
--- a/configure.ac
|
|
|
ad7ee3 |
+++ b/configure.ac
|
|
|
ad7ee3 |
@@ -79,6 +79,7 @@ AC_ARG_ENABLE([static-uinput],
|
|
|
ad7ee3 |
PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.12])
|
|
|
ad7ee3 |
PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11])
|
|
|
ad7ee3 |
PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.5])
|
|
|
ad7ee3 |
+PKG_CHECK_MODULES(ALSA, [alsa >= 1.0.22])
|
|
|
ad7ee3 |
|
|
|
ad7ee3 |
if test "$with_session_info" = "auto" || test "$with_session_info" = "systemd"; then
|
|
|
ad7ee3 |
PKG_CHECK_MODULES([LIBSYSTEMD_LOGIN],
|
|
|
ad7ee3 |
diff --git a/src/vdagent-audio.c b/src/vdagent-audio.c
|
|
|
ad7ee3 |
new file mode 100644
|
|
|
ad7ee3 |
index 0000000..6b11cd8
|
|
|
ad7ee3 |
--- /dev/null
|
|
|
ad7ee3 |
+++ b/src/vdagent-audio.c
|
|
|
ad7ee3 |
@@ -0,0 +1,168 @@
|
|
|
ad7ee3 |
+/* vdagent-audio.c vdagentd audio handling code
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ Copyright 2015 Red Hat, Inc.
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ This program is free software: you can redistribute it and/or modify
|
|
|
ad7ee3 |
+ it under the terms of the GNU General Public License as published by
|
|
|
ad7ee3 |
+ the Free Software Foundation, either version 3 of the License, or
|
|
|
ad7ee3 |
+ (at your option) any later version.
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ This program is distributed in the hope that it will be useful,
|
|
|
ad7ee3 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
ad7ee3 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
ad7ee3 |
+ GNU General Public License for more details.
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ You should have received a copy of the GNU General Public License
|
|
|
ad7ee3 |
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
ad7ee3 |
+*/
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+#ifdef HAVE_CONFIG_H
|
|
|
ad7ee3 |
+#include <config.h>
|
|
|
ad7ee3 |
+#endif
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+#include <glib.h>
|
|
|
ad7ee3 |
+#include <syslog.h>
|
|
|
ad7ee3 |
+#include <stdbool.h>
|
|
|
ad7ee3 |
+#include <alsa/asoundlib.h>
|
|
|
ad7ee3 |
+#include <alsa/mixer.h>
|
|
|
ad7ee3 |
+#include <alsa/error.h>
|
|
|
ad7ee3 |
+#include "vdagent-audio.h"
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+#define ALSA_MUTE 0
|
|
|
ad7ee3 |
+#define ALSA_UNMUTE 1
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+static snd_mixer_elem_t *
|
|
|
ad7ee3 |
+get_alsa_default_mixer_by_name(snd_mixer_t **handle, const char *name)
|
|
|
ad7ee3 |
+{
|
|
|
ad7ee3 |
+ snd_mixer_selem_id_t *sid;
|
|
|
ad7ee3 |
+ int err = 0;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ if ((err = snd_mixer_open(handle, 0)) < 0)
|
|
|
ad7ee3 |
+ goto fail;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ if ((err = snd_mixer_attach(*handle, "default")) < 0)
|
|
|
ad7ee3 |
+ goto fail;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ if ((err = snd_mixer_selem_register(*handle, NULL, NULL)) < 0)
|
|
|
ad7ee3 |
+ goto fail;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ if ((err = snd_mixer_load(*handle)) < 0)
|
|
|
ad7ee3 |
+ goto fail;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ snd_mixer_selem_id_alloca(&sid;;
|
|
|
ad7ee3 |
+ snd_mixer_selem_id_set_index(sid, 0);
|
|
|
ad7ee3 |
+ snd_mixer_selem_id_set_name(sid, name);
|
|
|
ad7ee3 |
+ return snd_mixer_find_selem(*handle, sid);
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+fail:
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "%s fail: %s", __func__, snd_strerror(err));
|
|
|
ad7ee3 |
+ return NULL;
|
|
|
ad7ee3 |
+}
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+static bool set_alsa_capture(uint8_t mute, uint8_t nchannels, uint16_t *volume)
|
|
|
ad7ee3 |
+{
|
|
|
ad7ee3 |
+ snd_mixer_t *handle = NULL;
|
|
|
ad7ee3 |
+ snd_mixer_elem_t *e;
|
|
|
ad7ee3 |
+ long min, max, vol;
|
|
|
ad7ee3 |
+ bool ret = true;
|
|
|
ad7ee3 |
+ int alsa_mute;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ e = get_alsa_default_mixer_by_name (&handle, "Capture");
|
|
|
ad7ee3 |
+ if (e == NULL) {
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "vdagent-audio: can't get default alsa mixer");
|
|
|
ad7ee3 |
+ ret = false;
|
|
|
ad7ee3 |
+ goto end;
|
|
|
ad7ee3 |
+ }
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ alsa_mute = (mute) ? ALSA_MUTE : ALSA_UNMUTE;
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_capture_switch_all(e, alsa_mute);
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ snd_mixer_selem_get_capture_volume_range(e, &min, &max;;
|
|
|
ad7ee3 |
+ switch (nchannels) {
|
|
|
ad7ee3 |
+ case 1: /* MONO */
|
|
|
ad7ee3 |
+ vol = CLAMP(volume[0], min, max);
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_capture_volume(e, SND_MIXER_SCHN_MONO, vol);
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "vdagent-audio: (capture-mono) %lu (%%%0.2f)",
|
|
|
ad7ee3 |
+ vol, (float) (100*vol/max));
|
|
|
ad7ee3 |
+ break;
|
|
|
ad7ee3 |
+ case 2: /* LEFT-RIGHT */
|
|
|
ad7ee3 |
+ vol = CLAMP(volume[0], min, max);
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_capture_volume(e, SND_MIXER_SCHN_FRONT_LEFT, vol);
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "vdagent-audio: (capture-left) %lu (%%%0.2f)",
|
|
|
ad7ee3 |
+ vol, (float) (100*vol/max));
|
|
|
ad7ee3 |
+ vol = CLAMP(volume[1], min, max);
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_capture_volume(e, SND_MIXER_SCHN_FRONT_RIGHT, vol);
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "vdagent-audio: (capture-right) %lu (%%%0.2f)",
|
|
|
ad7ee3 |
+ vol, (float) (100*vol/max));
|
|
|
ad7ee3 |
+ break;
|
|
|
ad7ee3 |
+ default:
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "vdagent-audio: number of channels not supported");
|
|
|
ad7ee3 |
+ ret = false;
|
|
|
ad7ee3 |
+ }
|
|
|
ad7ee3 |
+end:
|
|
|
ad7ee3 |
+ if (handle != NULL)
|
|
|
ad7ee3 |
+ snd_mixer_close(handle);
|
|
|
ad7ee3 |
+ return ret;
|
|
|
ad7ee3 |
+}
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+static bool set_alsa_playback (uint8_t mute, uint8_t nchannels, uint16_t *volume)
|
|
|
ad7ee3 |
+{
|
|
|
ad7ee3 |
+ snd_mixer_t *handle = NULL;
|
|
|
ad7ee3 |
+ snd_mixer_elem_t* e;
|
|
|
ad7ee3 |
+ long min, max, vol;
|
|
|
ad7ee3 |
+ bool ret = true;
|
|
|
ad7ee3 |
+ int alsa_mute;
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ e = get_alsa_default_mixer_by_name (&handle, "Master");
|
|
|
ad7ee3 |
+ if (e == NULL) {
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "vdagent-audio: can't get default alsa mixer");
|
|
|
ad7ee3 |
+ ret = false;
|
|
|
ad7ee3 |
+ goto end;
|
|
|
ad7ee3 |
+ }
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ alsa_mute = (mute) ? ALSA_MUTE : ALSA_UNMUTE;
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_playback_switch_all(e, alsa_mute);
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ snd_mixer_selem_get_playback_volume_range(e, &min, &max;;
|
|
|
ad7ee3 |
+ switch (nchannels) {
|
|
|
ad7ee3 |
+ case 1: /* MONO */
|
|
|
ad7ee3 |
+ vol = CLAMP(volume[0], min, max);
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_playback_volume(e, SND_MIXER_SCHN_MONO, vol);
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "vdagent-audio: (playback-mono) %lu (%%%0.2f)",
|
|
|
ad7ee3 |
+ vol, (float) (100*vol/max));
|
|
|
ad7ee3 |
+ break;
|
|
|
ad7ee3 |
+ case 2: /* LEFT-RIGHT */
|
|
|
ad7ee3 |
+ vol = CLAMP(volume[0], min, max);
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_playback_volume(e, SND_MIXER_SCHN_FRONT_LEFT, vol);
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "vdagent-audio: (playback-left) %lu (%%%0.2f)",
|
|
|
ad7ee3 |
+ vol, (float) (100*vol/max));
|
|
|
ad7ee3 |
+ vol = CLAMP(volume[1], min, max);
|
|
|
ad7ee3 |
+ snd_mixer_selem_set_playback_volume(e, SND_MIXER_SCHN_FRONT_RIGHT, vol);
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "vdagent-audio: (playback-right) %lu (%%%0.2f)",
|
|
|
ad7ee3 |
+ vol, (float) (100*vol/max));
|
|
|
ad7ee3 |
+ break;
|
|
|
ad7ee3 |
+ default:
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "vdagent-audio: number of channels not supported");
|
|
|
ad7ee3 |
+ ret = false;
|
|
|
ad7ee3 |
+ }
|
|
|
ad7ee3 |
+end:
|
|
|
ad7ee3 |
+ if (handle != NULL)
|
|
|
ad7ee3 |
+ snd_mixer_close(handle);
|
|
|
ad7ee3 |
+ return ret;
|
|
|
ad7ee3 |
+}
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+void vdagent_audio_playback_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume)
|
|
|
ad7ee3 |
+{
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "%s mute=%s nchannels=%u",
|
|
|
ad7ee3 |
+ __func__, (mute) ? "yes" : "no", nchannels);
|
|
|
ad7ee3 |
+ if (set_alsa_playback (mute, nchannels, volume) == false)
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "Fail to sync playback volume");
|
|
|
ad7ee3 |
+}
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+void vdagent_audio_record_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume)
|
|
|
ad7ee3 |
+{
|
|
|
ad7ee3 |
+ syslog(LOG_DEBUG, "%s mute=%s nchannels=%u",
|
|
|
ad7ee3 |
+ __func__, (mute) ? "yes" : "no", nchannels);
|
|
|
ad7ee3 |
+ if (set_alsa_capture (mute, nchannels, volume) == false)
|
|
|
ad7ee3 |
+ syslog(LOG_WARNING, "Fail to sync record volume");
|
|
|
ad7ee3 |
+}
|
|
|
ad7ee3 |
diff --git a/src/vdagent-audio.h b/src/vdagent-audio.h
|
|
|
ad7ee3 |
new file mode 100644
|
|
|
ad7ee3 |
index 0000000..6f29d4b
|
|
|
ad7ee3 |
--- /dev/null
|
|
|
ad7ee3 |
+++ b/src/vdagent-audio.h
|
|
|
ad7ee3 |
@@ -0,0 +1,27 @@
|
|
|
ad7ee3 |
+/* vdagent-audio.h vdagentd audio handling header
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ Copyright 2015 Red Hat, Inc.
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ This program is free software: you can redistribute it and/or modify
|
|
|
ad7ee3 |
+ it under the terms of the GNU General Public License as published by
|
|
|
ad7ee3 |
+ the Free Software Foundation, either version 3 of the License, or
|
|
|
ad7ee3 |
+ (at your option) any later version.
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ This program is distributed in the hope that it will be useful,
|
|
|
ad7ee3 |
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
ad7ee3 |
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
ad7ee3 |
+ GNU General Public License for more details.
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+ You should have received a copy of the GNU General Public License
|
|
|
ad7ee3 |
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
ad7ee3 |
+*/
|
|
|
ad7ee3 |
+#ifndef __VDAGENT_AUDIO_H
|
|
|
ad7ee3 |
+#define __VDAGENT_AUDIO_H
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+#include <stdio.h>
|
|
|
ad7ee3 |
+#include <stdint.h>
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+void vdagent_audio_playback_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume);
|
|
|
ad7ee3 |
+void vdagent_audio_record_sync(uint8_t mute, uint8_t nchannels, uint16_t *volume);
|
|
|
ad7ee3 |
+
|
|
|
ad7ee3 |
+#endif
|