|
|
1.1 root 1: /*
2: * Copyright (C) 2010 Red Hat, Inc.
3: *
4: * maintained by Gerd Hoffmann <[email protected]>
5: *
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.
10: *
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.
15: *
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/>.
18: */
19:
20: #include "hw/hw.h"
21: #include "qemu-timer.h"
22: #include "ui/qemu-spice.h"
23:
24: #define AUDIO_CAP "spice"
25: #include "audio.h"
26: #include "audio_int.h"
27:
28: #define LINE_IN_SAMPLES 1024
29: #define LINE_OUT_SAMPLES 1024
30:
31: typedef struct SpiceRateCtl {
32: int64_t start_ticks;
33: int64_t bytes_sent;
34: } SpiceRateCtl;
35:
36: typedef struct SpiceVoiceOut {
37: HWVoiceOut hw;
38: SpicePlaybackInstance sin;
39: SpiceRateCtl rate;
40: int active;
41: uint32_t *frame;
42: uint32_t *fpos;
43: uint32_t fsize;
44: } SpiceVoiceOut;
45:
46: typedef struct SpiceVoiceIn {
47: HWVoiceIn hw;
48: SpiceRecordInstance sin;
49: SpiceRateCtl rate;
50: int active;
51: uint32_t samples[LINE_IN_SAMPLES];
52: } SpiceVoiceIn;
53:
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,
59: };
60:
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,
66: };
67:
68: static void *spice_audio_init (void)
69: {
70: if (!using_spice) {
71: return NULL;
72: }
73: return &spice_audio_init;
74: }
75:
76: static void spice_audio_fini (void *opaque)
77: {
78: /* nothing */
79: }
80:
81: static void rate_start (SpiceRateCtl *rate)
82: {
83: memset (rate, 0, sizeof (*rate));
1.1.1.2 root 84: rate->start_ticks = qemu_get_clock_ns (vm_clock);
1.1 root 85: }
86:
87: static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
88: {
89: int64_t now;
90: int64_t ticks;
91: int64_t bytes;
92: int64_t samples;
93:
1.1.1.2 root 94: now = qemu_get_clock_ns (vm_clock);
1.1 root 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);
100: rate_start (rate);
101: samples = 0;
102: }
103: rate->bytes_sent += samples << info->shift;
104: return samples;
105: }
106:
107: /* playback */
108:
109: static int line_out_init (HWVoiceOut *hw, struct audsettings *as)
110: {
111: SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
112: struct audsettings settings;
113:
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;
118:
119: audio_pcm_init_info (&hw->info, &settings);
120: hw->samples = LINE_OUT_SAMPLES;
121: out->active = 0;
122:
123: out->sin.base.sif = &playback_sif.base;
124: qemu_spice_add_interface (&out->sin.base);
125: return 0;
126: }
127:
128: static void line_out_fini (HWVoiceOut *hw)
129: {
130: SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
131:
132: spice_server_remove_interface (&out->sin.base);
133: }
134:
135: static int line_out_run (HWVoiceOut *hw, int live)
136: {
137: SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
138: int rpos, decr;
139: int samples;
140:
141: if (!live) {
142: return 0;
143: }
144:
145: decr = rate_get_samples (&hw->info, &out->rate);
146: decr = audio_MIN (live, decr);
147:
148: samples = decr;
149: rpos = hw->rpos;
150: while (samples) {
151: int left_till_end_samples = hw->samples - rpos;
152: int len = audio_MIN (samples, left_till_end_samples);
153:
154: if (!out->frame) {
155: spice_server_playback_get_buffer (&out->sin, &out->frame, &out->fsize);
156: out->fpos = out->frame;
157: }
158: if (out->frame) {
159: len = audio_MIN (len, out->fsize);
160: hw->clip (out->fpos, hw->mix_buf + rpos, len);
161: out->fsize -= len;
162: out->fpos += len;
163: if (out->fsize == 0) {
164: spice_server_playback_put_samples (&out->sin, out->frame);
165: out->frame = out->fpos = NULL;
166: }
167: }
168: rpos = (rpos + len) % hw->samples;
169: samples -= len;
170: }
171: hw->rpos = rpos;
172: return decr;
173: }
174:
175: static int line_out_write (SWVoiceOut *sw, void *buf, int len)
176: {
177: return audio_pcm_sw_write (sw, buf, len);
178: }
179:
180: static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
181: {
182: SpiceVoiceOut *out = container_of (hw, SpiceVoiceOut, hw);
183:
184: switch (cmd) {
185: case VOICE_ENABLE:
186: if (out->active) {
187: break;
188: }
189: out->active = 1;
190: rate_start (&out->rate);
191: spice_server_playback_start (&out->sin);
192: break;
193: case VOICE_DISABLE:
194: if (!out->active) {
195: break;
196: }
197: out->active = 0;
198: if (out->frame) {
199: memset (out->fpos, 0, out->fsize << 2);
200: spice_server_playback_put_samples (&out->sin, out->frame);
201: out->frame = out->fpos = NULL;
202: }
203: spice_server_playback_stop (&out->sin);
204: break;
1.1.1.3 ! root 205: case VOICE_VOLUME:
! 206: {
! 207: #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
! 208: SWVoiceOut *sw;
! 209: va_list ap;
! 210: uint16_t vol[2];
! 211:
! 212: va_start (ap, cmd);
! 213: sw = va_arg (ap, SWVoiceOut *);
! 214: va_end (ap);
! 215:
! 216: vol[0] = sw->vol.l / ((1ULL << 16) + 1);
! 217: vol[1] = sw->vol.r / ((1ULL << 16) + 1);
! 218: spice_server_playback_set_volume (&out->sin, 2, vol);
! 219: spice_server_playback_set_mute (&out->sin, sw->vol.mute);
! 220: #endif
! 221: break;
! 222: }
1.1 root 223: }
1.1.1.3 ! root 224:
1.1 root 225: return 0;
226: }
227:
228: /* record */
229:
230: static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
231: {
232: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
233: struct audsettings settings;
234:
235: settings.freq = SPICE_INTERFACE_RECORD_FREQ;
236: settings.nchannels = SPICE_INTERFACE_RECORD_CHAN;
237: settings.fmt = AUD_FMT_S16;
238: settings.endianness = AUDIO_HOST_ENDIANNESS;
239:
240: audio_pcm_init_info (&hw->info, &settings);
241: hw->samples = LINE_IN_SAMPLES;
242: in->active = 0;
243:
244: in->sin.base.sif = &record_sif.base;
245: qemu_spice_add_interface (&in->sin.base);
246: return 0;
247: }
248:
249: static void line_in_fini (HWVoiceIn *hw)
250: {
251: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
252:
253: spice_server_remove_interface (&in->sin.base);
254: }
255:
256: static int line_in_run (HWVoiceIn *hw)
257: {
258: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
259: int num_samples;
260: int ready;
261: int len[2];
262: uint64_t delta_samp;
263: const uint32_t *samples;
264:
265: if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
266: return 0;
267: }
268:
269: delta_samp = rate_get_samples (&hw->info, &in->rate);
270: num_samples = audio_MIN (num_samples, delta_samp);
271:
272: ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
273: samples = in->samples;
274: if (ready == 0) {
275: static const uint32_t silence[LINE_IN_SAMPLES];
276: samples = silence;
277: ready = LINE_IN_SAMPLES;
278: }
279:
280: num_samples = audio_MIN (ready, num_samples);
281:
282: if (hw->wpos + num_samples > hw->samples) {
283: len[0] = hw->samples - hw->wpos;
284: len[1] = num_samples - len[0];
285: } else {
286: len[0] = num_samples;
287: len[1] = 0;
288: }
289:
290: hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
291:
292: if (len[1]) {
293: hw->conv (hw->conv_buf, samples + len[0], len[1]);
294: }
295:
296: hw->wpos = (hw->wpos + num_samples) % hw->samples;
297:
298: return num_samples;
299: }
300:
301: static int line_in_read (SWVoiceIn *sw, void *buf, int size)
302: {
303: return audio_pcm_sw_read (sw, buf, size);
304: }
305:
306: static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
307: {
308: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
309:
310: switch (cmd) {
311: case VOICE_ENABLE:
312: if (in->active) {
313: break;
314: }
315: in->active = 1;
316: rate_start (&in->rate);
317: spice_server_record_start (&in->sin);
318: break;
319: case VOICE_DISABLE:
320: if (!in->active) {
321: break;
322: }
323: in->active = 0;
324: spice_server_record_stop (&in->sin);
325: break;
1.1.1.3 ! root 326: case VOICE_VOLUME:
! 327: {
! 328: #if ((SPICE_INTERFACE_RECORD_MAJOR >= 2) && (SPICE_INTERFACE_RECORD_MINOR >= 2))
! 329: SWVoiceIn *sw;
! 330: va_list ap;
! 331: uint16_t vol[2];
! 332:
! 333: va_start (ap, cmd);
! 334: sw = va_arg (ap, SWVoiceIn *);
! 335: va_end (ap);
! 336:
! 337: vol[0] = sw->vol.l / ((1ULL << 16) + 1);
! 338: vol[1] = sw->vol.r / ((1ULL << 16) + 1);
! 339: spice_server_record_set_volume (&in->sin, 2, vol);
! 340: spice_server_record_set_mute (&in->sin, sw->vol.mute);
! 341: #endif
! 342: break;
! 343: }
1.1 root 344: }
1.1.1.3 ! root 345:
1.1 root 346: return 0;
347: }
348:
349: static struct audio_option audio_options[] = {
350: { /* end of list */ },
351: };
352:
353: static struct audio_pcm_ops audio_callbacks = {
354: .init_out = line_out_init,
355: .fini_out = line_out_fini,
356: .run_out = line_out_run,
357: .write = line_out_write,
358: .ctl_out = line_out_ctl,
359:
360: .init_in = line_in_init,
361: .fini_in = line_in_fini,
362: .run_in = line_in_run,
363: .read = line_in_read,
364: .ctl_in = line_in_ctl,
365: };
366:
367: struct audio_driver spice_audio_driver = {
368: .name = "spice",
369: .descr = "spice audio driver",
370: .options = audio_options,
371: .init = spice_audio_init,
372: .fini = spice_audio_fini,
373: .pcm_ops = &audio_callbacks,
374: .max_voices_out = 1,
375: .max_voices_in = 1,
376: .voice_size_out = sizeof (SpiceVoiceOut),
377: .voice_size_in = sizeof (SpiceVoiceIn),
1.1.1.3 ! root 378: #if ((SPICE_INTERFACE_PLAYBACK_MAJOR >= 1) && (SPICE_INTERFACE_PLAYBACK_MINOR >= 2))
! 379: .ctl_caps = VOICE_VOLUME_CAP
! 380: #endif
1.1 root 381: };
382:
383: void qemu_spice_audio_init (void)
384: {
385: spice_audio_driver.can_be_default = 1;
386: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.