static void init_physical_format(struct ao *ao) { struct priv *p = ao->priv; OSErr err; AudioStreamBasicDescription asbd; ca_fill_asbd(ao, &asbd); AudioStreamID *streams; size_t n_streams; err = CA_GET_ARY_O(p->device, kAudioDevicePropertyStreams, &streams, &n_streams); CHECK_CA_ERROR("could not get number of streams"); for (int i = 0; i < n_streams; i++) { AudioStreamRangedDescription *formats; size_t n_formats; err = CA_GET_ARY(streams[i], kAudioStreamPropertyAvailablePhysicalFormats, &formats, &n_formats); if (!CHECK_CA_WARN("could not get number of stream formats")) continue; // try next one MP_VERBOSE(ao, "Looking at formats in substream %d...\n", i); AudioStreamBasicDescription best_asbd = {0}; for (int j = 0; j < n_formats; j++) { AudioStreamBasicDescription *stream_asbd = &formats[j].mFormat; ca_print_asbd(ao, "- ", stream_asbd); if (!best_asbd.mFormatID || ca_asbd_is_better(&asbd, &best_asbd, stream_asbd)) best_asbd = *stream_asbd; } if (best_asbd.mFormatID) { p->original_asbd_stream = streams[i]; err = CA_GET(p->original_asbd_stream, kAudioStreamPropertyPhysicalFormat, &p->original_asbd); CHECK_CA_WARN("could not get current physical stream format"); ca_change_physical_format_sync(ao, streams[i], best_asbd); break; } } coreaudio_error: return; }
static OSStatus ca_change_mixing(struct ao *ao, AudioDeviceID device, uint32_t val, bool *changed) { *changed = false; AudioObjectPropertyAddress p_addr = (AudioObjectPropertyAddress) { .mSelector = kAudioDevicePropertySupportsMixing, .mScope = kAudioObjectPropertyScopeGlobal, .mElement = kAudioObjectPropertyElementMaster, }; if (AudioObjectHasProperty(device, &p_addr)) { OSStatus err; Boolean writeable = 0; err = CA_SETTABLE(device, kAudioDevicePropertySupportsMixing, &writeable); if (!CHECK_CA_WARN("can't tell if mixing property is settable")) { return err; } if (!writeable) return noErr; err = CA_SET(device, kAudioDevicePropertySupportsMixing, &val); if (err != noErr) return err; if (!CHECK_CA_WARN("can't set mix mode")) { return err; } *changed = true; } return noErr; }
static bool init_audiounit(struct ao *ao, AudioStreamBasicDescription asbd) { OSStatus err; uint32_t size; struct priv *p = ao->priv; AudioComponentDescription desc = (AudioComponentDescription) { .componentType = kAudioUnitType_Output, .componentSubType = (ao->device) ? kAudioUnitSubType_HALOutput : kAudioUnitSubType_DefaultOutput, .componentManufacturer = kAudioUnitManufacturer_Apple, .componentFlags = 0, .componentFlagsMask = 0, }; AudioComponent comp = AudioComponentFindNext(NULL, &desc); if (comp == NULL) { MP_ERR(ao, "unable to find audio component\n"); goto coreaudio_error; } err = AudioComponentInstanceNew(comp, &(p->audio_unit)); CHECK_CA_ERROR("unable to open audio component"); err = AudioUnitInitialize(p->audio_unit); CHECK_CA_ERROR_L(coreaudio_error_component, "unable to initialize audio unit"); size = sizeof(AudioStreamBasicDescription); err = AudioUnitSetProperty(p->audio_unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &asbd, size); CHECK_CA_ERROR_L(coreaudio_error_audiounit, "unable to set the input format on the audio unit"); err = AudioUnitSetProperty(p->audio_unit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &p->device, sizeof(p->device)); CHECK_CA_ERROR_L(coreaudio_error_audiounit, "can't link audio unit to selected device"); p->hw_latency_us = ca_get_hardware_latency(ao); AURenderCallbackStruct render_cb = (AURenderCallbackStruct) { .inputProc = render_cb_lpcm, .inputProcRefCon = ao, }; err = AudioUnitSetProperty(p->audio_unit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &render_cb, sizeof(AURenderCallbackStruct)); CHECK_CA_ERROR_L(coreaudio_error_audiounit, "unable to set render callback on audio unit"); return true; coreaudio_error_audiounit: AudioUnitUninitialize(p->audio_unit); coreaudio_error_component: AudioComponentInstanceDispose(p->audio_unit); coreaudio_error: return false; } static void stop(struct ao *ao) { struct priv *p = ao->priv; OSStatus err = AudioOutputUnitStop(p->audio_unit); CHECK_CA_WARN("can't stop audio unit"); } static void start(struct ao *ao) { struct priv *p = ao->priv; OSStatus err = AudioOutputUnitStart(p->audio_unit); CHECK_CA_WARN("can't start audio unit"); } static void uninit(struct ao *ao) { struct priv *p = ao->priv; AudioOutputUnitStop(p->audio_unit); AudioUnitUninitialize(p->audio_unit); AudioComponentInstanceDispose(p->audio_unit); if (p->original_asbd.mFormatID) { OSStatus err = CA_SET(p->original_asbd_stream, kAudioStreamPropertyPhysicalFormat, &p->original_asbd); CHECK_CA_WARN("could not restore physical stream format"); } } static OSStatus hotplug_cb(AudioObjectID id, UInt32 naddr, const AudioObjectPropertyAddress addr[], void *ctx) { reinit_device(ctx); ao_hotplug_event(ctx); return noErr; }