2
* Copyright (C) 2010 Red Hat, Inc.
4
* maintained by Gerd Hoffmann <kraxel@redhat.com>
6
* This program is free software; you can redistribute it and/or
7
* modify it under the terms of the GNU General Public License as
8
* published by the Free Software Foundation; either version 2 or
9
* (at your option) version 3 of the License.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, see <http://www.gnu.org/licenses/>.
21
#include "qemu-timer.h"
22
#include "ui/qemu-spice.h"
24
#define AUDIO_CAP "spice"
26
#include "audio_int.h"
28
#define LINE_IN_SAMPLES 1024
29
#define LINE_OUT_SAMPLES 1024
31
typedef struct SpiceRateCtl {
36
typedef struct SpiceVoiceOut {
38
SpicePlaybackInstance sin;
46
typedef struct SpiceVoiceIn {
48
SpiceRecordInstance sin;
51
uint32_t samples[LINE_IN_SAMPLES];
54
static const SpicePlaybackInterface playback_sif = {
55
.base.type = SPICE_INTERFACE_PLAYBACK,
56
.base.description = "playback",
57
.base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
58
.base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
61
static const SpiceRecordInterface record_sif = {
62
.base.type = SPICE_INTERFACE_RECORD,
63
.base.description = "record",
64
.base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
65
.base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
68
static void *spice_audio_init (void)
73
return &spice_audio_init;
76
static void spice_audio_fini (void *opaque)
81
static void rate_start (SpiceRateCtl *rate)
83
memset (rate, 0, sizeof (*rate));
84
rate->start_ticks = qemu_get_clock_ns (vm_clock);
87
static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
94
now = qemu_get_clock_ns (vm_clock);
95
ticks = now - rate->start_ticks;
96
bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
97
samples = (bytes - rate->bytes_sent) >> info->shift;
98
if (samples < 0 || samples > 65536) {
99
fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
103
rate->bytes_sent += samples << info->shift;
109
static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
111
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
112
struct audsettings settings;
114
settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
115
settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
116
settings.fmt = AUD_FMT_S16;
117
settings.endianness = AUDIO_HOST_ENDIANNESS;
119
audio_pcm_init_info (&hw->info, &settings);
120
hw->samples = LINE_OUT_SAMPLES;
123
out->sin.base.sif = &playback_sif.base;
124
qemu_spice_add_interface (&out->sin.base);
128
static void line_out_fini (HWVoiceOut *hw)
130
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
132
spice_server_remove_interface (&out->sin.base);
135
static int line_out_run (HWVoiceOut *hw, int live)
137
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
145
decr = rate_get_samples (&hw->info, &out->rate);
146
decr = audio_MIN (live, decr);
151
int left_till_end_samples = hw->samples - rpos;
152
int len = audio_MIN (samples, left_till_end_samples);
155
spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
156
out->fpos = out->frame;
159
len = audio_MIN (len, out->fsize);
160
hw->clip (out->fpos, hw->mix_buf + rpos, len);
163
if (out->fsize == 0) {
164
spice_server_playback_put_samples (&out->sin, out->frame);
165
out->frame = out->fpos = NULL;
168
rpos = (rpos + len) % hw->samples;
175
static int line_out_write (SWVoiceOut *sw, void *buf, int len)
177
return audio_pcm_sw_write (sw, buf, len);
180
static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
182
SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
190
rate_start (&out->rate);
191
spice_server_playback_start (&out->sin);
199
memset (out->fpos, 0, out->fsize << 2);
200
spice_server_playback_put_samples (&out->sin, out->frame);
201
out->frame = out->fpos = NULL;
203
spice_server_playback_stop (&out->sin);
211
static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
213
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
214
struct audsettings settings;
216
settings.freq = SPICE_INTERFACE_RECORD_FREQ;
217
settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
218
settings.fmt = AUD_FMT_S16;
219
settings.endianness = AUDIO_HOST_ENDIANNESS;
221
audio_pcm_init_info (&hw->info, &settings);
222
hw->samples = LINE_IN_SAMPLES;
225
in->sin.base.sif = &record_sif.base;
226
qemu_spice_add_interface (&in->sin.base);
230
static void line_in_fini (HWVoiceIn *hw)
232
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
234
spice_server_remove_interface (&in->sin.base);
237
static int line_in_run (HWVoiceIn *hw)
239
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
244
const uint32_t *samples;
246
if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
250
delta_samp = rate_get_samples (&hw->info, &in->rate);
251
num_samples = audio_MIN (num_samples, delta_samp);
253
ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
254
samples = in->samples;
256
static const uint32_t silence[LINE_IN_SAMPLES];
258
ready = LINE_IN_SAMPLES;
261
num_samples = audio_MIN (ready, num_samples);
263
if (hw->wpos + num_samples > hw->samples) {
264
len[0] = hw->samples - hw->wpos;
265
len[1] = num_samples - len[0];
267
len[0] = num_samples;
271
hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
274
hw->conv (hw->conv_buf, samples + len[0], len[1]);
277
hw->wpos = (hw->wpos + num_samples) % hw->samples;
282
static int line_in_read (SWVoiceIn *sw, void *buf, int size)
284
return audio_pcm_sw_read (sw, buf, size);
287
static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
289
SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
297
rate_start (&in->rate);
298
spice_server_record_start (&in->sin);
305
spice_server_record_stop (&in->sin);
311
static struct audio_option audio_options[] = {
312
{ /* end of list */ },
315
static struct audio_pcm_ops audio_callbacks = {
316
.init_out = line_out_init,
317
.fini_out = line_out_fini,
318
.run_out = line_out_run,
319
.write = line_out_write,
320
.ctl_out = line_out_ctl,
322
.init_in = line_in_init,
323
.fini_in = line_in_fini,
324
.run_in = line_in_run,
325
.read = line_in_read,
326
.ctl_in = line_in_ctl,
329
struct audio_driver spice_audio_driver = {
331
.descr = "spice audio driver",
332
.options = audio_options,
333
.init = spice_audio_init,
334
.fini = spice_audio_fini,
335
.pcm_ops = &audio_callbacks,
338
.voice_size_out = sizeof (SpiceVoiceOut),
339
.voice_size_in = sizeof (SpiceVoiceIn),
342
void qemu_spice_audio_init (void)
344
spice_audio_driver.can_be_default = 1;