static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceStateChanged( IMMNotificationClient *This, LPCWSTR pwstrDeviceId, DWORD dwNewState) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDeviceStateChanged triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { switch (dwNewState) { case DEVICE_STATE_DISABLED: case DEVICE_STATE_NOTPRESENT: case DEVICE_STATE_UNPLUGGED: MP_VERBOSE(ao, "OnDeviceStateChanged triggered on device %S: " "requesting ao reload\n", pwstrDeviceId); ao_request_reload(ao); break; case DEVICE_STATE_ACTIVE: break; } } return S_OK; }
static void subscribe_cb(pa_context *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct ao *ao = userdata; int type = t & PA_SUBSCRIPTION_MASK_SINK; int fac = t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK; if ((type == PA_SUBSCRIPTION_EVENT_NEW || type == PA_SUBSCRIPTION_EVENT_REMOVE) && fac == PA_SUBSCRIPTION_EVENT_SINK) { ao_hotplug_event(ao); } }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceAdded( IMMNotificationClient *This, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDeviceAdded triggered: sending hotplug event\n"); ao_hotplug_event(ao); } return S_OK; }
// maybe MPV can go over to the prefered device once it is plugged in? static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDeviceRemoved( IMMNotificationClient *This, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDeviceRemoved triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnDeviceRemoved triggered for device %S: " "requesting ao reload\n", pwstrDeviceId); ao_request_reload(ao); } return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( IMMNotificationClient *This, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; // don't care about "eCapture" or non-"eMultimedia" roles if (flow == eCapture || role != eMultimedia) return S_OK; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else { // stay on the device the user specified bstr device = wasapi_get_specified_device_string(ao); if (device.len) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "staying on specified device %.*s\n", BSTR_P(device)); return S_OK; } // don't reload if already on the new default if (pwstrDeviceId && !wcscmp(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "already using default device, no reload required\n"); return S_OK; } // if we got here, we need to reload MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n"); ao_request_reload(ao); } return S_OK; }
static HRESULT STDMETHODCALLTYPE sIMMNotificationClient_OnDefaultDeviceChanged( IMMNotificationClient *This, EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { change_notify *change = (change_notify *)This; struct ao *ao = change->ao; struct wasapi_state *state = ao->priv; /* don't care about "eCapture" or non-"eMultimedia" roles */ if (flow == eCapture || role != eMultimedia) return S_OK; if (change->is_hotplug) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: sending hotplug event\n"); ao_hotplug_event(ao); } else { /* stay on the device the user specified */ if (state->opt_device) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "staying on specified device %s\n", state->opt_device); return S_OK; } /* don't reload if already on the new default */ if (pwstrDeviceId && !lstrcmpW(pwstrDeviceId, change->monitored)) { MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: " "already using default device, no reload required\n"); return S_OK; } /* if we got here, we need to reload */ MP_VERBOSE(ao, "OnDefaultDeviceChanged triggered: requesting ao reload\n"); ao_request_reload(ao); } return S_OK; }
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; }