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

_______________________________________________
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/spice-devel

Reply via email to