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