|
|
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)); ! 84: rate->start_ticks = qemu_get_clock (vm_clock); ! 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: ! 94: now = qemu_get_clock (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); ! 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.