]> Git Repo - qemu.git/blame - audio/spiceaudio.c
spice: add audio
[qemu.git] / audio / spiceaudio.c
CommitLineData
3e313753
GH
1#include "hw/hw.h"
2#include "qemu-timer.h"
3#include "ui/qemu-spice.h"
4
5#define AUDIO_CAP "spice"
6#include "audio.h"
7#include "audio_int.h"
8
9#define LINE_IN_SAMPLES 1024
10#define LINE_OUT_SAMPLES 1024
11
12typedef struct SpiceRateCtl {
13 int64_t start_ticks;
14 int64_t bytes_sent;
15} SpiceRateCtl;
16
17typedef struct SpiceVoiceOut {
18 HWVoiceOut hw;
19 SpicePlaybackInstance sin;
20 SpiceRateCtl rate;
21 int active;
22 uint32_t *frame;
23 uint32_t *fpos;
24 uint32_t fsize;
25} SpiceVoiceOut;
26
27typedef struct SpiceVoiceIn {
28 HWVoiceIn hw;
29 SpiceRecordInstance sin;
30 SpiceRateCtl rate;
31 int active;
32 uint32_t samples[LINE_IN_SAMPLES];
33} SpiceVoiceIn;
34
35static const SpicePlaybackInterface playback_sif = {
36 .base.type = SPICE_INTERFACE_PLAYBACK,
37 .base.description = "playback",
38 .base.major_version = SPICE_INTERFACE_PLAYBACK_MAJOR,
39 .base.minor_version = SPICE_INTERFACE_PLAYBACK_MINOR,
40};
41
42static const SpiceRecordInterface record_sif = {
43 .base.type = SPICE_INTERFACE_RECORD,
44 .base.description = "record",
45 .base.major_version = SPICE_INTERFACE_RECORD_MAJOR,
46 .base.minor_version = SPICE_INTERFACE_RECORD_MINOR,
47};
48
49static void *spice_audio_init (void)
50{
51 if (!using_spice) {
52 return NULL;
53 }
54 return &spice_audio_init;
55}
56
57static void spice_audio_fini (void *opaque)
58{
59 /* nothing */
60}
61
62static void rate_start (SpiceRateCtl *rate)
63{
64 memset (rate, 0, sizeof (*rate));
65 rate->start_ticks = qemu_get_clock (vm_clock);
66}
67
68static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
69{
70 int64_t now;
71 int64_t ticks;
72 int64_t bytes;
73 int64_t samples;
74
75 now = qemu_get_clock (vm_clock);
76 ticks = now - rate->start_ticks;
77 bytes = muldiv64 (ticks, info->bytes_per_second, get_ticks_per_sec ());
78 samples = (bytes - rate->bytes_sent) >> info->shift;
79 if (samples < 0 || samples > 65536) {
80 fprintf (stderr, "Resetting rate control (%" PRId64 " samples)\n", samples);
81 rate_start (rate);
82 samples = 0;
83 }
84 rate->bytes_sent += samples << info->shift;
85 return samples;
86}
87
88/* playback */
89
90static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
91{
92 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
93 struct audsettings settings;
94
95 settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ;
96 settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN;
97 settings.fmt = AUD_FMT_S16;
98 settings.endianness = AUDIO_HOST_ENDIANNESS;
99
100 audio_pcm_init_info (&hw->info, &settings);
101 hw->samples = LINE_OUT_SAMPLES;
102 out->active = 0;
103
104 out->sin.base.sif = &playback_sif.base;
105 qemu_spice_add_interface (&out->sin.base);
106 return 0;
107}
108
109static void line_out_fini (HWVoiceOut *hw)
110{
111 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
112
113 spice_server_remove_interface (&out->sin.base);
114}
115
116static int line_out_run (HWVoiceOut *hw, int live)
117{
118 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
119 int rpos, decr;
120 int samples;
121
122 if (!live) {
123 return 0;
124 }
125
126 decr = rate_get_samples (&hw->info, &out->rate);
127 decr = audio_MIN (live, decr);
128
129 samples = decr;
130 rpos = hw->rpos;
131 while (samples) {
132 int left_till_end_samples = hw->samples - rpos;
133 int len = audio_MIN (samples, left_till_end_samples);
134
135 if (!out->frame) {
136 spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
137 out->fpos = out->frame;
138 }
139 if (out->frame) {
140 len = audio_MIN (len, out->fsize);
141 hw->clip (out->fpos, hw->mix_buf + rpos, len);
142 out->fsize -= len;
143 out->fpos += len;
144 if (out->fsize == 0) {
145 spice_server_playback_put_samples (&out->sin, out->frame);
146 out->frame = out->fpos = NULL;
147 }
148 }
149 rpos = (rpos + len) % hw->samples;
150 samples -= len;
151 }
152 hw->rpos = rpos;
153 return decr;
154}
155
156static int line_out_write (SWVoiceOut *sw, void *buf, int len)
157{
158 return audio_pcm_sw_write (sw, buf, len);
159}
160
161static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
162{
163 SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
164
165 switch (cmd) {
166 case VOICE_ENABLE:
167 if (out->active) {
168 break;
169 }
170 out->active = 1;
171 rate_start (&out->rate);
172 spice_server_playback_start (&out->sin);
173 break;
174 case VOICE_DISABLE:
175 if (!out->active) {
176 break;
177 }
178 out->active = 0;
179 if (out->frame) {
180 memset (out->fpos, 0, out->fsize << 2);
181 spice_server_playback_put_samples (&out->sin, out->frame);
182 out->frame = out->fpos = NULL;
183 }
184 spice_server_playback_stop (&out->sin);
185 break;
186 }
187 return 0;
188}
189
190/* record */
191
192static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
193{
194 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
195 struct audsettings settings;
196
197 settings.freq = SPICE_INTERFACE_RECORD_FREQ;
198 settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
199 settings.fmt = AUD_FMT_S16;
200 settings.endianness = AUDIO_HOST_ENDIANNESS;
201
202 audio_pcm_init_info (&hw->info, &settings);
203 hw->samples = LINE_IN_SAMPLES;
204 in->active = 0;
205
206 in->sin.base.sif = &record_sif.base;
207 qemu_spice_add_interface (&in->sin.base);
208 return 0;
209}
210
211static void line_in_fini (HWVoiceIn *hw)
212{
213 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
214
215 spice_server_remove_interface (&in->sin.base);
216}
217
218static int line_in_run (HWVoiceIn *hw)
219{
220 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
221 int num_samples;
222 int ready;
223 int len[2];
224 uint64_t delta_samp;
225 const uint32_t *samples;
226
227 if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
228 return 0;
229 }
230
231 delta_samp = rate_get_samples (&hw->info, &in->rate);
232 num_samples = audio_MIN (num_samples, delta_samp);
233
234 ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
235 samples = in->samples;
236 if (ready == 0) {
237 static const uint32_t silence[LINE_IN_SAMPLES];
238 samples = silence;
239 ready = LINE_IN_SAMPLES;
240 }
241
242 num_samples = audio_MIN (ready, num_samples);
243
244 if (hw->wpos + num_samples > hw->samples) {
245 len[0] = hw->samples - hw->wpos;
246 len[1] = num_samples - len[0];
247 } else {
248 len[0] = num_samples;
249 len[1] = 0;
250 }
251
252 hw->conv (hw->conv_buf + hw->wpos, samples, len[0], &nominal_volume);
253
254 if (len[1]) {
255 hw->conv (hw->conv_buf, samples + len[0], len[1],
256 &nominal_volume);
257 }
258
259 hw->wpos = (hw->wpos + num_samples) % hw->samples;
260
261 return num_samples;
262}
263
264static int line_in_read (SWVoiceIn *sw, void *buf, int size)
265{
266 return audio_pcm_sw_read (sw, buf, size);
267}
268
269static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
270{
271 SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
272
273 switch (cmd) {
274 case VOICE_ENABLE:
275 if (in->active) {
276 break;
277 }
278 in->active = 1;
279 rate_start (&in->rate);
280 spice_server_record_start (&in->sin);
281 break;
282 case VOICE_DISABLE:
283 if (!in->active) {
284 break;
285 }
286 in->active = 0;
287 spice_server_record_stop (&in->sin);
288 break;
289 }
290 return 0;
291}
292
293static struct audio_option audio_options[] = {
294 { /* end of list */ },
295};
296
297static struct audio_pcm_ops audio_callbacks = {
298 .init_out = line_out_init,
299 .fini_out = line_out_fini,
300 .run_out = line_out_run,
301 .write = line_out_write,
302 .ctl_out = line_out_ctl,
303
304 .init_in = line_in_init,
305 .fini_in = line_in_fini,
306 .run_in = line_in_run,
307 .read = line_in_read,
308 .ctl_in = line_in_ctl,
309};
310
311struct audio_driver spice_audio_driver = {
312 .name = "spice",
313 .descr = "spice audio driver",
314 .options = audio_options,
315 .init = spice_audio_init,
316 .fini = spice_audio_fini,
317 .pcm_ops = &audio_callbacks,
318 .max_voices_out = 1,
319 .max_voices_in = 1,
320 .voice_size_out = sizeof (SpiceVoiceOut),
321 .voice_size_in = sizeof (SpiceVoiceIn),
322};
323
324void qemu_spice_audio_init (void)
325{
326 spice_audio_driver.can_be_default = 1;
327}
This page took 0.055381 seconds and 4 git commands to generate.