summaryrefslogtreecommitdiff
path: root/server/snd_worker.c
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2011-05-23 12:20:17 +0200
committerMarc-André Lureau <marcandre.lureau@redhat.com>2011-06-22 12:12:26 +0200
commitd1758b328811979beb58ff9ddb9cf4f318fa28f7 (patch)
tree7d2f66e34e762db3194eb86d5d6387e286217b35 /server/snd_worker.c
parent5ec91855d818078523653e79562921bcea9bef14 (diff)
sndworker: add AudioVolume/AudioMute messages
These messages allow the guest to send the audio device volume to the client. It uses an arbitrary scale of 16bits, which works good enough for now. Save VolumeState in {Playback,Record}State, so that we can send the current volume on channel connection. Note about future improvements: - add exact dB support - add client to guest volume change Updated since v2: - bumped record and playback interface minor version to allow conditional compilation Updated since v1: - sync record volume on connection too
Diffstat (limited to 'server/snd_worker.c')
-rw-r--r--server/snd_worker.c172
1 files changed, 170 insertions, 2 deletions
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 8da11e1a..182eb3c2 100644
--- a/server/snd_worker.c
+++ b/server/snd_worker.c
@@ -52,20 +52,24 @@ enum PlaybackeCommand {
SND_PLAYBACK_MODE,
SND_PLAYBACK_CTRL,
SND_PLAYBACK_PCM,
+ SND_PLAYBACK_VOLUME,
};
enum RecordCommand {
SND_RECORD_MIGRATE,
SND_RECORD_CTRL,
+ SND_RECORD_VOLUME,
};
#define SND_PLAYBACK_MIGRATE_MASK (1 << SND_PLAYBACK_MIGRATE)
#define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE)
#define SND_PLAYBACK_CTRL_MASK (1 << SND_PLAYBACK_CTRL)
#define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM)
+#define SND_PLAYBACK_VOLUME_MASK (1 << SND_PLAYBACK_VOLUME)
#define SND_RECORD_MIGRATE_MASK (1 << SND_RECORD_MIGRATE)
#define SND_RECORD_CTRL_MASK (1 << SND_RECORD_CTRL)
+#define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME)
typedef struct SndChannel SndChannel;
typedef void (*send_messages_proc)(void *in_channel);
@@ -141,14 +145,22 @@ struct SndWorker {
int active;
};
+typedef struct SpiceVolumeState {
+ uint8_t volume_nchannels;
+ uint16_t *volume;
+ int mute;
+} SpiceVolumeState;
+
struct SpicePlaybackState {
struct SndWorker worker;
SpicePlaybackInstance *sin;
+ SpiceVolumeState volume;
};
struct SpiceRecordState {
struct SndWorker worker;
SpiceRecordInstance *sin;
+ SpiceVolumeState volume;
};
#define RECORD_MIG_VERSION 1
@@ -508,6 +520,54 @@ static int snd_playback_send_migrate(PlaybackChannel *channel)
return snd_begin_send_message((SndChannel *)channel);
}
+static int snd_send_volume(SndChannel *channel, SpiceVolumeState *st, int msg)
+{
+ SpiceMsgAudioVolume *vol;
+ uint8_t c;
+
+ vol = alloca(sizeof (SpiceMsgAudioVolume) +
+ st->volume_nchannels * sizeof (uint16_t));
+ if (!snd_reset_send_data(channel, msg)) {
+ return FALSE;
+ }
+ vol->nchannels = st->volume_nchannels;
+ for (c = 0; c < st->volume_nchannels; ++c) {
+ vol->volume[c] = st->volume[c];
+ }
+ spice_marshall_SpiceMsgAudioVolume(channel->send_data.marshaller, vol);
+
+ return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_volume(PlaybackChannel *playback_channel)
+{
+ SndChannel *channel = &playback_channel->base;
+ SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
+
+ return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
+}
+
+static int snd_send_mute(SndChannel *channel, SpiceVolumeState *st, int msg)
+{
+ SpiceMsgAudioMute mute;
+
+ if (!snd_reset_send_data(channel, msg)) {
+ return FALSE;
+ }
+ mute.mute = st->mute;
+ spice_marshall_SpiceMsgAudioMute(channel->send_data.marshaller, &mute);
+
+ return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_mute(PlaybackChannel *playback_channel)
+{
+ SndChannel *channel = &playback_channel->base;
+ SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
+
+ return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
+}
+
static int snd_playback_send_start(PlaybackChannel *playback_channel)
{
SndChannel *channel = (SndChannel *)playback_channel;
@@ -589,6 +649,22 @@ static int snd_record_send_ctl(RecordChannel *record_channel)
}
}
+static int snd_record_send_volume(RecordChannel *record_channel)
+{
+ SndChannel *channel = &record_channel->base;
+ SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+
+ return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
+}
+
+static int snd_record_send_mute(RecordChannel *record_channel)
+{
+ SndChannel *channel = &record_channel->base;
+ SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+
+ return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
+}
+
static int snd_record_send_migrate(RecordChannel *record_channel)
{
SndChannel *channel = (SndChannel *)record_channel;
@@ -704,6 +780,13 @@ static void snd_playback_send(void* data)
}
channel->command &= ~SND_PLAYBACK_CTRL_MASK;
}
+ if (channel->command & SND_PLAYBACK_VOLUME_MASK) {
+ if (!snd_playback_send_volume(playback_channel) ||
+ !snd_playback_send_mute(playback_channel)) {
+ return;
+ }
+ channel->command &= ~SND_PLAYBACK_VOLUME_MASK;
+ }
if (channel->command & SND_PLAYBACK_MIGRATE_MASK) {
if (!snd_playback_send_migrate(playback_channel)) {
return;
@@ -729,6 +812,13 @@ static void snd_record_send(void* data)
}
channel->command &= ~SND_RECORD_CTRL_MASK;
}
+ if (channel->command & SND_RECORD_VOLUME_MASK) {
+ if (!snd_record_send_volume(record_channel) ||
+ !snd_record_send_mute(record_channel)) {
+ return;
+ }
+ channel->command &= ~SND_RECORD_VOLUME_MASK;
+ }
if (channel->command & SND_RECORD_MIGRATE_MASK) {
if (!snd_record_send_migrate(record_channel)) {
return;
@@ -823,6 +913,38 @@ static void snd_set_command(SndChannel *channel, uint32_t command)
channel->command |= command;
}
+__visible__ void spice_server_playback_set_volume(SpicePlaybackInstance *sin,
+ uint8_t nchannels,
+ uint16_t *volume)
+{
+ SpiceVolumeState *st = &sin->st->volume;
+ SndChannel *channel = sin->st->worker.connection;
+ PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+ st->volume_nchannels = nchannels;
+ free(st->volume);
+ st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
+
+ if (!channel)
+ return;
+
+ snd_playback_send_volume(playback_channel);
+}
+
+__visible__ void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute)
+{
+ SpiceVolumeState *st = &sin->st->volume;
+ SndChannel *channel = sin->st->worker.connection;
+ PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+ st->mute = mute;
+
+ if (!channel)
+ return;
+
+ snd_playback_send_mute(playback_channel);
+}
+
__visible__ void spice_server_playback_start(SpicePlaybackInstance *sin)
{
SndChannel *channel = sin->st->worker.connection;
@@ -919,6 +1041,7 @@ static void on_new_playback_channel(SndWorker *worker)
if (!playback_channel->base.migrate && playback_channel->base.active) {
snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_CTRL_MASK);
}
+ snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_VOLUME_MASK);
if (playback_channel->base.active) {
reds_disable_mm_timer();
}
@@ -1006,6 +1129,38 @@ static void snd_record_migrate(Channel *channel)
}
}
+__visible__ void spice_server_record_set_volume(SpiceRecordInstance *sin,
+ uint8_t nchannels,
+ uint16_t *volume)
+{
+ SpiceVolumeState *st = &sin->st->volume;
+ SndChannel *channel = sin->st->worker.connection;
+ RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+
+ st->volume_nchannels = nchannels;
+ free(st->volume);
+ st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
+
+ if (!channel)
+ return;
+
+ snd_record_send_volume(record_channel);
+}
+
+__visible__ void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute)
+{
+ SpiceVolumeState *st = &sin->st->volume;
+ SndChannel *channel = sin->st->worker.connection;
+ RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+
+ st->mute = mute;
+
+ if (!channel)
+ return;
+
+ snd_record_send_mute(record_channel);
+}
+
__visible__ void spice_server_record_start(SpiceRecordInstance *sin)
{
SndChannel *channel = sin->st->worker.connection;
@@ -1087,6 +1242,7 @@ static void on_new_record_channel(SndWorker *worker)
RecordChannel *record_channel = (RecordChannel *)worker->connection;
ASSERT(record_channel);
+ snd_set_command((SndChannel *)record_channel, SND_RECORD_VOLUME_MASK);
if (!record_channel->base.migrate) {
if (record_channel->base.active) {
snd_set_command((SndChannel *)record_channel, SND_RECORD_CTRL_MASK);
@@ -1242,16 +1398,28 @@ static void snd_detach_common(SndWorker *worker)
reds_channel_dispose(&worker->base);
}
+static void spice_playback_state_free(SpicePlaybackState *st)
+{
+ free(st->volume.volume);
+ free(st);
+}
+
void snd_detach_playback(SpicePlaybackInstance *sin)
{
snd_detach_common(&sin->st->worker);
- free(sin->st);
+ spice_playback_state_free(sin->st);
+}
+
+static void spice_record_state_free(SpiceRecordState *st)
+{
+ free(st->volume.volume);
+ free(st);
}
void snd_detach_record(SpiceRecordInstance *sin)
{
snd_detach_common(&sin->st->worker);
- free(sin->st);
+ spice_record_state_free(sin->st);
}
void snd_set_playback_compression(int on)