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>
---
audio/coreaudio.m | 47 ++++++++++++++++++++++++++---------------------
1 file changed, 26 insertions(+), 21 deletions(-)
diff --git a/audio/coreaudio.m b/audio/coreaudio.m
index 736227eb2b7a..23c3d1f80ac5 100644
--- a/audio/coreaudio.m
+++ b/audio/coreaudio.m
@@ -357,8 +357,11 @@ static OSStatus out_device_ioproc(
static OSStatus init_out_device(CoreaudioVoiceOut *core)
{
+ AudioDeviceID device_id;
+ AudioDeviceIOProcID ioprocid;
AudioValueRange value_range;
OSStatus status;
+ UInt32 device_frame_size;
AudioStreamBasicDescription stream_basic_description = {
.mBitsPerChannel = audio_format_bits(core->hw.info.af),
@@ -371,20 +374,20 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
.mSampleRate = core->hw.info.freq
};
- status = coreaudio_get_voice_out(&core->device_id);
+ status = coreaudio_get_voice_out(&device_id);
if (status != kAudioHardwareNoError) {
coreaudio_playback_logerr(status,
"Could not get default output device");
return status;
}
- if (core->device_id == kAudioDeviceUnknown) {
+ if (device_id == kAudioDeviceUnknown) {
error_report("coreaudio: Could not initialize playback: "
"Unknown audio device");
return status;
}
/* get minimum and maximum buffer frame sizes */
- status = coreaudio_get_out_framesizerange(core->device_id, &value_range);
+ status = coreaudio_get_out_framesizerange(device_id, &value_range);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
@@ -395,33 +398,31 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
}
if (value_range.mMinimum > core->frame_size_setting) {
- core->device_frame_size = value_range.mMinimum;
+ device_frame_size = value_range.mMinimum;
warn_report("coreaudio: Upsizing buffer frames to %f",
value_range.mMinimum);
} else if (value_range.mMaximum < core->frame_size_setting) {
- core->device_frame_size = value_range.mMaximum;
+ device_frame_size = value_range.mMaximum;
warn_report("coreaudio: Downsizing buffer frames to %f",
value_range.mMaximum);
} else {
- core->device_frame_size = core->frame_size_setting;
+ device_frame_size = core->frame_size_setting;
}
/* set Buffer Frame Size */
- status = coreaudio_set_out_framesize(core->device_id,
- &core->device_frame_size);
+ status = coreaudio_set_out_framesize(device_id, &device_frame_size);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
if (status != kAudioHardwareNoError) {
coreaudio_playback_logerr(status,
"Could not set device buffer frame size %" PRIu32,
- (uint32_t)core->device_frame_size);
+ (uint32_t)device_frame_size);
return status;
}
/* get Buffer Frame Size */
- status = coreaudio_get_out_framesize(core->device_id,
- &core->device_frame_size);
+ status = coreaudio_get_out_framesize(device_id, &device_frame_size);
if (status == kAudioHardwareBadObjectError) {
return 0;
}
@@ -430,10 +431,9 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
"Could not get device buffer frame size");
return status;
}
- core->hw.samples = core->buffer_count * core->device_frame_size;
/* set Samplerate */
- status = coreaudio_set_out_streamformat(core->device_id,
+ status = coreaudio_set_out_streamformat(device_id,
&stream_basic_description);
if (status == kAudioHardwareBadObjectError) {
return 0;
@@ -442,7 +442,6 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
coreaudio_playback_logerr(status,
"Could not set samplerate %lf",
stream_basic_description.mSampleRate);
- core->device_id = kAudioDeviceUnknown;
return status;
}
@@ -456,20 +455,24 @@ static OSStatus init_out_device(CoreaudioVoiceOut *core)
* Therefore, the specified callback must be designed to avoid a deadlock
* with the callers of AudioObjectGetPropertyData.
*/
- core->ioprocid = NULL;
- status = AudioDeviceCreateIOProcID(core->device_id,
+ ioprocid = NULL;
+ status = AudioDeviceCreateIOProcID(device_id,
out_device_ioproc,
&core->hw,
- &core->ioprocid);
+ &ioprocid);
if (status == kAudioHardwareBadDeviceError) {
return 0;
}
- if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
+ if (status != kAudioHardwareNoError || ioprocid == NULL) {
coreaudio_playback_logerr(status, "Could not set IOProc");
- core->device_id = kAudioDeviceUnknown;
return status;
}
+ core->device_id = device_id;
+ core->device_frame_size = device_frame_size;
+ core->hw.samples = core->buffer_count * core->device_frame_size;
+ core->ioprocid = ioprocid;
+
return 0;
}
@@ -553,7 +556,9 @@ static OSStatus handle_voice_out_change(
fini_out_device(core);
}
- if (!init_out_device(core)) {
+ init_out_device(core);
+
+ if (core->device_id) {
update_out_device_playback_state(core);
}
@@ -650,7 +655,7 @@ static void audio_coreaudio_class_init(ObjectClass *klass, const void *data)
k->max_voices_out = 1;
k->max_voices_in = 0;
- k->voice_size_out = sizeof(coreaudioVoiceOut);
+ k->voice_size_out = sizeof(CoreaudioVoiceOut);
k->voice_size_in = 0;
k->init_out = coreaudio_init_out;
--
2.53.0
On Wednesday, 4 March 2026 07:16:57 CET Akihiko Odaki wrote:
> 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>
> ---
> audio/coreaudio.m | 47 ++++++++++++++++++++++++++---------------------
> 1 file changed, 26 insertions(+), 21 deletions(-)
>
> diff --git a/audio/coreaudio.m b/audio/coreaudio.m
> index 736227eb2b7a..23c3d1f80ac5 100644
> --- a/audio/coreaudio.m
> +++ b/audio/coreaudio.m
> @@ -357,8 +357,11 @@ static OSStatus out_device_ioproc(
>
> static OSStatus init_out_device(CoreaudioVoiceOut *core)
> {
> + AudioDeviceID device_id;
> + AudioDeviceIOProcID ioprocid;
> AudioValueRange value_range;
> OSStatus status;
> + UInt32 device_frame_size;
Shouldn't then be this here at the beginning?
core->device_id = kAudioDeviceUnknown;
core->ioprocid = NULL;
...
[...]
> if (status != kAudioHardwareNoError) {
> coreaudio_playback_logerr(status,
> "Could not set device buffer frame size %" PRIu32,
> - (uint32_t)core->device_frame_size);
> + (uint32_t)device_frame_size);
> return status;
> }
Why the cast here? device_frame_size is declared as UInt32.
/Christian
On 2026/03/04 20:56, Christian Schoenebeck wrote:
> On Wednesday, 4 March 2026 07:16:57 CET Akihiko Odaki wrote:
>> 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>
>> ---
>> audio/coreaudio.m | 47 ++++++++++++++++++++++++++---------------------
>> 1 file changed, 26 insertions(+), 21 deletions(-)
>>
>> diff --git a/audio/coreaudio.m b/audio/coreaudio.m
>> index 736227eb2b7a..23c3d1f80ac5 100644
>> --- a/audio/coreaudio.m
>> +++ b/audio/coreaudio.m
>> @@ -357,8 +357,11 @@ static OSStatus out_device_ioproc(
>>
>> static OSStatus init_out_device(CoreaudioVoiceOut *core)
>> {
>> + AudioDeviceID device_id;
>> + AudioDeviceIOProcID ioprocid;
>> AudioValueRange value_range;
>> OSStatus status;
>> + UInt32 device_frame_size;
>
> Shouldn't then be this here at the beginning?
>
> core->device_id = kAudioDeviceUnknown;
> core->ioprocid = NULL;
> ...
Setting device members is done in correspondence with actual device
operations. Concretely, fini_out_device() sets kAudioDeviceUnknown to
device_id to express it finalized the device. This function initializes
the device or does nothing if an error happens, so it sets device
members or does not touch otherwise.
>
> [...]
>> if (status != kAudioHardwareNoError) {
>> coreaudio_playback_logerr(status,
>> "Could not set device buffer frame size %" PRIu32,
>> - (uint32_t)core->device_frame_size);
>> + (uint32_t)device_frame_size);
>> return status;
>> }
>
> Why the cast here? device_frame_size is declared as UInt32.
The definition of uint32_t is not literally identical with UInt32, so
the compiler complains it is different from what the format string
(PRIu32) expects. I think I'm going to use uint32_t for the variable to
avoid this.
Regards,
Akihiko Odaki
© 2016 - 2026 Red Hat, Inc.