summaryrefslogtreecommitdiff
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
commitf5f5fde2be14d540dce574e7acc1fdb81e30af5e (patch)
tree1ebe1ea41c8d7d75eba80af0c2366088d1b056db
parent6d741c9c2ade99965e7eb153611b0ec0dd37be94 (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
-rw-r--r--client/marshallers.h2
-rw-r--r--server/snd_worker.c172
-rw-r--r--server/spice.h10
3 files changed, 180 insertions, 4 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/server/snd_worker.c b/server/snd_worker.c
index 8da11e1..182eb3c 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)
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 */