@samitouri / QOSamiQemu / commits / f47a5a3d47

coreaudio: Commit the result of init in the end

init_out_device may only commit some part of the result and leave the state inconsistent when it encounters a fatal error or the device gets unplugged during the operation, which is expressed by kAudioHardwareBadObjectError or kAudioHardwareBadDeviceError. Commit the result in the end of the function so that it commits the result iff it sees no fatal error and the device remains plugged. With this change, handle_voice_change can rely on core->outputDeviceID to know whether the output device is initialized after calling init_out_device. Signed-off-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> Message-Id: <20260304-coreaudio-v8-4-bf1d40731e73@rsg.ci.i.u-tokyo.ac.jp>

Akihiko Odaki committed Mar 4, 2026 at 15:16 UTC f47a5a3d47fdcc577ed009b414a07d1de483d1e1
1 file changed +26 -21
audio/coreaudio.m
+26 -21
@@ -357,8 +357,11 @@ static OSStatus out_device_ioproc(
357
358 static OSStatus init_out_device(CoreaudioVoiceOut *core)
359 {
360 + AudioDeviceID device_id;
361 + AudioDeviceIOProcID ioprocid;
362 AudioValueRange value_range;
363 OSStatus status;
364 + UInt32 device_frame_size;
365
366 AudioStreamBasicDescription stream_basic_description = {
367 .mBitsPerChannel = audio_format_bits(core->hw.info.af),
@@ -371,20 +374,20 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
374 .mSampleRate = core->hw.info.freq
375 };
376
374 - status = coreaudio_get_voice_out(&core->device_id);
377 + status = coreaudio_get_voice_out(&device_id);
378 if (status != kAudioHardwareNoError) {
379 coreaudio_playback_logerr(status,
380 "Could not get default output device");
381 return status;
382 }
380 - if (core->device_id == kAudioDeviceUnknown) {
383 + if (device_id == kAudioDeviceUnknown) {
384 error_report("coreaudio: Could not initialize playback: "
385 "Unknown audio device");
386 return status;
387 }
388
389 /* get minimum and maximum buffer frame sizes */
387 - status = coreaudio_get_out_framesizerange(core->device_id, &value_range);
390 + status = coreaudio_get_out_framesizerange(device_id, &value_range);
391 if (status == kAudioHardwareBadObjectError) {
392 return 0;
393 }
@@ -395,33 +398,31 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
398 }
399
400 if (value_range.mMinimum > core->frame_size_setting) {
398 - core->device_frame_size = value_range.mMinimum;
401 + device_frame_size = value_range.mMinimum;
402 warn_report("coreaudio: Upsizing buffer frames to %f",
403 value_range.mMinimum);
404 } else if (value_range.mMaximum < core->frame_size_setting) {
402 - core->device_frame_size = value_range.mMaximum;
405 + device_frame_size = value_range.mMaximum;
406 warn_report("coreaudio: Downsizing buffer frames to %f",
407 value_range.mMaximum);
408 } else {
406 - core->device_frame_size = core->frame_size_setting;
409 + device_frame_size = core->frame_size_setting;
410 }
411
412 /* set Buffer Frame Size */
410 - status = coreaudio_set_out_framesize(core->device_id,
411 - &core->device_frame_size);
413 + status = coreaudio_set_out_framesize(device_id, &device_frame_size);
414 if (status == kAudioHardwareBadObjectError) {
415 return 0;
416 }
417 if (status != kAudioHardwareNoError) {
418 coreaudio_playback_logerr(status,
419 "Could not set device buffer frame size %" PRIu32,
418 - (uint32_t)core->device_frame_size);
420 + (uint32_t)device_frame_size);
421 return status;
422 }
423
424 /* get Buffer Frame Size */
423 - status = coreaudio_get_out_framesize(core->device_id,
424 - &core->device_frame_size);
425 + status = coreaudio_get_out_framesize(device_id, &device_frame_size);
426 if (status == kAudioHardwareBadObjectError) {
427 return 0;
428 }
@@ -430,10 +431,9 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
431 "Could not get device buffer frame size");
432 return status;
433 }
433 - core->hw.samples = core->buffer_count * core->device_frame_size;
434
435 /* set Samplerate */
436 - status = coreaudio_set_out_streamformat(core->device_id,
436 + status = coreaudio_set_out_streamformat(device_id,
437 &stream_basic_description);
438 if (status == kAudioHardwareBadObjectError) {
439 return 0;
@@ -442,7 +442,6 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
442 coreaudio_playback_logerr(status,
443 "Could not set samplerate %lf",
444 stream_basic_description.mSampleRate);
445 - core->device_id = kAudioDeviceUnknown;
445 return status;
446 }
447
@@ -456,20 +455,24 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
455 * Therefore, the specified callback must be designed to avoid a deadlock
456 * with the callers of AudioObjectGetPropertyData.
457 */
459 - core->ioprocid = NULL;
460 - status = AudioDeviceCreateIOProcID(core->device_id,
458 + ioprocid = NULL;
459 + status = AudioDeviceCreateIOProcID(device_id,
460 out_device_ioproc,
461 &core->hw,
463 - &core->ioprocid);
462 + &ioprocid);
463 if (status == kAudioHardwareBadDeviceError) {
464 return 0;
465 }
467 - if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
466 + if (status != kAudioHardwareNoError || ioprocid == NULL) {
467 coreaudio_playback_logerr(status, "Could not set IOProc");
469 - core->device_id = kAudioDeviceUnknown;
468 return status;
469 }
470
471 + core->device_id = device_id;
472 + core->device_frame_size = device_frame_size;
473 + core->hw.samples = core->buffer_count * core->device_frame_size;
474 + core->ioprocid = ioprocid;
475 +
476 return 0;
477 }
478
@@ -553,7 +556,9 @@ static OSStatus handle_voice_out_change(
556 fini_out_device(core);
557 }
558
556 - if (!init_out_device(core)) {
559 + init_out_device(core);
560 +
561 + if (core->device_id) {
562 update_out_device_playback_state(core);
563 }
564
@@ -650,7 +655,7 @@ static void audio_coreaudio_class_init(ObjectClass *klass, const void *data)
655
656 k->max_voices_out = 1;
657 k->max_voices_in = 0;
653 - k->voice_size_out = sizeof(coreaudioVoiceOut);
658 + k->voice_size_out = sizeof(CoreaudioVoiceOut);
659 k->voice_size_in = 0;
660
661 k->init_out = coreaudio_init_out;