diff --git a/dep/cubeb/CMakeLists.txt b/dep/cubeb/CMakeLists.txt index cca61d5cf..89737b7ff 100644 --- a/dep/cubeb/CMakeLists.txt +++ b/dep/cubeb/CMakeLists.txt @@ -37,15 +37,15 @@ target_include_directories(cubeb PUBLIC $ $ ) -add_library(speex OBJECT subprojects/speex/resample.c) -set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -target_include_directories(speex INTERFACE subprojects) -target_compile_definitions(speex PUBLIC - OUTSIDE_SPEEX - FLOATING_POINT - EXPORT= - RANDOM_PREFIX=speex -) + add_library(speex OBJECT subprojects/speex/resample.c) + set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + target_include_directories(speex INTERFACE subprojects) + target_compile_definitions(speex PUBLIC + OUTSIDE_SPEEX + FLOATING_POINT + EXPORT= + RANDOM_PREFIX=speex + ) # $ required because of https://gitlab.kitware.com/cmake/cmake/-/issues/15415 target_link_libraries(cubeb PRIVATE $) @@ -58,17 +58,20 @@ find_package(Threads) target_link_libraries(cubeb PRIVATE Threads::Threads) if(LAZY_LOAD_LIBS) - if(NOT APPLE AND NOT WIN32) - # Skip checks on MacOS because it takes ages in XCode. - check_include_files(pulse/pulseaudio.h USE_PULSE) - check_include_files(alsa/asoundlib.h USE_ALSA) - check_include_files(jack/jack.h USE_JACK) - check_include_files(sndio.h USE_SNDIO) - - if(USE_PULSE OR USE_ALSA OR USE_JACK OR USE_SNDIO) - target_link_libraries(cubeb PRIVATE ${CMAKE_DL_LIBS}) + if(NOT APPLE AND NOT WIN32) # Skip checks on MacOS because it takes ages in XCode. + check_include_files(pulse/pulseaudio.h USE_PULSE) + check_include_files(alsa/asoundlib.h USE_ALSA) + check_include_files(jack/jack.h USE_JACK) + check_include_files(sndio.h USE_SNDIO) + + if(USE_PULSE OR USE_ALSA OR USE_JACK OR USE_SNDIO OR USE_AAUDIO) + target_link_libraries(cubeb PRIVATE ${CMAKE_DL_LIBS}) + + if(ANDROID) + target_compile_definitions(cubeb PRIVATE __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__) endif() endif() + endif() elseif(NOT APPLE AND NOT WIN32) @@ -123,56 +126,56 @@ if(USE_SNDIO) endif() if(APPLE) - check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT) - if(USE_AUDIOUNIT) - target_sources(cubeb PRIVATE - src/cubeb_audiounit.cpp - src/cubeb_osx_run_loop.cpp) - target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT) - target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices") - endif() +check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT) +if(USE_AUDIOUNIT) + target_sources(cubeb PRIVATE + src/cubeb_audiounit.cpp + src/cubeb_osx_run_loop.cpp) + target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT) + target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices") +endif() endif() if(WIN32) - check_include_files(audioclient.h USE_WASAPI) - if(USE_WASAPI) - target_sources(cubeb PRIVATE - src/cubeb_wasapi.cpp) - target_compile_definitions(cubeb PRIVATE USE_WASAPI) - target_link_libraries(cubeb PRIVATE avrt ole32 ksuser) - endif() +check_include_files(audioclient.h USE_WASAPI) +if(USE_WASAPI) + target_sources(cubeb PRIVATE + src/cubeb_wasapi.cpp) + target_compile_definitions(cubeb PRIVATE USE_WASAPI) + target_link_libraries(cubeb PRIVATE ole32 ksuser) +endif() - check_include_files("windows.h;mmsystem.h" USE_WINMM) - if(USE_WINMM) - target_sources(cubeb PRIVATE - src/cubeb_winmm.c) - target_compile_definitions(cubeb PRIVATE USE_WINMM) - target_link_libraries(cubeb PRIVATE winmm) - endif() +check_include_files("windows.h;mmsystem.h" USE_WINMM) +if(USE_WINMM) + target_sources(cubeb PRIVATE + src/cubeb_winmm.c) + target_compile_definitions(cubeb PRIVATE USE_WINMM) + target_link_libraries(cubeb PRIVATE winmm) +endif() endif() if(NOT WIN32 AND NOT APPLE) - check_include_files(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) - if(HAVE_SYS_SOUNDCARD_H) - try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests" - ${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c) - if(USE_OSS) - # strlcpy is not available on BSD systems that use glibc, - # like Debian kfreebsd, so try using libbsd if available - include(CheckSymbolExists) - check_symbol_exists(strlcpy string.h HAVE_STRLCPY) - if(NOT HAVE_STRLCPY) - pkg_check_modules(libbsd-overlay IMPORTED_TARGET libbsd-overlay) - if(libbsd-overlay_FOUND) - target_link_libraries(cubeb PRIVATE PkgConfig::libbsd-overlay) - set(HAVE_STRLCPY true) - endif() - endif() - if (HAVE_STRLCPY) - target_sources(cubeb PRIVATE - src/cubeb_oss.c) - target_compile_definitions(cubeb PRIVATE USE_OSS) +check_include_files(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) +if(HAVE_SYS_SOUNDCARD_H) + try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests" + ${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c) + if(USE_OSS) + # strlcpy is not available on BSD systems that use glibc, + # like Debian kfreebsd, so try using libbsd if available + include(CheckSymbolExists) + check_symbol_exists(strlcpy string.h HAVE_STRLCPY) + if(NOT HAVE_STRLCPY) + pkg_check_modules(libbsd-overlay IMPORTED_TARGET libbsd-overlay) + if(libbsd-overlay_FOUND) + target_link_libraries(cubeb PRIVATE PkgConfig::libbsd-overlay) + set(HAVE_STRLCPY true) endif() endif() + if (HAVE_STRLCPY) + target_sources(cubeb PRIVATE + src/cubeb_oss.c) + target_compile_definitions(cubeb PRIVATE USE_OSS) + endif() endif() endif() +endif() \ No newline at end of file diff --git a/dep/cubeb/include/cubeb/cubeb.h b/dep/cubeb/include/cubeb/cubeb.h index 9c7e607ad..5d30d52f9 100644 --- a/dep/cubeb/include/cubeb/cubeb.h +++ b/dep/cubeb/include/cubeb/cubeb.h @@ -258,11 +258,23 @@ typedef enum { the jack backend. */ } cubeb_stream_prefs; +/** + * Input stream audio processing parameters. Only applicable with + * CUBEB_STREAM_PREF_VOICE. + */ +typedef enum { + CUBEB_INPUT_PROCESSING_PARAM_NONE = 0x00, + CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION = 0x01, + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION = 0x02, + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL = 0x04, + CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION = 0x08, +} cubeb_input_processing_params; + /** Stream format initialization parameters. */ typedef struct { cubeb_sample_format format; /**< Requested sample format. One of #cubeb_sample_format. */ - uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */ + uint32_t rate; /**< Requested sample rate. Valid range is [1000, 384000]. */ uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */ cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the @@ -519,6 +531,18 @@ cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate); +/** Get the supported input processing features for this backend. See + cubeb_stream_set_input_processing for how to set them for a particular input + stream. + @param context A pointer to the cubeb context. + @param params Out parameter for the input processing params supported by + this backend. + @retval CUBEB_OK + @retval CUBEB_ERROR_NOT_SUPPORTED */ +CUBEB_EXPORT int +cubeb_get_supported_input_processing_params( + cubeb * context, cubeb_input_processing_params * params); + /** Destroy an application context. This must be called after all stream have * been destroyed. @param context A pointer to the cubeb context.*/ @@ -646,6 +670,30 @@ CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device); +/** Set input mute state for this stream. Some platforms notify the user when an + application is accessing audio input. When all inputs are muted they can + prove to the user that the application is not actively capturing any input. + @param stream the stream for which to set input mute state + @param muted whether the input should mute or not + @retval CUBEB_OK + @retval CUBEB_ERROR_INVALID_PARAMETER if this stream does not have an input + device + @retval CUBEB_ERROR_NOT_SUPPORTED */ +CUBEB_EXPORT int +cubeb_stream_set_input_mute(cubeb_stream * stream, int mute); + +/** Set what input processing features to enable for this stream. + @param stream the stream for which to set input processing features. + @param params what input processing features to use + @retval CUBEB_OK + @retval CUBEB_ERROR if params could not be applied + @retval CUBEB_ERROR_INVALID_PARAMETER if a given param is not supported by + this backend, or if this stream does not have an input device + @retval CUBEB_ERROR_NOT_SUPPORTED */ +CUBEB_EXPORT int +cubeb_stream_set_input_processing_params(cubeb_stream * stream, + cubeb_input_processing_params params); + /** Destroy a cubeb_device structure. @param stream the stream passed in cubeb_stream_get_current_device @param devices the devices to destroy diff --git a/dep/cubeb/src/cubeb-internal.h b/dep/cubeb/src/cubeb-internal.h index 79326e1eb..8357cdc7e 100644 --- a/dep/cubeb/src/cubeb-internal.h +++ b/dep/cubeb/src/cubeb-internal.h @@ -40,6 +40,8 @@ struct cubeb_ops { int (*get_min_latency)(cubeb * context, cubeb_stream_params params, uint32_t * latency_ms); int (*get_preferred_sample_rate)(cubeb * context, uint32_t * rate); + int (*get_supported_input_processing_params)( + cubeb * context, cubeb_input_processing_params * params); int (*enumerate_devices)(cubeb * context, cubeb_device_type type, cubeb_device_collection * collection); int (*device_collection_destroy)(cubeb * context, @@ -62,6 +64,9 @@ struct cubeb_ops { int (*stream_set_name)(cubeb_stream * stream, char const * stream_name); int (*stream_get_current_device)(cubeb_stream * stream, cubeb_device ** const device); + int (*stream_set_input_mute)(cubeb_stream * stream, int mute); + int (*stream_set_input_processing_params)( + cubeb_stream * stream, cubeb_input_processing_params params); int (*stream_device_destroy)(cubeb_stream * stream, cubeb_device * device); int (*stream_register_device_changed_callback)( cubeb_stream * stream, diff --git a/dep/cubeb/src/cubeb.c b/dep/cubeb/src/cubeb.c index d4a2b868c..da52563c3 100644 --- a/dep/cubeb/src/cubeb.c +++ b/dep/cubeb/src/cubeb.c @@ -67,7 +67,7 @@ validate_stream_params(cubeb_stream_params * input_stream_params, XASSERT(input_stream_params || output_stream_params); if (output_stream_params) { if (output_stream_params->rate < 1000 || - output_stream_params->rate > 192000 || + output_stream_params->rate > 768000 || output_stream_params->channels < 1 || output_stream_params->channels > UINT8_MAX) { return CUBEB_ERROR_INVALID_FORMAT; @@ -75,7 +75,7 @@ validate_stream_params(cubeb_stream_params * input_stream_params, } if (input_stream_params) { if (input_stream_params->rate < 1000 || - input_stream_params->rate > 192000 || + input_stream_params->rate > 768000 || input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) { return CUBEB_ERROR_INVALID_FORMAT; @@ -239,9 +239,6 @@ cubeb_get_backend_names() #if defined(USE_SNDIO) "sndio", #endif -#if defined(USE_SUN) - "sun", -#endif #if defined(USE_OSS) "oss", #endif @@ -304,6 +301,21 @@ cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate) return context->ops->get_preferred_sample_rate(context, rate); } +int +cubeb_get_supported_input_processing_params( + cubeb * context, cubeb_input_processing_params * params) +{ + if (!context || !params) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + + if (!context->ops->get_supported_input_processing_params) { + return CUBEB_ERROR_NOT_SUPPORTED; + } + + return context->ops->get_supported_input_processing_params(context, params); +} + void cubeb_destroy(cubeb * context) { @@ -463,6 +475,36 @@ cubeb_stream_get_current_device(cubeb_stream * stream, return stream->context->ops->stream_get_current_device(stream, device); } +int +cubeb_stream_set_input_mute(cubeb_stream * stream, int mute) +{ + if (!stream) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + + if (!stream->context->ops->stream_set_input_mute) { + return CUBEB_ERROR_NOT_SUPPORTED; + } + + return stream->context->ops->stream_set_input_mute(stream, mute); +} + +int +cubeb_stream_set_input_processing_params(cubeb_stream * stream, + cubeb_input_processing_params params) +{ + if (!stream) { + return CUBEB_ERROR_INVALID_PARAMETER; + } + + if (!stream->context->ops->stream_set_input_processing_params) { + return CUBEB_ERROR_NOT_SUPPORTED; + } + + return stream->context->ops->stream_set_input_processing_params(stream, + params); +} + int cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) { diff --git a/dep/cubeb/src/cubeb_alsa.c b/dep/cubeb/src/cubeb_alsa.c index 6b53df087..f114f27d7 100644 --- a/dep/cubeb/src/cubeb_alsa.c +++ b/dep/cubeb/src/cubeb_alsa.c @@ -1472,6 +1472,7 @@ static struct cubeb_ops const alsa_ops = { .get_max_channel_count = alsa_get_max_channel_count, .get_min_latency = alsa_get_min_latency, .get_preferred_sample_rate = alsa_get_preferred_sample_rate, + .get_supported_input_processing_params = NULL, .enumerate_devices = alsa_enumerate_devices, .device_collection_destroy = alsa_device_collection_destroy, .destroy = alsa_destroy, @@ -1485,6 +1486,8 @@ static struct cubeb_ops const alsa_ops = { .stream_set_volume = alsa_stream_set_volume, .stream_set_name = NULL, .stream_get_current_device = NULL, + .stream_set_input_mute = NULL, + .stream_set_input_processing_params = NULL, .stream_device_destroy = NULL, .stream_register_device_changed_callback = NULL, .register_device_collection_changed = NULL}; diff --git a/dep/cubeb/src/cubeb_audiounit.cpp b/dep/cubeb/src/cubeb_audiounit.cpp index 37036a3f2..d823e80ff 100644 --- a/dep/cubeb/src/cubeb_audiounit.cpp +++ b/dep/cubeb/src/cubeb_audiounit.cpp @@ -377,6 +377,15 @@ cubeb_channel_to_channel_label(cubeb_channel channel) } } +bool +is_common_sample_rate(Float64 sample_rate) +{ + /* Some commonly used sample rates and their multiples and divisors. */ + return sample_rate == 8000 || sample_rate == 16000 || sample_rate == 22050 || + sample_rate == 32000 || sample_rate == 44100 || sample_rate == 48000 || + sample_rate == 88200 || sample_rate == 96000; +} + #if TARGET_OS_IPHONE typedef UInt32 AudioDeviceID; typedef UInt32 AudioObjectID; @@ -2502,6 +2511,12 @@ audiounit_configure_output(cubeb_stream * stm) return CUBEB_ERROR; } stm->output_hw_rate = output_hw_desc.mSampleRate; + if (!is_common_sample_rate(stm->output_desc.mSampleRate)) { + /* For uncommon sample rates, we may run into issues with the OS + resampler if we don't do the resampling ourselves, so set the + AudioUnit sample rate to the hardware rate and resample. */ + stm->output_desc.mSampleRate = stm->output_hw_rate; + } LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate); stm->context->channels = output_hw_desc.mChannelsPerFrame; @@ -2709,11 +2724,16 @@ audiounit_setup_stream(cubeb_stream * stm) input_unconverted_params.rate = stm->input_hw_rate; } - /* Create resampler. Output params are unchanged - * because we do not need conversion on the output. */ + cubeb_stream_params output_unconverted_params; + if (has_output(stm)) { + output_unconverted_params = stm->output_stream_params; + output_unconverted_params.rate = stm->output_desc.mSampleRate; + } + + /* Create resampler. */ stm->resampler.reset(cubeb_resampler_create( stm, has_input(stm) ? &input_unconverted_params : NULL, - has_output(stm) ? &stm->output_stream_params : NULL, target_sample_rate, + has_output(stm) ? &output_unconverted_params : NULL, target_sample_rate, stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP, CUBEB_RESAMPLER_RECLOCK_NONE)); if (!stm->resampler) { @@ -3665,6 +3685,7 @@ cubeb_ops const audiounit_ops = { /*.get_max_channel_count =*/audiounit_get_max_channel_count, /*.get_min_latency =*/audiounit_get_min_latency, /*.get_preferred_sample_rate =*/audiounit_get_preferred_sample_rate, + /*.get_supported_input_processing_params =*/NULL, /*.enumerate_devices =*/audiounit_enumerate_devices, /*.device_collection_destroy =*/audiounit_device_collection_destroy, /*.destroy =*/audiounit_destroy, @@ -3678,6 +3699,8 @@ cubeb_ops const audiounit_ops = { /*.stream_set_volume =*/audiounit_stream_set_volume, /*.stream_set_name =*/NULL, /*.stream_get_current_device =*/audiounit_stream_get_current_device, + /*.stream_set_input_mute =*/NULL, + /*.stream_set_input_processing_params =*/NULL, /*.stream_device_destroy =*/audiounit_stream_device_destroy, /*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback, diff --git a/dep/cubeb/src/cubeb_jack.cpp b/dep/cubeb/src/cubeb_jack.cpp index 6f921d607..b417078fc 100644 --- a/dep/cubeb/src/cubeb_jack.cpp +++ b/dep/cubeb/src/cubeb_jack.cpp @@ -160,6 +160,7 @@ static struct cubeb_ops const cbjack_ops = { .get_max_channel_count = cbjack_get_max_channel_count, .get_min_latency = cbjack_get_min_latency, .get_preferred_sample_rate = cbjack_get_preferred_sample_rate, + .get_supported_input_processing_params = NULL, .enumerate_devices = cbjack_enumerate_devices, .device_collection_destroy = cbjack_device_collection_destroy, .destroy = cbjack_destroy, @@ -173,6 +174,8 @@ static struct cubeb_ops const cbjack_ops = { .stream_set_volume = cbjack_stream_set_volume, .stream_set_name = NULL, .stream_get_current_device = cbjack_stream_get_current_device, + .stream_set_input_mute = NULL, + .stream_set_input_processing_params = NULL, .stream_device_destroy = cbjack_stream_device_destroy, .stream_register_device_changed_callback = NULL, .register_device_collection_changed = NULL}; @@ -431,8 +434,10 @@ cbjack_process(jack_nframes_t nframes, void * arg) if (stm->devs & OUT_ONLY) { for (unsigned int c = 0; c < stm->out_params.channels; c++) { float * buffer_out = bufs_out[c]; - for (long f = 0; f < nframes; f++) { - buffer_out[f] = 0.f; + if (buffer_out) { + for (long f = 0; f < nframes; f++) { + buffer_out[f] = 0.f; + } } } } @@ -440,8 +445,10 @@ cbjack_process(jack_nframes_t nframes, void * arg) // paused, capture silence for (unsigned int c = 0; c < stm->in_params.channels; c++) { float * buffer_in = bufs_in[c]; - for (long f = 0; f < nframes; f++) { - buffer_in[f] = 0.f; + if (buffer_in) { + for (long f = 0; f < nframes; f++) { + buffer_in[f] = 0.f; + } } } } @@ -493,8 +500,10 @@ cbjack_process(jack_nframes_t nframes, void * arg) if (stm->devs & OUT_ONLY) { for (unsigned int c = 0; c < stm->out_params.channels; c++) { float * buffer_out = bufs_out[c]; - for (long f = 0; f < nframes; f++) { - buffer_out[f] = 0.f; + if (buffer_out) { + for (long f = 0; f < nframes; f++) { + buffer_out[f] = 0.f; + } } } } @@ -502,8 +511,10 @@ cbjack_process(jack_nframes_t nframes, void * arg) // capture silence for (unsigned int c = 0; c < stm->in_params.channels; c++) { float * buffer_in = bufs_in[c]; - for (long f = 0; f < nframes; f++) { - buffer_in[f] = 0.f; + if (buffer_in) { + for (long f = 0; f < nframes; f++) { + buffer_in[f] = 0.f; + } } } } @@ -542,20 +553,26 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, for (unsigned int c = 0; c < stream->out_params.channels; c++) { float * buffer = bufs_out[c]; for (long f = 0; f < done_frames; f++) { - buffer[f] = - out_interleaved_buffer[(f * stream->out_params.channels) + c] * - stream->volume; + if (buffer) { + buffer[f] = + out_interleaved_buffer[(f * stream->out_params.channels) + c] * + stream->volume; + } } if (done_frames < needed_frames) { // draining for (long f = done_frames; f < needed_frames; f++) { - buffer[f] = 0.f; + if (buffer) { + buffer[f] = 0.f; + } } } if (done_frames == 0) { // stop, but first zero out the existing buffer for (long f = 0; f < needed_frames; f++) { - buffer[f] = 0.f; + if (buffer) { + buffer[f] = 0.f; + } } } } diff --git a/dep/cubeb/src/cubeb_oss.c b/dep/cubeb/src/cubeb_oss.c index 8718fa306..3d09ba4db 100644 --- a/dep/cubeb/src/cubeb_oss.c +++ b/dep/cubeb/src/cubeb_oss.c @@ -1335,6 +1335,7 @@ static struct cubeb_ops const oss_ops = { .get_max_channel_count = oss_get_max_channel_count, .get_min_latency = oss_get_min_latency, .get_preferred_sample_rate = oss_get_preferred_sample_rate, + .get_supported_input_processing_params = NULL, .enumerate_devices = oss_enumerate_devices, .device_collection_destroy = oss_device_collection_destroy, .destroy = oss_destroy, @@ -1348,6 +1349,8 @@ static struct cubeb_ops const oss_ops = { .stream_set_volume = oss_stream_set_volume, .stream_set_name = NULL, .stream_get_current_device = oss_get_current_device, + .stream_set_input_mute = NULL, + .stream_set_input_processing_params = NULL, .stream_device_destroy = oss_stream_device_destroy, .stream_register_device_changed_callback = NULL, .register_device_collection_changed = NULL}; diff --git a/dep/cubeb/src/cubeb_pulse.c b/dep/cubeb/src/cubeb_pulse.c index 686640525..c761487e4 100644 --- a/dep/cubeb/src/cubeb_pulse.c +++ b/dep/cubeb/src/cubeb_pulse.c @@ -1690,6 +1690,7 @@ static struct cubeb_ops const pulse_ops = { .get_max_channel_count = pulse_get_max_channel_count, .get_min_latency = pulse_get_min_latency, .get_preferred_sample_rate = pulse_get_preferred_sample_rate, + .get_supported_input_processing_params = NULL, .enumerate_devices = pulse_enumerate_devices, .device_collection_destroy = pulse_device_collection_destroy, .destroy = pulse_destroy, @@ -1703,6 +1704,8 @@ static struct cubeb_ops const pulse_ops = { .stream_set_volume = pulse_stream_set_volume, .stream_set_name = pulse_stream_set_name, .stream_get_current_device = pulse_stream_get_current_device, + .stream_set_input_mute = NULL, + .stream_set_input_processing_params = NULL, .stream_device_destroy = pulse_stream_device_destroy, .stream_register_device_changed_callback = NULL, .register_device_collection_changed = diff --git a/dep/cubeb/src/cubeb_sndio.c b/dep/cubeb/src/cubeb_sndio.c index 944c28d42..cd295bff5 100644 --- a/dep/cubeb/src/cubeb_sndio.c +++ b/dep/cubeb/src/cubeb_sndio.c @@ -667,6 +667,7 @@ static struct cubeb_ops const sndio_ops = { .get_max_channel_count = sndio_get_max_channel_count, .get_min_latency = sndio_get_min_latency, .get_preferred_sample_rate = sndio_get_preferred_sample_rate, + .get_supported_input_processing_params = NULL, .enumerate_devices = sndio_enumerate_devices, .device_collection_destroy = sndio_device_collection_destroy, .destroy = sndio_destroy, @@ -679,6 +680,8 @@ static struct cubeb_ops const sndio_ops = { .stream_set_volume = sndio_stream_set_volume, .stream_set_name = NULL, .stream_get_current_device = NULL, + .stream_set_input_mute = NULL, + .stream_set_input_processing_params = NULL, .stream_device_destroy = NULL, .stream_register_device_changed_callback = NULL, .register_device_collection_changed = NULL}; diff --git a/dep/cubeb/src/cubeb_triple_buffer.h b/dep/cubeb/src/cubeb_triple_buffer.h index a5a5978fb..759b92e62 100644 --- a/dep/cubeb/src/cubeb_triple_buffer.h +++ b/dep/cubeb/src/cubeb_triple_buffer.h @@ -42,6 +42,13 @@ public: { return (shared_state.load(std::memory_order_relaxed) & BACK_DIRTY_BIT) != 0; } + // Reset state and indices to initial values. + void invalidate() + { + shared_state.store(0, std::memory_order_release); + input_idx = 1; + output_idx = 2; + } private: // Publish a value to the consumer. Returns true if the data was overwritten diff --git a/dep/cubeb/src/cubeb_utils.h b/dep/cubeb/src/cubeb_utils.h index fd7a3d740..851d24de2 100644 --- a/dep/cubeb/src/cubeb_utils.h +++ b/dep/cubeb/src/cubeb_utils.h @@ -182,7 +182,9 @@ public: if (length_ + length > capacity_) { reserve(length_ + length); } - PodCopy(data_ + length_, elements, length); + if (data_) { + PodCopy(data_ + length_, elements, length); + } length_ += length; } @@ -195,12 +197,14 @@ public: if (length_ + length > capacity_) { reserve(length + length_); } - PodZero(data_ + length_, length); + if (data_) { + PodZero(data_ + length_, length); + } length_ += length; } - /** Prepend `length` zero-ed elements to the end of the array, resizing the - * array if needed. + /** Prepend `length` zero-ed elements to the front of the array, resizing and + * shifting the array if needed. * @parameter length the number of elements to prepend to the array. */ void push_front_silence(size_t length) @@ -208,8 +212,10 @@ public: if (length_ + length > capacity_) { reserve(length + length_); } - PodMove(data_ + length, data_, length_); - PodZero(data_, length); + if (data_) { + PodMove(data_ + length, data_, length_); + PodZero(data_, length); + } length_ += length; } @@ -227,6 +233,9 @@ public: if (length > length_) { return false; } + if (!data_) { + return true; + } if (elements) { PodCopy(elements, data_, length); } diff --git a/dep/cubeb/src/cubeb_wasapi.cpp b/dep/cubeb/src/cubeb_wasapi.cpp index c2b4045de..29511a7d8 100644 --- a/dep/cubeb/src/cubeb_wasapi.cpp +++ b/dep/cubeb/src/cubeb_wasapi.cpp @@ -37,6 +37,31 @@ #include "cubeb_tracing.h" #include "cubeb_utils.h" +// Some people have reported glitches with IAudioClient3 capture streams: +// http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html +// https://bugzilla.mozilla.org/show_bug.cgi?id=1590902 +#define ALLOW_AUDIO_CLIENT_3_FOR_INPUT 0 +// IAudioClient3::GetSharedModeEnginePeriod() seem to return min latencies +// bigger than IAudioClient::GetDevicePeriod(), which is confusing (10ms vs +// 3ms), though the default latency is usually the same and we should use the +// IAudioClient3 function anyway, as it's more correct +#define USE_AUDIO_CLIENT_3_MIN_PERIOD 1 +// If this is true, we allow IAudioClient3 the creation of sessions with a +// latency above the default one (usually 10ms). +// Whether we should default this to true or false depend on many things: +// -Does creating a shared IAudioClient3 session (not locked to a format) +// actually forces all the IAudioClient(1) sessions to have the same latency? +// I could find no proof of that. +// -Does creating a shared IAudioClient3 session with a latency >= the default +// one actually improve the latency (as in how late the audio is) at all? +// -Maybe we could expose this as cubeb stream pref +// (e.g. take priority over other apps)? +#define ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT 1 +// If this is true and the user specified a target latency >= the IAudioClient3 +// max one, then we reject it and fall back to IAudioClient(1). There wouldn't +// be much point in having a low latency if that's not what the user wants. +#define REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX 0 + // Windows 10 exposes the IAudioClient3 interface to create low-latency streams. // Copy the interface definition from audioclient.h here to make the code // simpler and so that we can still access IAudioClient3 via COM if cubeb was @@ -204,6 +229,11 @@ struct auto_stream_ref { cubeb_stream * stm; }; +using set_mm_thread_characteristics_function = + decltype(&AvSetMmThreadCharacteristicsW); +using revert_mm_thread_characteristics_function = + decltype(&AvRevertMmThreadCharacteristics); + extern cubeb_ops const wasapi_ops; static com_heap_ptr @@ -301,6 +331,13 @@ struct cubeb { nullptr; void * output_collection_changed_user_ptr = nullptr; UINT64 performance_counter_frequency; + /* Library dynamically opened to increase the render thread priority, and + the two function pointers we need. */ + HMODULE mmcss_module = nullptr; + set_mm_thread_characteristics_function set_mm_thread_characteristics = + nullptr; + revert_mm_thread_characteristics_function revert_mm_thread_characteristics = + nullptr; }; class wasapi_endpoint_notification_client; @@ -1350,10 +1387,8 @@ refill_callback_output(cubeb_stream * stm) long got = refill(stm, nullptr, 0, output_buffer, output_frames); - if (got != output_frames) { - ALOGV("Output callback: output frames requested: %Iu, got %ld", output_frames, - got); - } + ALOGV("Output callback: output frames requested: %Iu, got %ld", output_frames, + got); if (got < 0) { return false; } @@ -1403,7 +1438,8 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream) /* We could consider using "Pro Audio" here for WebAudio and maybe WebRTC. */ - mmcss_handle = AvSetMmThreadCharacteristicsA("Audio", &mmcss_task_index); + mmcss_handle = + stm->context->set_mm_thread_characteristics(L"Audio", &mmcss_task_index); if (!mmcss_handle) { /* This is not fatal, but we might glitch under heavy load. */ LOG("Unable to use mmcss to bump the render thread priority: %lx", @@ -1511,7 +1547,7 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream) } if (mmcss_handle) { - AvRevertMmThreadCharacteristics(mmcss_handle); + stm->context->revert_mm_thread_characteristics(mmcss_handle); } if (FAILED(hr)) { @@ -1524,6 +1560,18 @@ static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream) void wasapi_destroy(cubeb * context); +HANDLE WINAPI +set_mm_thread_characteristics_noop(LPCWSTR, LPDWORD mmcss_task_index) +{ + return (HANDLE)1; +} + +BOOL WINAPI +revert_mm_thread_characteristics_noop(HANDLE mmcss_handle) +{ + return true; +} + HRESULT register_notification_client(cubeb_stream * stm) { @@ -1759,6 +1807,31 @@ wasapi_init(cubeb ** context, char const * context_name) ctx->performance_counter_frequency = 0; } + ctx->mmcss_module = LoadLibraryW(L"Avrt.dll"); + + bool success = false; + if (ctx->mmcss_module) { + ctx->set_mm_thread_characteristics = + reinterpret_cast( + GetProcAddress(ctx->mmcss_module, "AvSetMmThreadCharacteristicsW")); + ctx->revert_mm_thread_characteristics = + reinterpret_cast( + GetProcAddress(ctx->mmcss_module, + "AvRevertMmThreadCharacteristics")); + success = ctx->set_mm_thread_characteristics && + ctx->revert_mm_thread_characteristics; + } + if (!success) { + // This is not a fatal error, but we might end up glitching when + // the system is under high load. + LOG("Could not load avrt.dll or fetch AvSetMmThreadCharacteristicsW " + "AvRevertMmThreadCharacteristics: %lx", + GetLastError()); + ctx->set_mm_thread_characteristics = &set_mm_thread_characteristics_noop; + ctx->revert_mm_thread_characteristics = + &revert_mm_thread_characteristics_noop; + } + *context = ctx; return CUBEB_OK; @@ -1815,6 +1888,10 @@ wasapi_destroy(cubeb * context) } } + if (context->mmcss_module) { + FreeLibrary(context->mmcss_module); + } + delete context; } @@ -1872,6 +1949,44 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, return CUBEB_ERROR; } +#if USE_AUDIO_CLIENT_3_MIN_PERIOD + // This is unreliable as we can't know the actual mixer format cubeb will + // ask for later on (nor we can branch on ALLOW_AUDIO_CLIENT_3_FOR_INPUT), + // and the min latency can change based on that. + com_ptr client3; + hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL, + client3.receive_vpp()); + if (SUCCEEDED(hr)) { + WAVEFORMATEX * mix_format = nullptr; + hr = client3->GetMixFormat(&mix_format); + + if (SUCCEEDED(hr)) { + uint32_t default_period = 0, fundamental_period = 0, min_period = 0, + max_period = 0; + hr = client3->GetSharedModeEnginePeriod(mix_format, &default_period, + &fundamental_period, &min_period, + &max_period); + + auto sample_rate = mix_format->nSamplesPerSec; + CoTaskMemFree(mix_format); + if (SUCCEEDED(hr)) { + // Print values in the same format as IAudioDevice::GetDevicePeriod() + REFERENCE_TIME min_period_rt(frames_to_hns(sample_rate, min_period)); + REFERENCE_TIME default_period_rt( + frames_to_hns(sample_rate, default_period)); + LOG("default device period: %I64d, minimum device period: %I64d", + default_period_rt, min_period_rt); + + *latency_frames = hns_to_frames(params.rate, min_period_rt); + + LOG("Minimum latency in frames: %u", *latency_frames); + + return CUBEB_OK; + } + } + } +#endif + com_ptr client; hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp()); @@ -1891,18 +2006,8 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, LOG("default device period: %I64d, minimum device period: %I64d", default_period, minimum_period); - /* If we're on Windows 10, we can use IAudioClient3 to get minimal latency. - Otherwise, according to the docs, the best latency we can achieve is by - synchronizing the stream and the engine. - http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx - */ - - // #ifdef _WIN32_WINNT_WIN10 -#if 0 - *latency_frames = hns_to_frames(params.rate, minimum_period); -#else + // The minimum_period is only relevant in exclusive streams. *latency_frames = hns_to_frames(params.rate, default_period); -#endif LOG("Minimum latency in frames: %u", *latency_frames); @@ -1992,7 +2097,10 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, if (hr == S_FALSE) { /* Channel layout not supported, but WASAPI gives us a suggestion. Use it, and handle the eventual upmix/downmix ourselves. Ignore the subformat of - the suggestion, since it seems to always be IEEE_FLOAT. */ + the suggestion, since it seems to always be IEEE_FLOAT. + This fallback doesn't update the bit depth, so if a device + only supported bit depths cubeb doesn't support, so IAudioClient3 + streams might fail */ LOG("Using WASAPI suggested format: channels: %d", closest->nChannels); XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE); WAVEFORMATEXTENSIBLE * closest_pcm = @@ -2036,12 +2144,12 @@ initialize_iaudioclient2(com_ptr & audio_client) return CUBEB_OK; } -#if 0 bool initialize_iaudioclient3(com_ptr & audio_client, cubeb_stream * stm, const com_heap_ptr & mix_format, - DWORD flags, EDataFlow direction) + DWORD flags, EDataFlow direction, + REFERENCE_TIME latency_hns) { com_ptr audio_client3; audio_client->QueryInterface(audio_client3.receive()); @@ -2057,24 +2165,22 @@ initialize_iaudioclient3(com_ptr & audio_client, return false; } - // Some people have reported glitches with capture streams: - // http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html - if (direction == eCapture) { - LOG("Audio stream is capture, not using IAudioClient3"); - return false; - } - // Possibly initialize a shared-mode stream using IAudioClient3. Initializing // a stream this way lets you request lower latencies, but also locks the // global WASAPI engine at that latency. // - If we request a shared-mode stream, streams created with IAudioClient - // will - // have their latency adjusted to match. When the shared-mode stream is - // closed, they'll go back to normal. - // - If there's already a shared-mode stream running, then we cannot request - // the engine change to a different latency - we have to match it. - // - It's antisocial to lock the WASAPI engine at its default latency. If we - // would do this, then stop and use IAudioClient instead. + // might have their latency adjusted to match. When the shared-mode stream + // is closed, they'll go back to normal. + // - If there's already a shared-mode stream running, if it created with the + // AUDCLNT_STREAMOPTIONS_MATCH_FORMAT option, the audio engine would be + // locked to that format, so we have to match it (a custom one would fail). + // - We don't lock the WASAPI engine to a format, as it's antisocial towards + // other apps, especially if we locked to a latency >= than its default. + // - If the user requested latency is >= the default one, we might still + // accept it (without locking the format) depending on + // ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT, as we might want to prioritize + // to lower our latency over other apps + // (there might still be latency advantages compared to IAudioDevice(1)). HRESULT hr; uint32_t default_period = 0, fundamental_period = 0, min_period = 0, @@ -2086,28 +2192,59 @@ initialize_iaudioclient3(com_ptr & audio_client, LOG("Could not get shared mode engine period: error: %lx", hr); return false; } - uint32_t requested_latency = stm->latency; + uint32_t requested_latency = + hns_to_frames(mix_format->nSamplesPerSec, latency_hns); +#if !ALLOW_AUDIO_CLIENT_3_LATENCY_OVER_DEFAULT if (requested_latency >= default_period) { - LOG("Requested latency %i greater than default latency %i, not using " - "IAudioClient3", + LOG("Requested latency %i equal or greater than default latency %i," + " not using IAudioClient3", requested_latency, default_period); return false; } +#elif REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX + if (requested_latency > max_period) { + // Fallback to IAudioClient(1) as it's more accepting of large latencies + LOG("Requested latency %i greater than max latency %i," + " not using IAudioClient3", + requested_latency, max_period); + return false; + } +#endif LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i", default_period, fundamental_period, min_period, max_period); // Snap requested latency to a valid value uint32_t old_requested_latency = requested_latency; + // The period is required to be a multiple of the fundamental period + // (and >= min and <= max, which should still be true) + requested_latency -= requested_latency % fundamental_period; if (requested_latency < min_period) { requested_latency = min_period; } - requested_latency -= (requested_latency - min_period) % fundamental_period; + // Likely unnecessary, but won't hurt + if (requested_latency > max_period) { + requested_latency = max_period; + } if (requested_latency != old_requested_latency) { LOG("Requested latency %i was adjusted to %i", old_requested_latency, requested_latency); } - hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency, + DWORD new_flags = flags; + // Always add these flags to IAudioClient3, they might help + // if the stream doesn't have the same format as the audio engine. + new_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM; + new_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + + hr = audio_client3->InitializeSharedAudioStream(new_flags, requested_latency, mix_format.get(), NULL); + // This error should be returned first even if + // the period was locked (AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) + if (hr == AUDCLNT_E_INVALID_STREAM_FLAG) { + LOG("Got AUDCLNT_E_INVALID_STREAM_FLAG, removing some flags"); + hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency, + mix_format.get(), NULL); + } + if (SUCCEEDED(hr)) { return true; } else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) { @@ -2119,22 +2256,37 @@ initialize_iaudioclient3(com_ptr & audio_client, } uint32_t current_period = 0; - WAVEFORMATEX * current_format = nullptr; + WAVEFORMATEX * current_format_ptr = nullptr; // We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise // GetCurrentSharedModeEnginePeriod will return E_POINTER - hr = audio_client3->GetCurrentSharedModeEnginePeriod(¤t_format, + hr = audio_client3->GetCurrentSharedModeEnginePeriod(¤t_format_ptr, ¤t_period); - CoTaskMemFree(current_format); if (FAILED(hr)) { LOG("Could not get current shared mode engine period: error: %lx", hr); return false; } + com_heap_ptr current_format(current_format_ptr); + if (current_format->nSamplesPerSec != mix_format->nSamplesPerSec) { + // Unless some other external app locked the shared mode engine period + // within our audio initialization, this is unlikely to happen, though we + // can't respect the user selected latency, so we fallback on IAudioClient + LOG("IAudioClient3::GetCurrentSharedModeEnginePeriod() returned a " + "different mixer format (nSamplesPerSec) from " + "IAudioClient::GetMixFormat(); not using IAudioClient3"); + return false; + } - if (current_period >= default_period) { - LOG("Current shared mode engine period %i too high, not using IAudioClient", - current_period); +#if REJECT_AUDIO_CLIENT_3_LATENCY_OVER_MAX + // Reject IAudioClient3 if we can't respect the user target latency. + // We don't need to check against default_latency anymore, + // as the current_period is already the best one we could get. + if (old_requested_latency > current_period) { + LOG("Requested latency %i greater than currently locked shared mode " + "latency %i, not using IAudioClient3", + old_requested_latency, current_period); return false; } +#endif hr = audio_client3->InitializeSharedAudioStream(flags, current_period, mix_format.get(), NULL); @@ -2147,7 +2299,6 @@ initialize_iaudioclient3(com_ptr & audio_client, LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr); return false; } -#endif #define DIRECTION_NAME (direction == eCapture ? "capture" : "render") @@ -2171,6 +2322,12 @@ setup_wasapi_stream_one_side(cubeb_stream * stm, return CUBEB_ERROR; } +#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT + constexpr bool allow_audio_client_3 = true; +#else + const bool allow_audio_client_3 = direction == eRender; +#endif + stm->stream_reset_lock.assert_current_thread_owns(); // If user doesn't specify a particular device, we can choose another one when // the given devid is unavailable. @@ -2207,17 +2364,14 @@ setup_wasapi_stream_one_side(cubeb_stream * stm, /* Get a client. We will get all other interfaces we need from * this pointer. */ -#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902 - hr = device->Activate(__uuidof(IAudioClient3), - CLSCTX_INPROC_SERVER, - NULL, audio_client.receive_vpp()); - if (hr == E_NOINTERFACE) { -#endif - hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, - audio_client.receive_vpp()); -#if 0 + if (allow_audio_client_3) { + hr = device->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, NULL, + audio_client.receive_vpp()); + } + if (!allow_audio_client_3 || hr == E_NOINTERFACE) { + hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, + audio_client.receive_vpp()); } -#endif if (FAILED(hr)) { LOG("Could not activate the device to get an audio" @@ -2346,16 +2500,15 @@ setup_wasapi_stream_one_side(cubeb_stream * stm, } } -#if 0 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1590902 - if (initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction)) { + if (allow_audio_client_3 && + initialize_iaudioclient3(audio_client, stm, mix_format, flags, direction, + latency_hns)) { LOG("Initialized with IAudioClient3"); } else { -#endif - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns, 0, - mix_format.get(), NULL); -#if 0 + hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns, + 0, mix_format.get(), NULL); } -#endif + if (FAILED(hr)) { LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr); return CUBEB_ERROR; @@ -3351,6 +3504,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret, CUBEB_DEVICE_FMT_S16NE); ret.default_format = CUBEB_DEVICE_FMT_F32NE; prop_variant fmtvar; + WAVEFORMATEX * wfx = NULL; hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar); if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) { if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) { @@ -3360,8 +3514,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret, ret.max_rate = ret.min_rate = ret.default_rate = pcm->wf.nSamplesPerSec; ret.max_channels = pcm->wf.nChannels; } else if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX)) { - WAVEFORMATEX * wfx = - reinterpret_cast(fmtvar.blob.pBlobData); + wfx = reinterpret_cast(fmtvar.blob.pBlobData); if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || wfx->wFormatTag == WAVE_FORMAT_PCM) { @@ -3371,9 +3524,30 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info & ret, } } - if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp())) && - SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { +#if USE_AUDIO_CLIENT_3_MIN_PERIOD + // Here we assume an IAudioClient3 stream will successfully + // be initialized later (it might fail) +#if ALLOW_AUDIO_CLIENT_3_FOR_INPUT + constexpr bool allow_audio_client_3 = true; +#else + const bool allow_audio_client_3 = flow == eRender; +#endif + com_ptr client3; + uint32_t def, fun, min, max; + if (allow_audio_client_3 && wfx && + SUCCEEDED(dev->Activate(__uuidof(IAudioClient3), CLSCTX_INPROC_SERVER, + NULL, client3.receive_vpp())) && + SUCCEEDED( + client3->GetSharedModeEnginePeriod(wfx, &def, &fun, &min, &max))) { + ret.latency_lo = min; + // This latency might actually be used as "default" and not "max" later on, + // so we return the default (we never really want to use the max anyway) + ret.latency_hi = def; + } else +#endif + if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, + NULL, client.receive_vpp())) && + SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { ret.latency_lo = hns_to_frames(ret.default_rate, min_period); ret.latency_hi = hns_to_frames(ret.default_rate, def_period); } else { @@ -3562,6 +3736,7 @@ cubeb_ops const wasapi_ops = { /*.get_max_channel_count =*/wasapi_get_max_channel_count, /*.get_min_latency =*/wasapi_get_min_latency, /*.get_preferred_sample_rate =*/wasapi_get_preferred_sample_rate, + /*.get_supported_input_processing_params =*/NULL, /*.enumerate_devices =*/wasapi_enumerate_devices, /*.device_collection_destroy =*/wasapi_device_collection_destroy, /*.destroy =*/wasapi_destroy, @@ -3575,6 +3750,8 @@ cubeb_ops const wasapi_ops = { /*.stream_set_volume =*/wasapi_stream_set_volume, /*.stream_set_name =*/NULL, /*.stream_get_current_device =*/NULL, + /*.stream_set_input_mute =*/NULL, + /*.stream_set_input_processing_params =*/NULL, /*.stream_device_destroy =*/NULL, /*.stream_register_device_changed_callback =*/NULL, /*.register_device_collection_changed =*/ diff --git a/dep/cubeb/src/cubeb_winmm.c b/dep/cubeb/src/cubeb_winmm.c index 9aa176ead..b2234a9b5 100644 --- a/dep/cubeb/src/cubeb_winmm.c +++ b/dep/cubeb/src/cubeb_winmm.c @@ -1192,6 +1192,7 @@ static struct cubeb_ops const winmm_ops = { /*.get_max_channel_count=*/winmm_get_max_channel_count, /*.get_min_latency=*/winmm_get_min_latency, /*.get_preferred_sample_rate =*/winmm_get_preferred_sample_rate, + /*.get_supported_input_processing_params =*/NULL, /*.enumerate_devices =*/winmm_enumerate_devices, /*.device_collection_destroy =*/winmm_device_collection_destroy, /*.destroy =*/winmm_destroy, @@ -1205,6 +1206,8 @@ static struct cubeb_ops const winmm_ops = { /*.stream_set_volume =*/winmm_stream_set_volume, /*.stream_set_name =*/NULL, /*.stream_get_current_device =*/NULL, + /*.stream_set_input_mute =*/NULL, + /*.stream_set_input_processing_params =*/NULL, /*.stream_device_destroy =*/NULL, /*.stream_register_device_changed_callback=*/NULL, /*.register_device_collection_changed =*/NULL};