qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [RfC PATCH 11/11] spice: add audio


From: Gerd Hoffmann
Subject: [Qemu-devel] [RfC PATCH 11/11] spice: add audio
Date: Wed, 14 Apr 2010 11:55:22 +0200

---
 Makefile.objs      |    1 +
 audio/audio.c      |    3 +
 audio/audio_int.h  |    1 +
 audio/spiceaudio.c |  315 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 320 insertions(+), 0 deletions(-)
 create mode 100644 audio/spiceaudio.c

diff --git a/Makefile.objs b/Makefile.objs
index ab1af88..b11db4c 100644
--- a/Makefile.objs
+++ b/Makefile.objs
@@ -84,6 +84,7 @@ common-obj-$(CONFIG_POSIX) += migration-exec.o 
migration-unix.o migration-fd.o
 audio-obj-y = audio.o noaudio.o wavaudio.o mixeng.o
 audio-obj-$(CONFIG_SDL) += sdlaudio.o
 audio-obj-$(CONFIG_OSS) += ossaudio.o
+audio-obj-$(CONFIG_SPICE) += spiceaudio.o
 audio-obj-$(CONFIG_COREAUDIO) += coreaudio.o
 audio-obj-$(CONFIG_ALSA) += alsaaudio.o
 audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o
diff --git a/audio/audio.c b/audio/audio.c
index dbf0b96..67fc1d3 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -45,6 +45,9 @@
 */
 static struct audio_driver *drvtab[] = {
     CONFIG_AUDIO_DRIVERS
+#ifdef CONFIG_SPICE
+    &spice_audio_driver,
+#endif
     &no_audio_driver,
     &wav_audio_driver
 };
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 06e313f..d1f6c2d 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -209,6 +209,7 @@ extern struct audio_driver coreaudio_audio_driver;
 extern struct audio_driver dsound_audio_driver;
 extern struct audio_driver esd_audio_driver;
 extern struct audio_driver pa_audio_driver;
+extern struct audio_driver spice_audio_driver;
 extern struct audio_driver winwave_audio_driver;
 extern struct mixeng_volume nominal_volume;
 
diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c
new file mode 100644
index 0000000..0e18f2f
--- /dev/null
+++ b/audio/spiceaudio.c
@@ -0,0 +1,315 @@
+#include "hw/hw.h"
+#include "qemu-timer.h"
+#include "qemu-spice.h"
+
+#define AUDIO_CAP "spice"
+#include "audio.h"
+#include "audio_int.h"
+
+#define LINE_IN_SAMPLES 1024
+#define LINE_OUT_SAMPLES 1024
+
+typedef struct SpiceVoiceOut {
+    HWVoiceOut            hw;
+    SpicePlaybackInstance sin;
+    uint32_t              *frame;
+    uint32_t              *fpos;
+    uint32_t              fsize;
+    uint64_t              prev_ticks;
+    int                   active;
+} SpiceVoiceOut;
+
+typedef struct SpiceVoiceIn {
+    HWVoiceIn             hw;
+    SpiceRecordInstance   sin;
+    uint64_t              prev_ticks;
+    int                   active;
+    uint32_t              samples[LINE_IN_SAMPLES];
+} SpiceVoiceIn;
+
+static SpicePlaybackInterface playback_sif = {
+    .base.type          = SPICE_INTERFACE_PLAYBACK,
+    .base.description   = "playback",
+    .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
+    .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
+};
+
+static SpiceRecordInterface record_sif = {
+    .base.type          = SPICE_INTERFACE_RECORD,
+    .base.description   = "record",
+    .base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
+    .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
+};
+
+static void *spice_audio_init(void)
+{
+    if (!using_spice)
+        return NULL;
+    return qemu_malloc(42);
+}
+
+static void spice_audio_fini(void *opaque)
+{
+    qemu_free(opaque);
+}
+
+static uint64_t get_monotonic_time(void)
+{
+    struct timespec time_space;
+    clock_gettime(CLOCK_MONOTONIC, &time_space);
+    return (uint64_t)time_space.tv_sec * 1000 * 1000 * 1000 + 
time_space.tv_nsec;
+}
+
+/* playback */
+
+static int line_out_init(HWVoiceOut *hw, struct audsettings *as)
+{
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+    struct audsettings settings;
+
+    settings.freq       = SPICE_INTERFACE_PLAYBACK_FREQ;
+    settings.nchannels  = SPICE_INTERFACE_PLAYBACK_CHAN;
+    settings.fmt        = AUD_FMT_S16;
+    settings.endianness = AUDIO_HOST_ENDIANNESS;
+
+    audio_pcm_init_info(&hw->info, &settings);
+    hw->samples = LINE_OUT_SAMPLES;
+    out->active = 0;
+
+    out->sin.base.sif = &playback_sif.base;
+    spice_server_add_interface(spice_server, &out->sin.base);
+    return 0;
+}
+
+static void line_out_fini(HWVoiceOut *hw)
+{
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+
+    spice_server_remove_interface(&out->sin.base);
+}
+
+static int line_out_run(HWVoiceOut *hw, int live)
+{
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+    int rpos, decr;
+    int samples;
+    uint64_t now;
+    uint64_t ticks;
+    uint64_t bytes;
+
+    if (!live) {
+        return 0;
+    }
+
+    now = get_monotonic_time();
+    ticks = now - out->prev_ticks;
+    bytes = (ticks * hw->info.bytes_per_second) / (1000 * 1000 * 1000);
+    out->prev_ticks = now;
+
+    decr = (bytes > INT_MAX)
+        ? INT_MAX >> hw->info.shift
+        : (bytes + (1 << (hw->info.shift - 1))) >> hw->info.shift;
+    decr = audio_MIN(live, decr);
+
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int len = audio_MIN(samples, left_till_end_samples);
+
+        if (!out->frame) {
+            spice_server_playback_get_buffer(&out->sin, &out->frame, 
&out->fsize);
+            out->fpos = out->frame;
+        }
+        if (out->frame) {
+            len = audio_MIN(len, out->fsize);
+            hw->clip(out->fpos, hw->mix_buf + rpos, len);
+            out->fsize -= len;
+            out->fpos  += len;
+            if (out->fsize == 0) {
+                spice_server_playback_put_samples(&out->sin, out->frame);
+                out->frame = out->fpos = NULL;
+            }
+        }
+        rpos = (rpos + len) % hw->samples;
+        samples -= len;
+    }
+    hw->rpos = rpos;
+    return decr;
+}
+
+static int line_out_write(SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write(sw, buf, len);
+}
+
+static int line_out_ctl(HWVoiceOut *hw, int cmd, ...)
+{
+    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        if (out->active) {
+            break;
+        }
+        out->active = 1;
+        out->prev_ticks = get_monotonic_time();
+        spice_server_playback_start(&out->sin);
+        break;
+    case VOICE_DISABLE:
+        if (!out->active) {
+            break;
+        }
+        out->active = 0;
+        if (out->frame) {
+            memset(out->fpos, 0, out->fsize << 2);
+            spice_server_playback_put_samples(&out->sin, out->frame);
+            out->frame = out->fpos = NULL;
+            spice_server_playback_stop(&out->sin);
+        }
+        break;
+    }
+    return 0;
+}
+
+/* record */
+
+static int line_in_init(HWVoiceIn *hw, struct audsettings *as)
+{
+    SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
+    struct audsettings settings;
+
+    settings.freq       = SPICE_INTERFACE_RECORD_FREQ;
+    settings.nchannels  = SPICE_INTERFACE_RECORD_CHAN;
+    settings.fmt        = AUD_FMT_S16;
+    settings.endianness = AUDIO_HOST_ENDIANNESS;
+
+    audio_pcm_init_info(&hw->info, &settings);
+    hw->samples = LINE_IN_SAMPLES;
+    in->active = 0;
+
+    in->sin.base.sif = &record_sif.base;
+    spice_server_add_interface(spice_server, &in->sin.base);
+    return 0;
+}
+
+static void line_in_fini(HWVoiceIn *hw)
+{
+    SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
+
+    spice_server_remove_interface(&in->sin.base);
+}
+
+static int line_in_run(HWVoiceIn *hw)
+{
+    SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
+    int num_samples;
+    int ready;
+    int len[2];
+    uint64_t now;
+    uint64_t ticks;
+    uint64_t delta_samp;
+    uint32_t *samples;
+
+    if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in(hw))) {
+        return 0;
+    }
+
+    now = get_monotonic_time();
+    ticks = now - in->prev_ticks;
+    in->prev_ticks = now;
+    delta_samp = (ticks * hw->info.bytes_per_second) / (1000 * 1000 * 1000);
+    delta_samp = (delta_samp + (1 << (hw->info.shift - 1))) >> hw->info.shift;
+
+    num_samples = audio_MIN(num_samples, delta_samp);
+
+    ready = spice_server_record_get_samples(&in->sin, in->samples, 
num_samples);
+    samples = in->samples;
+    if (ready == 0) {
+        static uint32_t silence[LINE_IN_SAMPLES];
+        samples = silence;
+        ready = LINE_IN_SAMPLES;
+    }
+
+    num_samples = audio_MIN(ready, num_samples);
+
+    if (hw->wpos + num_samples > hw->samples) {
+        len[0] = hw->samples - hw->wpos;
+        len[1] = num_samples - len[0];
+    } else {
+        len[0] = num_samples;
+        len[1] = 0;
+    }
+
+    hw->conv(hw->conv_buf + hw->wpos, samples, len[0], &nominal_volume);
+
+    if (len[1]) {
+        hw->conv(hw->conv_buf, samples + len[0], len[1],
+                 &nominal_volume);
+    }
+
+    hw->wpos = (hw->wpos + num_samples) % hw->samples;
+
+    return num_samples;
+}
+
+static int line_in_read(SWVoiceIn *sw, void *buf, int size)
+{
+    return audio_pcm_sw_read(sw, buf, size);
+}
+
+static int line_in_ctl(HWVoiceIn *hw, int cmd, ...)
+{
+    SpiceVoiceIn *in = container_of(hw, SpiceVoiceIn, hw);
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        if (in->active) {
+            break;
+        }
+        in->active = 1;
+        in->prev_ticks = get_monotonic_time();
+        spice_server_record_start(&in->sin);
+        break;
+    case VOICE_DISABLE:
+        if (!in->active) {
+            break;
+        }
+        in->active = 0;
+        spice_server_record_stop(&in->sin);
+        break;
+    }
+    return 0;
+}
+
+static struct audio_option audio_options[] = {
+    { /* end of list */ },
+};
+
+static struct audio_pcm_ops audio_callbacks = {
+    .init_out = line_out_init,
+    .fini_out = line_out_fini,
+    .run_out  = line_out_run,
+    .write    = line_out_write,
+    .ctl_out  = line_out_ctl,
+
+    .init_in  = line_in_init,
+    .fini_in  = line_in_fini,
+    .run_in   = line_in_run,
+    .read     = line_in_read,
+    .ctl_in   = line_in_ctl,
+};
+
+struct audio_driver spice_audio_driver = {
+    .name           = "spice",
+    .descr          = "spice audio driver",
+    .options        = audio_options,
+    .init           = spice_audio_init,
+    .fini           = spice_audio_fini,
+    .pcm_ops        = &audio_callbacks,
+    .can_be_default = 1,
+    .max_voices_out = 1,
+    .max_voices_in  = 1,
+    .voice_size_out = sizeof(SpiceVoiceOut),
+    .voice_size_in  = sizeof(SpiceVoiceIn),
+};
-- 
1.6.6.1





reply via email to

[Prev in Thread] Current Thread [Next in Thread]