[Spice-devel] [PATCH v2] sndworker: add AudioVolume/AudioMute messages
Marc-André Lureau
marcandre.lureau at gmail.com
Mon Jun 20 14:40:01 PDT 2011
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 v1:
- sync record volume on connection too
---
client/marshallers.h | 2 +
common/messages.h | 9 +++
server/snd_worker.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++-
server/spice.h | 6 ++
spice.proto | 13 ++++
5 files changed, 200 insertions(+), 3 deletions(-)
diff --git a/client/marshallers.h b/client/marshallers.h
index c913a28..47faeff 100644
--- a/client/marshallers.h
+++ b/client/marshallers.h
@@ -26,6 +26,8 @@
typedef struct {
void (*msg_SpiceMsgEmpty)(SpiceMarshaller *m, SpiceMsgEmpty *msg);
void (*msg_SpiceMsgData)(SpiceMarshaller *m, SpiceMsgData *msg);
+ void (*msg_SpiceMsgAudioVolume)(SpiceMarshaller *m, SpiceMsgAudioVolume *msg);
+ void (*msg_SpiceMsgAudioMute)(SpiceMarshaller *m, SpiceMsgAudioMute *msg);
void (*msgc_ack_sync)(SpiceMarshaller *m, SpiceMsgcAckSync *msg);
void (*msgc_pong)(SpiceMarshaller *m, SpiceMsgPing *msg);
void (*msgc_disconnecting)(SpiceMarshaller *m, SpiceMsgDisconnect *msg);
diff --git a/common/messages.h b/common/messages.h
index 6fcd8be..8151dc0 100644
--- a/common/messages.h
+++ b/common/messages.h
@@ -369,6 +369,15 @@ typedef struct SpiceMsgcMouseRelease {
int32_t buttons_state;
} SpiceMsgcMouseRelease;
+typedef struct SpiceMsgAudioVolume {
+ uint8_t nchannels;
+ uint16_t volume[0];
+} SpiceMsgAudioVolume;
+
+typedef struct SpiceMsgAudioMute {
+ uint8_t mute;
+} SpiceMsgAudioMute;
+
typedef struct SpiceMsgPlaybackMode {
uint32_t time;
uint32_t mode; //SPICE_AUDIO_DATA_MODE_?
diff --git a/server/snd_worker.c b/server/snd_worker.c
index 8da11e1..e4cefb1 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
@@ -193,7 +205,6 @@ static void snd_disconnect_channel(SndChannel *channel)
channel->stream->watch = NULL;
reds_stream_free(channel->stream);
spice_marshaller_destroy(channel->send_data.marshaller);
- free(channel);
}
static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
@@ -508,6 +519,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 +648,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 +779,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 +811,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 +912,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 +1040,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 +1128,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 +1241,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 +1397,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)
diff --git a/server/spice.h b/server/spice.h
index 425d586..628a0a3 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -296,6 +296,9 @@ void spice_server_playback_get_buffer(SpicePlaybackInstance *sin,
uint32_t **samples, uint32_t *nsamples);
void spice_server_playback_put_samples(SpicePlaybackInstance *sin,
uint32_t *samples);
+void spice_server_playback_set_volume(SpicePlaybackInstance *sin,
+ uint8_t nchannels, uint16_t *volume);
+void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute);
#define SPICE_INTERFACE_RECORD "record"
#define SPICE_INTERFACE_RECORD_MAJOR 2
@@ -321,6 +324,9 @@ void spice_server_record_start(SpiceRecordInstance *sin);
void spice_server_record_stop(SpiceRecordInstance *sin);
uint32_t spice_server_record_get_samples(SpiceRecordInstance *sin,
uint32_t *samples, uint32_t bufsize);
+void spice_server_record_set_volume(SpiceRecordInstance *sin,
+ uint8_t nchannels, uint16_t *volume);
+void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute);
/* char device interfaces */
diff --git a/spice.proto b/spice.proto
index 6160de1..80c40d4 100644
--- a/spice.proto
+++ b/spice.proto
@@ -926,6 +926,15 @@ enum16 audio_fmt {
S16,
};
+message AudioVolume {
+ uint8 nchannels;
+ uint16 volume[nchannels] @end;
+};
+
+message AudioMute {
+ uint8 mute;
+};
+
channel PlaybackChannel : BaseChannel {
server:
message {
@@ -947,6 +956,8 @@ channel PlaybackChannel : BaseChannel {
} start;
Empty stop;
+ AudioVolume volume;
+ AudioMute mute;
};
channel RecordChannel : BaseChannel {
@@ -958,6 +969,8 @@ channel RecordChannel : BaseChannel {
} start = 101;
Empty stop;
+ AudioVolume volume;
+ AudioMute mute;
client:
message {
uint32 time;
--
1.7.5.2
More information about the Spice-devel
mailing list