|
|
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;
205: }
206: return 0;
207: }
208:
209: /* record */
210:
211: static int line_in_init (HWVoiceIn *hw, struct audsettings *as)
212: {
213: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
214: struct audsettings settings;
215:
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;
220:
221: audio_pcm_init_info (&hw->info, &settings);
222: hw->samples = LINE_IN_SAMPLES;
223: in->active = 0;
224:
225: in->sin.base.sif = &record_sif.base;
226: qemu_spice_add_interface (&in->sin.base);
227: return 0;
228: }
229:
230: static void line_in_fini (HWVoiceIn *hw)
231: {
232: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
233:
234: spice_server_remove_interface (&in->sin.base);
235: }
236:
237: static int line_in_run (HWVoiceIn *hw)
238: {
239: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
240: int num_samples;
241: int ready;
242: int len[2];
243: uint64_t delta_samp;
244: const uint32_t *samples;
245:
246: if (!(num_samples = hw->samples - audio_pcm_hw_get_live_in (hw))) {
247: return 0;
248: }
249:
250: delta_samp = rate_get_samples (&hw->info, &in->rate);
251: num_samples = audio_MIN (num_samples, delta_samp);
252:
253: ready = spice_server_record_get_samples (&in->sin, in->samples, num_samples);
254: samples = in->samples;
255: if (ready == 0) {
256: static const uint32_t silence[LINE_IN_SAMPLES];
257: samples = silence;
258: ready = LINE_IN_SAMPLES;
259: }
260:
261: num_samples = audio_MIN (ready, num_samples);
262:
263: if (hw->wpos + num_samples > hw->samples) {
264: len[0] = hw->samples - hw->wpos;
265: len[1] = num_samples - len[0];
266: } else {
267: len[0] = num_samples;
268: len[1] = 0;
269: }
270:
271: hw->conv (hw->conv_buf + hw->wpos, samples, len[0]);
272:
273: if (len[1]) {
274: hw->conv (hw->conv_buf, samples + len[0], len[1]);
275: }
276:
277: hw->wpos = (hw->wpos + num_samples) % hw->samples;
278:
279: return num_samples;
280: }
281:
282: static int line_in_read (SWVoiceIn *sw, void *buf, int size)
283: {
284: return audio_pcm_sw_read (sw, buf, size);
285: }
286:
287: static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
288: {
289: SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
290:
291: switch (cmd) {
292: case VOICE_ENABLE:
293: if (in->active) {
294: break;
295: }
296: in->active = 1;
297: rate_start (&in->rate);
298: spice_server_record_start (&in->sin);
299: break;
300: case VOICE_DISABLE:
301: if (!in->active) {
302: break;
303: }
304: in->active = 0;
305: spice_server_record_stop (&in->sin);
306: break;
307: }
308: return 0;
309: }
310:
311: static struct audio_option audio_options[] = {
312: { /* end of list */ },
313: };
314:
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,
321:
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,
327: };
328:
329: struct audio_driver spice_audio_driver = {
330: .name = "spice",
331: .descr = "spice audio driver",
332: .options = audio_options,
333: .init = spice_audio_init,
334: .fini = spice_audio_fini,
335: .pcm_ops = &audio_callbacks,
336: .max_voices_out = 1,
337: .max_voices_in = 1,
338: .voice_size_out = sizeof (SpiceVoiceOut),
339: .voice_size_in = sizeof (SpiceVoiceIn),
340: };
341:
342: void qemu_spice_audio_init (void)
343: {
344: spice_audio_driver.can_be_default = 1;
345: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.