| 1 | /* |
| 2 | * QEMU Audio subsystem header |
| 3 | * |
| 4 | * Copyright (c) 2005 Vassili Karpov (malc) |
| 5 | * |
| 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
| 7 | * of this software and associated documentation files (the "Software"), to deal |
| 8 | * in the Software without restriction, including without limitation the rights |
| 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 10 | * copies of the Software, and to permit persons to whom the Software is |
| 11 | * furnished to do so, subject to the following conditions: |
| 12 | * |
| 13 | * The above copyright notice and this permission notice shall be included in |
| 14 | * all copies or substantial portions of the Software. |
| 15 | * |
| 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| 19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 22 | * THE SOFTWARE. |
| 23 | */ |
| 24 | |
| 25 | #ifdef DAC |
| 26 | #define NAME "playback" |
| 27 | #define HWBUF hw->mix_buf |
| 28 | #define TYPE out |
| 29 | #define HW HWVoiceOut |
| 30 | #define SW SWVoiceOut |
| 31 | #else |
| 32 | #define NAME "capture" |
| 33 | #define TYPE in |
| 34 | #define HW HWVoiceIn |
| 35 | #define SW SWVoiceIn |
| 36 | #define HWBUF hw->conv_buf |
| 37 | #endif |
| 38 | |
| 39 | static void glue(audio_init_nb_voices_, TYPE)(AudioMixengBackend *s, |
| 40 | AudioMixengBackendClass *k, |
| 41 | int min_voices) |
| 42 | { |
| 43 | const char *be_name = object_class_get_name(OBJECT_CLASS(k)); |
| 44 | int max_voices = glue(k->max_voices_, TYPE); |
| 45 | size_t voice_size = glue(k->voice_size_, TYPE); |
| 46 | |
| 47 | glue(s->nb_hw_voices_, TYPE) = glue(audio_get_pdo_, TYPE)(s->dev)->voices; |
| 48 | if (glue(s->nb_hw_voices_, TYPE) > max_voices) { |
| 49 | if (!max_voices) { |
| 50 | #ifdef DAC |
| 51 | warn_report("audio: '%s' backend does not support " NAME, be_name); |
| 52 | #endif |
| 53 | } else { |
| 54 | warn_report("audio: '%s' backend does not support %d " NAME " voices, max %d", |
| 55 | be_name, glue(s->nb_hw_voices_, TYPE), max_voices); |
| 56 | } |
| 57 | glue(s->nb_hw_voices_, TYPE) = max_voices; |
| 58 | } |
| 59 | |
| 60 | if (glue(s->nb_hw_voices_, TYPE) < min_voices) { |
| 61 | warn_report("audio: Bogus number of " NAME " voices %d, setting to %d", |
| 62 | glue(s->nb_hw_voices_, TYPE), |
| 63 | min_voices); |
| 64 | } |
| 65 | |
| 66 | if (!voice_size && max_voices) { |
| 67 | audio_bug("backend=`%s' voice_size=0 max_voices=%d", |
| 68 | be_name, max_voices); |
| 69 | glue(s->nb_hw_voices_, TYPE) = 0; |
| 70 | } |
| 71 | |
| 72 | if (voice_size && !max_voices) { |
| 73 | audio_bug("backend=`%s' voice_size=%zu max_voices=0", |
| 74 | be_name, voice_size); |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw) |
| 79 | { |
| 80 | g_free(hw->buf_emul); |
| 81 | g_free(HWBUF.buffer); |
| 82 | HWBUF.buffer = NULL; |
| 83 | HWBUF.size = 0; |
| 84 | } |
| 85 | |
| 86 | static void glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw) |
| 87 | { |
| 88 | if (glue(audio_get_pdo_, TYPE)(hw->s->dev)->mixing_engine) { |
| 89 | size_t samples = hw->samples; |
| 90 | if (samples == 0) { |
| 91 | audio_bug("Attempted to allocate empty buffer"); |
| 92 | } |
| 93 | |
| 94 | HWBUF.buffer = g_new0(st_sample, samples); |
| 95 | HWBUF.size = samples; |
| 96 | HWBUF.pos = 0; |
| 97 | } else { |
| 98 | HWBUF.buffer = NULL; |
| 99 | HWBUF.size = 0; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw) |
| 104 | { |
| 105 | g_free(sw->resample_buf.buffer); |
| 106 | sw->resample_buf.buffer = NULL; |
| 107 | sw->resample_buf.size = 0; |
| 108 | |
| 109 | if (sw->rate) { |
| 110 | st_rate_stop (sw->rate); |
| 111 | } |
| 112 | sw->rate = NULL; |
| 113 | } |
| 114 | |
| 115 | static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) |
| 116 | { |
| 117 | HW *hw = sw->hw; |
| 118 | uint64_t samples; |
| 119 | |
| 120 | if (!glue(audio_get_pdo_, TYPE)(sw->s->dev)->mixing_engine) { |
| 121 | return 0; |
| 122 | } |
| 123 | |
| 124 | samples = muldiv64(HWBUF.size, sw->info.freq, hw->info.freq); |
| 125 | if (samples == 0) { |
| 126 | uint64_t f_fe_min; |
| 127 | uint64_t f_be = (uint32_t)hw->info.freq; |
| 128 | |
| 129 | /* f_fe_min = ceil(1 [frames] * f_be [Hz] / size_be [frames]) */ |
| 130 | f_fe_min = (f_be + HWBUF.size - 1) / HWBUF.size; |
| 131 | qemu_log_mask(LOG_UNIMP, |
| 132 | "audio: The guest selected a " NAME " sample rate" |
| 133 | " of %d Hz for %s. Only sample rates >= %" PRIu64 " Hz" |
| 134 | " are supported.\n", |
| 135 | sw->info.freq, sw->name, f_fe_min); |
| 136 | return -1; |
| 137 | } |
| 138 | |
| 139 | /* |
| 140 | * Allocate one additional audio frame that is needed for upsampling |
| 141 | * if the resample buffer size is small. For large buffer sizes take |
| 142 | * care of overflows and truncation. |
| 143 | */ |
| 144 | samples = samples < SIZE_MAX ? samples + 1 : SIZE_MAX; |
| 145 | sw->resample_buf.buffer = g_new0(st_sample, samples); |
| 146 | sw->resample_buf.size = samples; |
| 147 | sw->resample_buf.pos = 0; |
| 148 | |
| 149 | #ifdef DAC |
| 150 | sw->rate = st_rate_start(sw->info.freq, hw->info.freq); |
| 151 | #else |
| 152 | sw->rate = st_rate_start(hw->info.freq, sw->info.freq); |
| 153 | #endif |
| 154 | |
| 155 | return 0; |
| 156 | } |
| 157 | |
| 158 | static int glue (audio_pcm_sw_init_, TYPE) ( |
| 159 | SW *sw, |
| 160 | HW *hw, |
| 161 | const char *name, |
| 162 | const struct audsettings *as |
| 163 | ) |
| 164 | { |
| 165 | int err; |
| 166 | |
| 167 | audio_pcm_init_info (&sw->info, as); |
| 168 | sw->hw = hw; |
| 169 | sw->active = false; |
| 170 | #ifdef DAC |
| 171 | sw->total_hw_samples_mixed = 0; |
| 172 | sw->empty = true; |
| 173 | #endif |
| 174 | |
| 175 | if (audio_format_is_float(sw->info.af)) { |
| 176 | #ifdef DAC |
| 177 | sw->conv = mixeng_conv_float[sw->info.nchannels == 2] |
| 178 | [sw->info.swap_endianness]; |
| 179 | #else |
| 180 | sw->clip = mixeng_clip_float[sw->info.nchannels == 2] |
| 181 | [sw->info.swap_endianness]; |
| 182 | #endif |
| 183 | } else { |
| 184 | #ifdef DAC |
| 185 | sw->conv = mixeng_conv |
| 186 | #else |
| 187 | sw->clip = mixeng_clip |
| 188 | #endif |
| 189 | [sw->info.nchannels == 2] |
| 190 | [audio_format_is_signed(sw->info.af)] |
| 191 | [sw->info.swap_endianness] |
| 192 | [audio_format_to_index(sw->info.af)]; |
| 193 | } |
| 194 | |
| 195 | sw->name = g_strdup (name); |
| 196 | err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); |
| 197 | if (err) { |
| 198 | g_free (sw->name); |
| 199 | sw->name = NULL; |
| 200 | } |
| 201 | return err; |
| 202 | } |
| 203 | |
| 204 | static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) |
| 205 | { |
| 206 | glue (audio_pcm_sw_free_resources_, TYPE) (sw); |
| 207 | g_free (sw->name); |
| 208 | sw->name = NULL; |
| 209 | } |
| 210 | |
| 211 | static void glue (audio_pcm_hw_add_sw_, TYPE) (HW *hw, SW *sw) |
| 212 | { |
| 213 | QLIST_INSERT_HEAD (&hw->sw_head, sw, entries); |
| 214 | } |
| 215 | |
| 216 | static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw) |
| 217 | { |
| 218 | QLIST_REMOVE (sw, entries); |
| 219 | } |
| 220 | |
| 221 | static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp) |
| 222 | { |
| 223 | HW *hw = *hwp; |
| 224 | AudioMixengBackend *s = hw->s; |
| 225 | AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_GET_CLASS(s); |
| 226 | |
| 227 | if (!hw->sw_head.lh_first) { |
| 228 | #ifdef DAC |
| 229 | audio_detach_capture(hw); |
| 230 | #endif |
| 231 | QLIST_REMOVE(hw, entries); |
| 232 | glue(k->fini_, TYPE)(hw); |
| 233 | glue(s->nb_hw_voices_, TYPE) += 1; |
| 234 | glue(audio_pcm_hw_free_resources_ , TYPE) (hw); |
| 235 | object_unref(hw->s); |
| 236 | g_free(hw); |
| 237 | *hwp = NULL; |
| 238 | } |
| 239 | } |
| 240 | |
| 241 | static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioMixengBackend *s, HW *hw) |
| 242 | { |
| 243 | return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first; |
| 244 | } |
| 245 | |
| 246 | static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioMixengBackend *s, HW *hw) |
| 247 | { |
| 248 | while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { |
| 249 | if (hw->enabled) { |
| 250 | return hw; |
| 251 | } |
| 252 | } |
| 253 | return NULL; |
| 254 | } |
| 255 | |
| 256 | static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioMixengBackend *s, HW *hw, |
| 257 | struct audsettings *as) |
| 258 | { |
| 259 | while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) { |
| 260 | if (audio_pcm_info_eq (&hw->info, as)) { |
| 261 | return hw; |
| 262 | } |
| 263 | } |
| 264 | return NULL; |
| 265 | } |
| 266 | |
| 267 | static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioMixengBackend *s, |
| 268 | struct audsettings *as) |
| 269 | { |
| 270 | HW *hw; |
| 271 | AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_GET_CLASS(s); |
| 272 | |
| 273 | if (!glue(s->nb_hw_voices_, TYPE)) { |
| 274 | return NULL; |
| 275 | } |
| 276 | |
| 277 | if (!glue(k->init_, TYPE)) { |
| 278 | audio_bug("No host audio driver or missing init_%s", NAME); |
| 279 | return NULL; |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | * Since glue(s->nb_hw_voices_, TYPE) is != 0, glue(k->voice_size_, TYPE) |
| 284 | * is guaranteed to be != 0. See the audio_init_nb_voices_* functions. |
| 285 | */ |
| 286 | hw = g_malloc0(glue(k->voice_size_, TYPE)); |
| 287 | hw->s = AUDIO_MIXENG_BACKEND(object_ref(s)); |
| 288 | |
| 289 | QLIST_INIT (&hw->sw_head); |
| 290 | #ifdef DAC |
| 291 | QLIST_INIT (&hw->cap_head); |
| 292 | #endif |
| 293 | if (glue(k->init_, TYPE)(hw, as)) { |
| 294 | goto err0; |
| 295 | } |
| 296 | |
| 297 | if (hw->samples <= 0) { |
| 298 | audio_bug("hw->samples=%zd", hw->samples); |
| 299 | goto err1; |
| 300 | } |
| 301 | |
| 302 | if (audio_format_is_float(hw->info.af)) { |
| 303 | #ifdef DAC |
| 304 | hw->clip = mixeng_clip_float[hw->info.nchannels == 2] |
| 305 | [hw->info.swap_endianness]; |
| 306 | #else |
| 307 | hw->conv = mixeng_conv_float[hw->info.nchannels == 2] |
| 308 | [hw->info.swap_endianness]; |
| 309 | #endif |
| 310 | } else { |
| 311 | #ifdef DAC |
| 312 | hw->clip = mixeng_clip |
| 313 | #else |
| 314 | hw->conv = mixeng_conv |
| 315 | #endif |
| 316 | [hw->info.nchannels == 2] |
| 317 | [audio_format_is_signed(hw->info.af)] |
| 318 | [hw->info.swap_endianness] |
| 319 | [audio_format_to_index(hw->info.af)]; |
| 320 | } |
| 321 | |
| 322 | glue(audio_pcm_hw_alloc_resources_, TYPE)(hw); |
| 323 | |
| 324 | QLIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); |
| 325 | glue (s->nb_hw_voices_, TYPE) -= 1; |
| 326 | #ifdef DAC |
| 327 | audio_attach_capture (hw); |
| 328 | #endif |
| 329 | return hw; |
| 330 | |
| 331 | err1: |
| 332 | glue(k->fini_, TYPE)(hw); |
| 333 | err0: |
| 334 | object_unref(hw->s); |
| 335 | g_free (hw); |
| 336 | return NULL; |
| 337 | } |
| 338 | |
| 339 | AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) |
| 340 | { |
| 341 | switch (dev->driver) { |
| 342 | case AUDIODEV_DRIVER_NONE: |
| 343 | return dev->u.none.TYPE; |
| 344 | #ifdef CONFIG_AUDIO_ALSA |
| 345 | case AUDIODEV_DRIVER_ALSA: |
| 346 | return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); |
| 347 | #endif |
| 348 | #ifdef CONFIG_AUDIO_COREAUDIO |
| 349 | case AUDIODEV_DRIVER_COREAUDIO: |
| 350 | return qapi_AudiodevCoreaudioPerDirectionOptions_base( |
| 351 | dev->u.coreaudio.TYPE); |
| 352 | #endif |
| 353 | #ifdef CONFIG_DBUS_DISPLAY |
| 354 | case AUDIODEV_DRIVER_DBUS: |
| 355 | return dev->u.dbus.TYPE; |
| 356 | #endif |
| 357 | #ifdef CONFIG_AUDIO_DSOUND |
| 358 | case AUDIODEV_DRIVER_DSOUND: |
| 359 | return dev->u.dsound.TYPE; |
| 360 | #endif |
| 361 | #ifdef CONFIG_AUDIO_JACK |
| 362 | case AUDIODEV_DRIVER_JACK: |
| 363 | return qapi_AudiodevJackPerDirectionOptions_base(dev->u.jack.TYPE); |
| 364 | #endif |
| 365 | #ifdef CONFIG_AUDIO_OSS |
| 366 | case AUDIODEV_DRIVER_OSS: |
| 367 | return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); |
| 368 | #endif |
| 369 | #ifdef CONFIG_AUDIO_PA |
| 370 | case AUDIODEV_DRIVER_PA: |
| 371 | return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); |
| 372 | #endif |
| 373 | #ifdef CONFIG_AUDIO_PIPEWIRE |
| 374 | case AUDIODEV_DRIVER_PIPEWIRE: |
| 375 | return qapi_AudiodevPipewirePerDirectionOptions_base(dev->u.pipewire.TYPE); |
| 376 | #endif |
| 377 | #ifdef CONFIG_AUDIO_SDL |
| 378 | case AUDIODEV_DRIVER_SDL: |
| 379 | return qapi_AudiodevSdlPerDirectionOptions_base(dev->u.sdl.TYPE); |
| 380 | #endif |
| 381 | #ifdef CONFIG_AUDIO_SNDIO |
| 382 | case AUDIODEV_DRIVER_SNDIO: |
| 383 | return dev->u.sndio.TYPE; |
| 384 | #endif |
| 385 | #ifdef CONFIG_SPICE |
| 386 | case AUDIODEV_DRIVER_SPICE: |
| 387 | return dev->u.spice.TYPE; |
| 388 | #endif |
| 389 | case AUDIODEV_DRIVER_WAV: |
| 390 | return dev->u.wav.TYPE; |
| 391 | |
| 392 | case AUDIODEV_DRIVER__MAX: |
| 393 | break; |
| 394 | } |
| 395 | abort(); |
| 396 | } |
| 397 | |
| 398 | static HW *glue(audio_pcm_hw_add_, TYPE)(AudioMixengBackend *s, struct audsettings *as) |
| 399 | { |
| 400 | HW *hw; |
| 401 | AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); |
| 402 | |
| 403 | if (!pdo->mixing_engine || pdo->fixed_settings) { |
| 404 | hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as); |
| 405 | if (!pdo->mixing_engine || hw) { |
| 406 | return hw; |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as); |
| 411 | if (hw) { |
| 412 | return hw; |
| 413 | } |
| 414 | |
| 415 | hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as); |
| 416 | if (hw) { |
| 417 | return hw; |
| 418 | } |
| 419 | |
| 420 | return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL); |
| 421 | } |
| 422 | |
| 423 | static SW *glue(audio_pcm_create_voice_pair_, TYPE)( |
| 424 | AudioMixengBackend *s, |
| 425 | const char *sw_name, |
| 426 | const struct audsettings *as |
| 427 | ) |
| 428 | { |
| 429 | SW *sw; |
| 430 | HW *hw; |
| 431 | struct audsettings hw_as; |
| 432 | AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); |
| 433 | |
| 434 | if (pdo->fixed_settings) { |
| 435 | hw_as = audiodev_to_audsettings(pdo); |
| 436 | } else { |
| 437 | hw_as = *as; |
| 438 | } |
| 439 | |
| 440 | sw = g_new0(SW, 1); |
| 441 | sw->s = AUDIO_MIXENG_BACKEND(object_ref(s)); |
| 442 | |
| 443 | hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as); |
| 444 | if (!hw) { |
| 445 | error_report("audio: Could not create a backend for voice '%s'", sw_name); |
| 446 | goto err1; |
| 447 | } |
| 448 | |
| 449 | glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); |
| 450 | |
| 451 | if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) { |
| 452 | goto err2; |
| 453 | } |
| 454 | |
| 455 | return sw; |
| 456 | |
| 457 | err2: |
| 458 | glue (audio_pcm_hw_del_sw_, TYPE) (sw); |
| 459 | glue (audio_pcm_hw_gc_, TYPE) (&hw); |
| 460 | err1: |
| 461 | object_unref(sw->s); |
| 462 | g_free(sw); |
| 463 | return NULL; |
| 464 | } |
| 465 | |
| 466 | static void glue (audio_close_, TYPE) (SW *sw) |
| 467 | { |
| 468 | glue (audio_pcm_sw_fini_, TYPE) (sw); |
| 469 | glue (audio_pcm_hw_del_sw_, TYPE) (sw); |
| 470 | glue (audio_pcm_hw_gc_, TYPE) (&sw->hw); |
| 471 | |
| 472 | object_unref(sw->s); |
| 473 | g_free (sw); |
| 474 | } |
| 475 | |
| 476 | static void glue(audio_mixeng_backend_close_, TYPE)(AudioBackend *be, SW *sw) |
| 477 | { |
| 478 | if (sw) { |
| 479 | if (!be) { |
| 480 | audio_bug("backend=%p", be); |
| 481 | return; |
| 482 | } |
| 483 | |
| 484 | glue (audio_close_, TYPE) (sw); |
| 485 | } |
| 486 | } |
| 487 | |
| 488 | static SW *glue(audio_mixeng_backend_open_, TYPE) ( |
| 489 | AudioBackend *be, |
| 490 | SW *sw, |
| 491 | const char *name, |
| 492 | void *callback_opaque , |
| 493 | audio_callback_fn callback_fn, |
| 494 | const struct audsettings *as) |
| 495 | { |
| 496 | AudioMixengBackend *s = AUDIO_MIXENG_BACKEND(be); |
| 497 | AudioMixengBackendClass *k; |
| 498 | AudiodevPerDirectionOptions *pdo; |
| 499 | |
| 500 | k = AUDIO_MIXENG_BACKEND_GET_CLASS(s); |
| 501 | pdo = glue(audio_get_pdo_, TYPE)(s->dev); |
| 502 | |
| 503 | #ifdef DAC |
| 504 | trace_audio_open_out(name, as->freq, as->nchannels, as->fmt); |
| 505 | #else |
| 506 | trace_audio_open_in(name, as->freq, as->nchannels, as->fmt); |
| 507 | #endif |
| 508 | |
| 509 | if (audio_validate_settings(as)) { |
| 510 | g_autofree char *str = audsettings_to_string(as); |
| 511 | error_report("audio: Invalid audio settings: %s", str); |
| 512 | goto fail; |
| 513 | } |
| 514 | |
| 515 | if (!glue(k->init_, TYPE)) { |
| 516 | error_report("audio: Can not open `%s' (no host audio driver)", name); |
| 517 | goto fail; |
| 518 | } |
| 519 | |
| 520 | if (sw && audio_pcm_info_eq (&sw->info, as)) { |
| 521 | return sw; |
| 522 | } |
| 523 | |
| 524 | if (!pdo->fixed_settings && sw) { |
| 525 | glue(audio_be_close_, TYPE)(be, sw); |
| 526 | sw = NULL; |
| 527 | } |
| 528 | |
| 529 | if (sw) { |
| 530 | HW *hw = sw->hw; |
| 531 | |
| 532 | if (!hw) { |
| 533 | audio_bug("Internal logic error: voice '%s' has no backend", SW_NAME(sw)); |
| 534 | goto fail; |
| 535 | } |
| 536 | |
| 537 | glue (audio_pcm_sw_fini_, TYPE) (sw); |
| 538 | if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) { |
| 539 | goto fail; |
| 540 | } |
| 541 | } else { |
| 542 | sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as); |
| 543 | if (!sw) { |
| 544 | return NULL; |
| 545 | } |
| 546 | } |
| 547 | |
| 548 | sw->vol = nominal_volume; |
| 549 | sw->callback.fn = callback_fn; |
| 550 | sw->callback.opaque = callback_opaque; |
| 551 | |
| 552 | trace_audio_voice_pair(G_STRINGIFY(TYPE), name, |
| 553 | AudioFormat_str(sw->hw->info.af), |
| 554 | sw->hw->info.freq, sw->hw->info.nchannels, |
| 555 | AudioFormat_str(sw->info.af), |
| 556 | sw->info.freq, sw->info.nchannels); |
| 557 | |
| 558 | return sw; |
| 559 | |
| 560 | fail: |
| 561 | glue(audio_be_close_, TYPE)(be, sw); |
| 562 | return NULL; |
| 563 | } |
| 564 | |
| 565 | static bool glue(audio_mixeng_backend_is_active_, TYPE)(AudioBackend *be, SW *sw) |
| 566 | { |
| 567 | return sw ? sw->active : 0; |
| 568 | } |
| 569 | |
| 570 | #undef TYPE |
| 571 | #undef HW |
| 572 | #undef SW |
| 573 | #undef HWBUF |
| 574 | #undef NAME |