diff --git a/miniaudio.h b/miniaudio.h index 594d04f5..612775dd 100644 --- a/miniaudio.h +++ b/miniaudio.h @@ -9605,6 +9605,46 @@ the two. Use `ma_device_type_playback` for the playback side, or `ma_device_type MA_API void ma_device_get_internal_channel_map(const ma_device* pDevice, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap); +/* +Updates the public and internal device descriptors. + +A normal application should never be calling this directly. It would only be called by device backends in response to a device reroute. + +Information describing a device, such as its ID, data format and period size is specified in a structure referred to as a "descriptor". +Where this function matters is with the data format. With the sample format, channel count, sample rate and channel map, there is the +public facing data format which the application requests, and then there is the actual data format that is used by the backend internally. +Sitting between these two is a data converter. The public descriptor will never change after initialization, but sometimes the backend +will change the internal descriptor. When this happens, the data converter needs to be reinitialized, and that is when a backend would +want to call this function. + + +Parameters +---------- +pDevice (in) + A pointer to the device whose descriptor is being updated. + +deviceType (in) + Either `ma_device_type_playback` or `ma_device_type_capture` specifying which side of the device whose descriptor is being updated. + +pPublicDescriptor (in, optional) + A pointer to the public facing descriptor. This should always be NULL. + +pInternalDescriptor (in) + A pointer to the new internal descriptor. This cannot be NULL. + + +Return Value +------------ +`MA_SUCCESS` if successful; any other result code if an error occurs. + + +Remarks +------- +If you are a normal consumer of miniaudio, do not ever call this function. It should only be called by device backends. +*/ +MA_API ma_result ma_device_update_descriptor(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPublicDescriptor, const ma_device_descriptor* pInternalDescriptor); + + /* Performs post backend initialization routines for setting up internal data conversion. @@ -9651,7 +9691,7 @@ is specified as a parameter rather than deriving it from the device. You do not need to call this manually unless you are doing a custom backend, in which case you need only do it if you're manually performing rerouting or reinitialization. */ -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor); +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPublicDescriptorPlayback, const ma_device_descriptor* pPublicDescriptorCapture, const ma_device_descriptor* pInternalPlaybackDescriptor, const ma_device_descriptor* pInternalCaptureDescriptor); /* @@ -21115,7 +21155,7 @@ MA_API ma_uint32 ma_get_format_priority_index(ma_format format) /* Lower = bette return (ma_uint32)-1; } -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType); +static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPublicDescriptorPlayback, const ma_device_descriptor* pPublicDescriptorCapture, const ma_device_descriptor* pInternalDescriptorPlayback, const ma_device_descriptor* pInternalDescriptorCapture); static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor) { @@ -24227,6 +24267,7 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ma_device_state_wasapi* pDeviceStateWASAPI = ma_device_get_backend_state__wasapi(pDevice); ma_device_init_internal_data__wasapi data; ma_result result; + ma_device_descriptor internalDescriptor; const ma_device_id* pDeviceID = NULL; MA_ASSERT(pDevice != NULL); @@ -24264,20 +24305,17 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev if (deviceType == ma_device_type_playback) { - pDeviceID = pDevice->playback.pID; - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; + pDeviceID = pDevice->playback.pID; + data.shareMode = pDevice->playback.shareMode; } else { - pDeviceID = pDevice->playback.pID; - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; + pDeviceID = pDevice->playback.pID; + data.shareMode = pDevice->capture.shareMode; } - data.sampleRateIn = pDevice->sampleRate; + data.formatIn = ma_device_get_format(pDevice, deviceType); + data.channelsIn = ma_device_get_channels(pDevice, deviceType); + data.sampleRateIn = ma_device_get_sample_rate(pDevice); + ma_device_get_channel_map(pDevice, deviceType, data.channelMapIn, ma_countof(data.channelMapIn)); data.periodSizeInFramesIn = pDeviceStateWASAPI->originalPeriodSizeInFrames; data.periodSizeInMillisecondsIn = pDeviceStateWASAPI->originalPeriodSizeInMilliseconds; data.periodsIn = pDeviceStateWASAPI->originalPeriods; @@ -24293,15 +24331,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev /* At this point we have some new objects ready to go. We need to uninitialize the previous ones and then set the new ones. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { - pDeviceStateWASAPI->pAudioClientCapture = data.pAudioClient; - pDeviceStateWASAPI->pCaptureClient = data.pCaptureClient; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; + pDeviceStateWASAPI->pAudioClientCapture = data.pAudioClient; + pDeviceStateWASAPI->pCaptureClient = data.pCaptureClient; ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientCapture, (HANDLE)pDeviceStateWASAPI->hEventCapture); @@ -24313,15 +24344,8 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev } if (deviceType == ma_device_type_playback) { - pDeviceStateWASAPI->pAudioClientPlayback = data.pAudioClient; - pDeviceStateWASAPI->pRenderClient = data.pRenderClient; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; + pDeviceStateWASAPI->pAudioClientPlayback = data.pAudioClient; + pDeviceStateWASAPI->pRenderClient = data.pRenderClient; ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDeviceStateWASAPI->pAudioClientPlayback, (HANDLE)pDeviceStateWASAPI->hEventPlayback); @@ -24332,6 +24356,20 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); } + /* The internal data converter needs to be reinitialized. */ + MA_ZERO_OBJECT(&internalDescriptor); + internalDescriptor.format = data.formatOut; + internalDescriptor.channels = data.channelsOut; + internalDescriptor.sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(internalDescriptor.channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + internalDescriptor.periodSizeInFrames = data.periodSizeInFramesOut; + internalDescriptor.periodCount = data.periodsOut; + + result = ma_device_update_descriptor(pDevice, deviceType, NULL, &internalDescriptor); + if (result != MA_SUCCESS) { + return result; + } + return MA_SUCCESS; } @@ -24706,7 +24744,6 @@ static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type de return result; } - ma_device__post_init_setup(pDevice, deviceType); ma_device_post_notification_rerouted(pDevice); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ==="); @@ -39741,6 +39778,7 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev ma_device_state_coreaudio* pDeviceStateCoreAudio = ma_device_get_backend_state__coreaudio(pDevice); ma_context_state_coreaudio* pContextStateCoreAudio = ma_context_get_backend_state__coreaudio(ma_device_get_context(pDevice)); ma_device_init_internal_data__coreaudio data; + ma_device_descriptor internalDescriptor; ma_result result; /* This should only be called for playback or capture, not duplex. */ @@ -39751,12 +39789,8 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev data.allowNominalSampleRateChange = MA_FALSE; /* Don't change the nominal sample rate when switching devices. */ if (deviceType == ma_device_type_capture) { - data.formatIn = pDevice->capture.format; - data.channelsIn = pDevice->capture.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap)); - data.shareMode = pDevice->capture.shareMode; - data.registerStopEvent = MA_TRUE; + data.shareMode = pDevice->capture.shareMode; + data.registerStopEvent = MA_TRUE; if (disposePreviousAudioUnit) { pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitCapture); @@ -39766,18 +39800,19 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev ma_free(pDeviceStateCoreAudio->pAudioBufferList, ma_device_get_allocation_callbacks(pDevice)); } } else if (deviceType == ma_device_type_playback) { - data.formatIn = pDevice->playback.format; - data.channelsIn = pDevice->playback.channels; - data.sampleRateIn = pDevice->sampleRate; - MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap)); - data.shareMode = pDevice->playback.shareMode; - data.registerStopEvent = (pDevice->type != ma_device_type_duplex); + data.shareMode = pDevice->playback.shareMode; + data.registerStopEvent = (pDevice->type != ma_device_type_duplex); if (disposePreviousAudioUnit) { pContextStateCoreAudio->AudioOutputUnitStop(pDeviceStateCoreAudio->audioUnitPlayback); pContextStateCoreAudio->AudioComponentInstanceDispose(pDeviceStateCoreAudio->audioUnitPlayback); } } + + data.formatIn = ma_device_get_format(pDevice, deviceType); + data.channelsIn = ma_device_get_channels(pDevice, deviceType); + data.sampleRateIn = ma_device_get_sample_rate(pDevice); + ma_device_get_channel_map(pDevice, deviceType, data.channelMapIn, ma_countof(data.channelMapIn)); data.periodSizeInFramesIn = pDeviceStateCoreAudio->originalPeriodSizeInFrames; data.periodSizeInMillisecondsIn = pDeviceStateCoreAudio->originalPeriodSizeInMilliseconds; data.periodsIn = pDeviceStateCoreAudio->originalPeriods; @@ -39792,6 +39827,22 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev return result; } + /* The device's internal descriptor needs to be updated. */ + MA_ZERO_OBJECT(&internalDescriptor); + internalDescriptor.format = data.formatOut; + internalDescriptor.channels = data.channelsOut; + internalDescriptor.sampleRate = data.sampleRateOut; + MA_COPY_MEMORY(internalDescriptor.channelMap, data.channelMapOut, sizeof(data.channelMapOut)); + internalDescriptor.periodSizeInFrames = data.periodSizeInFramesOut; + internalDescriptor.periodCount = data.periodsOut; + + result = ma_device_update_descriptor(pDevice, deviceType, NULL, &internalDescriptor); + if (result != MA_SUCCESS) { + ma_device_uninit__coreaudio(pDevice); + return result; + } + + /* We need to reinitialize our async state object to account for the new internal format and buffer configuration. To do this we'll need to set up a dummy descriptor. @@ -39806,26 +39857,12 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev pDeviceStateCoreAudio->audioUnitCapture = data.audioUnit; pDeviceStateCoreAudio->pAudioBufferList = data.pAudioBufferList; pDeviceStateCoreAudio->audioBufferCapInFrames = data.periodSizeInFramesOut; - - pDevice->capture.internalFormat = data.formatOut; - pDevice->capture.internalChannels = data.channelsOut; - pDevice->capture.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->capture.internalPeriods = data.periodsOut; } else if (deviceType == ma_device_type_playback) { #if defined(MA_APPLE_DESKTOP) pDeviceStateCoreAudio->deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID; ma_get_AudioObject_uid(pDevice->pContext, pDeviceStateCoreAudio->deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio); #endif pDeviceStateCoreAudio->audioUnitPlayback = data.audioUnit; - - pDevice->playback.internalFormat = data.formatOut; - pDevice->playback.internalChannels = data.channelsOut; - pDevice->playback.internalSampleRate = data.sampleRateOut; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut)); - pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut; - pDevice->playback.internalPeriods = data.periodsOut; } { @@ -39833,18 +39870,18 @@ static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_dev ma_device_descriptor descriptorCapture; MA_ZERO_OBJECT(&descriptorPlayback); - descriptorPlayback.format = pDevice->playback.internalFormat; - descriptorPlayback.channels = pDevice->playback.internalChannels; - descriptorPlayback.sampleRate = pDevice->playback.internalSampleRate; + descriptorPlayback.format = ma_device_get_internal_format(pDevice, ma_device_type_playback); + descriptorPlayback.channels = ma_device_get_internal_channels(pDevice, ma_device_type_playback); + descriptorPlayback.sampleRate = ma_device_get_internal_sample_rate(pDevice, ma_device_type_playback); descriptorPlayback.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; descriptorPlayback.periodCount = pDevice->playback.internalPeriods; MA_ZERO_OBJECT(&descriptorCapture); - descriptorCapture.format = pDevice->capture.internalFormat; - descriptorCapture.channels = pDevice->capture.internalChannels; - descriptorCapture.sampleRate = pDevice->capture.internalSampleRate; - descriptorCapture.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; - descriptorCapture.periodCount = pDevice->capture.internalPeriods; + descriptorCapture.format = ma_device_get_internal_format(pDevice, ma_device_type_capture); + descriptorCapture.channels = ma_device_get_internal_channels(pDevice, ma_device_type_capture); + descriptorCapture.sampleRate = ma_device_get_internal_sample_rate(pDevice, ma_device_type_capture); + descriptorCapture.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; + descriptorCapture.periodCount = pDevice->capture.internalPeriods; result = ma_device_state_async_init(ma_device_get_type(pDevice), &descriptorPlayback, &descriptorCapture, ma_device_get_allocation_callbacks(pDevice), &pDeviceStateCoreAudio->async); if (result != MA_SUCCESS) { @@ -40120,8 +40157,6 @@ static ma_result ma_device_step_extra__coreaudio(ma_device* pDevice) } if (wasReinitialized && result == MA_SUCCESS) { - ma_device__post_init_setup(pDevice, deviceType); - /* Restart the device if required. If this fails we need to stop the device entirely. */ if (ma_device_get_status(pDevice) == ma_device_status_started) { OSStatus status; @@ -44620,7 +44655,7 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev /* The first thing to do is close the streams. */ ma_close_streams__aaudio(pDevice); - /* Now we need to reinitialize each streams. The hardest part with this is just filling output the config and descriptors. */ + /* Now we need to reinitialize each streams. The hardest part with this is just filling out the config and descriptors. */ deviceConfigAAudio = ma_device_config_aaudio_init(); deviceConfigAAudio.usage = pDeviceStateAAudio->usage; deviceConfigAAudio.contentType = pDeviceStateAAudio->contentType; @@ -44631,9 +44666,9 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { descriptorCapture.pDeviceID = NULL; /* Only doing rerouting with default devices. */ descriptorCapture.shareMode = pDevice->capture.shareMode; - descriptorCapture.format = pDevice->capture.format; - descriptorCapture.channels = pDevice->capture.channels; - descriptorCapture.sampleRate = pDevice->sampleRate; + descriptorCapture.format = ma_device_get_format(pDevice, ma_device_type_capture); + descriptorCapture.channels = ma_device_get_channels(pDevice, ma_device_type_capture); + descriptorCapture.sampleRate = ma_device_get_sample_rate(pDevice); descriptorCapture.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames; descriptorCapture.periodCount = 1; } @@ -44641,9 +44676,9 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { descriptorPlayback.pDeviceID = NULL; /* Only doing rerouting with default devices. */ descriptorPlayback.shareMode = pDevice->playback.shareMode; - descriptorPlayback.format = pDevice->playback.format; - descriptorPlayback.channels = pDevice->playback.channels; - descriptorPlayback.sampleRate = pDevice->sampleRate; + descriptorPlayback.format = ma_device_get_format(pDevice, ma_device_type_playback); + descriptorPlayback.channels = ma_device_get_channels(pDevice, ma_device_type_playback); + descriptorPlayback.sampleRate = ma_device_get_sample_rate(pDevice); descriptorPlayback.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames; descriptorPlayback.periodCount = 1; } @@ -44651,16 +44686,26 @@ static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type dev result = ma_device_init_streams__aaudio(pDevice, pDeviceStateAAudio, &deviceConfigAAudio, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change."); - /* Reroute failed! */ break; } - result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture); - if (result != MA_SUCCESS) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); - ma_close_streams__aaudio(pDevice); - /* Reroute failed! */ - break; + /* The internal data converters need to be updated. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + result = ma_device_update_descriptor(pDevice, ma_device_type_capture, NULL, &descriptorCapture); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); + ma_close_streams__aaudio(pDevice); + break; + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_update_descriptor(pDevice, ma_device_type_playback, NULL, &descriptorPlayback); + if (result != MA_SUCCESS) { + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change."); + ma_close_streams__aaudio(pDevice); + break; + } } /* We'll only ever do this in response to a reroute. */ @@ -47387,81 +47432,101 @@ static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint } -static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType) +MA_API ma_result ma_device_update_descriptor(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPublicDescriptor, const ma_device_descriptor* pInternalDescriptor) { ma_result result; + ma_device_descriptor publicDescriptor; + ma_device_descriptor internalDescriptor; + ma_data_converter_config converterConfig; - MA_ASSERT(pDevice != NULL); - - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (pDevice->capture.format == ma_format_unknown) { - pDevice->capture.format = pDevice->capture.internalFormat; - } - if (pDevice->capture.channels == 0) { - pDevice->capture.channels = pDevice->capture.internalChannels; - } - if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS); - if (pDevice->capture.internalChannels == pDevice->capture.channels) { - ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels); - } else { - if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels); - } - } - } + if (pDevice == NULL || pInternalDescriptor == NULL) { + return MA_INVALID_ARGS; } - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (pDevice->playback.format == ma_format_unknown) { - pDevice->playback.format = pDevice->playback.internalFormat; - } - if (pDevice->playback.channels == 0) { - pDevice->playback.channels = pDevice->playback.internalChannels; - } - if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) { - MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS); - if (pDevice->playback.internalChannels == pDevice->playback.channels) { - ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels); - } else { - if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) { - ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels); - } else { - ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels); - } - } - } + /* This function should never be called with ma_device_type_duplex. It should be called once for each side. */ + if (deviceType == ma_device_type_duplex) { + return MA_INVALID_ARGS; } - if (pDevice->sampleRate == 0) { - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - pDevice->sampleRate = pDevice->capture.internalSampleRate; + /* The public descriptor can be null only if the device has already been initialized, in which case we can synthesise it from existing info. */ + if (pPublicDescriptor == NULL) { + if (ma_device_get_status(pDevice) == ma_device_status_uninitialized) { + return MA_INVALID_ARGS; /* Can't know what the public format it. */ + } + + publicDescriptor.format = ma_device_get_format(pDevice, deviceType); + publicDescriptor.channels = ma_device_get_channels(pDevice, deviceType); + publicDescriptor.sampleRate = ma_device_get_sample_rate(pDevice); + ma_device_get_channel_map(pDevice, deviceType, publicDescriptor.channelMap, ma_countof(publicDescriptor.channelMap)); + } else { + publicDescriptor = *pPublicDescriptor; + } + + if (pInternalDescriptor == NULL) { + return MA_INVALID_ARGS; /* Internal format must always be provided. */ + } else { + internalDescriptor = *pInternalDescriptor; + } + + + /* At this point we should not be needing to access `pPublic/InternalDescriptor`. We should be accessing `public/internalDescriptor` instead. We'll clear this to null so we can catch any errors. */ + pPublicDescriptor = NULL; + pInternalDescriptor = NULL; + + /* Make sure we have valid values for everything on the public side. */ + if (publicDescriptor.format == ma_format_unknown) { + publicDescriptor.format = internalDescriptor.format; + } + + if (publicDescriptor.channels == 0) { + publicDescriptor.channels = internalDescriptor.channels; + } + + if (publicDescriptor.sampleRate == 0) { + publicDescriptor.sampleRate = internalDescriptor.sampleRate; + } + + if (publicDescriptor.channelMap[0] == MA_CHANNEL_NONE) { + MA_ASSERT(publicDescriptor.channels <= MA_MAX_CHANNELS); + + if (publicDescriptor.channels == internalDescriptor.channels) { + ma_channel_map_copy(publicDescriptor.channelMap, internalDescriptor.channelMap, internalDescriptor.channels); } else { - pDevice->sampleRate = pDevice->playback.internalSampleRate; + ma_channel_mix_mode channelMixMode; + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + channelMixMode = pDevice->capture.channelMixMode; + } else { + channelMixMode = pDevice->playback.channelMixMode; + } + + if (channelMixMode == ma_channel_mix_mode_simple) { + ma_channel_map_init_blank(publicDescriptor.channelMap, publicDescriptor.channels); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_default, publicDescriptor.channelMap, ma_countof(publicDescriptor.channelMap), publicDescriptor.channels); + } } } - /* Data converters. */ - if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - /* Converting from internal device format to client format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->capture.internalFormat; - converterConfig.channelsIn = pDevice->capture.internalChannels; - converterConfig.sampleRateIn = pDevice->capture.internalSampleRate; - converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap; - converterConfig.formatOut = pDevice->capture.format; - converterConfig.channelsOut = pDevice->capture.channels; - converterConfig.sampleRateOut = pDevice->sampleRate; - converterConfig.pChannelMapOut = pDevice->capture.channelMap; + + /* The data converter can now be initialized. */ + converterConfig = ma_data_converter_config_init_default(); + converterConfig.allowDynamicSampleRate = MA_FALSE; + converterConfig.resampling.algorithm = pDevice->resampling.algorithm; + converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; + converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; + converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + converterConfig.formatIn = internalDescriptor.format; + converterConfig.channelsIn = internalDescriptor.channels; + converterConfig.sampleRateIn = internalDescriptor.sampleRate; + converterConfig.pChannelMapIn = internalDescriptor.channelMap; + converterConfig.formatOut = publicDescriptor.format; + converterConfig.channelsOut = publicDescriptor.channels; + converterConfig.sampleRateOut = publicDescriptor.sampleRate; + converterConfig.pChannelMapOut = publicDescriptor.channelMap; converterConfig.channelMixMode = pDevice->capture.channelMixMode; converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_status(pDevice) != ma_device_status_uninitialized) { @@ -47472,26 +47537,17 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d if (result != MA_SUCCESS) { return result; } - } - - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - /* Converting from client format to device format. */ - ma_data_converter_config converterConfig = ma_data_converter_config_init_default(); - converterConfig.formatIn = pDevice->playback.format; - converterConfig.channelsIn = pDevice->playback.channels; - converterConfig.sampleRateIn = pDevice->sampleRate; - converterConfig.pChannelMapIn = pDevice->playback.channelMap; - converterConfig.formatOut = pDevice->playback.internalFormat; - converterConfig.channelsOut = pDevice->playback.internalChannels; - converterConfig.sampleRateOut = pDevice->playback.internalSampleRate; - converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap; - converterConfig.channelMixMode = pDevice->playback.channelMixMode; - converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels; - converterConfig.allowDynamicSampleRate = MA_FALSE; - converterConfig.resampling.algorithm = pDevice->resampling.algorithm; - converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder; - converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable; - converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData; + } else { + converterConfig.formatIn = publicDescriptor.format; + converterConfig.channelsIn = publicDescriptor.channels; + converterConfig.sampleRateIn = publicDescriptor.sampleRate; + converterConfig.pChannelMapIn = publicDescriptor.channelMap; + converterConfig.formatOut = internalDescriptor.format; + converterConfig.channelsOut = internalDescriptor.channels; + converterConfig.sampleRateOut = internalDescriptor.sampleRate; + converterConfig.pChannelMapOut = internalDescriptor.channelMap; + converterConfig.channelMixMode = pDevice->capture.channelMixMode; + converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels; /* Make sure the old converter is uninitialized first. */ if (ma_device_get_status(pDevice) != ma_device_status_uninitialized) { @@ -47505,6 +47561,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d } + /* TODO: Reassess this. Doesn't feel right that this needs to be done here. Why can we not just used a fixed sized buffer like 1024? */ /* If the device is doing playback (ma_device_type_playback or ma_device_type_duplex), there's a couple of situations where we'll need a heap allocated cache. @@ -47524,7 +47581,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d in fixed sized chunks and then cache any residual unused input frames, those of which will be processed at a later stage. */ - if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + if (deviceType == ma_device_type_playback) { ma_uint64 unused; pDevice->playback.inputCacheConsumed = 0; @@ -47538,9 +47595,9 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d ma_uint64 newInputCacheCap; ma_uint64 newInputCacheSizeInBytes; - newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames); + newInputCacheCap = ma_calculate_frame_count_after_resampling(ma_device_get_internal_sample_rate(pDevice, ma_device_type_playback), ma_device_get_sample_rate(pDevice), pDevice->playback.internalPeriodSizeInFrames); - newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels); + newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(ma_device_get_format(pDevice, ma_device_type_playback), ma_device_get_channels(pDevice, ma_device_type_playback)); if (newInputCacheSizeInBytes > MA_SIZE_MAX) { ma_free(pDevice->playback.pInputCache, ma_device_get_allocation_callbacks(pDevice)); pDevice->playback.pInputCache = NULL; @@ -47566,53 +47623,115 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d } } + + /* Update the internal format, channels, rate, map. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + pDevice->capture.internalFormat = internalDescriptor.format; + pDevice->capture.internalChannels = internalDescriptor.channels; + pDevice->capture.internalSampleRate = internalDescriptor.sampleRate; + pDevice->capture.internalPeriodSizeInFrames = internalDescriptor.periodSizeInFrames; + pDevice->capture.internalPeriods = internalDescriptor.periodCount; + } + + if (deviceType == ma_device_type_playback) { + pDevice->playback.internalFormat = internalDescriptor.format; + pDevice->playback.internalChannels = internalDescriptor.channels; + pDevice->playback.internalSampleRate = internalDescriptor.sampleRate; + pDevice->playback.internalPeriodSizeInFrames = internalDescriptor.periodSizeInFrames; + pDevice->playback.internalPeriods = internalDescriptor.periodCount; + } + + /* Update the public format, channels, rate. */ + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) { + pDevice->capture.format = publicDescriptor.format; + pDevice->capture.channels = publicDescriptor.channels; + } + + if (deviceType == ma_device_type_playback) { + pDevice->playback.format = publicDescriptor.format; + pDevice->playback.channels = publicDescriptor.channels; + } + + pDevice->sampleRate = publicDescriptor.sampleRate; + return MA_SUCCESS; } -MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture) + +static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPublicDescriptorPlayback, const ma_device_descriptor* pPublicDescriptorCapture, const ma_device_descriptor* pInternalDescriptorPlayback, const ma_device_descriptor* pInternalDescriptorCapture) { + ma_result result; + + MA_ASSERT(pDevice != NULL); + + if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { + result = ma_device_update_descriptor(pDevice, ma_device_type_capture, pPublicDescriptorCapture, pInternalDescriptorCapture); + if (result != MA_SUCCESS) { + return result; + } + } + + if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { + result = ma_device_update_descriptor(pDevice, ma_device_type_playback, pPublicDescriptorPlayback, pInternalDescriptorPlayback); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPublicDescriptorPlayback, const ma_device_descriptor* pPublicDescriptorCapture, const ma_device_descriptor* pInternalDescriptorPlayback, const ma_device_descriptor* pInternalDescriptorCapture) +{ + ma_result result; + if (pDevice == NULL) { return MA_INVALID_ARGS; } /* Capture. */ if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) { - if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) { + if (ma_device_descriptor_is_valid(pInternalDescriptorCapture) == MA_FALSE) { return MA_INVALID_ARGS; } - pDevice->capture.internalFormat = pDescriptorCapture->format; - pDevice->capture.internalChannels = pDescriptorCapture->channels; - pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate; - MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap)); - pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames; - pDevice->capture.internalPeriods = pDescriptorCapture->periodCount; + pDevice->capture.internalFormat = pInternalDescriptorCapture->format; + pDevice->capture.internalChannels = pInternalDescriptorCapture->channels; + pDevice->capture.internalSampleRate = pInternalDescriptorCapture->sampleRate; + MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pInternalDescriptorCapture->channelMap, sizeof(pInternalDescriptorCapture->channelMap)); + pDevice->capture.internalPeriodSizeInFrames = pInternalDescriptorCapture->periodSizeInFrames; + pDevice->capture.internalPeriods = pInternalDescriptorCapture->periodCount; if (pDevice->capture.internalPeriodSizeInFrames == 0) { - pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate); + pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pInternalDescriptorCapture->periodSizeInMilliseconds, pInternalDescriptorCapture->sampleRate); } } /* Playback. */ if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) { - if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) { + if (ma_device_descriptor_is_valid(pInternalDescriptorPlayback) == MA_FALSE) { return MA_INVALID_ARGS; } - pDevice->playback.internalFormat = pDescriptorPlayback->format; - pDevice->playback.internalChannels = pDescriptorPlayback->channels; - pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate; - MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap)); - pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames; - pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount; + pDevice->playback.internalFormat = pInternalDescriptorPlayback->format; + pDevice->playback.internalChannels = pInternalDescriptorPlayback->channels; + pDevice->playback.internalSampleRate = pInternalDescriptorPlayback->sampleRate; + MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pInternalDescriptorPlayback->channelMap, sizeof(pInternalDescriptorPlayback->channelMap)); + pDevice->playback.internalPeriodSizeInFrames = pInternalDescriptorPlayback->periodSizeInFrames; + pDevice->playback.internalPeriods = pInternalDescriptorPlayback->periodCount; if (pDevice->playback.internalPeriodSizeInFrames == 0) { - pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate); + pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pInternalDescriptorPlayback->periodSizeInMilliseconds, pInternalDescriptorPlayback->sampleRate); } } /* Update data conversion. */ - return ma_device__post_init_setup(pDevice, deviceType); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ + result = ma_device__post_init_setup(pDevice, deviceType, pPublicDescriptorPlayback, pPublicDescriptorCapture, pInternalDescriptorPlayback, pInternalDescriptorCapture); /* TODO: Should probably rename ma_device__post_init_setup() to something better. */ + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; } @@ -48817,6 +48936,8 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC { ma_result result; ma_device_backend_info backendInfo; + ma_device_descriptor publicDescriptorPlayback; + ma_device_descriptor publicDescriptorCapture; ma_device_descriptor descriptorPlayback; ma_device_descriptor descriptorCapture; ma_threading_mode threadingMode; @@ -48973,6 +49094,14 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } #endif + /* + The backend will change the descriptor from the public format to its internal format. We need to keep track of the + public descriptors in order to know how to initialize the data converter later on. + */ + publicDescriptorPlayback = descriptorPlayback; + publicDescriptorCapture = descriptorCapture; + + /* We are now ready to initialize the backend. */ initParams.init.pDeviceBackendConfig = ma_device_config_find_backend_config(pConfig, pContext->pVTable); initParams.init.pDescriptorPlayback = &descriptorPlayback; initParams.init.pDescriptorCapture = &descriptorCapture; @@ -49037,7 +49166,7 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } } - result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture); + result = ma_device_post_init(pDevice, pConfig->deviceType, &publicDescriptorPlayback, &publicDescriptorCapture, &descriptorPlayback, &descriptorCapture); if (result != MA_SUCCESS) { ma_device_uninit(pDevice); return result; @@ -49141,11 +49270,16 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s", pDevice->capture.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s", pDevice->capture.converter.isPassthrough ? "YES" : "NO"); { + ma_channel internalChannelMap[MA_MAX_CHANNELS]; + ma_channel publicChannelMap[MA_MAX_CHANNELS]; char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr)); - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}", channelMapStr); - ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr)); + ma_device_get_internal_channel_map(pDevice, ma_device_type_capture, internalChannelMap, ma_countof(internalChannelMap)); + ma_channel_map_to_string(internalChannelMap, ma_device_get_internal_channels(pDevice, ma_device_type_capture), channelMapStr, sizeof(channelMapStr)); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}", channelMapStr); + + ma_device_get_channel_map(pDevice, ma_device_type_capture, publicChannelMap, ma_countof(publicChannelMap)); + ma_channel_map_to_string(publicChannelMap, ma_device_get_channels(pDevice, ma_device_type_capture), channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}", channelMapStr); } } @@ -49165,11 +49299,16 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s", pDevice->playback.converter.hasResampler ? "YES" : "NO"); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s", pDevice->playback.converter.isPassthrough ? "YES" : "NO"); { + ma_channel publicChannelMap[MA_MAX_CHANNELS]; + ma_channel internalChannelMap[MA_MAX_CHANNELS]; char channelMapStr[1024]; - ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr)); + + ma_device_get_channel_map(pDevice, ma_device_type_playback, publicChannelMap, ma_countof(publicChannelMap)); + ma_channel_map_to_string(publicChannelMap, ma_device_get_channels(pDevice, ma_device_type_playback), channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}", channelMapStr); - ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr)); + ma_device_get_internal_channel_map(pDevice, ma_device_type_playback, internalChannelMap, ma_countof(internalChannelMap)); + ma_channel_map_to_string(internalChannelMap, ma_device_get_internal_channels(pDevice, ma_device_type_playback), channelMapStr, sizeof(channelMapStr)); ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}", channelMapStr); } }