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 --- client/marshallers.h | 2 + common/messages.h | 9 +++ server/snd_worker.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++- server/spice.h | 6 ++ spice.proto | 13 ++++ 5 files changed, 199 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..c8773c1 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; @@ -1242,16 +1396,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 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel