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
---
client/marshallers.h | 2 +
common/messages.h | 9 +++
server/snd_worker.c | 173 +++++++++++++++++++++++++++++++++++++++++++++++++-
server/spice.h | 10 ++-
spice.proto | 13 ++++
5 files changed, 202 insertions(+), 5 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..f64ff41 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -268,7 +268,7 @@ struct SpiceTabletInstance {
#define SPICE_INTERFACE_PLAYBACK "playback"
#define SPICE_INTERFACE_PLAYBACK_MAJOR 1
-#define SPICE_INTERFACE_PLAYBACK_MINOR 1
+#define SPICE_INTERFACE_PLAYBACK_MINOR 2
typedef struct SpicePlaybackInterface SpicePlaybackInterface;
typedef struct SpicePlaybackInstance SpicePlaybackInstance;
typedef struct SpicePlaybackState SpicePlaybackState;
@@ -296,10 +296,13 @@ 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
-#define SPICE_INTERFACE_RECORD_MINOR 1
+#define SPICE_INTERFACE_RECORD_MINOR 2
typedef struct SpiceRecordInterface SpiceRecordInterface;
typedef struct SpiceRecordInstance SpiceRecordInstance;
typedef struct SpiceRecordState SpiceRecordState;
@@ -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
_______________________________________________
Spice-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/spice-devel