From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Victor Toso Date: Fri, 6 Mar 2015 18:14:21 +0100 Subject: [PATCH] audio: add functions to set volume/mute with alsa This patch includes the vdagent-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 | 12 +++- configure.ac | 1 + src/vdagent-audio.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/vdagent-audio.h | 27 +++++++++ 4 files changed, 205 insertions(+), 3 deletions(-) create mode 100644 src/vdagent-audio.c create mode 100644 src/vdagent-audio.h diff --git a/Makefile.am b/Makefile.am index a06a259..2effe4e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4,9 +4,14 @@ NULL = bin_PROGRAMS = src/spice-vdagent sbin_PROGRAMS = src/spice-vdagentd -src_spice_vdagent_CFLAGS = $(X_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) -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_vdagent_CFLAGS = $(X_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(ALSA_CFLAGS) +src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(ALSA_LIBS) +src_spice_vdagent_SOURCES = src/vdagent.c \ + src/vdagent-x11.c \ + src/vdagent-x11-randr.c \ + src/vdagent-file-xfers.c \ + src/vdagent-audio.c \ + src/udscs.c src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(LIBSYSTEMD_LOGIN_CFLAGS) \ $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS) @@ -30,6 +35,7 @@ endif noinst_HEADERS = src/glib-compat.h \ src/session-info.h \ src/udscs.h \ + src/vdagent-audio.h \ src/vdagent-file-xfers.h \ src/vdagent-virtio-port.h \ src/vdagent-x11.h \ diff --git a/configure.ac b/configure.ac index a1ce6c0..377fc74 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/vdagent-audio.c b/src/vdagent-audio.c new file mode 100644 index 0000000..6b11cd8 --- /dev/null +++ b/src/vdagent-audio.c @@ -0,0 +1,168 @@ +/* vdagent-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 . +*/ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include "vdagent-audio.h" + +#define ALSA_MUTE 0 +#define ALSA_UNMUTE 1 + +static snd_mixer_elem_t * +get_alsa_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 set_alsa_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 = get_alsa_default_mixer_by_name (&handle, "Capture"); + if (e == NULL) { + syslog(LOG_WARNING, "vdagent-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, "vdagent-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, "vdagent-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, "vdagent-audio: (capture-right) %lu (%%%0.2f)", + vol, (float) (100*vol/max)); + break; + default: + syslog(LOG_WARNING, "vdagent-audio: number of channels not supported"); + ret = false; + } +end: + if (handle != NULL) + snd_mixer_close(handle); + return ret; +} + +static bool set_alsa_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 = get_alsa_default_mixer_by_name (&handle, "Master"); + if (e == NULL) { + syslog(LOG_WARNING, "vdagent-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, "vdagent-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, "vdagent-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, "vdagent-audio: (playback-right) %lu (%%%0.2f)", + vol, (float) (100*vol/max)); + break; + default: + syslog(LOG_WARNING, "vdagent-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 (set_alsa_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 (set_alsa_capture (mute, nchannels, volume) == false) + syslog(LOG_WARNING, "Fail to sync record volume"); +} diff --git a/src/vdagent-audio.h b/src/vdagent-audio.h new file mode 100644 index 0000000..6f29d4b --- /dev/null +++ b/src/vdagent-audio.h @@ -0,0 +1,27 @@ +/* vdagent-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 . +*/ +#ifndef __VDAGENT_AUDIO_H +#define __VDAGENT_AUDIO_H + +#include +#include + +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