diff --git a/extras/backends/pipewire/miniaudio_pipewire.c b/extras/backends/pipewire/miniaudio_pipewire.c index d76bb031..933cc87c 100644 --- a/extras/backends/pipewire/miniaudio_pipewire.c +++ b/extras/backends/pipewire/miniaudio_pipewire.c @@ -1672,12 +1672,22 @@ static void ma_context_uninit__pipewire(ma_context* pContext) } -static ma_device_enumeration_result ma_context_enumerate_default_device_by_type__pipewire(ma_context* pContext, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData) +static ma_device_enumeration_result ma_context_enumerate_default_device_by_type__pipewire(ma_context* pContext, ma_device_type deviceType, ma_uint32 sampleRate, ma_enum_devices_callback_proc callback, void* pUserData) { ma_device_info deviceInfo; + ma_uint32 minSampleRate; + ma_uint32 maxSampleRate; (void)pContext; + if (sampleRate != 0) { + minSampleRate = sampleRate; + maxSampleRate = sampleRate; + } else { + minSampleRate = ma_standard_sample_rate_min; + maxSampleRate = ma_standard_sample_rate_max; + } + MA_PIPEWIRE_ZERO_OBJECT(&deviceInfo); /* Default. */ @@ -1693,12 +1703,8 @@ static ma_device_enumeration_result ma_context_enumerate_default_device_by_type_ ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "Default Capture Device", (size_t)-1); } - /* Data Format. PipeWire supports everything. Its maximum channel count is 64. */ - ma_device_info_add_native_data_format(&deviceInfo, ma_format_f32, 1, 64, ma_standard_sample_rate_min, ma_standard_sample_rate_max); - ma_device_info_add_native_data_format(&deviceInfo, ma_format_s16, 1, 64, ma_standard_sample_rate_min, ma_standard_sample_rate_max); - ma_device_info_add_native_data_format(&deviceInfo, ma_format_s32, 1, 64, ma_standard_sample_rate_min, ma_standard_sample_rate_max); - ma_device_info_add_native_data_format(&deviceInfo, ma_format_s24, 1, 64, ma_standard_sample_rate_min, ma_standard_sample_rate_max); - ma_device_info_add_native_data_format(&deviceInfo, ma_format_u8, 1, 64, ma_standard_sample_rate_min, ma_standard_sample_rate_max); + /* Data Format. */ + ma_device_info_add_native_data_format(&deviceInfo, ma_format_f32, 1, 64, minSampleRate, maxSampleRate); return callback(deviceType, &deviceInfo, pUserData); } @@ -1707,6 +1713,7 @@ static ma_device_enumeration_result ma_context_enumerate_default_device_by_type_ #define MA_PW_CORE_SYNC_FLAG_ENUM_DONE (1 << 0) #define MA_PW_CORE_SYNC_FLAG_DEFAULTS_DONE (1 << 1) +#define MA_PW_CORE_SYNC_FLAG_SETTINGS_DONE (1 << 2) typedef struct { @@ -1714,11 +1721,15 @@ typedef struct struct ma_pw_loop* pLoop; struct ma_pw_core* pCore; struct ma_pw_registry* pRegistry; - struct ma_pw_metadata* pMetadata; - struct ma_spa_hook metadataListener; + struct ma_pw_metadata* pMetadata; /* "default" metadata. */ + struct ma_pw_metadata* pMetadataSettings; /* "settings" metadata. */ + struct ma_spa_hook metadataListener; /* "default" metadata */ + struct ma_spa_hook metadataListenerSettings; /* "settings" metadata. */ int seqDefaults; + int seqSettings; int seqEnumeration; ma_uint32 syncFlags; + ma_uint32 clockRate; /* "clock.rate" from the "settings" metadata. */ const ma_allocation_callbacks* pAllocationCallbacks; struct { @@ -1873,6 +1884,33 @@ static struct ma_pw_metadata_events ma_gMetadataEventsPipeWire = }; +static int ma_on_metadata_property_settings__pipewire(void* data, ma_uint32 subject, const char* key, const char* type, const char* value) +{ + ma_enumerate_devices_data_pipewire* pEnumData = (ma_enumerate_devices_data_pipewire*)data; + + (void)pEnumData; + (void)subject; + (void)type; + + if (key == NULL) { + return 0; + } + + if (strcmp(key, "clock.rate") == 0) { + pEnumData->clockRate = (ma_uint32)atoi(value); + } + + /*printf("Metadata Property: Subject=%u, Key=%s, Type=%s, Value=%s\n", subject, key, type, value);*/ + return 0; +} + +static struct ma_pw_metadata_events ma_gMetadataEventsPipeWire_Settings = +{ + MA_PW_VERSION_METADATA_EVENTS, + ma_on_metadata_property_settings__pipewire +}; + + typedef struct { enum ma_spa_audio_format format; @@ -2106,16 +2144,6 @@ static void ma_add_native_data_format__pipewire(ma_context_state_pipewire* pCont } #endif -static void ma_add_native_data_format__pipewire(ma_context_state_pipewire* pContextStatePipeWire, struct ma_pw_core* pCore, struct ma_pw_loop* pLoop, ma_device_type deviceType, ma_device_info* pDeviceInfo) -{ - (void)pContextStatePipeWire; - (void)pCore; - (void)pLoop; - (void)deviceType; - - ma_device_info_add_native_data_format(pDeviceInfo, ma_format_f32, 1, 64, ma_standard_sample_rate_min, ma_standard_sample_rate_max); -} - static void ma_registry_event_global_add_enumeration_by_type__pipewire(ma_enumerate_devices_data_pipewire* pEnumData, ma_uint32 id, ma_uint32 permissions, const char* type, ma_uint32 version, const struct ma_spa_dict* props, ma_device_type deviceType) { ma_device_info deviceInfo; @@ -2167,26 +2195,41 @@ static void ma_registry_event_global_add_enumeration__pipewire(void* pUserData, return; } - /* We need to check for our default devices. */ + /* Some device info needs to be retrieved from metadata. */ if (strcmp(type, MA_PW_TYPE_INTERFACE_Metadata) == 0) { const char* pName; pName = ma_spa_dict_lookup(props, MA_PW_KEY_METADATA_NAME); - if (pName != NULL && strcmp(pName, "default") == 0) { - pEnumData->pMetadata = (struct ma_pw_metadata*)pEnumData->pContextStatePipeWire->pw_registry_bind(pEnumData->pRegistry, id, MA_PW_TYPE_INTERFACE_Metadata, MA_PW_VERSION_METADATA, 0); - if (pEnumData->pMetadata != NULL) { - MA_PIPEWIRE_ZERO_OBJECT(&pEnumData->metadataListener); + if (pName != NULL) { + /* Default devices. */ + if (strcmp(pName, "default") == 0) { + pEnumData->pMetadata = (struct ma_pw_metadata*)pEnumData->pContextStatePipeWire->pw_registry_bind(pEnumData->pRegistry, id, MA_PW_TYPE_INTERFACE_Metadata, MA_PW_VERSION_METADATA, 0); + if (pEnumData->pMetadata != NULL) { + MA_PIPEWIRE_ZERO_OBJECT(&pEnumData->metadataListener); + + /* Not using pw_metadata_add_listener() because it appears to be an inline function and thus not exported by libpipewire. */ + ((struct ma_pw_metadata_methods*)((struct ma_spa_interface*)pEnumData->pMetadata)->cb.funcs)->add_listener(pEnumData->pMetadata, &pEnumData->metadataListener, &ma_gMetadataEventsPipeWire, pEnumData); + + /*spa_api_method_r(int, -ENOTSUP, ma_pw_metadata, (struct spa_interface*)pEnumData->pMetadata, add_listener, 0, &pEnumData->metadataListener, &ma_gMetadataEventsPipeWire, pEnumData);*/ + /*pEnumData->pContextStatePipeWire->pw_metadata_add_listener(pMetadata, &pEnumData->metadataListener, &ma_gMetadataEventsPipeWire, NULL);*/ - /* Not using pw_metadata_add_listener() because it appears to be an inline function and thus not exported by libpipewire. */ - ((struct ma_pw_metadata_methods*)((struct ma_spa_interface*)pEnumData->pMetadata)->cb.funcs)->add_listener(pEnumData->pMetadata, &pEnumData->metadataListener, &ma_gMetadataEventsPipeWire, pEnumData); + pEnumData->seqDefaults = pEnumData->pContextStatePipeWire->pw_core_sync(pEnumData->pCore, MA_PW_ID_CORE, 0); + } + } - /*spa_api_method_r(int, -ENOTSUP, ma_pw_metadata, (struct spa_interface*)pEnumData->pMetadata, add_listener, 0, &pEnumData->metadataListener, &ma_gMetadataEventsPipeWire, pEnumData);*/ - /*pEnumData->pContextStatePipeWire->pw_metadata_add_listener(pMetadata, &pEnumData->metadataListener, &ma_gMetadataEventsPipeWire, NULL);*/ + /* Sample rate. */ + if (strcmp(pName, "settings") == 0) { + pEnumData->pMetadataSettings = (struct ma_pw_metadata*)pEnumData->pContextStatePipeWire->pw_registry_bind(pEnumData->pRegistry, id, MA_PW_TYPE_INTERFACE_Metadata, MA_PW_VERSION_METADATA, 0); + if (pEnumData->pMetadataSettings != NULL) { + MA_PIPEWIRE_ZERO_OBJECT(&pEnumData->metadataListenerSettings); + ((struct ma_pw_metadata_methods*)((struct ma_spa_interface*)pEnumData->pMetadataSettings)->cb.funcs)->add_listener(pEnumData->pMetadataSettings, &pEnumData->metadataListenerSettings, &ma_gMetadataEventsPipeWire_Settings, pEnumData); - pEnumData->seqDefaults = pEnumData->pContextStatePipeWire->pw_core_sync(pEnumData->pCore, MA_PW_ID_CORE, 0); + pEnumData->seqSettings = pEnumData->pContextStatePipeWire->pw_core_sync(pEnumData->pCore, MA_PW_ID_CORE, 0); + } } } + return; } @@ -2281,12 +2324,18 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma for (;;) { pContextStatePipeWire->pw_loop_iterate(pLoop, -1); - if (enumData.syncFlags & MA_PW_CORE_SYNC_FLAG_ENUM_DONE) { + if ((enumData.syncFlags & MA_PW_CORE_SYNC_FLAG_ENUM_DONE) != 0) { if (enumData.seqDefaults == 0) { break; /* We don't have a "default" metadata. */ } + if ((enumData.syncFlags & MA_PW_CORE_SYNC_FLAG_DEFAULTS_DONE) != 0) { + break; + } - if (enumData.syncFlags & MA_PW_CORE_SYNC_FLAG_DEFAULTS_DONE) { + if (enumData.seqSettings == 0) { + break; /* We don't have a "settings" metadata. */ + } + if ((enumData.syncFlags & MA_PW_CORE_SYNC_FLAG_SETTINGS_DONE) != 0) { break; } } @@ -2298,6 +2347,16 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma size_t iDevice; ma_bool32 hasDefaultPlaybackDevice = MA_FALSE; ma_bool32 hasDefaultCaptureDevice = MA_FALSE; + ma_uint32 minSampleRate; + ma_uint32 maxSampleRate; + + if (enumData.clockRate != 0) { + minSampleRate = enumData.clockRate; + maxSampleRate = enumData.clockRate; + } else { + minSampleRate = ma_standard_sample_rate_min; + maxSampleRate = ma_standard_sample_rate_max; + } /* Playback devices. */ for (iDevice = 0; iDevice < enumData.playback.deviceInfoCount; iDevice += 1) { @@ -2308,7 +2367,7 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma } /* Now we need to open the stream and get it's native data format. */ - ma_add_native_data_format__pipewire(pContextStatePipeWire, pCore, pLoop, ma_device_type_playback, &enumData.playback.pDeviceInfos[iDevice]); + ma_device_info_add_native_data_format(&enumData.playback.pDeviceInfos[iDevice], ma_format_f32, 1, 64, minSampleRate, maxSampleRate); cbResult = callback(ma_device_type_playback, &enumData.playback.pDeviceInfos[iDevice], pUserData); } @@ -2316,7 +2375,7 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma if (enumData.playback.deviceInfoCount > 0 && !hasDefaultPlaybackDevice) { if (cbResult == MA_DEVICE_ENUMERATION_CONTINUE) { - cbResult = ma_context_enumerate_default_device_by_type__pipewire(pContext, ma_device_type_playback, callback, pUserData); + cbResult = ma_context_enumerate_default_device_by_type__pipewire(pContext, ma_device_type_playback, enumData.clockRate, callback, pUserData); } } @@ -2330,7 +2389,7 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma } /* Now we need to open the stream and get it's native data format. */ - ma_add_native_data_format__pipewire(pContextStatePipeWire, pCore, pLoop, ma_device_type_capture, &enumData.capture.pDeviceInfos[iDevice]); + ma_device_info_add_native_data_format(&enumData.capture.pDeviceInfos[iDevice], ma_format_f32, 1, 64, minSampleRate, maxSampleRate); cbResult = callback(ma_device_type_capture, &enumData.capture.pDeviceInfos[iDevice], pUserData); } @@ -2338,7 +2397,7 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma if (enumData.capture.deviceInfoCount > 0 && !hasDefaultCaptureDevice) { if (cbResult == MA_DEVICE_ENUMERATION_CONTINUE) { - cbResult = ma_context_enumerate_default_device_by_type__pipewire(pContext, ma_device_type_capture, callback, pUserData); + cbResult = ma_context_enumerate_default_device_by_type__pipewire(pContext, ma_device_type_capture, enumData.clockRate, callback, pUserData); } }