diff --git a/CMakeLists.txt b/CMakeLists.txt index c834e9396..a810e11c2 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -131,7 +131,7 @@ add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS) if (MSVC) add_compile_options($<$:/std:c++latest>) - # cubeb and boost still make use of deprecated result_of. + # boost still makes use of deprecated result_of. add_definitions(-D_HAS_DEPRECATED_RESULT_OF) else() set(CMAKE_CXX_STANDARD 20) diff --git a/README.md b/README.md index 1f03b7018..62dfa5c2c 100755 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ yuzu emulator early access ============= -This is the source code for early-access 2289. +This is the source code for early-access 2291. ## Legal Notice diff --git a/externals/cubeb/.clang-format b/externals/cubeb/.clang-format new file mode 100755 index 000000000..b8b0df0bf --- /dev/null +++ b/externals/cubeb/.clang-format @@ -0,0 +1,13 @@ +IndentWidth: 2 +UseTab: Never +ReflowComments: true +PointerAlignment: Middle +AlignAfterOpenBracket: Align +AlwaysBreakAfterReturnType: TopLevel +ColumnLimit: 80 +BreakBeforeBraces: Custom +BraceWrapping: + AfterFunction: true + AfterControlStatement: Never +SpaceBeforeParens: ControlStatements +BreakBeforeBinaryOperators: None diff --git a/externals/cubeb/.github/workflows/build.yml b/externals/cubeb/.github/workflows/build.yml new file mode 100755 index 000000000..a5f533b8b --- /dev/null +++ b/externals/cubeb/.github/workflows/build.yml @@ -0,0 +1,75 @@ +name: Build + +on: [push, pull_request] + +jobs: + build: + runs-on: ${{ matrix.os }} + env: + BUILD_TYPE: ${{ matrix.type }} + strategy: + matrix: + os: [ubuntu-20.04, windows-2019, macos-10.15] + type: [Release, Debug] + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Install Dependencies (Linux) + run: sudo apt-get update && sudo apt-get install libpulse-dev pulseaudio + if: matrix.os == 'ubuntu-20.04' + + - name: Start Sound Server (Linux) + run: pulseaudio -D --start + if: matrix.os == 'ubuntu-20.04' + + - name: Configure CMake + shell: bash + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE + + - name: Build + shell: bash + run: cmake --build build + + - name: Test + shell: bash + run: (cd build && ctest -V) + if: ${{ matrix.os == 'ubuntu-20.04' || matrix.os == 'macos-10.15' }} + + build-android: + runs-on: ubuntu-20.04 + env: + BUILD_TYPE: ${{ matrix.type }} + strategy: + matrix: + type: [Release, Debug] + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Configure CMake + shell: bash + run: cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=android-26 + + - name: Build + shell: bash + run: cmake --build build + + check_format: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + with: + submodules: true + + - name: Configure CMake + shell: bash + run: cmake -S . -B build + + - name: Check format + shell: bash + run: cmake --build build --target clang-format-check + diff --git a/externals/cubeb/CMakeLists.txt b/externals/cubeb/CMakeLists.txt index 5aeb6229b..6cd808e5a 100755 --- a/externals/cubeb/CMakeLists.txt +++ b/externals/cubeb/CMakeLists.txt @@ -1,7 +1,7 @@ # TODO # - backend selection via command line, rather than simply detecting headers. -cmake_minimum_required(VERSION 3.1 FATAL_ERROR) +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) project(cubeb VERSION 0.0.0) @@ -9,33 +9,42 @@ option(BUILD_SHARED_LIBS "Build shared libraries" OFF) option(BUILD_TESTS "Build tests" ON) option(BUILD_RUST_LIBS "Build rust backends" OFF) option(BUILD_TOOLS "Build tools" ON) +option(BUNDLE_SPEEX "Bundle the speex library" OFF) +option(LAZY_LOAD_LIBS "Lazily load shared libraries" ON) +option(USE_SANITIZERS "Use sanitizers" ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) endif() -if(POLICY CMP0063) - cmake_policy(SET CMP0063 NEW) -endif() set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) -if(NOT COMMAND add_sanitizers) - list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake") - find_package(Sanitizers) +if(USE_SANITIZERS) if(NOT COMMAND add_sanitizers) - message(FATAL_ERROR "Could not find sanitizers-cmake: run\n\tgit submodule update --init --recursive\nin base git checkout") + list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake") + find_package(Sanitizers) + if(NOT COMMAND add_sanitizers) + message(FATAL_ERROR "Could not find sanitizers-cmake: run\n\tgit submodule update --init --recursive\nin base git checkout") + endif() endif() +else() + macro(add_sanitizers UNUSED) + endmacro() endif() if(BUILD_TESTS) + find_package(GTest QUIET) + if(TARGET GTest::Main) + add_library(gtest_main ALIAS GTest::Main) + endif() if(NOT TARGET gtest_main) if(NOT EXISTS "${PROJECT_SOURCE_DIR}/googletest/CMakeLists.txt") message(FATAL_ERROR "Could not find googletest: run\n\tgit submodule update --init --recursive\nin base git checkout") endif() - add_definitions(-DGTEST_HAS_TR1_TUPLE=0) + add_definitions(-DGTEST_HAS_TR1_TUPLE=0 -DGTEST_HAS_RTTI=0) set(gtest_force_shared_crt ON CACHE BOOL "") add_subdirectory(googletest) endif() @@ -60,7 +69,10 @@ endif() set(CMAKE_CXX_WARNING_LEVEL 4) if(NOT MSVC) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter -fno-exceptions -fno-rtti") +else() + string(REPLACE "/GR" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # Disable RTTI + string(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) # Disable Exceptions endif() add_library(cubeb @@ -70,15 +82,14 @@ add_library(cubeb src/cubeb_log.cpp src/cubeb_strings.c src/cubeb_utils.cpp - $) +) target_include_directories(cubeb PUBLIC $ $ ) -target_include_directories(cubeb PRIVATE src) -target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX) -target_compile_definitions(cubeb PRIVATE FLOATING_POINT) -target_compile_definitions(cubeb PRIVATE EXPORT=) -target_compile_definitions(cubeb PRIVATE RANDOM_PREFIX=speex) +set_target_properties(cubeb PROPERTIES + VERSION ${cubeb_VERSION} + SOVERSION ${cubeb_VERSION_MAJOR} +) add_sanitizers(cubeb) @@ -88,17 +99,9 @@ target_include_directories(cubeb PUBLIC $ ) -if(UNIX) - include(GNUInstallDirs) -else() - set(CMAKE_INSTALL_LIBDIR "lib") - set(CMAKE_INSTALL_BINDIR "bin") - set(CMAKE_INSTALL_DATADIR "share") - set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATADIR}/doc") - set(CMAKE_INSTALL_INCLUDEDIR "include") -endif() +include(GNUInstallDirs) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME} TYPE INCLUDE) install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) include(CMakePackageConfigHelpers) @@ -113,34 +116,135 @@ configure_package_config_file( INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) -install(TARGETS cubeb - EXPORT "${PROJECT_NAME}Targets" - DESTINATION ${CMAKE_INSTALL_PREFIX} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} - INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} -) install( FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) + +install(TARGETS cubeb EXPORT "${PROJECT_NAME}Targets") install( EXPORT "${PROJECT_NAME}Targets" NAMESPACE "${PROJECT_NAME}::" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) -add_library(speex OBJECT - src/speex/resample.c) -set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE) -target_compile_definitions(speex PRIVATE OUTSIDE_SPEEX) -target_compile_definitions(speex PRIVATE FLOATING_POINT) -target_compile_definitions(speex PRIVATE EXPORT=) -target_compile_definitions(speex PRIVATE RANDOM_PREFIX=speex) +if(NOT BUNDLE_SPEEX) + find_package(PkgConfig) + if(PKG_CONFIG_FOUND) + pkg_check_modules(speexdsp IMPORTED_TARGET speexdsp) + if(speexdsp_FOUND) + add_library(speex ALIAS PkgConfig::speexdsp) + endif() + endif() +endif() + +if(NOT TARGET speex) + add_library(speex STATIC 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 + ) +endif() + +# $ required because of https://gitlab.kitware.com/cmake/cmake/-/issues/15415 +target_link_libraries(cubeb PRIVATE $) include(CheckIncludeFiles) +# Threads needed by cubeb_log, _pulse, _alsa, _jack, _sndio, _oss and _sun +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads) +target_link_libraries(cubeb PRIVATE Threads::Threads) + +if(LAZY_LOAD_LIBS) + 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) + check_include_files(aaudio/AAudio.h USE_AAUDIO) + + if(USE_PULSE OR USE_ALSA OR USE_JACK OR USE_SNDIO OR USE_AAUDIO) + target_link_libraries(cubeb PRIVATE ${CMAKE_DL_LIBS}) + endif() + +else() + + find_package(PkgConfig REQUIRED) + + pkg_check_modules(libpulse IMPORTED_TARGET libpulse) + if(libpulse_FOUND) + set(USE_PULSE ON) + target_compile_definitions(cubeb PRIVATE DISABLE_LIBPULSE_DLOPEN) + target_link_libraries(cubeb PRIVATE PkgConfig::libpulse) + endif() + + pkg_check_modules(alsa IMPORTED_TARGET alsa) + if(alsa_FOUND) + set(USE_ALSA ON) + target_compile_definitions(cubeb PRIVATE DISABLE_LIBASOUND_DLOPEN) + target_link_libraries(cubeb PRIVATE PkgConfig::alsa) + endif() + + pkg_check_modules(jack IMPORTED_TARGET jack) + if(jack_FOUND) + set(USE_JACK ON) + target_compile_definitions(cubeb PRIVATE DISABLE_LIBJACK_DLOPEN) + target_link_libraries(cubeb PRIVATE PkgConfig::jack) + endif() + + check_include_files(sndio.h USE_SNDIO) + if(USE_SNDIO) + target_compile_definitions(cubeb PRIVATE DISABLE_LIBSNDIO_DLOPEN) + target_link_libraries(cubeb PRIVATE sndio) + endif() + + check_include_files(aaudio/AAudio.h USE_AAUDIO) + if(USE_AAUDIO) + target_compile_definitions(cubeb PRIVATE DISABLE_LIBAAUDIO_DLOPEN) + target_link_libraries(cubeb PRIVATE aaudio) + endif() +endif() + +if(USE_PULSE) + target_sources(cubeb PRIVATE src/cubeb_pulse.c) + target_compile_definitions(cubeb PRIVATE USE_PULSE) +endif() + +if(USE_ALSA) + target_sources(cubeb PRIVATE src/cubeb_alsa.c) + target_compile_definitions(cubeb PRIVATE USE_ALSA) +endif() + +if(USE_JACK) + target_sources(cubeb PRIVATE src/cubeb_jack.cpp) + target_compile_definitions(cubeb PRIVATE USE_JACK) +endif() + +if(USE_SNDIO) + target_sources(cubeb PRIVATE src/cubeb_sndio.c) + target_compile_definitions(cubeb PRIVATE USE_SNDIO) +endif() + +if(USE_AAUDIO) + target_sources(cubeb PRIVATE src/cubeb_aaudio.cpp) + target_compile_definitions(cubeb PRIVATE USE_AAUDIO) + + # set this definition to enable low latency mode. Possibly bad for battery + target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_LATENCY) + + # set this definition to enable power saving mode. Possibly resulting + # in high latency + # target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_POWER_SAVING) + + # set this mode to make the backend use an exclusive stream. + # will decrease latency. + # target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_EXCLUSIVE_STREAM) +endif() + check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT) if(USE_AUDIOUNIT) target_sources(cubeb PRIVATE @@ -150,30 +254,6 @@ if(USE_AUDIOUNIT) target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices") endif() -check_include_files(pulse/pulseaudio.h USE_PULSE) -if(USE_PULSE) - target_sources(cubeb PRIVATE - src/cubeb_pulse.c) - target_compile_definitions(cubeb PRIVATE USE_PULSE) - target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS}) -endif() - -check_include_files(alsa/asoundlib.h USE_ALSA) -if(USE_ALSA) - target_sources(cubeb PRIVATE - src/cubeb_alsa.c) - target_compile_definitions(cubeb PRIVATE USE_ALSA) - target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS}) -endif() - -check_include_files(jack/jack.h USE_JACK) -if(USE_JACK) - target_sources(cubeb PRIVATE - src/cubeb_jack.cpp) - target_compile_definitions(cubeb PRIVATE USE_JACK) - target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS}) -endif() - check_include_files(audioclient.h USE_WASAPI) if(USE_WASAPI) target_sources(cubeb PRIVATE @@ -207,7 +287,6 @@ if(HAVE_SYS_SOUNDCARD_H) target_sources(cubeb PRIVATE src/cubeb_oss.c) target_compile_definitions(cubeb PRIVATE USE_OSS) - target_link_libraries(cubeb PRIVATE pthread) endif() endif() @@ -219,20 +298,11 @@ if(USE_AUDIOTRACK) target_link_libraries(cubeb PRIVATE log) endif() -check_include_files(sndio.h USE_SNDIO) -if(USE_SNDIO) - target_sources(cubeb PRIVATE - src/cubeb_sndio.c) - target_compile_definitions(cubeb PRIVATE USE_SNDIO) - target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS}) -endif() - check_include_files(sys/audioio.h USE_SUN) if(USE_SUN) target_sources(cubeb PRIVATE src/cubeb_sun.c) target_compile_definitions(cubeb PRIVATE USE_SUN) - target_link_libraries(cubeb PRIVATE pthread) endif() check_include_files(kai.h USE_KAI) @@ -295,12 +365,11 @@ if(BUILD_TESTS) macro(cubeb_add_test NAME) add_executable(test_${NAME} test/test_${NAME}.cpp) - target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include) - target_include_directories(test_${NAME} PRIVATE src) + target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include src) target_link_libraries(test_${NAME} PRIVATE cubeb gtest_main) add_test(${NAME} test_${NAME}) add_sanitizers(test_${NAME}) - install(TARGETS test_${NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + install(TARGETS test_${NAME}) endmacro(cubeb_add_test) cubeb_add_test(sanity) @@ -310,17 +379,12 @@ if(BUILD_TESTS) cubeb_add_test(devices) cubeb_add_test(callback_ret) - add_executable(test_resampler test/test_resampler.cpp src/cubeb_resampler.cpp $) - target_include_directories(test_resampler PRIVATE ${gtest_SOURCE_DIR}/include) - target_include_directories(test_resampler PRIVATE src) - target_compile_definitions(test_resampler PRIVATE OUTSIDE_SPEEX) - target_compile_definitions(test_resampler PRIVATE FLOATING_POINT) - target_compile_definitions(test_resampler PRIVATE EXPORT=) - target_compile_definitions(test_resampler PRIVATE RANDOM_PREFIX=speex) - target_link_libraries(test_resampler PRIVATE cubeb gtest_main) + add_executable(test_resampler test/test_resampler.cpp src/cubeb_resampler.cpp src/cubeb_log.cpp) + target_include_directories(test_resampler PRIVATE ${gtest_SOURCE_DIR}/include src) + target_link_libraries(test_resampler PRIVATE cubeb gtest_main speex) add_test(resampler test_resampler) add_sanitizers(test_resampler) - install(TARGETS test_resampler DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + install(TARGETS test_resampler) cubeb_add_test(duplex) @@ -342,5 +406,17 @@ if(BUILD_TOOLS) target_include_directories(cubeb-test PRIVATE src) target_link_libraries(cubeb-test PRIVATE cubeb) add_sanitizers(cubeb-test) - install(TARGETS cubeb-test DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}) + install(TARGETS cubeb-test) endif() + +add_custom_target(clang-format-check + find + ${CMAKE_CURRENT_SOURCE_DIR}/src + ${CMAKE_CURRENT_SOURCE_DIR}/include + -type f (-name "*.cpp" -o -name "*.c" -o -name "*.h") + -not -path "*/subprojects/speex/*" + -print0 + | xargs -0 clang-format -Werror -n + COMMENT "Check formatting with clang-format" + VERBATIM) + diff --git a/externals/cubeb/Config.cmake.in b/externals/cubeb/Config.cmake.in index c5326ef33..be464aa49 100755 --- a/externals/cubeb/Config.cmake.in +++ b/externals/cubeb/Config.cmake.in @@ -1,4 +1,4 @@ @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake") -check_required_components(cubeb) \ No newline at end of file +check_required_components(cubeb) diff --git a/externals/cubeb/README.md b/externals/cubeb/README.md index 9954c37c8..92df4f22c 100755 --- a/externals/cubeb/README.md +++ b/externals/cubeb/README.md @@ -1,5 +1,4 @@ -[![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb) -[![Build status](https://ci.appveyor.com/api/projects/status/osv2r0m1j1nt9csr/branch/master?svg=true)](https://ci.appveyor.com/project/kinetiknz/cubeb/branch/master) +[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml) See INSTALL.md for build instructions. diff --git a/externals/cubeb/include/cubeb/cubeb.h b/externals/cubeb/include/cubeb/cubeb.h index e88536f0a..f653f5b7d 100755 --- a/externals/cubeb/include/cubeb/cubeb.h +++ b/externals/cubeb/include/cubeb/cubeb.h @@ -7,9 +7,9 @@ #if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382) #define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 +#include "cubeb_export.h" #include #include -#include "cubeb_export.h" #if defined(__cplusplus) extern "C" { @@ -122,8 +122,10 @@ extern "C" { /** @file The libcubeb C API. */ -typedef struct cubeb cubeb; /**< Opaque handle referencing the application state. */ -typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */ +typedef struct cubeb + cubeb; /**< Opaque handle referencing the application state. */ +typedef struct cubeb_stream + cubeb_stream; /**< Opaque handle referencing the stream state. */ /** Sample format enumeration. */ typedef enum { @@ -155,8 +157,10 @@ typedef void const * cubeb_devid; /** Level (verbosity) of logging for a particular cubeb context. */ typedef enum { CUBEB_LOG_DISABLED = 0, /** < Logging disabled */ - CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */ - CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */ + CUBEB_LOG_NORMAL = + 1, /**< Logging lifetime operation (creation/destruction). */ + CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance + implications. */ } cubeb_log_level; typedef enum { @@ -190,10 +194,10 @@ enum { CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT, CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY, CUBEB_LAYOUT_3F = - CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER, + CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER, CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY, CUBEB_LAYOUT_2F1 = - CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER, + CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER, CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY, CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER, @@ -222,46 +226,50 @@ enum { /** Miscellaneous stream preferences. */ typedef enum { - CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */ - CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be - specified on the input params and an - output device to loopback from should - be passed in place of an input device. */ + CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */ + CUBEB_STREAM_PREF_LOOPBACK = + 0x01, /**< Request a loopback stream. Should be + specified on the input params and an + output device to loopback from should + be passed in place of an input device. */ CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching default device on OS changes. */ - CUBEB_STREAM_PREF_VOICE = 0x04, /**< This stream is going to transport voice data. - Depending on the backend and platform, this can - change the audio input or output devices - selected, as well as the quality of the stream, - for example to accomodate bluetooth SCO modes on - bluetooth devices. */ - CUBEB_STREAM_PREF_RAW = 0x08, /**< Windows only. Bypass all signal processing - except for always on APO, driver and hardware. */ - CUBEB_STREAM_PREF_PERSIST = 0x10, /**< Request that the volume and mute settings - should persist across restarts of the stream - and/or application. May not be honored for - all backends and platforms. */ - - CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT = 0x20 /**< Don't automatically try to connect - ports. Only affects the jack - backend. */ + CUBEB_STREAM_PREF_VOICE = + 0x04, /**< This stream is going to transport voice data. + Depending on the backend and platform, this can + change the audio input or output devices + selected, as well as the quality of the stream, + for example to accomodate bluetooth SCO modes on + bluetooth devices. */ + CUBEB_STREAM_PREF_RAW = + 0x08, /**< Windows only. Bypass all signal processing + except for always on APO, driver and hardware. */ + CUBEB_STREAM_PREF_PERSIST = 0x10, /**< Request that the volume and mute + settings should persist across restarts + of the stream and/or application. This is + obsolete and ignored by all backends. */ + CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT = 0x20 /**< Don't automatically try to + connect ports. Only affects + the jack backend. */ } cubeb_stream_prefs; /** 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 channels; /**< Requested channel count. Valid range is [1, 8]. */ - cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */ - cubeb_stream_prefs prefs; /**< Requested preferences. */ + 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 channels; /**< Requested channel count. Valid range is [1, 8]. */ + cubeb_channel_layout + layout; /**< Requested channel layout. This must be consistent with the + provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */ + cubeb_stream_prefs prefs; /**< Requested preferences. */ } cubeb_stream_params; /** Audio device description */ typedef struct { char * output_name; /**< The name of the output device */ - char * input_name; /**< The name of the input device */ + char * input_name; /**< The name of the input device */ } cubeb_device; /** Stream states signaled via state_callback. */ @@ -274,12 +282,15 @@ typedef enum { /** Result code enumeration. */ enum { - CUBEB_OK = 0, /**< Success. */ - CUBEB_ERROR = -1, /**< Unclassified error. */ - CUBEB_ERROR_INVALID_FORMAT = -2, /**< Unsupported #cubeb_stream_params requested. */ + CUBEB_OK = 0, /**< Success. */ + CUBEB_ERROR = -1, /**< Unclassified error. */ + CUBEB_ERROR_INVALID_FORMAT = + -2, /**< Unsupported #cubeb_stream_params requested. */ CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */ - CUBEB_ERROR_NOT_SUPPORTED = -4, /**< Optional function not implemented in current backend. */ - CUBEB_ERROR_DEVICE_UNAVAILABLE = -5 /**< Device specified by #cubeb_devid not available. */ + CUBEB_ERROR_NOT_SUPPORTED = + -4, /**< Optional function not implemented in current backend. */ + CUBEB_ERROR_DEVICE_UNAVAILABLE = + -5 /**< Device specified by #cubeb_devid not available. */ }; /** @@ -295,50 +306,56 @@ typedef enum { * The state of a device. */ typedef enum { - CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system level. */ - CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is plugged into it. */ - CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */ + CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system + level. */ + CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is + plugged into it. */ + CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */ } cubeb_device_state; /** * Architecture specific sample type. */ typedef enum { - CUBEB_DEVICE_FMT_S16LE = 0x0010, /**< 16-bit integers, Little Endian. */ - CUBEB_DEVICE_FMT_S16BE = 0x0020, /**< 16-bit integers, Big Endian. */ - CUBEB_DEVICE_FMT_F32LE = 0x1000, /**< 32-bit floating point, Little Endian. */ - CUBEB_DEVICE_FMT_F32BE = 0x2000 /**< 32-bit floating point, Big Endian. */ + CUBEB_DEVICE_FMT_S16LE = 0x0010, /**< 16-bit integers, Little Endian. */ + CUBEB_DEVICE_FMT_S16BE = 0x0020, /**< 16-bit integers, Big Endian. */ + CUBEB_DEVICE_FMT_F32LE = 0x1000, /**< 32-bit floating point, Little Endian. */ + CUBEB_DEVICE_FMT_F32BE = 0x2000 /**< 32-bit floating point, Big Endian. */ } cubeb_device_fmt; #if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) /** 16-bit integers, native endianess, when on a Big Endian environment. */ -#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE -/** 32-bit floating points, native endianess, when on a Big Endian environment. */ -#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE +#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE +/** 32-bit floating points, native endianess, when on a Big Endian environment. + */ +#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE #else /** 16-bit integers, native endianess, when on a Little Endian environment. */ -#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE +#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE /** 32-bit floating points, native endianess, when on a Little Endian * environment. */ -#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE +#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE #endif /** All the 16-bit integers types. */ -#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE) +#define CUBEB_DEVICE_FMT_S16_MASK \ + (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE) /** All the 32-bit floating points types. */ -#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE) +#define CUBEB_DEVICE_FMT_F32_MASK \ + (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE) /** All the device formats types. */ -#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK) +#define CUBEB_DEVICE_FMT_ALL \ + (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK) /** Channel type for a `cubeb_stream`. Depending on the backend and platform * used, this can control inter-stream interruption, ducking, and volume * control. */ typedef enum { - CUBEB_DEVICE_PREF_NONE = 0x00, - CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01, - CUBEB_DEVICE_PREF_VOICE = 0x02, - CUBEB_DEVICE_PREF_NOTIFICATION = 0x04, - CUBEB_DEVICE_PREF_ALL = 0x0F + CUBEB_DEVICE_PREF_NONE = 0x00, + CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01, + CUBEB_DEVICE_PREF_VOICE = 0x02, + CUBEB_DEVICE_PREF_NOTIFICATION = 0x04, + CUBEB_DEVICE_PREF_ALL = 0x0F } cubeb_device_pref; /** This structure holds the characteristics @@ -347,25 +364,30 @@ typedef enum { * `cubeb_device_collection` and must be destroyed via * `cubeb_device_collection_destroy`. */ typedef struct { - cubeb_devid devid; /**< Device identifier handle. */ - char const * device_id; /**< Device identifier which might be presented in a UI. */ - char const * friendly_name; /**< Friendly device name which might be presented in a UI. */ - char const * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */ - char const * vendor_name; /**< Optional vendor name, may be NULL. */ + cubeb_devid devid; /**< Device identifier handle. */ + char const * + device_id; /**< Device identifier which might be presented in a UI. */ + char const * friendly_name; /**< Friendly device name which might be presented + in a UI. */ + char const * group_id; /**< Two devices have the same group identifier if they + belong to the same physical device; for example a + headset and microphone. */ + char const * vendor_name; /**< Optional vendor name, may be NULL. */ - cubeb_device_type type; /**< Type of device (Input/Output). */ - cubeb_device_state state; /**< State of device disabled/enabled/unplugged. */ - cubeb_device_pref preferred;/**< Preferred device. */ + cubeb_device_type type; /**< Type of device (Input/Output). */ + cubeb_device_state state; /**< State of device disabled/enabled/unplugged. */ + cubeb_device_pref preferred; /**< Preferred device. */ - cubeb_device_fmt format; /**< Sample format supported. */ - cubeb_device_fmt default_format; /**< The default sample format for this device. */ - uint32_t max_channels; /**< Channels. */ - uint32_t default_rate; /**< Default/Preferred sample rate. */ - uint32_t max_rate; /**< Maximum sample rate supported. */ - uint32_t min_rate; /**< Minimum sample rate supported. */ + cubeb_device_fmt format; /**< Sample format supported. */ + cubeb_device_fmt + default_format; /**< The default sample format for this device. */ + uint32_t max_channels; /**< Channels. */ + uint32_t default_rate; /**< Default/Preferred sample rate. */ + uint32_t max_rate; /**< Maximum sample rate supported. */ + uint32_t min_rate; /**< Minimum sample rate supported. */ - uint32_t latency_lo; /**< Lowest possible latency in frames. */ - uint32_t latency_hi; /**< Higest possible latency in frames. */ + uint32_t latency_lo; /**< Lowest possible latency in frames. */ + uint32_t latency_hi; /**< Higest possible latency in frames. */ } cubeb_device_info; /** Device collection. @@ -398,34 +420,32 @@ typedef struct { being stopped. @retval CUBEB_ERROR on error, in which case the data callback will stop and the stream will enter a shutdown state. */ -typedef long (* cubeb_data_callback)(cubeb_stream * stream, - void * user_ptr, - void const * input_buffer, - void * output_buffer, - long nframes); +typedef long (*cubeb_data_callback)(cubeb_stream * stream, void * user_ptr, + void const * input_buffer, + void * output_buffer, long nframes); /** User supplied state callback. @param stream The stream for this this callback fired. @param user_ptr The pointer passed to cubeb_stream_init. @param state The new state of the stream. */ -typedef void (* cubeb_state_callback)(cubeb_stream * stream, - void * user_ptr, - cubeb_state state); +typedef void (*cubeb_state_callback)(cubeb_stream * stream, void * user_ptr, + cubeb_state state); /** * User supplied callback called when the underlying device changed. * @param user The pointer passed to cubeb_stream_init. */ -typedef void (* cubeb_device_changed_callback)(void * user_ptr); +typedef void (*cubeb_device_changed_callback)(void * user_ptr); /** * User supplied callback called when the underlying device collection changed. * @param context A pointer to the cubeb context. - * @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */ -typedef void (* cubeb_device_collection_changed_callback)(cubeb * context, - void * user_ptr); + * @param user_ptr The pointer passed to + * cubeb_register_device_collection_changed. */ +typedef void (*cubeb_device_collection_changed_callback)(cubeb * context, + void * user_ptr); /** User supplied callback called when a message needs logging. */ -typedef void (* cubeb_log_callback)(char const * fmt, ...); +typedef void (*cubeb_log_callback)(char const * fmt, ...); /** Initialize an application context. This will perform any library or application scoped initialization. @@ -445,13 +465,15 @@ typedef void (* cubeb_log_callback)(char const * fmt, ...); @retval CUBEB_OK in case of success. @retval CUBEB_ERROR in case of error, for example because the host has no audio hardware. */ -CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name, - char const * backend_name); +CUBEB_EXPORT int +cubeb_init(cubeb ** context, char const * context_name, + char const * backend_name); /** Get a read-only string identifying this context's current backend. @param context A pointer to the cubeb context. @retval Read-only string identifying current backend. */ -CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context); +CUBEB_EXPORT char const * +cubeb_get_backend_id(cubeb * context); /** Get the maximum possible number of channels. @param context A pointer to the cubeb context. @@ -460,7 +482,8 @@ CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context); @retval CUBEB_ERROR_INVALID_PARAMETER @retval CUBEB_ERROR_NOT_SUPPORTED @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels); +CUBEB_EXPORT int +cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels); /** Get the minimal latency value, in frames, that is guaranteed to work when creating a stream for the specified sample rate. This is platform, @@ -473,9 +496,9 @@ CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_cha @retval CUBEB_OK @retval CUBEB_ERROR_INVALID_PARAMETER @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context, - cubeb_stream_params * params, - uint32_t * latency_frames); +CUBEB_EXPORT int +cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, + uint32_t * latency_frames); /** Get the preferred sample rate for this backend: this is hardware and platform dependent, and can avoid resampling, and/or trigger fastpaths. @@ -484,12 +507,14 @@ CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context, @retval CUBEB_OK @retval CUBEB_ERROR_INVALID_PARAMETER @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate); +CUBEB_EXPORT int +cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate); /** Destroy an application context. This must be called after all stream have * been destroyed. @param context A pointer to the cubeb context.*/ -CUBEB_EXPORT void cubeb_destroy(cubeb * context); +CUBEB_EXPORT void +cubeb_destroy(cubeb * context); /** Initialize a stream associated with the supplied application context. @param context A pointer to the cubeb context. @@ -497,19 +522,21 @@ CUBEB_EXPORT void cubeb_destroy(cubeb * context); cubeb stream. @param stream_name A name for this stream. @param input_device Device for the input side of the stream. If NULL the - default input device is used. Passing a valid cubeb_devid - means the stream only ever uses that device. Passing a NULL - cubeb_devid allows the stream to follow that device type's - OS default. + default input device is used. Passing a valid + cubeb_devid means the stream only ever uses that device. Passing a NULL + cubeb_devid allows the stream to follow that device + type's OS default. @param input_stream_params Parameters for the input side of the stream, or NULL if this stream is output only. @param output_device Device for the output side of the stream. If NULL the - default output device is used. Passing a valid cubeb_devid - means the stream only ever uses that device. Passing a NULL - cubeb_devid allows the stream to follow that device type's - OS default. + default output device is used. Passing a valid + cubeb_devid means the stream only ever uses that device. Passing a NULL + cubeb_devid allows the stream to follow that device + type's OS default. @param output_stream_params Parameters for the output side of the stream, or - NULL if this stream is input only. + NULL if this stream is input only. When input + and output stream parameters are supplied, their + rate has to be the same. @param latency_frames Stream latency in frames. Valid range is [1, 96000]. @param data_callback Will be called to preroll data before playback is @@ -521,49 +548,42 @@ CUBEB_EXPORT void cubeb_destroy(cubeb * context); @retval CUBEB_ERROR @retval CUBEB_ERROR_INVALID_FORMAT @retval CUBEB_ERROR_DEVICE_UNAVAILABLE */ -CUBEB_EXPORT int cubeb_stream_init(cubeb * context, - cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, - cubeb_stream_params * input_stream_params, - cubeb_devid output_device, - cubeb_stream_params * output_stream_params, - uint32_t latency_frames, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr); +CUBEB_EXPORT int +cubeb_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + uint32_t latency_frames, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr); /** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a stream. @param stream The stream to destroy. */ -CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream); +CUBEB_EXPORT void +cubeb_stream_destroy(cubeb_stream * stream); /** Start playback. @param stream @retval CUBEB_OK @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream); +CUBEB_EXPORT int +cubeb_stream_start(cubeb_stream * stream); /** Stop playback. @param stream @retval CUBEB_OK @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream); - -/** Reset stream to the default device. - @param stream - @retval CUBEB_OK - @retval CUBEB_ERROR_INVALID_PARAMETER - @retval CUBEB_ERROR_NOT_SUPPORTED - @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_reset_default_device(cubeb_stream * stream); +CUBEB_EXPORT int +cubeb_stream_stop(cubeb_stream * stream); /** Get the current stream playback position. @param stream @param position Playback position in frames. @retval CUBEB_OK @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position); +CUBEB_EXPORT int +cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position); /** Get the latency for this stream, in frames. This is the number of frames between the time cubeb acquires the data in the callback and the listener @@ -573,7 +593,8 @@ CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * pos @retval CUBEB_OK @retval CUBEB_ERROR_NOT_SUPPORTED @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency); +CUBEB_EXPORT int +cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency); /** Get the input latency for this stream, in frames. This is the number of frames between the time the audio input devices records the data, and they @@ -584,7 +605,8 @@ CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * late @retval CUBEB_OK @retval CUBEB_ERROR_NOT_SUPPORTED @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency); +CUBEB_EXPORT int +cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency); /** Set the volume for a stream. @param stream the stream for which to adjust the volume. @param volume a float between 0.0 (muted) and 1.0 (maximum volume) @@ -592,7 +614,8 @@ CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t @retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or stream is an invalid pointer @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume); +CUBEB_EXPORT int +cubeb_stream_set_volume(cubeb_stream * stream, float volume); /** Change a stream's name. @param stream the stream for which to set the name. @@ -600,7 +623,8 @@ CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume); @retval CUBEB_OK @retval CUBEB_ERROR_INVALID_PARAMETER if any pointer is invalid @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name); +CUBEB_EXPORT int +cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name); /** Get the current output device for this stream. @param stm the stream for which to query the current output device @@ -609,8 +633,9 @@ CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * strea @retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are invalid pointers @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm, - cubeb_device ** const device); +CUBEB_EXPORT int +cubeb_stream_get_current_device(cubeb_stream * stm, + cubeb_device ** const device); /** Destroy a cubeb_device structure. @param stream the stream passed in cubeb_stream_get_current_device @@ -618,8 +643,8 @@ CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm, @retval CUBEB_OK in case of success @retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream, - cubeb_device * devices); +CUBEB_EXPORT int +cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * devices); /** Set a callback to be notified when the output device changes. @param stream the stream for which to set the callback. @@ -629,32 +654,38 @@ CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream, @retval CUBEB_ERROR_INVALID_PARAMETER if either stream or device_changed_callback are invalid pointers. @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, - cubeb_device_changed_callback device_changed_callback); +CUBEB_EXPORT int +cubeb_stream_register_device_changed_callback( + cubeb_stream * stream, + cubeb_device_changed_callback device_changed_callback); -/** Return the user data pointer registered with the stream with cubeb_stream_init. +/** Return the user data pointer registered with the stream with + cubeb_stream_init. @param stream the stream for which to retrieve user data pointer. @retval user data pointer */ -CUBEB_EXPORT void * cubeb_stream_user_ptr(cubeb_stream * stream); +CUBEB_EXPORT void * +cubeb_stream_user_ptr(cubeb_stream * stream); /** Returns enumerated devices. @param context @param devtype device type to include - @param collection output collection. Must be destroyed with cubeb_device_collection_destroy + @param collection output collection. Must be destroyed with + cubeb_device_collection_destroy @retval CUBEB_OK in case of success @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection * collection); +CUBEB_EXPORT int +cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype, + cubeb_device_collection * collection); /** Destroy a cubeb_device_collection, and its `cubeb_device_info`. @param context @param collection collection to destroy @retval CUBEB_OK @retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */ -CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection); +CUBEB_EXPORT int +cubeb_device_collection_destroy(cubeb * context, + cubeb_device_collection * collection); /** Registers a callback which is called when the system detects a new device or a device is removed. @@ -662,17 +693,18 @@ CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context, @param devtype device type to include. Different callbacks and user pointers can be registered for each devtype. The hybrid devtype `CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid - and will register the provided callback and user pointer in both sides. + and will register the provided callback and user pointer in both + sides. @param callback a function called whenever the system device list changes. Passing NULL allow to unregister a function. You have to unregister first before you register a new callback. @param user_ptr pointer to user specified data which will be present in subsequent callbacks. @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback callback, - void * user_ptr); +CUBEB_EXPORT int +cubeb_register_device_collection_changed( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback callback, void * user_ptr); /** Set a callback to be called with a message. @param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL. @@ -682,8 +714,9 @@ CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context, @retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are invalid pointers, or if level is not in cubeb_log_level. */ -CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level, - cubeb_log_callback log_callback); +CUBEB_EXPORT int +cubeb_set_log_callback(cubeb_log_level log_level, + cubeb_log_callback log_callback); #if defined(__cplusplus) } diff --git a/externals/cubeb/src/android/audiotrack_definitions.h b/externals/cubeb/src/android/audiotrack_definitions.h index cd501533d..f6b6931fa 100755 --- a/externals/cubeb/src/android/audiotrack_definitions.h +++ b/externals/cubeb/src/android/audiotrack_definitions.h @@ -22,23 +22,25 @@ */ /* - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h + * From + * https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h */ typedef int32_t status_t; /* - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h + * From + * https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h */ struct Buffer { - uint32_t flags; - int channelCount; - int format; - size_t frameCount; - size_t size; + uint32_t flags; + int channelCount; + int format; + size_t frameCount; + size_t size; union { - void* raw; - short* i16; - int8_t* i8; + void * raw; + short * i16; + int8_t * i8; }; }; @@ -52,25 +54,28 @@ enum event_type { }; /** - * From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h - * and + * From + * https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h + * and * https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h */ #define AUDIO_STREAM_TYPE_MUSIC 3 enum { - AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, + AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1, AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2, - AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, - AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) + AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS, + AUDIO_CHANNEL_OUT_STEREO_ICS = + (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS) } AudioTrack_ChannelMapping_ICS; enum { AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy = 0x4, AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy = 0x8, AUDIO_CHANNEL_OUT_MONO_Legacy = AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy, - AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy) + AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy | + AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy) } AudioTrack_ChannelMapping_Legacy; typedef enum { @@ -78,4 +83,3 @@ typedef enum { AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1, AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT), } AudioTrack_SampleType; - diff --git a/externals/cubeb/src/android/cubeb-output-latency.h b/externals/cubeb/src/android/cubeb-output-latency.h index 2128cd176..870a884a3 100755 --- a/externals/cubeb/src/android/cubeb-output-latency.h +++ b/externals/cubeb/src/android/cubeb-output-latency.h @@ -1,9 +1,9 @@ #ifndef _CUBEB_OUTPUT_LATENCY_H_ #define _CUBEB_OUTPUT_LATENCY_H_ -#include -#include "cubeb_media_library.h" #include "../cubeb-jni.h" +#include "cubeb_media_library.h" +#include struct output_latency_function { media_lib * from_lib; @@ -23,7 +23,7 @@ cubeb_output_latency_load_method(int version) ol->version = version; - if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){ + if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) { ol->from_jni = cubeb_jni_init(); return ol; } @@ -36,7 +36,7 @@ bool cubeb_output_latency_method_is_loaded(output_latency_function * ol) { assert(ol); - if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){ + if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) { return !!ol->from_jni; } @@ -66,7 +66,7 @@ cubeb_get_output_latency(output_latency_function * ol) { assert(cubeb_output_latency_method_is_loaded(ol)); - if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){ + if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) { return cubeb_get_output_latency_from_jni(ol->from_jni); } diff --git a/externals/cubeb/src/android/cubeb_media_library.h b/externals/cubeb/src/android/cubeb_media_library.h index ab21b779d..27fbc86ec 100755 --- a/externals/cubeb/src/android/cubeb_media_library.h +++ b/externals/cubeb/src/android/cubeb_media_library.h @@ -3,7 +3,7 @@ struct media_lib { void * libmedia; - int32_t (* get_output_latency)(uint32_t * latency, int stream_type); + int32_t (*get_output_latency)(uint32_t * latency, int stream_type); }; typedef struct media_lib media_lib; @@ -17,15 +17,17 @@ cubeb_load_media_library() return NULL; } - // Get the latency, in ms, from AudioFlinger. First, try the most recent signature. - // status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType) - ml.get_output_latency = - dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"); + // Get the latency, in ms, from AudioFlinger. First, try the most recent + // signature. status_t AudioSystem::getOutputLatency(uint32_t* latency, + // audio_stream_type_t streamType) + ml.get_output_latency = dlsym( + ml.libmedia, + "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"); if (!ml.get_output_latency) { // In case of failure, try the signature from legacy version. // status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType) ml.get_output_latency = - dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji"); + dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji"); if (!ml.get_output_latency) { return NULL; } diff --git a/externals/cubeb/src/android/sles_definitions.h b/externals/cubeb/src/android/sles_definitions.h index 06d2e8d49..b107003d1 100755 --- a/externals/cubeb/src/android/sles_definitions.h +++ b/externals/cubeb/src/android/sles_definitions.h @@ -29,23 +29,23 @@ /** Audio recording preset */ /** Audio recording preset key */ -#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset") +#define SL_ANDROID_KEY_RECORDING_PRESET \ + ((const SLchar *)"androidRecordingPreset") /** Audio recording preset values */ /** preset "none" cannot be set, it is used to indicate the current settings * do not match any of the presets. */ -#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32) 0x00000000) +#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32)0x00000000) /** generic recording configuration on the platform */ -#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32) 0x00000001) +#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32)0x00000001) /** uses the microphone audio source with the same orientation as the camera * if available, the main device microphone otherwise */ -#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32) 0x00000002) +#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32)0x00000002) /** uses the main microphone tuned for voice recognition */ -#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32) 0x00000003) +#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32)0x00000003) /** uses the main microphone tuned for audio communications */ -#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004) +#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32)0x00000004) /** uses the main microphone unprocessed */ -#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32) 0x00000005) - +#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32)0x00000005) /*---------------------------------------------------------------------------*/ /* Android AudioPlayer configuration */ @@ -53,22 +53,21 @@ /** Audio playback stream type */ /** Audio playback stream type key */ -#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType") +#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar *)"androidPlaybackStreamType") /** Audio playback stream type values */ /* same as android.media.AudioManager.STREAM_VOICE_CALL */ -#define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000) +#define SL_ANDROID_STREAM_VOICE ((SLint32)0x00000000) /* same as android.media.AudioManager.STREAM_SYSTEM */ -#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001) +#define SL_ANDROID_STREAM_SYSTEM ((SLint32)0x00000001) /* same as android.media.AudioManager.STREAM_RING */ -#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002) +#define SL_ANDROID_STREAM_RING ((SLint32)0x00000002) /* same as android.media.AudioManager.STREAM_MUSIC */ -#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003) +#define SL_ANDROID_STREAM_MEDIA ((SLint32)0x00000003) /* same as android.media.AudioManager.STREAM_ALARM */ -#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004) +#define SL_ANDROID_STREAM_ALARM ((SLint32)0x00000004) /* same as android.media.AudioManager.STREAM_NOTIFICATION */ -#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005) - +#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32)0x00000005) /*---------------------------------------------------------------------------*/ /* Android AudioPlayer and AudioRecorder configuration */ @@ -85,18 +84,21 @@ * granted or not. */ /** Audio Performance mode key */ -#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode") +#define SL_ANDROID_KEY_PERFORMANCE_MODE \ + ((const SLchar *)"androidPerformanceMode") /** Audio performance values */ -/* No specific performance requirement. Allows HW and SW pre/post processing. */ -#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000) +/* No specific performance requirement. Allows HW and SW pre/post + * processing. */ +#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32)0x00000000) /* Priority given to latency. No HW or software pre/post processing. * This is the default if no performance mode is specified. */ -#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001) -/* Priority given to latency while still allowing HW pre and post processing. */ -#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002) +#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32)0x00000001) +/* Priority given to latency while still allowing HW pre and post + * processing. */ +#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32)0x00000002) /* Priority given to power saving if latency is not a concern. * Allows HW and SW pre/post processing. */ -#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003) +#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32)0x00000003) #endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */ diff --git a/externals/cubeb/src/cubeb-internal.h b/externals/cubeb/src/cubeb-internal.h index 9d3d1dd2f..79326e1eb 100755 --- a/externals/cubeb/src/cubeb-internal.h +++ b/externals/cubeb/src/cubeb-internal.h @@ -8,8 +8,8 @@ #define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 #include "cubeb/cubeb.h" -#include "cubeb_log.h" #include "cubeb_assert.h" +#include "cubeb_log.h" #include #include @@ -21,7 +21,7 @@ #define CLANG_ANALYZER_NORETURN #endif // ifndef CLANG_ANALYZER_NORETURN #endif // __has_feature(attribute_analyzer_noreturn) -#else // __clang__ +#else // __clang__ #define CLANG_ANALYZER_NORETURN #endif @@ -34,48 +34,41 @@ extern "C" { #endif struct cubeb_ops { - int (* init)(cubeb ** context, char const * context_name); - char const * (* get_backend_id)(cubeb * context); - int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels); - 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 (* enumerate_devices)(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection); - int (* device_collection_destroy)(cubeb * context, - cubeb_device_collection * collection); - void (* destroy)(cubeb * context); - int (* stream_init)(cubeb * context, - cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, - cubeb_stream_params * input_stream_params, - cubeb_devid output_device, - cubeb_stream_params * output_stream_params, - unsigned int latency, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr); - void (* stream_destroy)(cubeb_stream * stream); - int (* stream_start)(cubeb_stream * stream); - int (* stream_stop)(cubeb_stream * stream); - int (* stream_reset_default_device)(cubeb_stream * stream); - int (* stream_get_position)(cubeb_stream * stream, uint64_t * position); - int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency); - int (* stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency); - int (* stream_set_volume)(cubeb_stream * stream, float volumes); - 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_device_destroy)(cubeb_stream * stream, - cubeb_device * device); - int (* stream_register_device_changed_callback)(cubeb_stream * stream, - cubeb_device_changed_callback device_changed_callback); - int (* register_device_collection_changed)(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback callback, - void * user_ptr); + int (*init)(cubeb ** context, char const * context_name); + char const * (*get_backend_id)(cubeb * context); + int (*get_max_channel_count)(cubeb * context, uint32_t * max_channels); + 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 (*enumerate_devices)(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection); + int (*device_collection_destroy)(cubeb * context, + cubeb_device_collection * collection); + void (*destroy)(cubeb * context); + int (*stream_init)(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr); + void (*stream_destroy)(cubeb_stream * stream); + int (*stream_start)(cubeb_stream * stream); + int (*stream_stop)(cubeb_stream * stream); + int (*stream_get_position)(cubeb_stream * stream, uint64_t * position); + int (*stream_get_latency)(cubeb_stream * stream, uint32_t * latency); + int (*stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency); + int (*stream_set_volume)(cubeb_stream * stream, float volumes); + 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_device_destroy)(cubeb_stream * stream, cubeb_device * device); + int (*stream_register_device_changed_callback)( + cubeb_stream * stream, + cubeb_device_changed_callback device_changed_callback); + int (*register_device_collection_changed)( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback callback, void * user_ptr); }; #endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */ diff --git a/externals/cubeb/src/cubeb-jni.cpp b/externals/cubeb/src/cubeb-jni.cpp index a5066967a..8e7345b8a 100755 --- a/externals/cubeb/src/cubeb-jni.cpp +++ b/externals/cubeb/src/cubeb-jni.cpp @@ -1,6 +1,8 @@ +/* clang-format off */ #include "jni.h" #include #include "cubeb-jni-instances.h" +/* clang-format on */ #define AUDIO_STREAM_TYPE_MUSIC 3 @@ -10,8 +12,7 @@ struct cubeb_jni { jmethodID s_get_output_latency_id = nullptr; }; -extern "C" -cubeb_jni * +extern "C" cubeb_jni * cubeb_jni_init() { jobject ctx_obj = cubeb_jni_get_context_instance(); @@ -23,18 +24,28 @@ cubeb_jni_init() cubeb_jni * cubeb_jni_ptr = new cubeb_jni; assert(cubeb_jni_ptr); - // Find the audio manager object and make it global to call it from another method + // Find the audio manager object and make it global to call it from another + // method jclass context_class = jni_env->FindClass("android/content/Context"); - jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;"); - jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field); - jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"); - jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr); - cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast(jni_env->NewGlobalRef(audio_manager_obj)); + jfieldID audio_service_field = jni_env->GetStaticFieldID( + context_class, "AUDIO_SERVICE", "Ljava/lang/String;"); + jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, + audio_service_field); + jmethodID get_system_service_id = + jni_env->GetMethodID(context_class, "getSystemService", + "(Ljava/lang/String;)Ljava/lang/Object;"); + jobject audio_manager_obj = + jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr); + cubeb_jni_ptr->s_audio_manager_obj = + reinterpret_cast(jni_env->NewGlobalRef(audio_manager_obj)); - // Make the audio manager class a global reference in order to preserve method id + // Make the audio manager class a global reference in order to preserve method + // id jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager"); - cubeb_jni_ptr->s_audio_manager_class = reinterpret_cast(jni_env->NewGlobalRef(audio_manager_class)); - cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I"); + cubeb_jni_ptr->s_audio_manager_class = + reinterpret_cast(jni_env->NewGlobalRef(audio_manager_class)); + cubeb_jni_ptr->s_get_output_latency_id = + jni_env->GetMethodID(audio_manager_class, "getOutputLatency", "(I)I"); jni_env->DeleteLocalRef(ctx_obj); jni_env->DeleteLocalRef(context_class); @@ -45,16 +56,19 @@ cubeb_jni_init() return cubeb_jni_ptr; } -extern "C" -int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr) +extern "C" int +cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr) { assert(cubeb_jni_ptr); JNIEnv * jni_env = cubeb_get_jni_env_for_thread(); - return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC + return jni_env->CallIntMethod( + cubeb_jni_ptr->s_audio_manager_obj, + cubeb_jni_ptr->s_get_output_latency_id, + AUDIO_STREAM_TYPE_MUSIC); // param: AudioManager.STREAM_MUSIC } -extern "C" -void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr) +extern "C" void +cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr) { assert(cubeb_jni_ptr); diff --git a/externals/cubeb/src/cubeb-jni.h b/externals/cubeb/src/cubeb-jni.h index 8c7ddb6ac..c4a712a06 100755 --- a/externals/cubeb/src/cubeb-jni.h +++ b/externals/cubeb/src/cubeb-jni.h @@ -3,8 +3,11 @@ typedef struct cubeb_jni cubeb_jni; -cubeb_jni * cubeb_jni_init(); -int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr); -void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr); +cubeb_jni * +cubeb_jni_init(); +int +cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr); +void +cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr); #endif // _CUBEB_JNI_H_ diff --git a/externals/cubeb/src/cubeb-sles.h b/externals/cubeb/src/cubeb-sles.h index ac22150e1..ca93543c0 100755 --- a/externals/cubeb/src/cubeb-sles.h +++ b/externals/cubeb/src/cubeb-sles.h @@ -10,27 +10,22 @@ #include static SLresult -cubeb_get_sles_engine(SLObjectItf * pEngine, - SLuint32 numOptions, +cubeb_get_sles_engine(SLObjectItf * pEngine, SLuint32 numOptions, const SLEngineOption * pEngineOptions, SLuint32 numInterfaces, const SLInterfaceID * pInterfaceIds, const SLboolean * pInterfaceRequired) { - return slCreateEngine(pEngine, - numOptions, - pEngineOptions, - numInterfaces, - pInterfaceIds, - pInterfaceRequired); + return slCreateEngine(pEngine, numOptions, pEngineOptions, numInterfaces, + pInterfaceIds, pInterfaceRequired); } static void cubeb_destroy_sles_engine(SLObjectItf * self) { if (*self != NULL) { - (**self)->Destroy(*self); - *self = NULL; + (**self)->Destroy(*self); + *self = NULL; } } diff --git a/externals/cubeb/src/cubeb.c b/externals/cubeb/src/cubeb.c index 74c17dbe6..b3d32eea3 100755 --- a/externals/cubeb/src/cubeb.c +++ b/externals/cubeb/src/cubeb.c @@ -5,14 +5,14 @@ * accompanying file LICENSE for details. */ #undef NDEBUG +#include "cubeb/cubeb.h" +#include "cubeb-internal.h" #include #include #include #include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0]))) +#define NELEMS(x) ((int)(sizeof(x) / sizeof(x[0]))) struct cubeb { struct cubeb_ops * ops; @@ -28,46 +28,64 @@ struct cubeb_stream { }; #if defined(USE_PULSE) -int pulse_init(cubeb ** context, char const * context_name); +int +pulse_init(cubeb ** context, char const * context_name); #endif #if defined(USE_PULSE_RUST) -int pulse_rust_init(cubeb ** contet, char const * context_name); +int +pulse_rust_init(cubeb ** contet, char const * context_name); #endif #if defined(USE_JACK) -int jack_init (cubeb ** context, char const * context_name); +int +jack_init(cubeb ** context, char const * context_name); #endif #if defined(USE_ALSA) -int alsa_init(cubeb ** context, char const * context_name); +int +alsa_init(cubeb ** context, char const * context_name); #endif #if defined(USE_AUDIOUNIT) -int audiounit_init(cubeb ** context, char const * context_name); +int +audiounit_init(cubeb ** context, char const * context_name); #endif #if defined(USE_AUDIOUNIT_RUST) -int audiounit_rust_init(cubeb ** contet, char const * context_name); +int +audiounit_rust_init(cubeb ** contet, char const * context_name); #endif #if defined(USE_WINMM) -int winmm_init(cubeb ** context, char const * context_name); +int +winmm_init(cubeb ** context, char const * context_name); #endif #if defined(USE_WASAPI) -int wasapi_init(cubeb ** context, char const * context_name); +int +wasapi_init(cubeb ** context, char const * context_name); #endif #if defined(USE_SNDIO) -int sndio_init(cubeb ** context, char const * context_name); +int +sndio_init(cubeb ** context, char const * context_name); #endif #if defined(USE_SUN) -int sun_init(cubeb ** context, char const * context_name); +int +sun_init(cubeb ** context, char const * context_name); #endif #if defined(USE_OPENSL) -int opensl_init(cubeb ** context, char const * context_name); +int +opensl_init(cubeb ** context, char const * context_name); #endif #if defined(USE_OSS) -int oss_init(cubeb ** context, char const * context_name); +int +oss_init(cubeb ** context, char const * context_name); +#endif +#if defined(USE_AAUDIO) +int +aaudio_init(cubeb ** context, char const * context_name); #endif #if defined(USE_AUDIOTRACK) -int audiotrack_init(cubeb ** context, char const * context_name); +int +audiotrack_init(cubeb ** context, char const * context_name); #endif #if defined(USE_KAI) -int kai_init(cubeb ** context, char const * context_name); +int +kai_init(cubeb ** context, char const * context_name); #endif static int @@ -76,28 +94,32 @@ 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->channels < 1 || output_stream_params->channels > UINT8_MAX) { + if (output_stream_params->rate < 1000 || + output_stream_params->rate > 192000 || + output_stream_params->channels < 1 || + output_stream_params->channels > UINT8_MAX) { return CUBEB_ERROR_INVALID_FORMAT; } } if (input_stream_params) { - if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 || - input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) { + if (input_stream_params->rate < 1000 || + input_stream_params->rate > 192000 || + input_stream_params->channels < 1 || + input_stream_params->channels > UINT8_MAX) { return CUBEB_ERROR_INVALID_FORMAT; } } // Rate and sample format must be the same for input and output, if using a // duplex stream if (input_stream_params && output_stream_params) { - if (input_stream_params->rate != output_stream_params->rate || + if (input_stream_params->rate != output_stream_params->rate || input_stream_params->format != output_stream_params->format) { return CUBEB_ERROR_INVALID_FORMAT; } } - cubeb_stream_params * params = input_stream_params ? - input_stream_params : output_stream_params; + cubeb_stream_params * params = + input_stream_params ? input_stream_params : output_stream_params; switch (params->format) { case CUBEB_SAMPLE_S16LE: @@ -120,9 +142,10 @@ validate_latency(int latency) } int -cubeb_init(cubeb ** context, char const * context_name, char const * backend_name) +cubeb_init(cubeb ** context, char const * context_name, + char const * backend_name) { - int (* init_oneshot)(cubeb **, char const *) = NULL; + int (*init_oneshot)(cubeb **, char const *) = NULL; if (backend_name != NULL) { if (!strcmp(backend_name, "pulse")) { @@ -172,6 +195,10 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam } else if (!strcmp(backend_name, "oss")) { #if defined(USE_OSS) init_oneshot = oss_init; +#endif + } else if (!strcmp(backend_name, "aaudio")) { +#if defined(USE_AAUDIO) + init_oneshot = aaudio_init; #endif } else if (!strcmp(backend_name, "audiotrack")) { #if defined(USE_AUDIOTRACK) @@ -186,7 +213,7 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam } } - int (* default_init[])(cubeb **, char const *) = { + int (*default_init[])(cubeb **, char const *) = { /* * init_oneshot must be at the top to allow user * to override all other choices @@ -207,7 +234,7 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #if defined(USE_ALSA) alsa_init, #endif -#if defined (USE_OSS) +#if defined(USE_OSS) oss_init, #endif #if defined(USE_AUDIOUNIT_RUST) @@ -227,6 +254,11 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam #endif #if defined(USE_OPENSL) opensl_init, +#endif + // TODO: should probably be preferred over OpenSLES when available. + // Initialization will fail on old android devices. +#if defined(USE_AAUDIO) + aaudio_init, #endif #if defined(USE_AUDIOTRACK) audiotrack_init, @@ -241,7 +273,7 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam return CUBEB_ERROR_INVALID_PARAMETER; } -#define OK(fn) assert((* context)->ops->fn) +#define OK(fn) assert((*context)->ops->fn) for (i = 0; i < NELEMS(default_init); ++i) { if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) { /* Assert that the minimal API is implemented. */ @@ -283,7 +315,8 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels) } int -cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms) +cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, + uint32_t * latency_ms) { if (!context || !params || !latency_ms) { return CUBEB_ERROR_INVALID_PARAMETER; @@ -321,15 +354,13 @@ cubeb_destroy(cubeb * context) } int -cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, +cubeb_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + unsigned int latency, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) { int r; @@ -337,24 +368,20 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n return CUBEB_ERROR_INVALID_PARAMETER; } - if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK || + if ((r = validate_stream_params(input_stream_params, output_stream_params)) != + CUBEB_OK || (r = validate_latency(latency)) != CUBEB_OK) { return r; } - r = context->ops->stream_init(context, stream, stream_name, - input_device, - input_stream_params, - output_device, - output_stream_params, - latency, - data_callback, - state_callback, - user_ptr); + r = context->ops->stream_init(context, stream, stream_name, input_device, + input_stream_params, output_device, + output_stream_params, latency, data_callback, + state_callback, user_ptr); if (r == CUBEB_ERROR_INVALID_FORMAT) { - LOG("Invalid format, %p %p %d %d", - output_stream_params, input_stream_params, + LOG("Invalid format, %p %p %d %d", output_stream_params, + input_stream_params, output_stream_params && output_stream_params->format, input_stream_params && input_stream_params->format); } @@ -392,20 +419,6 @@ cubeb_stream_stop(cubeb_stream * stream) return stream->context->ops->stream_stop(stream); } -int -cubeb_stream_reset_default_device(cubeb_stream * stream) -{ - if (!stream) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_reset_default_device) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return stream->context->ops->stream_reset_default_device(stream); -} - int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position) { @@ -472,8 +485,9 @@ cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name) return stream->context->ops->stream_set_name(stream, stream_name); } -int cubeb_stream_get_current_device(cubeb_stream * stream, - cubeb_device ** const device) +int +cubeb_stream_get_current_device(cubeb_stream * stream, + cubeb_device ** const device) { if (!stream || !device) { return CUBEB_ERROR_INVALID_PARAMETER; @@ -486,8 +500,8 @@ int cubeb_stream_get_current_device(cubeb_stream * stream, return stream->context->ops->stream_get_current_device(stream, device); } -int cubeb_stream_device_destroy(cubeb_stream * stream, - cubeb_device * device) +int +cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) { if (!stream || !device) { return CUBEB_ERROR_INVALID_PARAMETER; @@ -500,8 +514,10 @@ int cubeb_stream_device_destroy(cubeb_stream * stream, return stream->context->ops->stream_device_destroy(stream, device); } -int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, - cubeb_device_changed_callback device_changed_callback) +int +cubeb_stream_register_device_changed_callback( + cubeb_stream * stream, + cubeb_device_changed_callback device_changed_callback) { if (!stream) { return CUBEB_ERROR_INVALID_PARAMETER; @@ -511,10 +527,12 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream, return CUBEB_ERROR_NOT_SUPPORTED; } - return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback); + return stream->context->ops->stream_register_device_changed_callback( + stream, device_changed_callback); } -void * cubeb_stream_user_ptr(cubeb_stream * stream) +void * +cubeb_stream_user_ptr(cubeb_stream * stream) { if (!stream) { return NULL; @@ -523,56 +541,56 @@ void * cubeb_stream_user_ptr(cubeb_stream * stream) return stream->user_ptr; } -static -void log_device(cubeb_device_info * device_info) +static void +log_device(cubeb_device_info * device_info) { char devfmts[128] = ""; - const char * devtype, * devstate, * devdeffmt; + const char *devtype, *devstate, *devdeffmt; switch (device_info->type) { - case CUBEB_DEVICE_TYPE_INPUT: - devtype = "input"; - break; - case CUBEB_DEVICE_TYPE_OUTPUT: - devtype = "output"; - break; - case CUBEB_DEVICE_TYPE_UNKNOWN: - default: - devtype = "unknown?"; - break; + case CUBEB_DEVICE_TYPE_INPUT: + devtype = "input"; + break; + case CUBEB_DEVICE_TYPE_OUTPUT: + devtype = "output"; + break; + case CUBEB_DEVICE_TYPE_UNKNOWN: + default: + devtype = "unknown?"; + break; }; switch (device_info->state) { - case CUBEB_DEVICE_STATE_DISABLED: - devstate = "disabled"; - break; - case CUBEB_DEVICE_STATE_UNPLUGGED: - devstate = "unplugged"; - break; - case CUBEB_DEVICE_STATE_ENABLED: - devstate = "enabled"; - break; - default: - devstate = "unknown?"; - break; + case CUBEB_DEVICE_STATE_DISABLED: + devstate = "disabled"; + break; + case CUBEB_DEVICE_STATE_UNPLUGGED: + devstate = "unplugged"; + break; + case CUBEB_DEVICE_STATE_ENABLED: + devstate = "enabled"; + break; + default: + devstate = "unknown?"; + break; }; switch (device_info->default_format) { - case CUBEB_DEVICE_FMT_S16LE: - devdeffmt = "S16LE"; - break; - case CUBEB_DEVICE_FMT_S16BE: - devdeffmt = "S16BE"; - break; - case CUBEB_DEVICE_FMT_F32LE: - devdeffmt = "F32LE"; - break; - case CUBEB_DEVICE_FMT_F32BE: - devdeffmt = "F32BE"; - break; - default: - devdeffmt = "unknown?"; - break; + case CUBEB_DEVICE_FMT_S16LE: + devdeffmt = "S16LE"; + break; + case CUBEB_DEVICE_FMT_S16BE: + devdeffmt = "S16BE"; + break; + case CUBEB_DEVICE_FMT_F32LE: + devdeffmt = "F32LE"; + break; + case CUBEB_DEVICE_FMT_F32BE: + devdeffmt = "F32BE"; + break; + default: + devdeffmt = "unknown?"; + break; }; if (device_info->format & CUBEB_DEVICE_FMT_S16LE) { @@ -599,20 +617,17 @@ void log_device(cubeb_device_info * device_info) "\tRate:\t[%u, %u] (default: %u)\n" "\tLatency: lo %u frames, hi %u frames", device_info->device_id, device_info->preferred ? " (PREFERRED)" : "", - device_info->friendly_name, - device_info->group_id, - device_info->vendor_name, - devtype, - devstate, - device_info->max_channels, - (devfmts[0] == '\0') ? devfmts : devfmts + 1, (unsigned int)device_info->format, devdeffmt, - device_info->min_rate, device_info->max_rate, device_info->default_rate, - device_info->latency_lo, device_info->latency_hi); + device_info->friendly_name, device_info->group_id, + device_info->vendor_name, devtype, devstate, device_info->max_channels, + (devfmts[0] == '\0') ? devfmts : devfmts + 1, + (unsigned int)device_info->format, devdeffmt, device_info->min_rate, + device_info->max_rate, device_info->default_rate, device_info->latency_lo, + device_info->latency_hi); } -int cubeb_enumerate_devices(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection * collection) +int +cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype, + cubeb_device_collection * collection) { int rv; if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0) @@ -633,8 +648,9 @@ int cubeb_enumerate_devices(cubeb * context, return rv; } -int cubeb_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection) +int +cubeb_device_collection_destroy(cubeb * context, + cubeb_device_collection * collection) { int r; @@ -656,23 +672,26 @@ int cubeb_device_collection_destroy(cubeb * context, return r; } -int cubeb_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback callback, - void * user_ptr) +int +cubeb_register_device_collection_changed( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback callback, void * user_ptr) { - if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0) + if (context == NULL || + (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0) return CUBEB_ERROR_INVALID_PARAMETER; if (!context->ops->register_device_collection_changed) { return CUBEB_ERROR_NOT_SUPPORTED; } - return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr); + return context->ops->register_device_collection_changed(context, devtype, + callback, user_ptr); } -int cubeb_set_log_callback(cubeb_log_level log_level, - cubeb_log_callback log_callback) +int +cubeb_set_log_callback(cubeb_log_level log_level, + cubeb_log_callback log_callback) { if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) { return CUBEB_ERROR_INVALID_FORMAT; @@ -700,4 +719,3 @@ int cubeb_set_log_callback(cubeb_log_level log_level, return CUBEB_OK; } - diff --git a/externals/cubeb/src/cubeb_aaudio.cpp b/externals/cubeb/src/cubeb_aaudio.cpp new file mode 100755 index 000000000..b12c5966f --- /dev/null +++ b/externals/cubeb/src/cubeb_aaudio.cpp @@ -0,0 +1,1505 @@ +/* ex: set tabstop=2 shiftwidth=2 expandtab: + * Copyright © 2019 Jan Kelling + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" +#include "cubeb_android.h" +#include "cubeb_log.h" +#include "cubeb_resampler.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef DISABLE_LIBAAUDIO_DLOPEN +#define WRAP(x) x +#else +#define WRAP(x) (*cubeb_##x) +#define LIBAAUDIO_API_VISIT(X) \ + X(AAudio_convertResultToText) \ + X(AAudio_convertStreamStateToText) \ + X(AAudio_createStreamBuilder) \ + X(AAudioStreamBuilder_openStream) \ + X(AAudioStreamBuilder_setChannelCount) \ + X(AAudioStreamBuilder_setBufferCapacityInFrames) \ + X(AAudioStreamBuilder_setDirection) \ + X(AAudioStreamBuilder_setFormat) \ + X(AAudioStreamBuilder_setSharingMode) \ + X(AAudioStreamBuilder_setPerformanceMode) \ + X(AAudioStreamBuilder_setSampleRate) \ + X(AAudioStreamBuilder_delete) \ + X(AAudioStreamBuilder_setDataCallback) \ + X(AAudioStreamBuilder_setErrorCallback) \ + X(AAudioStream_close) \ + X(AAudioStream_read) \ + X(AAudioStream_requestStart) \ + X(AAudioStream_requestPause) \ + X(AAudioStream_setBufferSizeInFrames) \ + X(AAudioStream_getTimestamp) \ + X(AAudioStream_requestFlush) \ + X(AAudioStream_requestStop) \ + X(AAudioStream_getPerformanceMode) \ + X(AAudioStream_getSharingMode) \ + X(AAudioStream_getBufferSizeInFrames) \ + X(AAudioStream_getBufferCapacityInFrames) \ + X(AAudioStream_getSampleRate) \ + X(AAudioStream_waitForStateChange) \ + X(AAudioStream_getFramesRead) \ + X(AAudioStream_getState) \ + X(AAudioStream_getFramesWritten) \ + X(AAudioStream_getFramesPerBurst) \ + X(AAudioStreamBuilder_setInputPreset) \ + X(AAudioStreamBuilder_setUsage) + +// not needed or added later on +// X(AAudioStreamBuilder_setFramesPerDataCallback) \ + // X(AAudioStreamBuilder_setDeviceId) \ + // X(AAudioStreamBuilder_setSamplesPerFrame) \ + // X(AAudioStream_getSamplesPerFrame) \ + // X(AAudioStream_getDeviceId) \ + // X(AAudioStream_write) \ + // X(AAudioStream_getChannelCount) \ + // X(AAudioStream_getFormat) \ + // X(AAudioStream_getXRunCount) \ + // X(AAudioStream_isMMapUsed) \ + // X(AAudioStreamBuilder_setContentType) \ + // X(AAudioStreamBuilder_setSessionId) \ + // X(AAudioStream_getUsage) \ + // X(AAudioStream_getContentType) \ + // X(AAudioStream_getInputPreset) \ + // X(AAudioStream_getSessionId) \ +// END: not needed or added later on + +#define MAKE_TYPEDEF(x) static decltype(x) * cubeb_##x; +LIBAAUDIO_API_VISIT(MAKE_TYPEDEF) +#undef MAKE_TYPEDEF +#endif + +const uint8_t MAX_STREAMS = 16; + +using unique_lock = std::unique_lock; +using lock_guard = std::lock_guard; + +enum class stream_state { + INIT = 0, + STOPPED, + STOPPING, + STARTED, + STARTING, + DRAINING, + ERROR, + SHUTDOWN, +}; + +struct cubeb_stream { + /* Note: Must match cubeb_stream layout in cubeb.c. */ + cubeb * context{}; + void * user_ptr{}; + + std::atomic in_use{false}; + std::atomic state{stream_state::INIT}; + + AAudioStream * ostream{}; + AAudioStream * istream{}; + cubeb_data_callback data_callback{}; + cubeb_state_callback state_callback{}; + cubeb_resampler * resampler{}; + + // mutex synchronizes access to the stream from the state thread + // and user-called functions. Everything that is accessed in the + // aaudio data (or error) callback is synchronized only via atomics. + std::mutex mutex; + + std::unique_ptr in_buf; + unsigned in_frame_size{}; // size of one input frame + + cubeb_sample_format out_format{}; + std::atomic volume{1.f}; + unsigned out_channels{}; + unsigned out_frame_size{}; + int64_t latest_output_latency = 0; + int64_t latest_input_latency = 0; + bool voice_input; + bool voice_output; + uint64_t previous_clock; +}; + +struct cubeb { + struct cubeb_ops const * ops{}; + void * libaaudio{}; + + struct { + // The state thread: it waits for state changes and stops + // drained streams. + std::thread thread; + std::thread notifier; + std::mutex mutex; + std::condition_variable cond; + std::atomic join{false}; + std::atomic waiting{false}; + } state; + + // streams[i].in_use signals whether a stream is used + struct cubeb_stream streams[MAX_STREAMS]; +}; + +// Only allowed from state thread, while mutex on stm is locked +static void +shutdown(cubeb_stream * stm) +{ + if (stm->istream) { + WRAP(AAudioStream_requestStop)(stm->istream); + } + if (stm->ostream) { + WRAP(AAudioStream_requestStop)(stm->ostream); + } + + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); + stm->state.store(stream_state::SHUTDOWN); +} + +// Returns whether the given state is one in which we wait for +// an asynchronous change +static bool +waiting_state(stream_state state) +{ + switch (state) { + case stream_state::DRAINING: + case stream_state::STARTING: + case stream_state::STOPPING: + return true; + default: + return false; + } +} + +static void +update_state(cubeb_stream * stm) +{ + // Fast path for streams that don't wait for state change or are invalid + enum stream_state old_state = stm->state.load(); + if (old_state == stream_state::INIT || old_state == stream_state::STARTED || + old_state == stream_state::STOPPED || + old_state == stream_state::SHUTDOWN) { + return; + } + + // If the main thread currently operates on this thread, we don't + // have to wait for it + unique_lock lock(stm->mutex, std::try_to_lock); + if (!lock.owns_lock()) { + return; + } + + // check again: if this is true now, the stream was destroyed or + // changed between our fast path check and locking the mutex + old_state = stm->state.load(); + if (old_state == stream_state::INIT || old_state == stream_state::STARTED || + old_state == stream_state::STOPPED || + old_state == stream_state::SHUTDOWN) { + return; + } + + // We compute the new state the stream has and then compare_exchange it + // if it has changed. This way we will never just overwrite state + // changes that were set from the audio thread in the meantime, + // such as a DRAINING or error state. + enum stream_state new_state; + do { + if (old_state == stream_state::SHUTDOWN) { + return; + } + + if (old_state == stream_state::ERROR) { + shutdown(stm); + return; + } + + new_state = old_state; + + aaudio_stream_state_t istate = 0; + aaudio_stream_state_t ostate = 0; + + // We use waitForStateChange (with zero timeout) instead of just + // getState since only the former internally updates the state. + // See the docs of aaudio getState/waitForStateChange for details, + // why we are passing STATE_UNKNOWN. + aaudio_result_t res; + if (stm->istream) { + res = WRAP(AAudioStream_waitForStateChange)( + stm->istream, AAUDIO_STREAM_STATE_UNKNOWN, &istate, 0); + if (res != AAUDIO_OK) { + LOG("AAudioStream_waitForStateChanged: %s", + WRAP(AAudio_convertResultToText)(res)); + return; + } + assert(istate); + } + + if (stm->ostream) { + res = WRAP(AAudioStream_waitForStateChange)( + stm->ostream, AAUDIO_STREAM_STATE_UNKNOWN, &ostate, 0); + if (res != AAUDIO_OK) { + LOG("AAudioStream_waitForStateChanged: %s", + WRAP(AAudio_convertResultToText)(res)); + return; + } + assert(ostate); + } + + // handle invalid stream states + if (istate == AAUDIO_STREAM_STATE_PAUSING || + istate == AAUDIO_STREAM_STATE_PAUSED || + istate == AAUDIO_STREAM_STATE_FLUSHING || + istate == AAUDIO_STREAM_STATE_FLUSHED || + istate == AAUDIO_STREAM_STATE_UNKNOWN || + istate == AAUDIO_STREAM_STATE_DISCONNECTED) { + const char * name = WRAP(AAudio_convertStreamStateToText)(istate); + LOG("Unexpected android input stream state %s", name); + shutdown(stm); + return; + } + + if (ostate == AAUDIO_STREAM_STATE_PAUSING || + ostate == AAUDIO_STREAM_STATE_PAUSED || + ostate == AAUDIO_STREAM_STATE_FLUSHING || + ostate == AAUDIO_STREAM_STATE_FLUSHED || + ostate == AAUDIO_STREAM_STATE_UNKNOWN || + ostate == AAUDIO_STREAM_STATE_DISCONNECTED) { + const char * name = WRAP(AAudio_convertStreamStateToText)(istate); + LOG("Unexpected android output stream state %s", name); + shutdown(stm); + return; + } + + switch (old_state) { + case stream_state::STARTING: + if ((!istate || istate == AAUDIO_STREAM_STATE_STARTED) && + (!ostate || ostate == AAUDIO_STREAM_STATE_STARTED)) { + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); + new_state = stream_state::STARTED; + } + break; + case stream_state::DRAINING: + // The DRAINING state means that we want to stop the streams but + // may not have done so yet. + // The aaudio docs state that returning STOP from the callback isn't + // enough, the stream has to be stopped from another thread + // afterwards. + // No callbacks are triggered anymore when requestStop returns. + // That is important as we otherwise might read from a closed istream + // for a duplex stream. + // Therefor it is important to close ostream first. + if (ostate && ostate != AAUDIO_STREAM_STATE_STOPPING && + ostate != AAUDIO_STREAM_STATE_STOPPED) { + res = WRAP(AAudioStream_requestStop)(stm->ostream); + if (res != AAUDIO_OK) { + LOG("AAudioStream_requestStop: %s", + WRAP(AAudio_convertResultToText)(res)); + return; + } + } + if (istate && istate != AAUDIO_STREAM_STATE_STOPPING && + istate != AAUDIO_STREAM_STATE_STOPPED) { + res = WRAP(AAudioStream_requestStop)(stm->istream); + if (res != AAUDIO_OK) { + LOG("AAudioStream_requestStop: %s", + WRAP(AAudio_convertResultToText)(res)); + return; + } + } + + // we always wait until both streams are stopped until we + // send CUBEB_STATE_DRAINED. Then we can directly transition + // our logical state to STOPPED, not triggering + // an additional CUBEB_STATE_STOPPED callback (which might + // be unexpected for the user). + if ((!ostate || ostate == AAUDIO_STREAM_STATE_STOPPED) && + (!istate || istate == AAUDIO_STREAM_STATE_STOPPED)) { + new_state = stream_state::STOPPED; + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); + } + break; + case stream_state::STOPPING: + assert(!istate || istate == AAUDIO_STREAM_STATE_STOPPING || + istate == AAUDIO_STREAM_STATE_STOPPED); + assert(!ostate || ostate == AAUDIO_STREAM_STATE_STOPPING || + ostate == AAUDIO_STREAM_STATE_STOPPED); + if ((!istate || istate == AAUDIO_STREAM_STATE_STOPPED) && + (!ostate || ostate == AAUDIO_STREAM_STATE_STOPPED)) { + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); + new_state = stream_state::STOPPED; + } + break; + default: + assert(false && "Unreachable: invalid state"); + } + } while (old_state != new_state && + !stm->state.compare_exchange_strong(old_state, new_state)); +} + +// See https://nyorain.github.io/lock-free-wakeup.html for a note +// why this is needed. The audio thread notifies the state thread about +// state changes and must not block. The state thread on the other hand should +// sleep until there is work to be done. So we need a lockfree producer +// and blocking producer. This can only be achieved safely with a new thread +// that only serves as notifier backup (in case the notification happens +// right between the state thread checking and going to sleep in which case +// this thread will kick in and signal it right again). +static void +notifier_thread(cubeb * ctx) +{ + unique_lock lock(ctx->state.mutex); + + while (!ctx->state.join.load()) { + ctx->state.cond.wait(lock); + if (ctx->state.waiting.load()) { + // This must signal our state thread since there is no other + // thread currently waiting on the condition variable. + // The state change thread is guaranteed to be waiting since + // we hold the mutex it locks when awake. + ctx->state.cond.notify_one(); + } + } + + // make sure other thread joins as well + ctx->state.cond.notify_one(); + LOG("Exiting notifier thread"); +} + +static void +state_thread(cubeb * ctx) +{ + unique_lock lock(ctx->state.mutex); + + bool waiting = false; + while (!ctx->state.join.load()) { + waiting |= ctx->state.waiting.load(); + if (waiting) { + ctx->state.waiting.store(false); + waiting = false; + for (unsigned i = 0u; i < MAX_STREAMS; ++i) { + cubeb_stream * stm = &ctx->streams[i]; + update_state(stm); + waiting |= waiting_state(atomic_load(&stm->state)); + } + + // state changed from another thread, update again immediately + if (ctx->state.waiting.load()) { + waiting = true; + continue; + } + + // Not waiting for any change anymore: we can wait on the + // condition variable without timeout + if (!waiting) { + continue; + } + + // while any stream is waiting for state change we sleep with regular + // timeouts. But we wake up immediately if signaled. + // This might seem like a poor man's implementation of state change + // waiting but (as of october 2020), the implementation of + // AAudioStream_waitForStateChange is just sleeping with regular + // timeouts as well: + // https://android.googlesource.com/platform/frameworks/av/+/refs/heads/master/media/libaaudio/src/core/AudioStream.cpp + auto dur = std::chrono::milliseconds(5); + ctx->state.cond.wait_for(lock, dur); + } else { + ctx->state.cond.wait(lock); + } + } + + // make sure other thread joins as well + ctx->state.cond.notify_one(); + LOG("Exiting state thread"); +} + +static char const * +aaudio_get_backend_id(cubeb * /* ctx */) +{ + return "aaudio"; +} + +static int +aaudio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) +{ + assert(ctx && max_channels); + // NOTE: we might get more, AAudio docs don't specify anything. + *max_channels = 2; + return CUBEB_OK; +} + +static void +aaudio_destroy(cubeb * ctx) +{ + assert(ctx); + +#ifndef NDEBUG + // make sure all streams were destroyed + for (unsigned i = 0u; i < MAX_STREAMS; ++i) { + assert(!ctx->streams[i].in_use.load()); + } +#endif + + // broadcast joining to both threads + // they will additionally signal each other before joining + ctx->state.join.store(true); + ctx->state.cond.notify_all(); + + if (ctx->state.thread.joinable()) { + ctx->state.thread.join(); + } + if (ctx->state.notifier.joinable()) { + ctx->state.notifier.join(); + } + + if (ctx->libaaudio) { + dlclose(ctx->libaaudio); + } + delete ctx; +} + +static void +apply_volume(cubeb_stream * stm, void * audio_data, uint32_t num_frames) +{ + float volume = stm->volume.load(); + // optimization: we don't have to change anything in this case + if (volume == 1.f) { + return; + } + + switch (stm->out_format) { + case CUBEB_SAMPLE_S16NE: + for (uint32_t i = 0u; i < num_frames * stm->out_channels; ++i) { + (static_cast(audio_data))[i] *= volume; + } + break; + case CUBEB_SAMPLE_FLOAT32NE: + for (uint32_t i = 0u; i < num_frames * stm->out_channels; ++i) { + (static_cast(audio_data))[i] *= volume; + } + break; + default: + assert(false && "Unreachable: invalid stream out_format"); + } +} + +// Returning AAUDIO_CALLBACK_RESULT_STOP seems to put the stream in +// an invalid state. Seems like an AAudio bug/bad documentation. +// We therefore only return it on error. + +static aaudio_data_callback_result_t +aaudio_duplex_data_cb(AAudioStream * astream, void * user_data, + void * audio_data, int32_t num_frames) +{ + cubeb_stream * stm = (cubeb_stream *)user_data; + assert(stm->ostream == astream); + assert(stm->istream); + assert(num_frames >= 0); + + stream_state state = atomic_load(&stm->state); + // int istate = WRAP(AAudioStream_getState)(stm->istream); + // int ostate = WRAP(AAudioStream_getState)(stm->ostream); + // ALOGV("aaudio duplex data cb on stream %p: state %ld (in: %d, out: %d), + // num_frames: %ld", + // (void*) stm, state, istate, ostate, num_frames); + + // all other states may happen since the callback might be called + // from within requestStart + assert(state != stream_state::SHUTDOWN); + + // This might happen when we started draining but not yet actually + // stopped the stream from the state thread. + if (state == stream_state::DRAINING) { + std::memset(audio_data, 0x0, num_frames * stm->out_frame_size); + return AAUDIO_CALLBACK_RESULT_CONTINUE; + } + + // The aaudio docs state that AAudioStream_read must not be called on + // the stream associated with a callback. But we call it on the input stream + // while this callback is for the output stream so this is ok. + // We also pass timeout 0, giving us strong non-blocking guarantees. + // This is exactly how it's done in the aaudio duplex example code snippet. + long in_num_frames = + WRAP(AAudioStream_read)(stm->istream, stm->in_buf.get(), num_frames, 0); + if (in_num_frames < 0) { // error + stm->state.store(stream_state::ERROR); + LOG("AAudioStream_read: %s", + WRAP(AAudio_convertResultToText)(in_num_frames)); + return AAUDIO_CALLBACK_RESULT_STOP; + } + + // This can happen shortly after starting the stream. AAudio might immediately + // begin to buffer output but not have any input ready yet. We could + // block AAudioStream_read (passing a timeout > 0) but that leads to issues + // since blocking in this callback is a bad idea in general and it might break + // the stream when it is stopped by another thread shortly after being + // started. We therefore simply send silent input to the application, as shown + // in the AAudio duplex stream code example. + if (in_num_frames < num_frames) { + // LOG("AAudioStream_read returned not enough frames: %ld instead of %d", + // in_num_frames, num_frames); + unsigned left = num_frames - in_num_frames; + char * buf = stm->in_buf.get() + in_num_frames * stm->in_frame_size; + std::memset(buf, 0x0, left * stm->in_frame_size); + in_num_frames = num_frames; + } + + long done_frames = + cubeb_resampler_fill(stm->resampler, stm->in_buf.get(), &in_num_frames, + audio_data, num_frames); + + if (done_frames < 0 || done_frames > num_frames) { + LOG("Error in data callback or resampler: %ld", done_frames); + stm->state.store(stream_state::ERROR); + return AAUDIO_CALLBACK_RESULT_STOP; + } else if (done_frames < num_frames) { + stm->state.store(stream_state::DRAINING); + stm->context->state.waiting.store(true); + stm->context->state.cond.notify_one(); + + char * begin = + static_cast(audio_data) + done_frames * stm->out_frame_size; + std::memset(begin, 0x0, (num_frames - done_frames) * stm->out_frame_size); + } + + apply_volume(stm, audio_data, done_frames); + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static aaudio_data_callback_result_t +aaudio_output_data_cb(AAudioStream * astream, void * user_data, + void * audio_data, int32_t num_frames) +{ + cubeb_stream * stm = (cubeb_stream *)user_data; + assert(stm->ostream == astream); + assert(!stm->istream); + assert(num_frames >= 0); + + stream_state state = stm->state.load(); + // int ostate = WRAP(AAudioStream_getState)(stm->ostream); + // ALOGV("aaudio output data cb on stream %p: state %ld (%d), num_frames: + // %ld", + // (void*) stm, state, ostate, num_frames); + + // all other states may happen since the callback might be called + // from within requestStart + assert(state != stream_state::SHUTDOWN); + + // This might happen when we started draining but not yet actually + // stopped the stream from the state thread. + if (state == stream_state::DRAINING) { + std::memset(audio_data, 0x0, num_frames * stm->out_frame_size); + return AAUDIO_CALLBACK_RESULT_CONTINUE; + } + + long done_frames = + cubeb_resampler_fill(stm->resampler, NULL, NULL, audio_data, num_frames); + if (done_frames < 0 || done_frames > num_frames) { + LOG("Error in data callback or resampler: %ld", done_frames); + stm->state.store(stream_state::ERROR); + return AAUDIO_CALLBACK_RESULT_STOP; + } else if (done_frames < num_frames) { + stm->state.store(stream_state::DRAINING); + stm->context->state.waiting.store(true); + stm->context->state.cond.notify_one(); + + char * begin = + static_cast(audio_data) + done_frames * stm->out_frame_size; + std::memset(begin, 0x0, (num_frames - done_frames) * stm->out_frame_size); + } + + apply_volume(stm, audio_data, done_frames); + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static aaudio_data_callback_result_t +aaudio_input_data_cb(AAudioStream * astream, void * user_data, + void * audio_data, int32_t num_frames) +{ + cubeb_stream * stm = (cubeb_stream *)user_data; + assert(stm->istream == astream); + assert(!stm->ostream); + assert(num_frames >= 0); + + stream_state state = stm->state.load(); + // int istate = WRAP(AAudioStream_getState)(stm->istream); + // ALOGV("aaudio input data cb on stream %p: state %ld (%d), num_frames: %ld", + // (void*) stm, state, istate, num_frames); + + // all other states may happen since the callback might be called + // from within requestStart + assert(state != stream_state::SHUTDOWN); + + // This might happen when we started draining but not yet actually + // STOPPED the stream from the state thread. + if (state == stream_state::DRAINING) { + return AAUDIO_CALLBACK_RESULT_CONTINUE; + } + + long input_frame_count = num_frames; + long done_frames = cubeb_resampler_fill(stm->resampler, audio_data, + &input_frame_count, NULL, 0); + if (done_frames < 0 || done_frames > num_frames) { + LOG("Error in data callback or resampler: %ld", done_frames); + stm->state.store(stream_state::ERROR); + return AAUDIO_CALLBACK_RESULT_STOP; + } else if (done_frames < input_frame_count) { + // we don't really drain an input stream, just have to + // stop it from the state thread. That is signaled via the + // DRAINING state. + stm->state.store(stream_state::DRAINING); + stm->context->state.waiting.store(true); + stm->context->state.cond.notify_one(); + } + + return AAUDIO_CALLBACK_RESULT_CONTINUE; +} + +static void +aaudio_error_cb(AAudioStream * astream, void * user_data, aaudio_result_t error) +{ + cubeb_stream * stm = static_cast(user_data); + assert(stm->ostream == astream || stm->istream == astream); + LOG("AAudio error callback: %s", WRAP(AAudio_convertResultToText)(error)); + stm->state.store(stream_state::ERROR); +} + +static int +realize_stream(AAudioStreamBuilder * sb, const cubeb_stream_params * params, + AAudioStream ** stream, unsigned * frame_size) +{ + aaudio_result_t res; + assert(params->rate); + assert(params->channels); + + WRAP(AAudioStreamBuilder_setSampleRate)(sb, params->rate); + WRAP(AAudioStreamBuilder_setChannelCount)(sb, params->channels); + + aaudio_format_t fmt; + switch (params->format) { + case CUBEB_SAMPLE_S16NE: + fmt = AAUDIO_FORMAT_PCM_I16; + *frame_size = sizeof(int16_t) * params->channels; + break; + case CUBEB_SAMPLE_FLOAT32NE: + fmt = AAUDIO_FORMAT_PCM_FLOAT; + *frame_size = sizeof(float) * params->channels; + break; + default: + return CUBEB_ERROR_INVALID_FORMAT; + } + + WRAP(AAudioStreamBuilder_setFormat)(sb, fmt); + res = WRAP(AAudioStreamBuilder_openStream)(sb, stream); + if (res == AAUDIO_ERROR_INVALID_FORMAT) { + LOG("AAudio device doesn't support output format %d", fmt); + return CUBEB_ERROR_INVALID_FORMAT; + } else if (params->rate && res == AAUDIO_ERROR_INVALID_RATE) { + // The requested rate is not supported. + // Just try again with default rate, we create a resampler anyways + WRAP(AAudioStreamBuilder_setSampleRate)(sb, AAUDIO_UNSPECIFIED); + res = WRAP(AAudioStreamBuilder_openStream)(sb, stream); + LOG("Requested rate of %u is not supported, inserting resampler", + params->rate); + } + + // When the app has no permission to record audio + // (android.permission.RECORD_AUDIO) but requested and input stream, this will + // return INVALID_ARGUMENT. + if (res != AAUDIO_OK) { + LOG("AAudioStreamBuilder_openStream: %s", + WRAP(AAudio_convertResultToText)(res)); + return CUBEB_ERROR; + } + + return CUBEB_OK; +} + +static void +aaudio_stream_destroy(cubeb_stream * stm) +{ + lock_guard lock(stm->mutex); + assert(stm->state == stream_state::STOPPED || + stm->state == stream_state::STOPPING || + stm->state == stream_state::INIT || + stm->state == stream_state::DRAINING || + stm->state == stream_state::ERROR || + stm->state == stream_state::SHUTDOWN); + + aaudio_result_t res; + + // No callbacks are triggered anymore when requestStop returns. + // That is important as we otherwise might read from a closed istream + // for a duplex stream. + if (stm->ostream) { + if (stm->state != stream_state::STOPPED && + stm->state != stream_state::STOPPING && + stm->state != stream_state::SHUTDOWN) { + res = WRAP(AAudioStream_requestStop)(stm->ostream); + if (res != AAUDIO_OK) { + LOG("AAudioStreamBuilder_requestStop: %s", + WRAP(AAudio_convertResultToText)(res)); + } + } + + WRAP(AAudioStream_close)(stm->ostream); + stm->ostream = NULL; + } + + if (stm->istream) { + if (stm->state != stream_state::STOPPED && + stm->state != stream_state::STOPPING && + stm->state != stream_state::SHUTDOWN) { + res = WRAP(AAudioStream_requestStop)(stm->istream); + if (res != AAUDIO_OK) { + LOG("AAudioStreamBuilder_requestStop: %s", + WRAP(AAudio_convertResultToText)(res)); + } + } + + WRAP(AAudioStream_close)(stm->istream); + stm->istream = NULL; + } + + if (stm->resampler) { + cubeb_resampler_destroy(stm->resampler); + stm->resampler = NULL; + } + + stm->in_buf = {}; + stm->in_frame_size = {}; + stm->out_format = {}; + stm->out_channels = {}; + stm->out_frame_size = {}; + + stm->state.store(stream_state::INIT); + stm->in_use.store(false); +} + +static int +aaudio_stream_init_impl(cubeb_stream * stm, cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency_frames) +{ + assert(stm->state.load() == stream_state::INIT); + stm->in_use.store(true); + + aaudio_result_t res; + AAudioStreamBuilder * sb; + res = WRAP(AAudio_createStreamBuilder)(&sb); + if (res != AAUDIO_OK) { + LOG("AAudio_createStreamBuilder: %s", + WRAP(AAudio_convertResultToText)(res)); + return CUBEB_ERROR; + } + + // make sure the builder is always destroyed + struct StreamBuilderDestructor { + void operator()(AAudioStreamBuilder * sb) + { + WRAP(AAudioStreamBuilder_delete)(sb); + } + }; + + std::unique_ptr sbPtr(sb); + + WRAP(AAudioStreamBuilder_setErrorCallback)(sb, aaudio_error_cb, stm); + WRAP(AAudioStreamBuilder_setBufferCapacityInFrames)(sb, latency_frames); + + AAudioStream_dataCallback in_data_callback{}; + AAudioStream_dataCallback out_data_callback{}; + if (output_stream_params && input_stream_params) { + out_data_callback = aaudio_duplex_data_cb; + in_data_callback = NULL; + } else if (input_stream_params) { + in_data_callback = aaudio_input_data_cb; + } else if (output_stream_params) { + out_data_callback = aaudio_output_data_cb; + } else { + LOG("Tried to open stream without input or output parameters"); + return CUBEB_ERROR; + } + +#ifdef CUBEB_AAUDIO_EXCLUSIVE_STREAM + LOG("AAudio setting exclusive share mode for stream"); + WRAP(AAudioStreamBuilder_setSharingMode)(sb, AAUDIO_SHARING_MODE_EXCLUSIVE); +#endif + + if (latency_frames <= POWERSAVE_LATENCY_FRAMES_THRESHOLD) { + LOG("AAudio setting low latency mode for stream"); + WRAP(AAudioStreamBuilder_setPerformanceMode) + (sb, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + } else { + LOG("AAudio setting power saving mode for stream"); + WRAP(AAudioStreamBuilder_setPerformanceMode) + (sb, AAUDIO_PERFORMANCE_MODE_POWER_SAVING); + } + + unsigned frame_size; + + // initialize streams + // output + uint32_t target_sample_rate = 0; + cubeb_stream_params out_params; + if (output_stream_params) { + int output_preset = stm->voice_output ? AAUDIO_USAGE_VOICE_COMMUNICATION + : AAUDIO_USAGE_MEDIA; + WRAP(AAudioStreamBuilder_setUsage)(sb, output_preset); + WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_OUTPUT); + WRAP(AAudioStreamBuilder_setDataCallback)(sb, out_data_callback, stm); + int res_err = + realize_stream(sb, output_stream_params, &stm->ostream, &frame_size); + if (res_err) { + return res_err; + } + + // output debug information + aaudio_sharing_mode_t sm = WRAP(AAudioStream_getSharingMode)(stm->ostream); + aaudio_performance_mode_t pm = + WRAP(AAudioStream_getPerformanceMode)(stm->ostream); + int bcap = WRAP(AAudioStream_getBufferCapacityInFrames)(stm->ostream); + int bsize = WRAP(AAudioStream_getBufferSizeInFrames)(stm->ostream); + int rate = WRAP(AAudioStream_getSampleRate)(stm->ostream); + LOG("AAudio output stream sharing mode: %d", sm); + LOG("AAudio output stream performance mode: %d", pm); + LOG("AAudio output stream buffer capacity: %d", bcap); + LOG("AAudio output stream buffer size: %d", bsize); + LOG("AAudio output stream buffer rate: %d", rate); + + target_sample_rate = output_stream_params->rate; + out_params = *output_stream_params; + out_params.rate = rate; + + stm->out_channels = output_stream_params->channels; + stm->out_format = output_stream_params->format; + stm->out_frame_size = frame_size; + stm->volume.store(1.f); + } + + // input + cubeb_stream_params in_params; + if (input_stream_params) { + // Match what the OpenSL backend does for now, we could use UNPROCESSED and + // VOICE_COMMUNICATION here, but we'd need to make it clear that + // application-level AEC and other voice processing should be disabled + // there. + int input_preset = stm->voice_input ? AAUDIO_INPUT_PRESET_VOICE_RECOGNITION + : AAUDIO_INPUT_PRESET_CAMCORDER; + WRAP(AAudioStreamBuilder_setInputPreset)(sb, input_preset); + WRAP(AAudioStreamBuilder_setDirection)(sb, AAUDIO_DIRECTION_INPUT); + WRAP(AAudioStreamBuilder_setDataCallback)(sb, in_data_callback, stm); + int res_err = + realize_stream(sb, input_stream_params, &stm->istream, &frame_size); + if (res_err) { + return res_err; + } + + // output debug information + aaudio_sharing_mode_t sm = WRAP(AAudioStream_getSharingMode)(stm->istream); + aaudio_performance_mode_t pm = + WRAP(AAudioStream_getPerformanceMode)(stm->istream); + int bcap = WRAP(AAudioStream_getBufferCapacityInFrames)(stm->istream); + int bsize = WRAP(AAudioStream_getBufferSizeInFrames)(stm->istream); + int rate = WRAP(AAudioStream_getSampleRate)(stm->istream); + LOG("AAudio input stream sharing mode: %d", sm); + LOG("AAudio input stream performance mode: %d", pm); + LOG("AAudio input stream buffer capacity: %d", bcap); + LOG("AAudio input stream buffer size: %d", bsize); + LOG("AAudio input stream buffer rate: %d", rate); + + stm->in_buf.reset(new char[bcap * frame_size]()); + assert(!target_sample_rate || + target_sample_rate == input_stream_params->rate); + + target_sample_rate = input_stream_params->rate; + in_params = *input_stream_params; + in_params.rate = rate; + stm->in_frame_size = frame_size; + } + + // initialize resampler + stm->resampler = cubeb_resampler_create( + stm, input_stream_params ? &in_params : NULL, + output_stream_params ? &out_params : NULL, target_sample_rate, + stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT); + + if (!stm->resampler) { + LOG("Failed to create resampler"); + return CUBEB_ERROR; + } + + // the stream isn't started initially. We don't need to differentiate + // between a stream that was just initialized and one that played + // already but was stopped. + stm->state.store(stream_state::STOPPED); + LOG("Cubeb stream (%p) INIT success", (void *)stm); + return CUBEB_OK; +} + +static int +aaudio_stream_init(cubeb * ctx, cubeb_stream ** stream, + char const * /* stream_name */, cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency_frames, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) +{ + assert(!input_device); + assert(!output_device); + + // atomically find a free stream. + cubeb_stream * stm = NULL; + unique_lock lock; + for (unsigned i = 0u; i < MAX_STREAMS; ++i) { + // This check is only an optimization, we don't strictly need it + // since we check again after locking the mutex. + if (ctx->streams[i].in_use.load()) { + continue; + } + + // if this fails, another thread initialized this stream + // between our check of in_use and this. + lock = unique_lock(ctx->streams[i].mutex, std::try_to_lock); + if (!lock.owns_lock()) { + continue; + } + + if (ctx->streams[i].in_use.load()) { + lock = {}; + continue; + } + + stm = &ctx->streams[i]; + break; + } + + if (!stm) { + LOG("Error: maximum number of streams reached"); + return CUBEB_ERROR; + } + + stm->context = ctx; + stm->user_ptr = user_ptr; + stm->data_callback = data_callback; + stm->state_callback = state_callback; + stm->voice_input = input_stream_params && + !!(input_stream_params->prefs & CUBEB_STREAM_PREF_VOICE); + stm->voice_output = output_stream_params && + !!(output_stream_params->prefs & CUBEB_STREAM_PREF_VOICE); + stm->previous_clock = 0; + + LOG("cubeb stream prefs: voice_input: %s voice_output: %s", + stm->voice_input ? "true" : "false", + stm->voice_output ? "true" : "false"); + + int err = aaudio_stream_init_impl(stm, input_device, input_stream_params, + output_device, output_stream_params, + latency_frames); + if (err != CUBEB_OK) { + // This is needed since aaudio_stream_destroy will lock the mutex again. + // It's no problem that there is a gap in between as the stream isn't + // actually in u se. + lock.unlock(); + aaudio_stream_destroy(stm); + return err; + } + + *stream = stm; + return CUBEB_OK; +} + +static int +aaudio_stream_start(cubeb_stream * stm) +{ + assert(stm && stm->in_use.load()); + lock_guard lock(stm->mutex); + + stream_state state = stm->state.load(); + int istate = stm->istream ? WRAP(AAudioStream_getState)(stm->istream) : 0; + int ostate = stm->ostream ? WRAP(AAudioStream_getState)(stm->ostream) : 0; + LOGV("STARTING stream %p: %d (%d %d)", (void *)stm, state, istate, ostate); + + switch (state) { + case stream_state::STARTED: + case stream_state::STARTING: + LOG("cubeb stream %p already STARTING/STARTED", (void *)stm); + return CUBEB_OK; + case stream_state::ERROR: + case stream_state::SHUTDOWN: + return CUBEB_ERROR; + case stream_state::INIT: + assert(false && "Invalid stream"); + return CUBEB_ERROR; + case stream_state::STOPPED: + case stream_state::STOPPING: + case stream_state::DRAINING: + break; + } + + aaudio_result_t res; + + // Important to start istream before ostream. + // As soon as we start ostream, the callbacks might be triggered an we + // might read from istream (on duplex). If istream wasn't started yet + // this is a problem. + if (stm->istream) { + res = WRAP(AAudioStream_requestStart)(stm->istream); + if (res != AAUDIO_OK) { + LOG("AAudioStream_requestStart (istream): %s", + WRAP(AAudio_convertResultToText)(res)); + stm->state.store(stream_state::ERROR); + return CUBEB_ERROR; + } + } + + if (stm->ostream) { + res = WRAP(AAudioStream_requestStart)(stm->ostream); + if (res != AAUDIO_OK) { + LOG("AAudioStream_requestStart (ostream): %s", + WRAP(AAudio_convertResultToText)(res)); + stm->state.store(stream_state::ERROR); + return CUBEB_ERROR; + } + } + + int ret = CUBEB_OK; + bool success; + + while (!(success = stm->state.compare_exchange_strong( + state, stream_state::STARTING))) { + // we land here only if the state has changed in the meantime + switch (state) { + // If an error ocurred in the meantime, we can't change that. + // The stream will be stopped when shut down. + case stream_state::ERROR: + ret = CUBEB_ERROR; + break; + // The only situation in which the state could have switched to draining + // is if the callback was already fired and requested draining. Don't + // overwrite that. It's not an error either though. + case stream_state::DRAINING: + break; + + // If the state switched [DRAINING -> STOPPING] or [DRAINING/STOPPING -> + // STOPPED] in the meantime, we can simply overwrite that since we restarted + // the stream. + case stream_state::STOPPING: + case stream_state::STOPPED: + continue; + + // There is no situation in which the state could have been valid before + // but now in shutdown mode, since we hold the streams mutex. + // There is also no way that it switched *into* STARTING or + // STARTED mode. + default: + assert(false && "Invalid state change"); + ret = CUBEB_ERROR; + break; + } + + break; + } + + if (success) { + stm->context->state.waiting.store(true); + stm->context->state.cond.notify_one(); + } + + return ret; +} + +static int +aaudio_stream_stop(cubeb_stream * stm) +{ + assert(stm && stm->in_use.load()); + lock_guard lock(stm->mutex); + + stream_state state = stm->state.load(); + int istate = stm->istream ? WRAP(AAudioStream_getState)(stm->istream) : 0; + int ostate = stm->ostream ? WRAP(AAudioStream_getState)(stm->ostream) : 0; + LOGV("STOPPING stream %p: %d (%d %d)", (void *)stm, state, istate, ostate); + + switch (state) { + case stream_state::STOPPED: + case stream_state::STOPPING: + case stream_state::DRAINING: + LOG("cubeb stream %p already STOPPING/STOPPED", (void *)stm); + return CUBEB_OK; + case stream_state::ERROR: + case stream_state::SHUTDOWN: + return CUBEB_ERROR; + case stream_state::INIT: + assert(false && "Invalid stream"); + return CUBEB_ERROR; + case stream_state::STARTED: + case stream_state::STARTING: + break; + } + + aaudio_result_t res; + + // No callbacks are triggered anymore when requestStop returns. + // That is important as we otherwise might read from a closed istream + // for a duplex stream. + // Therefor it is important to close ostream first. + if (stm->ostream) { + // Could use pause + flush here as well, the public cubeb interface + // doesn't state behavior. + res = WRAP(AAudioStream_requestStop)(stm->ostream); + if (res != AAUDIO_OK) { + LOG("AAudioStream_requestStop (ostream): %s", + WRAP(AAudio_convertResultToText)(res)); + stm->state.store(stream_state::ERROR); + return CUBEB_ERROR; + } + } + + if (stm->istream) { + res = WRAP(AAudioStream_requestStop)(stm->istream); + if (res != AAUDIO_OK) { + LOG("AAudioStream_requestStop (istream): %s", + WRAP(AAudio_convertResultToText)(res)); + stm->state.store(stream_state::ERROR); + return CUBEB_ERROR; + } + } + + int ret = CUBEB_OK; + bool success; + while (!(success = atomic_compare_exchange_strong(&stm->state, &state, + stream_state::STOPPING))) { + // we land here only if the state has changed in the meantime + switch (state) { + // If an error ocurred in the meantime, we can't change that. + // The stream will be STOPPED when shut down. + case stream_state::ERROR: + ret = CUBEB_ERROR; + break; + // If it was switched to DRAINING in the meantime, it was or + // will be STOPPED soon anyways. We don't interfere with + // the DRAINING process, no matter in which state. + // Not an error + case stream_state::DRAINING: + case stream_state::STOPPING: + case stream_state::STOPPED: + break; + + // If the state switched from STARTING to STARTED in the meantime + // we can simply overwrite that since we just STOPPED it. + case stream_state::STARTED: + continue; + + // There is no situation in which the state could have been valid before + // but now in shutdown mode, since we hold the streams mutex. + // There is also no way that it switched *into* STARTING mode. + default: + assert(false && "Invalid state change"); + ret = CUBEB_ERROR; + break; + } + + break; + } + + if (success) { + stm->context->state.waiting.store(true); + stm->context->state.cond.notify_one(); + } + + return ret; +} + +static int +aaudio_stream_get_position(cubeb_stream * stm, uint64_t * position) +{ + assert(stm && stm->in_use.load()); + lock_guard lock(stm->mutex); + + stream_state state = stm->state.load(); + AAudioStream * stream = stm->ostream ? stm->ostream : stm->istream; + switch (state) { + case stream_state::ERROR: + case stream_state::SHUTDOWN: + return CUBEB_ERROR; + case stream_state::DRAINING: + case stream_state::STOPPED: + case stream_state::STOPPING: + // getTimestamp is only valid when the stream is playing. + // Simply return the number of frames passed to aaudio + *position = WRAP(AAudioStream_getFramesRead)(stream); + if (*position < stm->previous_clock) { + *position = stm->previous_clock; + } else { + stm->previous_clock = *position; + } + return CUBEB_OK; + case stream_state::INIT: + assert(false && "Invalid stream"); + return CUBEB_ERROR; + case stream_state::STARTED: + case stream_state::STARTING: + break; + } + + int64_t pos; + int64_t ns; + aaudio_result_t res; + res = WRAP(AAudioStream_getTimestamp)(stream, CLOCK_MONOTONIC, &pos, &ns); + if (res != AAUDIO_OK) { + // When the audio stream is not running, invalid_state is returned and we + // simply fall back to the method we use for non-playing streams. + if (res == AAUDIO_ERROR_INVALID_STATE) { + *position = WRAP(AAudioStream_getFramesRead)(stream); + if (*position < stm->previous_clock) { + *position = stm->previous_clock; + } else { + stm->previous_clock = *position; + } + return CUBEB_OK; + } + + LOG("AAudioStream_getTimestamp: %s", WRAP(AAudio_convertResultToText)(res)); + return CUBEB_ERROR; + } + + *position = pos; + if (*position < stm->previous_clock) { + *position = stm->previous_clock; + } else { + stm->previous_clock = *position; + } + return CUBEB_OK; +} + +static int +aaudio_stream_get_latency(cubeb_stream * stm, uint32_t * latency) +{ + int64_t pos; + int64_t ns; + aaudio_result_t res; + + if (!stm->ostream) { + LOG("error: aaudio_stream_get_latency on input-only stream"); + return CUBEB_ERROR; + } + + res = + WRAP(AAudioStream_getTimestamp)(stm->ostream, CLOCK_MONOTONIC, &pos, &ns); + if (res != AAUDIO_OK) { + LOG("aaudio_stream_get_latency, AAudioStream_getTimestamp: %s, returning " + "memoized value", + WRAP(AAudio_convertResultToText)(res)); + // Expected when the stream is paused. + *latency = stm->latest_output_latency; + return CUBEB_OK; + } + + int64_t read = WRAP(AAudioStream_getFramesRead)(stm->ostream); + + *latency = stm->latest_output_latency = read - pos; + LOG("aaudio_stream_get_latency, %u", *latency); + + return CUBEB_OK; +} + +static int +aaudio_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency) +{ + int64_t pos; + int64_t ns; + aaudio_result_t res; + + if (!stm->istream) { + LOG("error: aaudio_stream_get_input_latency on an ouput-only stream"); + return CUBEB_ERROR; + } + + res = + WRAP(AAudioStream_getTimestamp)(stm->istream, CLOCK_MONOTONIC, &pos, &ns); + if (res != AAUDIO_OK) { + // Expected when the stream is paused. + LOG("aaudio_stream_get_input_latency, AAudioStream_getTimestamp: %s, " + "returning memoized value", + WRAP(AAudio_convertResultToText)(res)); + *latency = stm->latest_input_latency; + return CUBEB_OK; + } + + int64_t written = WRAP(AAudioStream_getFramesWritten)(stm->istream); + + *latency = stm->latest_input_latency = written - pos; + LOG("aaudio_stream_get_input_latency, %u", *latency); + + return CUBEB_OK; +} + +static int +aaudio_stream_set_volume(cubeb_stream * stm, float volume) +{ + assert(stm && stm->in_use.load() && stm->ostream); + stm->volume.store(volume); + return CUBEB_OK; +} + +aaudio_data_callback_result_t +dummy_callback(AAudioStream * stream, void * userData, void * audioData, + int32_t numFrames) +{ + return AAUDIO_CALLBACK_RESULT_STOP; +} + +// Returns a dummy stream with all default settings +static AAudioStream * +init_dummy_stream() +{ + AAudioStreamBuilder * streamBuilder; + aaudio_result_t res; + res = WRAP(AAudio_createStreamBuilder)(&streamBuilder); + if (res != AAUDIO_OK) { + LOG("init_dummy_stream: AAudio_createStreamBuilder: %s", + WRAP(AAudio_convertResultToText)(res)); + return nullptr; + } + WRAP(AAudioStreamBuilder_setDataCallback) + (streamBuilder, dummy_callback, nullptr); + WRAP(AAudioStreamBuilder_setPerformanceMode) + (streamBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); + + AAudioStream * stream; + res = WRAP(AAudioStreamBuilder_openStream)(streamBuilder, &stream); + if (res != AAUDIO_OK) { + LOG("init_dummy_stream: AAudioStreamBuilder_openStream %s", + WRAP(AAudio_convertResultToText)(res)); + return nullptr; + } + WRAP(AAudioStreamBuilder_delete)(streamBuilder); + + return stream; +} + +static void +destroy_dummy_stream(AAudioStream * stream) +{ + WRAP(AAudioStream_close)(stream); +} + +static int +aaudio_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_frames) +{ + AAudioStream * stream = init_dummy_stream(); + + if (!stream) { + return CUBEB_ERROR; + } + + // https://android.googlesource.com/platform/compatibility/cdd/+/refs/heads/master/5_multimedia/5_6_audio-latency.md + *latency_frames = WRAP(AAudioStream_getFramesPerBurst)(stream); + + LOG("aaudio_get_min_latency: %u frames", *latency_frames); + + destroy_dummy_stream(stream); + + return CUBEB_OK; +} + +int +aaudio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ + AAudioStream * stream = init_dummy_stream(); + + if (!stream) { + return CUBEB_ERROR; + } + + *rate = WRAP(AAudioStream_getSampleRate)(stream); + + LOG("aaudio_get_preferred_sample_rate %uHz", *rate); + + destroy_dummy_stream(stream); + + return CUBEB_OK; +} + +extern "C" int +aaudio_init(cubeb ** context, char const * context_name); + +const static struct cubeb_ops aaudio_ops = { + /*.init =*/aaudio_init, + /*.get_backend_id =*/aaudio_get_backend_id, + /*.get_max_channel_count =*/aaudio_get_max_channel_count, + /* .get_min_latency =*/aaudio_get_min_latency, + /*.get_preferred_sample_rate =*/aaudio_get_preferred_sample_rate, + /*.enumerate_devices =*/NULL, + /*.device_collection_destroy =*/NULL, + /*.destroy =*/aaudio_destroy, + /*.stream_init =*/aaudio_stream_init, + /*.stream_destroy =*/aaudio_stream_destroy, + /*.stream_start =*/aaudio_stream_start, + /*.stream_stop =*/aaudio_stream_stop, + /*.stream_get_position =*/aaudio_stream_get_position, + /*.stream_get_latency =*/aaudio_stream_get_latency, + /*.stream_get_input_latency =*/aaudio_stream_get_input_latency, + /*.stream_set_volume =*/aaudio_stream_set_volume, + /*.stream_set_name =*/NULL, + /*.stream_get_current_device =*/NULL, + /*.stream_device_destroy =*/NULL, + /*.stream_register_device_changed_callback =*/NULL, + /*.register_device_collection_changed =*/NULL}; + +extern "C" /*static*/ int +aaudio_init(cubeb ** context, char const * /* context_name */) +{ + // load api + void * libaaudio = NULL; +#ifndef DISABLE_LIBAAUDIO_DLOPEN + libaaudio = dlopen("libaaudio.so", RTLD_NOW); + if (!libaaudio) { + return CUBEB_ERROR; + } + +#define LOAD(x) \ + { \ + cubeb_##x = (decltype(x) *)(dlsym(libaaudio, #x)); \ + if (!WRAP(x)) { \ + LOG("AAudio: Failed to load %s", #x); \ + dlclose(libaaudio); \ + return CUBEB_ERROR; \ + } \ + } + + LIBAAUDIO_API_VISIT(LOAD); +#undef LOAD +#endif + + cubeb * ctx = new cubeb; + ctx->ops = &aaudio_ops; + ctx->libaaudio = libaaudio; + + ctx->state.thread = std::thread(state_thread, ctx); + + // NOTE: using platform-specific APIs we could set the priority of the + // notifier thread lower than the priority of the state thread. + // This way, it's more likely that the state thread will be woken up + // by the condition variable signal when both are currently waiting + ctx->state.notifier = std::thread(notifier_thread, ctx); + + *context = ctx; + return CUBEB_OK; +} diff --git a/externals/cubeb/src/cubeb_alsa.c b/externals/cubeb/src/cubeb_alsa.c index deac27112..b1464d154 100755 --- a/externals/cubeb/src/cubeb_alsa.c +++ b/externals/cubeb/src/cubeb_alsa.c @@ -8,56 +8,56 @@ #define _DEFAULT_SOURCE #define _BSD_SOURCE #define _XOPEN_SOURCE 500 -#include -#include +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" +#include #include +#include #include #include +#include +#include #include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" #ifdef DISABLE_LIBASOUND_DLOPEN #define WRAP(x) x #else -#define WRAP(x) cubeb_##x -#define LIBASOUND_API_VISIT(X) \ - X(snd_config) \ - X(snd_config_add) \ - X(snd_config_copy) \ - X(snd_config_delete) \ - X(snd_config_get_id) \ - X(snd_config_get_string) \ - X(snd_config_imake_integer) \ - X(snd_config_search) \ - X(snd_config_search_definition) \ - X(snd_lib_error_set_handler) \ - X(snd_pcm_avail_update) \ - X(snd_pcm_close) \ - X(snd_pcm_delay) \ - X(snd_pcm_drain) \ - X(snd_pcm_frames_to_bytes) \ - X(snd_pcm_get_params) \ - X(snd_pcm_hw_params_any) \ - X(snd_pcm_hw_params_get_channels_max) \ - X(snd_pcm_hw_params_get_rate) \ - X(snd_pcm_hw_params_set_rate_near) \ - X(snd_pcm_hw_params_sizeof) \ - X(snd_pcm_nonblock) \ - X(snd_pcm_open) \ - X(snd_pcm_open_lconf) \ - X(snd_pcm_pause) \ - X(snd_pcm_poll_descriptors) \ - X(snd_pcm_poll_descriptors_count) \ - X(snd_pcm_poll_descriptors_revents) \ - X(snd_pcm_readi) \ - X(snd_pcm_recover) \ - X(snd_pcm_set_params) \ - X(snd_pcm_start) \ - X(snd_pcm_state) \ - X(snd_pcm_writei) \ +#define WRAP(x) (*cubeb_##x) +#define LIBASOUND_API_VISIT(X) \ + X(snd_config) \ + X(snd_config_add) \ + X(snd_config_copy) \ + X(snd_config_delete) \ + X(snd_config_get_id) \ + X(snd_config_get_string) \ + X(snd_config_imake_integer) \ + X(snd_config_search) \ + X(snd_config_search_definition) \ + X(snd_lib_error_set_handler) \ + X(snd_pcm_avail_update) \ + X(snd_pcm_close) \ + X(snd_pcm_delay) \ + X(snd_pcm_drain) \ + X(snd_pcm_frames_to_bytes) \ + X(snd_pcm_get_params) \ + X(snd_pcm_hw_params_any) \ + X(snd_pcm_hw_params_get_channels_max) \ + X(snd_pcm_hw_params_get_rate) \ + X(snd_pcm_hw_params_set_rate_near) \ + X(snd_pcm_hw_params_sizeof) \ + X(snd_pcm_nonblock) \ + X(snd_pcm_open) \ + X(snd_pcm_open_lconf) \ + X(snd_pcm_pause) \ + X(snd_pcm_poll_descriptors) \ + X(snd_pcm_poll_descriptors_count) \ + X(snd_pcm_poll_descriptors_revents) \ + X(snd_pcm_readi) \ + X(snd_pcm_recover) \ + X(snd_pcm_set_params) \ + X(snd_pcm_start) \ + X(snd_pcm_state) \ + X(snd_pcm_writei) #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; LIBASOUND_API_VISIT(MAKE_TYPEDEF); @@ -101,7 +101,8 @@ struct cubeb { int shutdown; - /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */ + /* Control pipe for forcing poll to wake and rebuild fds or recalculate the + * timeout. */ int control_fd_read; int control_fd_write; @@ -116,13 +117,7 @@ struct cubeb { int is_pa; }; -enum stream_state { - INACTIVE, - RUNNING, - DRAINING, - PROCESSING, - ERROR -}; +enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR }; struct cubeb_stream { /* Note: Must match cubeb_stream layout in cubeb.c. */ @@ -146,7 +141,8 @@ struct cubeb_stream { enum stream_state state; struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */ - struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */ + struct pollfd * + fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */ nfds_t nfds; struct timeval drain_timeout; @@ -294,8 +290,10 @@ set_timeout(struct timeval * timeout, unsigned int ms) static void stream_buffer_decrement(cubeb_stream * stm, long count) { - char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count); - memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count)); + char * bufremains = + stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count); + memmove(stm->buffer, bufremains, + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count)); stm->bufframes -= count; } @@ -327,7 +325,8 @@ alsa_process_stream(cubeb_stream * stm) /* Call _poll_descriptors_revents() even if we don't use it to let underlying plugins clear null events. Otherwise poll() may wake up again and again, producing unnecessary CPU usage. */ - WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents); + WRAP(snd_pcm_poll_descriptors_revents) + (stm->pcm, stm->fds, stm->nfds, &revents); avail = WRAP(snd_pcm_avail_update)(stm->pcm); @@ -337,8 +336,9 @@ alsa_process_stream(cubeb_stream * stm) return RUNNING; } - /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. */ - if ((unsigned int) avail > stm->buffer_size) { + /* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. + */ + if ((unsigned int)avail > stm->buffer_size) { avail = stm->buffer_size; } @@ -352,7 +352,7 @@ alsa_process_stream(cubeb_stream * stm) // TODO: should it be marked as DRAINING? } - got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail); + got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail); if (got < 0) { avail = got; // the error handler below will recover us @@ -366,18 +366,24 @@ alsa_process_stream(cubeb_stream * stm) /* Capture: Pass read frames to callback function */ if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 && - (!stm->other_stream || stm->other_stream->bufframes < stm->other_stream->buffer_size)) { + (!stm->other_stream || + stm->other_stream->bufframes < stm->other_stream->buffer_size)) { snd_pcm_sframes_t wrote = stm->bufframes; struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm; - void * other_buffer = stm->other_stream ? stm->other_stream->buffer + stm->other_stream->bufframes : NULL; + void * other_buffer = stm->other_stream ? stm->other_stream->buffer + + stm->other_stream->bufframes + : NULL; /* Correct write size to the other stream available space */ - if (stm->other_stream && wrote > (snd_pcm_sframes_t) (stm->other_stream->buffer_size - stm->other_stream->bufframes)) { + if (stm->other_stream && + wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size - + stm->other_stream->bufframes)) { wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes; } pthread_mutex_unlock(&stm->mutex); - wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, other_buffer, wrote); + wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, + other_buffer, wrote); pthread_mutex_lock(&stm->mutex); if (wrote < 0) { @@ -392,14 +398,17 @@ alsa_process_stream(cubeb_stream * stm) } /* Playback: Don't have enough data? Let's ask for more. */ - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes && + if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && + avail > (snd_pcm_sframes_t)stm->bufframes && (!stm->other_stream || stm->other_stream->bufframes > 0)) { long got = avail - stm->bufframes; void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL; - char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); + char * buftail = + stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); /* Correct read size to the other stream available frames */ - if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) { + if (stm->other_stream && + got > (snd_pcm_sframes_t)stm->other_stream->bufframes) { got = stm->other_stream->bufframes; } @@ -419,11 +428,13 @@ alsa_process_stream(cubeb_stream * stm) } /* Playback: Still don't have enough data? Add some silence. */ - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes) { + if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && + avail > (snd_pcm_sframes_t)stm->bufframes) { long drain_frames = avail - stm->bufframes; - double drain_time = (double) drain_frames / stm->params.rate; + double drain_time = (double)drain_frames / stm->params.rate; - char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); + char * buftail = + stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes); memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames)); stm->bufframes = avail; @@ -440,12 +451,12 @@ alsa_process_stream(cubeb_stream * stm) snd_pcm_sframes_t wrote; if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) { - float * b = (float *) stm->buffer; + float * b = (float *)stm->buffer; for (uint32_t i = 0; i < avail * stm->params.channels; i++) { b[i] *= stm->volume; } } else { - short * b = (short *) stm->buffer; + short * b = (short *)stm->buffer; for (uint32_t i = 0; i < avail * stm->params.channels; i++) { b[i] *= stm->volume; } @@ -467,8 +478,7 @@ alsa_process_stream(cubeb_stream * stm) avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0); /* Capture pcm must be started after initial setup/recover */ - if (avail >= 0 && - stm->stream_type == SND_PCM_STREAM_CAPTURE && + if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE && WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) { avail = WRAP(snd_pcm_start)(stm->pcm); } @@ -533,7 +543,8 @@ alsa_run(cubeb * ctx) stm = ctx->streams[i]; /* We can't use snd_pcm_poll_descriptors_revents here because of https://github.com/kinetiknz/cubeb/issues/135. */ - if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) { + if (stm && stm->state == RUNNING && stm->fds && + any_revents(stm->fds, stm->nfds)) { alsa_set_stream_state(stm, PROCESSING); pthread_mutex_unlock(&ctx->mutex); state = alsa_process_stream(stm); @@ -548,7 +559,8 @@ alsa_run(cubeb * ctx) if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) { alsa_set_stream_state(stm, INACTIVE); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - } else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) { + } else if (stm->state == RUNNING && + ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) { alsa_set_stream_state(stm, ERROR); stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); } @@ -593,7 +605,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) r = WRAP(snd_config_get_string)(slave_pcm, &string); if (r >= 0) { - r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def); + r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, + &slave_def); if (r < 0) { return NULL; } @@ -611,7 +624,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) } r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); - if (r < 0 || r > (int) sizeof(node_name)) { + if (r < 0 || r > (int)sizeof(node_name)) { break; } r = WRAP(snd_config_search)(lconf, node_name, &pcm); @@ -633,7 +646,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) higher than requested latency, but the plugin does not update its (and ALSA's) internal state to reflect that, leading to an immediate underrun situation. Inspired by WINE's make_handle_underrun_config. - Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */ + Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 + */ static snd_config_t * init_local_config_with_workaround(char const * pcm_name) { @@ -646,11 +660,11 @@ init_local_config_with_workaround(char const * pcm_name) lconf = NULL; - if (*WRAP(snd_config) == NULL) { + if (WRAP(snd_config) == NULL) { return NULL; } - r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config)); + r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config)); if (r < 0) { return NULL; } @@ -667,7 +681,7 @@ init_local_config_with_workaround(char const * pcm_name) } r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); - if (r < 0 || r > (int) sizeof(node_name)) { + if (r < 0 || r > (int)sizeof(node_name)) { break; } r = WRAP(snd_config_search)(lconf, node_name, &pcm_node); @@ -675,12 +689,14 @@ init_local_config_with_workaround(char const * pcm_name) break; } - /* If this PCM has a slave, walk the slave configurations until we reach the bottom. */ + /* If this PCM has a slave, walk the slave configurations until we reach the + * bottom. */ while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) { pcm_node = node; } - /* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */ + /* Fetch the PCM node's type, and bail out if it's not the PulseAudio + * plugin. */ r = WRAP(snd_config_search)(pcm_node, "type", &node); if (r < 0) { break; @@ -722,13 +738,15 @@ init_local_config_with_workaround(char const * pcm_name) } static int -alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t stream, snd_config_t * local_config) +alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, + snd_pcm_stream_t stream, snd_config_t * local_config) { int r; pthread_mutex_lock(&cubeb_alsa_mutex); if (local_config) { - r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config); + r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, + local_config); } else { r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK); } @@ -819,12 +837,13 @@ alsa_init(cubeb ** context, char const * context_name) } } -#define LOAD(x) { \ - cubeb_##x = dlsym(libasound, #x); \ - if (!cubeb_##x) { \ - dlclose(libasound); \ - return CUBEB_ERROR; \ - } \ +#define LOAD(x) \ + { \ + cubeb_##x = dlsym(libasound, #x); \ + if (!cubeb_##x) { \ + dlclose(libasound); \ + return CUBEB_ERROR; \ + } \ } LIBASOUND_API_VISIT(LOAD); @@ -876,7 +895,8 @@ alsa_init(cubeb ** context, char const * context_name) /* Open a dummy PCM to force the configuration space to be evaluated so that init_local_config_with_workaround can find and modify the default node. */ - r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, NULL); + r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, + NULL); if (r >= 0) { alsa_locked_pcm_close(dummy); } @@ -886,7 +906,8 @@ alsa_init(cubeb ** context, char const * context_name) pthread_mutex_unlock(&cubeb_alsa_mutex); if (ctx->local_config) { ctx->is_pa = 1; - r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, ctx->local_config); + r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, + SND_PCM_STREAM_PLAYBACK, ctx->local_config); /* If we got a local_config, we found a PA PCM. If opening a PCM with that config fails with EINVAL, the PA PCM is too old for this workaround. */ if (r == -EINVAL) { @@ -944,17 +965,17 @@ alsa_destroy(cubeb * ctx) free(ctx); } -static void alsa_stream_destroy(cubeb_stream * stm); +static void +alsa_stream_destroy(cubeb_stream * stm); static int -alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, - snd_pcm_stream_t stream_type, +alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, + char const * stream_name, snd_pcm_stream_t stream_type, cubeb_devid deviceid, cubeb_stream_params * stream_params, unsigned int latency_frames, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + cubeb_state_callback state_callback, void * user_ptr) { (void)stream_name; cubeb_stream * stm; @@ -962,7 +983,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream snd_pcm_format_t format; snd_pcm_uframes_t period_size; int latency_us = 0; - char const * pcm_name = deviceid ? (char const *) deviceid : CUBEB_ALSA_PCM_NAME; + char const * pcm_name = + deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME; assert(ctx && stream); @@ -1018,7 +1040,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream r = pthread_cond_init(&stm->cond, NULL); assert(r == 0); - r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, ctx->local_config); + r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, + ctx->local_config); if (r < 0) { alsa_stream_destroy(stm); return CUBEB_ERROR; @@ -1034,12 +1057,12 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream Only resort to this hack if the handle_underrun workaround failed. */ if (!ctx->local_config && ctx->is_pa) { const int min_latency = 5e5; - latency_us = latency_us < min_latency ? min_latency: latency_us; + latency_us = latency_us < min_latency ? min_latency : latency_us; } r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, - stm->params.channels, stm->params.rate, 1, - latency_us); + stm->params.channels, stm->params.rate, 1, + latency_us); if (r < 0) { alsa_stream_destroy(stm); return CUBEB_ERROR_INVALID_FORMAT; @@ -1048,9 +1071,11 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size); assert(r == 0); - /* Double internal buffer size to have enough space when waiting for the other side of duplex connection */ + /* Double internal buffer size to have enough space when waiting for the other + * side of duplex connection */ stm->buffer_size *= 2; - stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size)); + stm->buffer = + calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size)); assert(stm->buffer); stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm); @@ -1059,7 +1084,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd)); assert(stm->saved_fds); r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds); - assert((nfds_t) r == stm->nfds); + assert((nfds_t)r == stm->nfds); if (alsa_register_stream(ctx, stm) != 0) { alsa_stream_destroy(stm); @@ -1077,22 +1102,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency_frames, - cubeb_data_callback data_callback, cubeb_state_callback state_callback, - void * user_ptr) + unsigned int latency_frames, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) { int result = CUBEB_OK; - cubeb_stream * instm = NULL, * outstm = NULL; + cubeb_stream *instm = NULL, *outstm = NULL; if (result == CUBEB_OK && input_stream_params) { - result = alsa_stream_init_single(ctx, &instm, stream_name, SND_PCM_STREAM_CAPTURE, - input_device, input_stream_params, latency_frames, + result = alsa_stream_init_single(ctx, &instm, stream_name, + SND_PCM_STREAM_CAPTURE, input_device, + input_stream_params, latency_frames, data_callback, state_callback, user_ptr); } if (result == CUBEB_OK && output_stream_params) { - result = alsa_stream_init_single(ctx, &outstm, stream_name, SND_PCM_STREAM_PLAYBACK, - output_device, output_stream_params, latency_frames, + result = alsa_stream_init_single(ctx, &outstm, stream_name, + SND_PCM_STREAM_PLAYBACK, output_device, + output_stream_params, latency_frames, data_callback, state_callback, user_ptr); } @@ -1116,8 +1142,7 @@ alsa_stream_destroy(cubeb_stream * stm) int r; cubeb * ctx; - assert(stm && (stm->state == INACTIVE || - stm->state == ERROR || + assert(stm && (stm->state == INACTIVE || stm->state == ERROR || stm->state == DRAINING)); ctx = stm->context; @@ -1159,7 +1184,7 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { int r; cubeb_stream * stm; - snd_pcm_hw_params_t* hw_params; + snd_pcm_hw_params_t * hw_params; cubeb_stream_params params; params.rate = 44100; params.format = CUBEB_SAMPLE_FLOAT32NE; @@ -1169,7 +1194,8 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) assert(ctx); - r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL, NULL, NULL); + r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL, + NULL, NULL); if (r != CUBEB_OK) { return CUBEB_ERROR; } @@ -1192,7 +1218,8 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int -alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { +alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) +{ (void)ctx; int r, dir; snd_pcm_t * pcm; @@ -1202,7 +1229,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { /* get a pcm, disabling resampling, so we get a rate the * hardware/dmix/pulse/etc. supports. */ - r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE); + r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, + SND_PCM_NO_AUTO_RESAMPLE); if (r < 0) { return CUBEB_ERROR; } @@ -1235,7 +1263,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { } static int -alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) +alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_frames) { (void)ctx; /* 40ms is found to be an acceptable minimum, even on a super low-end @@ -1331,7 +1360,7 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position) assert(delay >= 0); *position = 0; - if (stm->stream_position >= (snd_pcm_uframes_t) delay) { + if (stm->stream_position >= (snd_pcm_uframes_t)delay) { *position = stm->stream_position - delay; } @@ -1346,7 +1375,8 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { snd_pcm_sframes_t delay; /* This function returns the delay in frames until a frame written using - snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */ + snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. + */ if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) { return CUBEB_ERROR; } @@ -1371,7 +1401,7 @@ static int alsa_enumerate_devices(cubeb * context, cubeb_device_type type, cubeb_device_collection * collection) { - cubeb_device_info* device = NULL; + cubeb_device_info * device = NULL; if (!context) return CUBEB_ERROR; @@ -1390,13 +1420,13 @@ alsa_enumerate_devices(cubeb * context, cubeb_device_type type, } char const * a_name = "default"; - device = (cubeb_device_info *) calloc(1, sizeof(cubeb_device_info)); + device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info)); assert(device); if (!device) return CUBEB_ERROR; device->device_id = a_name; - device->devid = (cubeb_devid) device->device_id; + device->devid = (cubeb_devid)device->device_id; device->friendly_name = a_name; device->group_id = a_name; device->vendor_name = a_name; @@ -1423,32 +1453,30 @@ alsa_device_collection_destroy(cubeb * context, cubeb_device_collection * collection) { assert(collection->count == 1); - (void) context; + (void)context; free(collection->device); return CUBEB_OK; } static struct cubeb_ops const alsa_ops = { - .init = alsa_init, - .get_backend_id = alsa_get_backend_id, - .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, - .enumerate_devices = alsa_enumerate_devices, - .device_collection_destroy = alsa_device_collection_destroy, - .destroy = alsa_destroy, - .stream_init = alsa_stream_init, - .stream_destroy = alsa_stream_destroy, - .stream_start = alsa_stream_start, - .stream_stop = alsa_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = alsa_stream_get_position, - .stream_get_latency = alsa_stream_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = alsa_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = NULL, - .stream_device_destroy = NULL, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; + .init = alsa_init, + .get_backend_id = alsa_get_backend_id, + .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, + .enumerate_devices = alsa_enumerate_devices, + .device_collection_destroy = alsa_device_collection_destroy, + .destroy = alsa_destroy, + .stream_init = alsa_stream_init, + .stream_destroy = alsa_stream_destroy, + .stream_start = alsa_stream_start, + .stream_stop = alsa_stream_stop, + .stream_get_position = alsa_stream_get_position, + .stream_get_latency = alsa_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = alsa_stream_set_volume, + .stream_set_name = NULL, + .stream_get_current_device = NULL, + .stream_device_destroy = NULL, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; diff --git a/externals/cubeb/src/cubeb_android.h b/externals/cubeb/src/cubeb_android.h new file mode 100755 index 000000000..c21a941ab --- /dev/null +++ b/externals/cubeb/src/cubeb_android.h @@ -0,0 +1,17 @@ +#ifndef CUBEB_ANDROID_H +#define CUBEB_ANDROID_H + +#ifdef __cplusplus +extern "C" { +#endif +// If the latency requested is above this threshold, this stream is considered +// intended for playback (vs. real-time). Tell Android it should favor saving +// power over performance or latency. +// This is around 100ms at 44100 or 48000 +const uint16_t POWERSAVE_LATENCY_FRAMES_THRESHOLD = 4000; + +#ifdef __cplusplus +}; +#endif + +#endif // CUBEB_ANDROID_H diff --git a/externals/cubeb/src/cubeb_array_queue.h b/externals/cubeb/src/cubeb_array_queue.h index a8ea4cd17..d6d958132 100755 --- a/externals/cubeb/src/cubeb_array_queue.h +++ b/externals/cubeb/src/cubeb_array_queue.h @@ -16,8 +16,7 @@ extern "C" { #endif -typedef struct -{ +typedef struct { void ** buf; size_t num; size_t writePos; @@ -25,10 +24,11 @@ typedef struct pthread_mutex_t mutex; } array_queue; -array_queue * array_queue_create(size_t num) +array_queue * +array_queue_create(size_t num) { assert(num != 0); - array_queue * new_queue = (array_queue*)calloc(1, sizeof(array_queue)); + array_queue * new_queue = (array_queue *)calloc(1, sizeof(array_queue)); new_queue->buf = (void **)calloc(1, sizeof(void *) * num); new_queue->readPos = 0; new_queue->writePos = 0; @@ -39,7 +39,8 @@ array_queue * array_queue_create(size_t num) return new_queue; } -void array_queue_destroy(array_queue * aq) +void +array_queue_destroy(array_queue * aq) { assert(aq); @@ -48,14 +49,14 @@ void array_queue_destroy(array_queue * aq) free(aq); } -int array_queue_push(array_queue * aq, void * item) +int +array_queue_push(array_queue * aq, void * item) { assert(item); pthread_mutex_lock(&aq->mutex); int ret = -1; - if(aq->buf[aq->writePos % aq->num] == NULL) - { + if (aq->buf[aq->writePos % aq->num] == NULL) { aq->buf[aq->writePos % aq->num] = item; aq->writePos = (aq->writePos + 1) % aq->num; ret = 0; @@ -65,12 +66,12 @@ int array_queue_push(array_queue * aq, void * item) return ret; } -void* array_queue_pop(array_queue * aq) +void * +array_queue_pop(array_queue * aq) { pthread_mutex_lock(&aq->mutex); void * value = aq->buf[aq->readPos % aq->num]; - if(value) - { + if (value) { aq->buf[aq->readPos % aq->num] = NULL; aq->readPos = (aq->readPos + 1) % aq->num; } @@ -78,7 +79,8 @@ void* array_queue_pop(array_queue * aq) return value; } -size_t array_queue_get_size(array_queue * aq) +size_t +array_queue_get_size(array_queue * aq) { pthread_mutex_lock(&aq->mutex); ssize_t r = aq->writePos - aq->readPos; @@ -94,4 +96,4 @@ size_t array_queue_get_size(array_queue * aq) } #endif -#endif //CUBE_ARRAY_QUEUE_H +#endif // CUBE_ARRAY_QUEUE_H diff --git a/externals/cubeb/src/cubeb_assert.h b/externals/cubeb/src/cubeb_assert.h index 9257a2c86..d81a1eaca 100755 --- a/externals/cubeb/src/cubeb_assert.h +++ b/externals/cubeb/src/cubeb_assert.h @@ -16,7 +16,8 @@ * export a function or macro called XASSERT that aborts the program. */ -#define XASSERT(expr) do { \ +#define XASSERT(expr) \ + do { \ if (!(expr)) { \ fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \ abort(); \ diff --git a/externals/cubeb/src/cubeb_audiotrack.c b/externals/cubeb/src/cubeb_audiotrack.c index 6496df042..59deba148 100755 --- a/externals/cubeb/src/cubeb_audiotrack.c +++ b/externals/cubeb/src/cubeb_audiotrack.c @@ -8,20 +8,21 @@ #if !defined(NDEBUG) #define NDEBUG #endif +#include #include +#include #include #include #include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" #include "android/audiotrack_definitions.h" +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" #ifndef ALOG #if defined(DEBUG) || defined(FORCE_ALOG) -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) +#define ALOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb", ##args) #else #define ALOG(args...) #endif @@ -35,37 +36,44 @@ * call dlsym to get the symbol |mangled_name|, handle the error and store the * pointer in |pointer|. Because depending on Android version, we want different * symbols, not finding a symbol is not an error. */ -#define DLSYM_DLERROR(mangled_name, pointer, lib) \ - do { \ - pointer = dlsym(lib, mangled_name); \ - if (!pointer) { \ - ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ - } else { \ - ALOG("%stm: OK", mangled_name); \ - } \ - } while(0); +#define DLSYM_DLERROR(mangled_name, pointer, lib) \ + do { \ + pointer = dlsym(lib, mangled_name); \ + if (!pointer) { \ + ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \ + } else { \ + ALOG("%stm: OK", mangled_name); \ + } \ + } while (0); static struct cubeb_ops const audiotrack_ops; -void audiotrack_destroy(cubeb * context); -void audiotrack_stream_destroy(cubeb_stream * stream); +void +audiotrack_destroy(cubeb * context); +void +audiotrack_stream_destroy(cubeb_stream * stream); struct AudioTrack { - /* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */ - /* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate); + /* only available on ICS and later. The second int paramter is in fact of type + * audio_stream_type_t. */ + /* static */ status_t (*get_min_frame_count)(int * frame_count, + int stream_type, uint32_t rate); /* if we have a recent ctor, but can't find the above symbol, we * can get the minimum frame count with this signature, and we are * running gingerbread. */ - /* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate); - void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int); - void* (*dtor)(void* instance); - void (*start)(void* instance); - void (*pause)(void* instance); - uint32_t (*latency)(void* instance); - status_t (*check)(void* instance); - status_t (*get_position)(void* instance, uint32_t* position); - /* static */ int (*get_output_samplingrate)(int* samplerate, int stream); - status_t (*set_marker_position)(void* instance, unsigned int); - status_t (*set_volume)(void* instance, float left, float right); + /* static */ status_t (*get_min_frame_count_gingerbread)(int * frame_count, + int stream_type, + uint32_t rate); + void * (*ctor)(void * instance, int, unsigned int, int, int, int, + unsigned int, void (*)(int, void *, void *), void *, int, int); + void * (*dtor)(void * instance); + void (*start)(void * instance); + void (*pause)(void * instance); + uint32_t (*latency)(void * instance); + status_t (*check)(void * instance); + status_t (*get_position)(void * instance, uint32_t * position); + /* static */ int (*get_output_samplingrate)(int * samplerate, int stream); + status_t (*set_marker_position)(void * instance, unsigned int); + status_t (*set_volume)(void * instance, float left, float right); }; struct cubeb { @@ -89,19 +97,20 @@ struct cubeb_stream { }; static void -audiotrack_refill(int event, void* user, void* info) +audiotrack_refill(int event, void * user, void * info) { cubeb_stream * stream = user; switch (event) { case EVENT_MORE_DATA: { long got = 0; - struct Buffer * b = (struct Buffer*)info; + struct Buffer * b = (struct Buffer *)info; if (stream->draining) { return; } - got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount); + got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, + b->frameCount); stream->written += got; @@ -109,7 +118,8 @@ audiotrack_refill(int event, void* user, void* info) stream->draining = 1; /* set a marker so we are notified when the are done draining, that is, * when every frame has been played by android. */ - stream->context->klass.set_marker_position(stream->instance, stream->written); + stream->context->klass.set_marker_position(stream->instance, + stream->written); } break; @@ -125,7 +135,9 @@ audiotrack_refill(int event, void* user, void* info) stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); break; case EVENT_NEW_POS: - assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack."); + assert( + 0 && + "We don't support the setPositionUpdatePeriod feature of audiotrack."); break; case EVENT_BUFFER_END: assert(0 && "Should not happen."); @@ -142,14 +154,17 @@ audiotrack_version_is_gingerbread(cubeb * ctx) } int -audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count) +audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, + int * min_frame_count) { status_t status; /* Recent Android have a getMinFrameCount method. */ if (!audiotrack_version_is_gingerbread(ctx)) { - status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); + status = ctx->klass.get_min_frame_count( + min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); } else { - status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); + status = ctx->klass.get_min_frame_count_gingerbread( + min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate); } if (status != 0) { ALOG("error getting the min frame count"); @@ -162,7 +177,7 @@ int audiotrack_init(cubeb ** context, char const * context_name) { cubeb * ctx; - struct AudioTrack* c; + struct AudioTrack * c; assert(context); *context = NULL; @@ -182,34 +197,45 @@ audiotrack_init(cubeb ** context, char const * context_name) } /* Recent Android first, then Gingerbread. */ - DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", + ctx->klass.ctor, ctx->library); DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library); - DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library); - DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library); + DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, + ctx->library); + DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, + ctx->library); - DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library); + DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", + ctx->klass.get_output_samplingrate, ctx->library); - /* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */ - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library); + /* |getMinFrameCount| is available on gingerbread and ICS with different + * signatures. */ + DLSYM_DLERROR( + "_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", + ctx->klass.get_min_frame_count, ctx->library); if (!ctx->klass.get_min_frame_count) { - DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", + ctx->klass.get_min_frame_count_gingerbread, ctx->library); } - DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library); - DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, + ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, + ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", + ctx->klass.get_position, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", + ctx->klass.set_marker_position, ctx->library); + DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, + ctx->library); /* check that we have a combination of symbol that makes sense */ c = &ctx->klass; - if(!(c->ctor && - c->dtor && c->latency && c->check && - /* at least one way to get the minimum frame count to request. */ - (c->get_min_frame_count || - c->get_min_frame_count_gingerbread) && - c->start && c->pause && c->get_position && c->set_marker_position)) { + if (!(c->ctor && c->dtor && c->latency && c->check && + /* at least one way to get the minimum frame count to request. */ + (c->get_min_frame_count || c->get_min_frame_count_gingerbread) && + c->start && c->pause && c->get_position && c->set_marker_position)) { ALOG("Could not find all the symbols we need."); audiotrack_destroy(ctx); return CUBEB_ERROR; @@ -234,14 +260,16 @@ audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) assert(ctx && max_channels); /* The android mixer handles up to two channels, see - http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */ + http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 + */ *max_channels = 2; return CUBEB_OK; } static int -audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms) +audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_ms) { /* We always use the lowest latency possible when using this backend (see * audiotrack_stream_init), so this value is not going to be used. */ @@ -276,15 +304,13 @@ audiotrack_destroy(cubeb * context) } int -audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, +audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + unsigned int latency, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; int32_t channels; @@ -303,7 +329,8 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_ return CUBEB_ERROR_INVALID_FORMAT; } - if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) { + if (audiotrack_get_min_frame_count(ctx, output_stream_params, + (int *)&min_frame_count)) { return CUBEB_ERROR; } @@ -317,21 +344,25 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_ stm->params = *output_stream_params; stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); - (*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad; + (*(uint32_t *)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = + 0xbaadbaad; assert(stm->instance && "cubeb: EOM"); /* gingerbread uses old channel layout enum */ if (audiotrack_version_is_gingerbread(ctx)) { - channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy; + channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy + : AUDIO_CHANNEL_OUT_MONO_Legacy; } else { - channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS; + channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS + : AUDIO_CHANNEL_OUT_MONO_ICS; } ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate, AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0, audiotrack_refill, stm, 0, 0); - assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad); + assert((*(uint32_t *)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - + 4)) == 0xbaadbaad); if (ctx->klass.check(stm->instance)) { ALOG("stream not initialized properly."); @@ -418,26 +449,24 @@ audiotrack_stream_set_volume(cubeb_stream * stream, float volume) } static struct cubeb_ops const audiotrack_ops = { - .init = audiotrack_init, - .get_backend_id = audiotrack_get_backend_id, - .get_max_channel_count = audiotrack_get_max_channel_count, - .get_min_latency = audiotrack_get_min_latency, - .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, - .enumerate_devices = NULL, - .device_collection_destroy = NULL, - .destroy = audiotrack_destroy, - .stream_init = audiotrack_stream_init, - .stream_destroy = audiotrack_stream_destroy, - .stream_start = audiotrack_stream_start, - .stream_stop = audiotrack_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = audiotrack_stream_get_position, - .stream_get_latency = audiotrack_stream_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = audiotrack_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = NULL, - .stream_device_destroy = NULL, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; + .init = audiotrack_init, + .get_backend_id = audiotrack_get_backend_id, + .get_max_channel_count = audiotrack_get_max_channel_count, + .get_min_latency = audiotrack_get_min_latency, + .get_preferred_sample_rate = audiotrack_get_preferred_sample_rate, + .enumerate_devices = NULL, + .device_collection_destroy = NULL, + .destroy = audiotrack_destroy, + .stream_init = audiotrack_stream_init, + .stream_destroy = audiotrack_stream_destroy, + .stream_start = audiotrack_stream_start, + .stream_stop = audiotrack_stream_stop, + .stream_get_position = audiotrack_stream_get_position, + .stream_get_latency = audiotrack_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = audiotrack_stream_set_volume, + .stream_set_name = NULL, + .stream_get_current_device = NULL, + .stream_device_destroy = NULL, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; diff --git a/externals/cubeb/src/cubeb_audiounit.cpp b/externals/cubeb/src/cubeb_audiounit.cpp index f0586a15b..02cd13469 100755 --- a/externals/cubeb/src/cubeb_audiounit.cpp +++ b/externals/cubeb/src/cubeb_audiounit.cpp @@ -6,23 +6,23 @@ */ #undef NDEBUG +#include #include #include #include #include #include -#include #if !TARGET_OS_IPHONE #include #include #include #include #endif -#include -#include -#include "cubeb/cubeb.h" #include "cubeb-internal.h" +#include "cubeb/cubeb.h" #include "cubeb_mixer.h" +#include +#include #if !TARGET_OS_IPHONE #include "cubeb_osx_run_loop.h" #endif @@ -30,10 +30,10 @@ #include "cubeb_ring_array.h" #include #include -#include #include -#include #include +#include +#include using namespace std; @@ -41,8 +41,8 @@ using namespace std; typedef UInt32 AudioFormatFlags; #endif -#define AU_OUT_BUS 0 -#define AU_IN_BUS 1 +#define AU_OUT_BUS 0 +#define AU_IN_BUS 1 const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb"; const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice"; @@ -50,12 +50,20 @@ const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice"; #ifdef ALOGV #undef ALOGV #endif -#define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);}) +#define ALOGV(msg, ...) \ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), \ + ^{ \ + LOGV(msg, ##__VA_ARGS__); \ + }) #ifdef ALOG #undef ALOG #endif -#define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);}) +#define ALOG(msg, ...) \ + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), \ + ^{ \ + LOG(msg, ##__VA_ARGS__); \ + }) /* Testing empirically, some headsets report a minimal latency that is very * low, but this does not work in practice. Lie and say the minimum is 256 @@ -64,64 +72,63 @@ const uint32_t SAFE_MIN_LATENCY_FRAMES = 128; const uint32_t SAFE_MAX_LATENCY_FRAMES = 512; const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = { - kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster -}; + kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = { - kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster -}; + kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = { - kAudioDevicePropertyDeviceIsAlive, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster -}; + kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = { - kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster -}; + kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = { - kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster -}; + kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput, + kAudioObjectPropertyElementMaster}; const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = { - kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster -}; + kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster}; typedef uint32_t device_flags_value; enum device_flags { - DEV_UNKNOWN = 0x00, /* Unknown */ - DEV_INPUT = 0x01, /* Record device like mic */ - DEV_OUTPUT = 0x02, /* Playback device like speakers */ - DEV_SYSTEM_DEFAULT = 0x04, /* System default device */ - DEV_SELECTED_DEFAULT = 0x08, /* User selected to use the system default device */ + DEV_UNKNOWN = 0x00, /* Unknown */ + DEV_INPUT = 0x01, /* Record device like mic */ + DEV_OUTPUT = 0x02, /* Playback device like speakers */ + DEV_SYSTEM_DEFAULT = 0x04, /* System default device */ + DEV_SELECTED_DEFAULT = + 0x08, /* User selected to use the system default device */ }; -void audiounit_stream_stop_internal(cubeb_stream * stm); -static int audiounit_stream_start_internal(cubeb_stream * stm); -static void audiounit_close_stream(cubeb_stream *stm); -static int audiounit_setup_stream(cubeb_stream *stm); +void +audiounit_stream_stop_internal(cubeb_stream * stm); +static int +audiounit_stream_start_internal(cubeb_stream * stm); +static void +audiounit_close_stream(cubeb_stream * stm); +static int +audiounit_setup_stream(cubeb_stream * stm); static vector audiounit_get_devices_of_type(cubeb_device_type devtype); -static UInt32 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope); +static UInt32 +audiounit_get_device_presentation_latency(AudioObjectID devid, + AudioObjectPropertyScope scope); #if !TARGET_OS_IPHONE -static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type); -static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm); -static int audiounit_uninstall_system_changed_callback(cubeb_stream * stm); -static void audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags); +static AudioObjectID +audiounit_get_default_device_id(cubeb_device_type type); +static int +audiounit_uninstall_device_changed_callback(cubeb_stream * stm); +static int +audiounit_uninstall_system_changed_callback(cubeb_stream * stm); +static void +audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags); #endif extern cubeb_ops const audiounit_ops; @@ -131,27 +138,31 @@ struct cubeb { owned_critical_section mutex; int active_streams = 0; uint32_t global_latency_frames = 0; - cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr; + cubeb_device_collection_changed_callback input_collection_changed_callback = + nullptr; void * input_collection_changed_user_ptr = nullptr; - cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr; + cubeb_device_collection_changed_callback output_collection_changed_callback = + nullptr; void * output_collection_changed_user_ptr = nullptr; // Store list of devices to detect changes vector input_device_array; vector output_device_array; // The queue should be released when it’s no longer needed. - dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL); + dispatch_queue_t serial_queue = + dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL); // Current used channel layout - atomic layout{ CUBEB_LAYOUT_UNDEFINED }; + atomic layout{CUBEB_LAYOUT_UNDEFINED}; uint32_t channels = 0; }; static unique_ptr make_sized_audio_channel_layout(size_t sz) { - assert(sz >= sizeof(AudioChannelLayout)); - AudioChannelLayout * acl = reinterpret_cast(calloc(1, sz)); - assert(acl); // Assert the allocation works. - return unique_ptr(acl, free); + assert(sz >= sizeof(AudioChannelLayout)); + AudioChannelLayout * acl = + reinterpret_cast(calloc(1, sz)); + assert(acl); // Assert the allocation works. + return unique_ptr(acl, free); } enum class io_side { @@ -183,13 +194,10 @@ struct property_listener { property_listener(AudioDeviceID id, const AudioObjectPropertyAddress * address, - AudioObjectPropertyListenerProc proc, - cubeb_stream * stm) - : device_id(id) - , property_address(address) - , callback(proc) - , stream(stm) - {} + AudioObjectPropertyListenerProc proc, cubeb_stream * stm) + : device_id(id), property_address(address), callback(proc), stream(stm) + { + } }; struct cubeb_stream { @@ -205,8 +213,12 @@ struct cubeb_stream { cubeb_device_changed_callback device_changed_callback = nullptr; owned_critical_section device_changed_callback_lock; /* Stream creation parameters */ - cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE }; - cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE }; + cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, + CUBEB_LAYOUT_UNDEFINED, + CUBEB_STREAM_PREF_NONE}; + cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, + CUBEB_LAYOUT_UNDEFINED, + CUBEB_STREAM_PREF_NONE}; device_info input_device; device_info output_device; /* Format descriptions */ @@ -226,27 +238,29 @@ struct cubeb_stream { // Only accessed on input/output callback thread and during initial configure. unique_ptr input_linear_buffer; /* Frame counters */ - atomic frames_played{ 0 }; + atomic frames_played{0}; uint64_t frames_queued = 0; // How many frames got read from the input since the stream started (includes // padded silence) - atomic frames_read{ 0 }; + atomic frames_read{0}; // How many frames got written to the output device since the stream started - atomic frames_written{ 0 }; - atomic shutdown{ true }; - atomic draining{ false }; - atomic reinit_pending { false }; - atomic destroy_pending{ false }; + atomic frames_written{0}; + atomic shutdown{true}; + atomic draining{false}; + atomic reinit_pending{false}; + atomic destroy_pending{false}; /* Latency requested by the user. */ uint32_t latency_frames = 0; - atomic current_latency_frames{ 0 }; - atomic total_output_latency_frames { 0 }; + atomic current_latency_frames{0}; + atomic total_output_latency_frames{0}; unique_ptr resampler; /* This is true if a device change callback is currently running. */ - atomic switching_device{ false }; - atomic buffer_size_change_state{ false }; - AudioDeviceID aggregate_device_id = kAudioObjectUnknown; // the aggregate device id - AudioObjectID plugin_id = kAudioObjectUnknown; // used to create aggregate device + atomic switching_device{false}; + atomic buffer_size_change_state{false}; + AudioDeviceID aggregate_device_id = + kAudioObjectUnknown; // the aggregate device id + AudioObjectID plugin_id = + kAudioObjectUnknown; // used to create aggregate device /* Mixer interface */ unique_ptr mixer; /* Buffer where remixing/resampling will occur when upmixing is required */ @@ -261,12 +275,14 @@ struct cubeb_stream { unique_ptr output_source_listener; }; -bool has_input(cubeb_stream * stm) +bool +has_input(cubeb_stream * stm) { return stm->input_stream_params.rate != 0; } -bool has_output(cubeb_stream * stm) +bool +has_output(cubeb_stream * stm) { return stm->output_stream_params.rate != 0; } @@ -275,44 +291,44 @@ cubeb_channel channel_label_to_cubeb_channel(UInt32 label) { switch (label) { - case kAudioChannelLabel_Left: - return CHANNEL_FRONT_LEFT; - case kAudioChannelLabel_Right: - return CHANNEL_FRONT_RIGHT; - case kAudioChannelLabel_Center: - return CHANNEL_FRONT_CENTER; - case kAudioChannelLabel_LFEScreen: - return CHANNEL_LOW_FREQUENCY; - case kAudioChannelLabel_LeftSurround: - return CHANNEL_BACK_LEFT; - case kAudioChannelLabel_RightSurround: - return CHANNEL_BACK_RIGHT; - case kAudioChannelLabel_LeftCenter: - return CHANNEL_FRONT_LEFT_OF_CENTER; - case kAudioChannelLabel_RightCenter: - return CHANNEL_FRONT_RIGHT_OF_CENTER; - case kAudioChannelLabel_CenterSurround: - return CHANNEL_BACK_CENTER; - case kAudioChannelLabel_LeftSurroundDirect: - return CHANNEL_SIDE_LEFT; - case kAudioChannelLabel_RightSurroundDirect: - return CHANNEL_SIDE_RIGHT; - case kAudioChannelLabel_TopCenterSurround: - return CHANNEL_TOP_CENTER; - case kAudioChannelLabel_VerticalHeightLeft: - return CHANNEL_TOP_FRONT_LEFT; - case kAudioChannelLabel_VerticalHeightCenter: - return CHANNEL_TOP_FRONT_CENTER; - case kAudioChannelLabel_VerticalHeightRight: - return CHANNEL_TOP_FRONT_RIGHT; - case kAudioChannelLabel_TopBackLeft: - return CHANNEL_TOP_BACK_LEFT; - case kAudioChannelLabel_TopBackCenter: - return CHANNEL_TOP_BACK_CENTER; - case kAudioChannelLabel_TopBackRight: - return CHANNEL_TOP_BACK_RIGHT; - default: - return CHANNEL_UNKNOWN; + case kAudioChannelLabel_Left: + return CHANNEL_FRONT_LEFT; + case kAudioChannelLabel_Right: + return CHANNEL_FRONT_RIGHT; + case kAudioChannelLabel_Center: + return CHANNEL_FRONT_CENTER; + case kAudioChannelLabel_LFEScreen: + return CHANNEL_LOW_FREQUENCY; + case kAudioChannelLabel_LeftSurround: + return CHANNEL_BACK_LEFT; + case kAudioChannelLabel_RightSurround: + return CHANNEL_BACK_RIGHT; + case kAudioChannelLabel_LeftCenter: + return CHANNEL_FRONT_LEFT_OF_CENTER; + case kAudioChannelLabel_RightCenter: + return CHANNEL_FRONT_RIGHT_OF_CENTER; + case kAudioChannelLabel_CenterSurround: + return CHANNEL_BACK_CENTER; + case kAudioChannelLabel_LeftSurroundDirect: + return CHANNEL_SIDE_LEFT; + case kAudioChannelLabel_RightSurroundDirect: + return CHANNEL_SIDE_RIGHT; + case kAudioChannelLabel_TopCenterSurround: + return CHANNEL_TOP_CENTER; + case kAudioChannelLabel_VerticalHeightLeft: + return CHANNEL_TOP_FRONT_LEFT; + case kAudioChannelLabel_VerticalHeightCenter: + return CHANNEL_TOP_FRONT_CENTER; + case kAudioChannelLabel_VerticalHeightRight: + return CHANNEL_TOP_FRONT_RIGHT; + case kAudioChannelLabel_TopBackLeft: + return CHANNEL_TOP_BACK_LEFT; + case kAudioChannelLabel_TopBackCenter: + return CHANNEL_TOP_BACK_CENTER; + case kAudioChannelLabel_TopBackRight: + return CHANNEL_TOP_BACK_RIGHT; + default: + return CHANNEL_UNKNOWN; } } @@ -320,44 +336,44 @@ AudioChannelLabel cubeb_channel_to_channel_label(cubeb_channel channel) { switch (channel) { - case CHANNEL_FRONT_LEFT: - return kAudioChannelLabel_Left; - case CHANNEL_FRONT_RIGHT: - return kAudioChannelLabel_Right; - case CHANNEL_FRONT_CENTER: - return kAudioChannelLabel_Center; - case CHANNEL_LOW_FREQUENCY: - return kAudioChannelLabel_LFEScreen; - case CHANNEL_BACK_LEFT: - return kAudioChannelLabel_LeftSurround; - case CHANNEL_BACK_RIGHT: - return kAudioChannelLabel_RightSurround; - case CHANNEL_FRONT_LEFT_OF_CENTER: - return kAudioChannelLabel_LeftCenter; - case CHANNEL_FRONT_RIGHT_OF_CENTER: - return kAudioChannelLabel_RightCenter; - case CHANNEL_BACK_CENTER: - return kAudioChannelLabel_CenterSurround; - case CHANNEL_SIDE_LEFT: - return kAudioChannelLabel_LeftSurroundDirect; - case CHANNEL_SIDE_RIGHT: - return kAudioChannelLabel_RightSurroundDirect; - case CHANNEL_TOP_CENTER: - return kAudioChannelLabel_TopCenterSurround; - case CHANNEL_TOP_FRONT_LEFT: - return kAudioChannelLabel_VerticalHeightLeft; - case CHANNEL_TOP_FRONT_CENTER: - return kAudioChannelLabel_VerticalHeightCenter; - case CHANNEL_TOP_FRONT_RIGHT: - return kAudioChannelLabel_VerticalHeightRight; - case CHANNEL_TOP_BACK_LEFT: - return kAudioChannelLabel_TopBackLeft; - case CHANNEL_TOP_BACK_CENTER: - return kAudioChannelLabel_TopBackCenter; - case CHANNEL_TOP_BACK_RIGHT: - return kAudioChannelLabel_TopBackRight; - default: - return kAudioChannelLabel_Unknown; + case CHANNEL_FRONT_LEFT: + return kAudioChannelLabel_Left; + case CHANNEL_FRONT_RIGHT: + return kAudioChannelLabel_Right; + case CHANNEL_FRONT_CENTER: + return kAudioChannelLabel_Center; + case CHANNEL_LOW_FREQUENCY: + return kAudioChannelLabel_LFEScreen; + case CHANNEL_BACK_LEFT: + return kAudioChannelLabel_LeftSurround; + case CHANNEL_BACK_RIGHT: + return kAudioChannelLabel_RightSurround; + case CHANNEL_FRONT_LEFT_OF_CENTER: + return kAudioChannelLabel_LeftCenter; + case CHANNEL_FRONT_RIGHT_OF_CENTER: + return kAudioChannelLabel_RightCenter; + case CHANNEL_BACK_CENTER: + return kAudioChannelLabel_CenterSurround; + case CHANNEL_SIDE_LEFT: + return kAudioChannelLabel_LeftSurroundDirect; + case CHANNEL_SIDE_RIGHT: + return kAudioChannelLabel_RightSurroundDirect; + case CHANNEL_TOP_CENTER: + return kAudioChannelLabel_TopCenterSurround; + case CHANNEL_TOP_FRONT_LEFT: + return kAudioChannelLabel_VerticalHeightLeft; + case CHANNEL_TOP_FRONT_CENTER: + return kAudioChannelLabel_VerticalHeightCenter; + case CHANNEL_TOP_FRONT_RIGHT: + return kAudioChannelLabel_VerticalHeightRight; + case CHANNEL_TOP_BACK_LEFT: + return kAudioChannelLabel_TopBackLeft; + case CHANNEL_TOP_BACK_CENTER: + return kAudioChannelLabel_TopBackCenter; + case CHANNEL_TOP_BACK_RIGHT: + return kAudioChannelLabel_TopBackRight; + default: + return kAudioChannelLabel_Unknown; } } @@ -425,10 +441,8 @@ audiounit_make_silent(AudioBuffer * ioData) } static OSStatus -audiounit_render_input(cubeb_stream * stm, - AudioUnitRenderActionFlags * flags, - AudioTimeStamp const * tstamp, - UInt32 bus, +audiounit_render_input(cubeb_stream * stm, AudioUnitRenderActionFlags * flags, + AudioTimeStamp const * tstamp, UInt32 bus, UInt32 input_frames) { /* Create the AudioBufferList to store input. */ @@ -436,16 +450,13 @@ audiounit_render_input(cubeb_stream * stm, input_buffer_list.mBuffers[0].mDataByteSize = stm->input_desc.mBytesPerFrame * input_frames; input_buffer_list.mBuffers[0].mData = nullptr; - input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame; + input_buffer_list.mBuffers[0].mNumberChannels = + stm->input_desc.mChannelsPerFrame; input_buffer_list.mNumberBuffers = 1; /* Render input samples */ - OSStatus r = AudioUnitRender(stm->input_unit, - flags, - tstamp, - bus, - input_frames, - &input_buffer_list); + OSStatus r = AudioUnitRender(stm->input_unit, flags, tstamp, bus, + input_frames, &input_buffer_list); if (r != noErr) { LOG("AudioUnitRender rv=%d", r); @@ -461,35 +472,34 @@ audiounit_render_input(cubeb_stream * stm, // For now state that no error occurred and feed silence, stream will be // resumed once reinit has completed. ALOGV("(%p) input: reinit pending feeding silence instead", stm); - stm->input_linear_buffer->push_silence(input_frames * stm->input_desc.mChannelsPerFrame); + stm->input_linear_buffer->push_silence(input_frames * + stm->input_desc.mChannelsPerFrame); } else { /* Copy input data in linear buffer. */ stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData, - input_frames * stm->input_desc.mChannelsPerFrame); + input_frames * + stm->input_desc.mChannelsPerFrame); } /* Advance input frame counter. */ assert(input_frames > 0); stm->frames_read += input_frames; - ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %lu.", - stm, - (unsigned int) input_buffer_list.mNumberBuffers, - (unsigned int) input_buffer_list.mBuffers[0].mDataByteSize, - (unsigned int) input_buffer_list.mBuffers[0].mNumberChannels, - (unsigned int) input_frames, + ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, " + "total frames %lu.", + stm, (unsigned int)input_buffer_list.mNumberBuffers, + (unsigned int)input_buffer_list.mBuffers[0].mDataByteSize, + (unsigned int)input_buffer_list.mBuffers[0].mNumberChannels, + (unsigned int)input_frames, stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame); return noErr; } static OSStatus -audiounit_input_callback(void * user_ptr, - AudioUnitRenderActionFlags * flags, - AudioTimeStamp const * tstamp, - UInt32 bus, - UInt32 input_frames, - AudioBufferList * /* bufs */) +audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags, + AudioTimeStamp const * tstamp, UInt32 bus, + UInt32 input_frames, AudioBufferList * /* bufs */) { cubeb_stream * stm = static_cast(user_ptr); @@ -524,13 +534,13 @@ audiounit_input_callback(void * user_ptr, /* Input only. Call the user callback through resampler. Resampler will deliver input buffer in the correct rate. */ - assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame); - long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; + assert(input_frames <= stm->input_linear_buffer->length() / + stm->input_desc.mChannelsPerFrame); + long total_input_frames = + stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; long outframes = cubeb_resampler_fill(stm->resampler.get(), stm->input_linear_buffer->data(), - &total_input_frames, - NULL, - 0); + &total_input_frames, NULL, 0); stm->draining = outframes < total_input_frames; // Reset input buffer @@ -540,24 +550,17 @@ audiounit_input_callback(void * user_ptr, } static void -audiounit_mix_output_buffer(cubeb_stream * stm, - size_t output_frames, - void * input_buffer, - size_t input_buffer_size, - void * output_buffer, - size_t output_buffer_size) +audiounit_mix_output_buffer(cubeb_stream * stm, size_t output_frames, + void * input_buffer, size_t input_buffer_size, + void * output_buffer, size_t output_buffer_size) { assert(input_buffer_size >= cubeb_sample_size(stm->output_stream_params.format) * - stm->output_stream_params.channels * output_frames); + stm->output_stream_params.channels * output_frames); assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames); - int r = cubeb_mixer_mix(stm->mixer.get(), - output_frames, - input_buffer, - input_buffer_size, - output_buffer, - output_buffer_size); + int r = cubeb_mixer_mix(stm->mixer.get(), output_frames, input_buffer, + input_buffer_size, output_buffer, output_buffer_size); if (r != 0) { LOG("Remix error = %d", r); } @@ -579,10 +582,8 @@ minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames) static OSStatus audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * /* flags */, - AudioTimeStamp const * tstamp, - UInt32 bus, - UInt32 output_frames, - AudioBufferList * outBufferList) + AudioTimeStamp const * tstamp, UInt32 bus, + UInt32 output_frames, AudioBufferList * outBufferList) { assert(AU_OUT_BUS == bus); assert(outBufferList->mNumberBuffers == 1); @@ -596,18 +597,22 @@ audiounit_output_callback(void * user_ptr, const int ns2s = 1e9; // The total output latency is the timestamp difference + the stream latency + // the hardware latency. - stm->total_output_latency_frames = output_latency_ns * stm->output_hw_rate / ns2s + stm->current_latency_frames; + stm->total_output_latency_frames = + output_latency_ns * stm->output_hw_rate / ns2s + + stm->current_latency_frames; - ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %lu.", - stm, - (unsigned int) outBufferList->mNumberBuffers, - (unsigned int) outBufferList->mBuffers[0].mDataByteSize, - (unsigned int) outBufferList->mBuffers[0].mNumberChannels, - (unsigned int) output_frames, - has_input(stm) ? stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame : 0); + ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input " + "frames %lu.", + stm, (unsigned int)outBufferList->mNumberBuffers, + (unsigned int)outBufferList->mBuffers[0].mDataByteSize, + (unsigned int)outBufferList->mBuffers[0].mNumberChannels, + (unsigned int)output_frames, + has_input(stm) ? stm->input_linear_buffer->length() / + stm->input_desc.mChannelsPerFrame + : 0); long input_frames = 0; - void * output_buffer = NULL, * input_buffer = NULL; + void *output_buffer = NULL, *input_buffer = NULL; if (stm->shutdown) { ALOG("(%p) output shutdown.", stm); @@ -648,31 +653,35 @@ audiounit_output_callback(void * user_ptr, * currently switching, we add some silence as well to compensate for the * fact that we're lacking some input data. */ uint32_t input_frames_needed = - minimum_resampling_input_frames(stm, stm->frames_written); + minimum_resampling_input_frames(stm, stm->frames_written); long missing_frames = input_frames_needed - stm->frames_read; if (missing_frames > 0) { - stm->input_linear_buffer->push_silence(missing_frames * stm->input_desc.mChannelsPerFrame); + stm->input_linear_buffer->push_silence(missing_frames * + stm->input_desc.mChannelsPerFrame); stm->frames_read = input_frames_needed; - ALOG("(%p) %s pushed %ld frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," : - stm->switching_device ? "Device switching," : "Drop out,", missing_frames); + ALOG("(%p) %s pushed %ld frames of input silence.", stm, + stm->frames_read == 0 ? "Input hasn't started," + : stm->switching_device ? "Device switching," + : "Drop out,", + missing_frames); } input_buffer = stm->input_linear_buffer->data(); - // Number of input frames in the buffer. It will change to actually used frames - // inside fill - input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; + // Number of input frames in the buffer. It will change to actually used + // frames inside fill + input_frames = + stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame; } /* Call user callback through resampler. */ - long outframes = cubeb_resampler_fill(stm->resampler.get(), - input_buffer, + long outframes = cubeb_resampler_fill(stm->resampler.get(), input_buffer, input_buffer ? &input_frames : NULL, - output_buffer, - output_frames); + output_buffer, output_frames); if (input_buffer) { // Pop from the buffer the frames used by the the resampler. - stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame); + stm->input_linear_buffer->pop(input_frames * + stm->input_desc.mChannelsPerFrame); } if (outframes < 0 || outframes > output_frames) { @@ -688,7 +697,7 @@ audiounit_output_callback(void * user_ptr, return noErr; } - stm->draining = (UInt32) outframes < output_frames; + stm->draining = (UInt32)outframes < output_frames; stm->frames_played = stm->frames_queued; stm->frames_queued += outframes; @@ -698,17 +707,16 @@ audiounit_output_callback(void * user_ptr, size_t channels = stm->output_stream_params.channels; size_t missing_samples = (output_frames - outframes) * channels; size_t size_sample = cubeb_sample_size(stm->output_stream_params.format); - /* number of bytes that have been filled with valid audio by the callback. */ + /* number of bytes that have been filled with valid audio by the callback. + */ size_t audio_byte_count = outframes * channels * size_sample; - PodZero((uint8_t*)output_buffer + audio_byte_count, + PodZero((uint8_t *)output_buffer + audio_byte_count, missing_samples * size_sample); } /* Mixing */ if (stm->mixer) { - audiounit_mix_output_buffer(stm, - output_frames, - output_buffer, + audiounit_mix_output_buffer(stm, output_frames, output_buffer, stm->temp_buffer_size, outBufferList->mBuffers[0].mData, outBufferList->mBuffers[0].mDataByteSize); @@ -739,8 +747,10 @@ audiounit_get_backend_id(cubeb * /* ctx */) #if !TARGET_OS_IPHONE -static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume); -static int audiounit_stream_set_volume(cubeb_stream * stm, float volume); +static int +audiounit_stream_get_volume(cubeb_stream * stm, float * volume); +static int +audiounit_stream_set_volume(cubeb_stream * stm, float volume); static int audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side) @@ -781,12 +791,11 @@ audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side) assert(info->id); assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) || - !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT); + !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT); return CUBEB_OK; } - static int audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags) { @@ -818,11 +827,14 @@ audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags) * - The bluetooth device changed from A2DP to/from HFP/HSP profile * We first attempt to re-use the same device id, should that fail we will * default to the (potentially new) default device. */ - AudioDeviceID input_device = flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown; + AudioDeviceID input_device = + flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown; if (flags & DEV_INPUT) { r = audiounit_set_device_info(stm, input_device, io_side::INPUT); if (r != CUBEB_OK) { - LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm); + LOG("(%p) Set input device info failed. This can happen when last " + "media device is unplugged", + stm); return CUBEB_ERROR; } } @@ -832,7 +844,9 @@ audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags) * failures. It will change soon when reinit mechanism will be updated. */ r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT); if (r != CUBEB_OK) { - LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm); + LOG("(%p) Set output device info failed. This can happen when last media " + "device is unplugged", + stm); return CUBEB_ERROR; } @@ -842,7 +856,8 @@ audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags) // Attempt to re-use the same device-id failed, so attempt again with // default input device. audiounit_close_stream(stm); - if (audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::INPUT) != CUBEB_OK || + if (audiounit_set_device_info(stm, kAudioObjectUnknown, + io_side::INPUT) != CUBEB_OK || audiounit_setup_stream(stm) != CUBEB_OK) { LOG("(%p) Second stream reinit failed.", stm); return CUBEB_ERROR; @@ -897,62 +912,68 @@ audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags) static char const * event_addr_to_string(AudioObjectPropertySelector selector) { - switch(selector) { - case kAudioHardwarePropertyDefaultOutputDevice: - return "kAudioHardwarePropertyDefaultOutputDevice"; - case kAudioHardwarePropertyDefaultInputDevice: - return "kAudioHardwarePropertyDefaultInputDevice"; - case kAudioDevicePropertyDeviceIsAlive: - return "kAudioDevicePropertyDeviceIsAlive"; - case kAudioDevicePropertyDataSource: - return "kAudioDevicePropertyDataSource"; - default: - return "Unknown"; + switch (selector) { + case kAudioHardwarePropertyDefaultOutputDevice: + return "kAudioHardwarePropertyDefaultOutputDevice"; + case kAudioHardwarePropertyDefaultInputDevice: + return "kAudioHardwarePropertyDefaultInputDevice"; + case kAudioDevicePropertyDeviceIsAlive: + return "kAudioDevicePropertyDeviceIsAlive"; + case kAudioDevicePropertyDataSource: + return "kAudioDevicePropertyDataSource"; + default: + return "Unknown"; } } static OSStatus -audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count, - const AudioObjectPropertyAddress * addresses, - void * user) +audiounit_property_listener_callback( + AudioObjectID id, UInt32 address_count, + const AudioObjectPropertyAddress * addresses, void * user) { - cubeb_stream * stm = (cubeb_stream*) user; + cubeb_stream * stm = (cubeb_stream *)user; if (stm->switching_device) { - LOG("Switching is already taking place. Skip Event %s for id=%d", event_addr_to_string(addresses[0].mSelector), id); + LOG("Switching is already taking place. Skip Event %s for id=%d", + event_addr_to_string(addresses[0].mSelector), id); return noErr; } stm->switching_device = true; - LOG("(%p) Audio device changed, %u events.", stm, (unsigned int) address_count); + LOG("(%p) Audio device changed, %u events.", stm, + (unsigned int)address_count); for (UInt32 i = 0; i < address_count; i++) { - switch(addresses[i].mSelector) { - case kAudioHardwarePropertyDefaultOutputDevice: { - LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice for id=%d", (unsigned int) i, id); - } - break; - case kAudioHardwarePropertyDefaultInputDevice: { - LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id=%d", (unsigned int) i, id); - } - break; - case kAudioDevicePropertyDeviceIsAlive: { - LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for id=%d", (unsigned int) i, id); - // If this is the default input device ignore the event, - // kAudioHardwarePropertyDefaultInputDevice will take care of the switch - if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) { - LOG("It's the default input device, ignore the event"); - stm->switching_device = false; - return noErr; - } - } - break; - case kAudioDevicePropertyDataSource: { - LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", (unsigned int) i, id); - } - break; - default: - LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector); + switch (addresses[i].mSelector) { + case kAudioHardwarePropertyDefaultOutputDevice: { + LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice " + "for id=%d", + (unsigned int)i, id); + } break; + case kAudioHardwarePropertyDefaultInputDevice: { + LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice " + "for id=%d", + (unsigned int)i, id); + } break; + case kAudioDevicePropertyDeviceIsAlive: { + LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for " + "id=%d", + (unsigned int)i, id); + // If this is the default input device ignore the event, + // kAudioHardwarePropertyDefaultInputDevice will take care of the switch + if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) { + LOG("It's the default input device, ignore the event"); stm->switching_device = false; return noErr; + } + } break; + case kAudioDevicePropertyDataSource: { + LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", + (unsigned int)i, id); + } break; + default: + LOG("Event[%u] - mSelector == Unexpected Event id %d, return", + (unsigned int)i, addresses[i].mSelector); + stm->switching_device = false; + return noErr; } } @@ -966,18 +987,18 @@ audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count, } for (UInt32 i = 0; i < address_count; i++) { - switch(addresses[i].mSelector) { + switch (addresses[i].mSelector) { case kAudioHardwarePropertyDefaultOutputDevice: case kAudioHardwarePropertyDefaultInputDevice: case kAudioDevicePropertyDeviceIsAlive: /* fall through */ case kAudioDevicePropertyDataSource: { - auto_lock dev_cb_lock(stm->device_changed_callback_lock); - if (stm->device_changed_callback) { - stm->device_changed_callback(stm->user_ptr); - } - break; + auto_lock dev_cb_lock(stm->device_changed_callback_lock); + if (stm->device_changed_callback) { + stm->device_changed_callback(stm->user_ptr); } + break; + } } } @@ -992,18 +1013,16 @@ audiounit_add_listener(const property_listener * listener) assert(listener); return AudioObjectAddPropertyListener(listener->device_id, listener->property_address, - listener->callback, - listener->stream); + listener->callback, listener->stream); } OSStatus audiounit_remove_listener(const property_listener * listener) { assert(listener); - return AudioObjectRemovePropertyListener(listener->device_id, - listener->property_address, - listener->callback, - listener->stream); + return AudioObjectRemovePropertyListener( + listener->device_id, listener->property_address, listener->callback, + listener->stream); } static int @@ -1013,40 +1032,47 @@ audiounit_install_device_changed_callback(cubeb_stream * stm) int r = CUBEB_OK; if (stm->output_unit) { - /* This event will notify us when the data source on the same device changes, - * for example when the user plugs in a normal (non-usb) headset in the - * headphone jack. */ + /* This event will notify us when the data source on the same device + * changes, for example when the user plugs in a normal (non-usb) headset in + * the headphone jack. */ stm->output_source_listener.reset(new property_listener( - stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS, - &audiounit_property_listener_callback, stm)); + stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS, + &audiounit_property_listener_callback, stm)); rv = audiounit_add_listener(stm->output_source_listener.get()); if (rv != noErr) { stm->output_source_listener.reset(); - LOG("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id); + LOG("AudioObjectAddPropertyListener/output/" + "kAudioDevicePropertyDataSource rv=%d, device id=%d", + rv, stm->output_device.id); r = CUBEB_ERROR; } } if (stm->input_unit) { - /* This event will notify us when the data source on the input device changes. */ + /* This event will notify us when the data source on the input device + * changes. */ stm->input_source_listener.reset(new property_listener( - stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS, - &audiounit_property_listener_callback, stm)); + stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS, + &audiounit_property_listener_callback, stm)); rv = audiounit_add_listener(stm->input_source_listener.get()); if (rv != noErr) { stm->input_source_listener.reset(); - LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id); + LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource " + "rv=%d, device id=%d", + rv, stm->input_device.id); r = CUBEB_ERROR; } /* Event to notify when the input is going away. */ stm->input_alive_listener.reset(new property_listener( - stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS, - &audiounit_property_listener_callback, stm)); + stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS, + &audiounit_property_listener_callback, stm)); rv = audiounit_add_listener(stm->input_alive_listener.get()); if (rv != noErr) { stm->input_alive_listener.reset(); - LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", rv, stm->input_device.id); + LOG("AudioObjectAddPropertyListener/input/" + "kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", + rv, stm->input_device.id); r = CUBEB_ERROR; } } @@ -1061,16 +1087,18 @@ audiounit_install_system_changed_callback(cubeb_stream * stm) if (stm->output_unit) { /* This event will notify us when the default audio device changes, - * for example when the user plugs in a USB headset and the system chooses it - * automatically as the default, or when another device is chosen in the + * for example when the user plugs in a USB headset and the system chooses + * it automatically as the default, or when another device is chosen in the * dropdown list. */ stm->default_output_listener.reset(new property_listener( - kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS, - &audiounit_property_listener_callback, stm)); + kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS, + &audiounit_property_listener_callback, stm)); r = audiounit_add_listener(stm->default_output_listener.get()); if (r != noErr) { stm->default_output_listener.reset(); - LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r); + LOG("AudioObjectAddPropertyListener/output/" + "kAudioHardwarePropertyDefaultOutputDevice rv=%d", + r); return CUBEB_ERROR; } } @@ -1078,12 +1106,14 @@ audiounit_install_system_changed_callback(cubeb_stream * stm) if (stm->input_unit) { /* This event will notify us when the default input device changes. */ stm->default_input_listener.reset(new property_listener( - kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS, - &audiounit_property_listener_callback, stm)); + kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS, + &audiounit_property_listener_callback, stm)); r = audiounit_add_listener(stm->default_input_listener.get()); if (r != noErr) { stm->default_input_listener.reset(); - LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r); + LOG("AudioObjectAddPropertyListener/input/" + "kAudioHardwarePropertyDefaultInputDevice rv=%d", + r); return CUBEB_ERROR; } } @@ -1101,7 +1131,9 @@ audiounit_uninstall_device_changed_callback(cubeb_stream * stm) if (stm->output_source_listener) { rv = audiounit_remove_listener(stm->output_source_listener.get()); if (rv != noErr) { - LOG("AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id); + LOG("AudioObjectRemovePropertyListener/output/" + "kAudioDevicePropertyDataSource rv=%d, device id=%d", + rv, stm->output_device.id); r = CUBEB_ERROR; } stm->output_source_listener.reset(); @@ -1110,7 +1142,9 @@ audiounit_uninstall_device_changed_callback(cubeb_stream * stm) if (stm->input_source_listener) { rv = audiounit_remove_listener(stm->input_source_listener.get()); if (rv != noErr) { - LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id); + LOG("AudioObjectRemovePropertyListener/input/" + "kAudioDevicePropertyDataSource rv=%d, device id=%d", + rv, stm->input_device.id); r = CUBEB_ERROR; } stm->input_source_listener.reset(); @@ -1119,7 +1153,9 @@ audiounit_uninstall_device_changed_callback(cubeb_stream * stm) if (stm->input_alive_listener) { rv = audiounit_remove_listener(stm->input_alive_listener.get()); if (rv != noErr) { - LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", rv, stm->input_device.id); + LOG("AudioObjectRemovePropertyListener/input/" + "kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", + rv, stm->input_device.id); r = CUBEB_ERROR; } stm->input_alive_listener.reset(); @@ -1159,10 +1195,8 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range) OSStatus r; AudioDeviceID output_device_id; AudioObjectPropertyAddress output_device_buffer_size_range = { - kAudioDevicePropertyBufferFrameSizeRange, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster}; output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); if (output_device_id == kAudioObjectUnknown) { @@ -1174,11 +1208,8 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range) size = sizeof(*latency_range); r = AudioObjectGetPropertyData(output_device_id, - &output_device_buffer_size_range, - 0, - NULL, - &size, - latency_range); + &output_device_buffer_size_range, 0, NULL, + &size, latency_range); if (r != noErr) { LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r); return CUBEB_ERROR; @@ -1202,8 +1233,8 @@ audiounit_get_default_device_id(cubeb_device_type type) AudioDeviceID devid; UInt32 size = sizeof(AudioDeviceID); - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, - adr, 0, NULL, &size, &devid) != noErr) { + if (AudioObjectGetPropertyData(kAudioObjectSystemObject, adr, 0, NULL, &size, + &devid) != noErr) { return kAudioObjectUnknown; } @@ -1214,7 +1245,7 @@ int audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { #if TARGET_OS_IPHONE - //TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels] + // TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels] *max_channels = 2; #else UInt32 size; @@ -1222,10 +1253,8 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) AudioDeviceID output_device_id; AudioStreamBasicDescription stream_format; AudioObjectPropertyAddress stream_format_address = { - kAudioDevicePropertyStreamFormat, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, + kAudioObjectPropertyElementMaster}; assert(ctx && max_channels); @@ -1236,12 +1265,8 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) size = sizeof(stream_format); - r = AudioObjectGetPropertyData(output_device_id, - &stream_format_address, - 0, - NULL, - &size, - &stream_format); + r = AudioObjectGetPropertyData(output_device_id, &stream_format_address, 0, + NULL, &size, &stream_format); if (r != noErr) { LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r); return CUBEB_ERROR; @@ -1253,12 +1278,11 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int -audiounit_get_min_latency(cubeb * /* ctx */, - cubeb_stream_params /* params */, +audiounit_get_min_latency(cubeb * /* ctx */, cubeb_stream_params /* params */, uint32_t * latency_frames) { #if TARGET_OS_IPHONE - //TODO: [[AVAudioSession sharedInstance] inputLatency] + // TODO: [[AVAudioSession sharedInstance] inputLatency] return CUBEB_ERROR_NOT_SUPPORTED; #else AudioValueRange latency_range; @@ -1267,8 +1291,8 @@ audiounit_get_min_latency(cubeb * /* ctx */, return CUBEB_ERROR; } - *latency_frames = max(latency_range.mMinimum, - SAFE_MIN_LATENCY_FRAMES); + *latency_frames = + max(latency_range.mMinimum, SAFE_MIN_LATENCY_FRAMES); #endif return CUBEB_OK; @@ -1278,7 +1302,7 @@ static int audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate) { #if TARGET_OS_IPHONE - //TODO + // TODO return CUBEB_ERROR_NOT_SUPPORTED; #else UInt32 size; @@ -1286,10 +1310,8 @@ audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate) Float64 fsamplerate; AudioDeviceID output_device_id; AudioObjectPropertyAddress samplerate_address = { - kAudioDevicePropertyNominalSampleRate, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; + kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); if (output_device_id == kAudioObjectUnknown) { @@ -1297,12 +1319,8 @@ audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate) } size = sizeof(fsamplerate); - r = AudioObjectGetPropertyData(output_device_id, - &samplerate_address, - 0, - NULL, - &size, - &fsamplerate); + r = AudioObjectGetPropertyData(output_device_id, &samplerate_address, 0, NULL, + &size, &fsamplerate); if (r != noErr) { return CUBEB_ERROR; @@ -1325,7 +1343,8 @@ audiounit_convert_channel_layout(AudioChannelLayout * layout) return CUBEB_LAYOUT_STEREO; } - if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) { + if (layout->mChannelLayoutTag != + kAudioChannelLayoutTag_UseChannelDescriptions) { // kAudioChannelLayoutTag_UseChannelBitmap // kAudioChannelLayoutTag_Mono // kAudioChannelLayoutTag_Stereo @@ -1337,7 +1356,7 @@ audiounit_convert_channel_layout(AudioChannelLayout * layout) cubeb_channel_layout cl = 0; for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) { cubeb_channel cc = channel_label_to_cubeb_channel( - layout->mChannelDescriptions[i].mChannelLabel); + layout->mChannelDescriptions[i].mChannelLabel); if (cc == CHANNEL_UNKNOWN) { return CUBEB_LAYOUT_UNDEFINED; } @@ -1352,27 +1371,24 @@ audiounit_get_preferred_channel_layout(AudioUnit output_unit) { OSStatus rv = noErr; UInt32 size = 0; - rv = AudioUnitGetPropertyInfo(output_unit, - kAudioDevicePropertyPreferredChannelLayout, - kAudioUnitScope_Output, - AU_OUT_BUS, - &size, - nullptr); + rv = AudioUnitGetPropertyInfo( + output_unit, kAudioDevicePropertyPreferredChannelLayout, + kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr); if (rv != noErr) { - LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv); + LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout " + "rv=%d", + rv); return CUBEB_LAYOUT_UNDEFINED; } assert(size > 0); auto layout = make_sized_audio_channel_layout(size); - rv = AudioUnitGetProperty(output_unit, - kAudioDevicePropertyPreferredChannelLayout, - kAudioUnitScope_Output, - AU_OUT_BUS, - layout.get(), - &size); + rv = AudioUnitGetProperty( + output_unit, kAudioDevicePropertyPreferredChannelLayout, + kAudioUnitScope_Output, AU_OUT_BUS, layout.get(), &size); if (rv != noErr) { - LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv); + LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d", + rv); return CUBEB_LAYOUT_UNDEFINED; } @@ -1384,25 +1400,20 @@ audiounit_get_current_channel_layout(AudioUnit output_unit) { OSStatus rv = noErr; UInt32 size = 0; - rv = AudioUnitGetPropertyInfo(output_unit, - kAudioUnitProperty_AudioChannelLayout, - kAudioUnitScope_Output, - AU_OUT_BUS, - &size, - nullptr); + rv = AudioUnitGetPropertyInfo( + output_unit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr); if (rv != noErr) { - LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", rv); + LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", + rv); // This property isn't known before macOS 10.12, attempt another method. return audiounit_get_preferred_channel_layout(output_unit); } assert(size > 0); auto layout = make_sized_audio_channel_layout(size); - rv = AudioUnitGetProperty(output_unit, - kAudioUnitProperty_AudioChannelLayout, - kAudioUnitScope_Output, - AU_OUT_BUS, - layout.get(), + rv = AudioUnitGetProperty(output_unit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Output, AU_OUT_BUS, layout.get(), &size); if (rv != noErr) { LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv); @@ -1412,9 +1423,11 @@ audiounit_get_current_channel_layout(AudioUnit output_unit) return audiounit_convert_channel_layout(layout.get()); } -static int audiounit_create_unit(AudioUnit * unit, device_info * device); +static int +audiounit_create_unit(AudioUnit * unit, device_info * device); -static OSStatus audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype); +static OSStatus +audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype); static void audiounit_destroy(cubeb * ctx) @@ -1425,7 +1438,8 @@ audiounit_destroy(cubeb * ctx) // Disabling this assert for bug 1083664 -- we seem to leak a stream // assert(ctx->active_streams == 0); if (audiounit_active_streams(ctx) > 0) { - LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, audiounit_active_streams(ctx)); + LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, + audiounit_active_streams(ctx)); } /* Unregister the callback if necessary. */ @@ -1442,7 +1456,8 @@ audiounit_destroy(cubeb * ctx) delete ctx; } -static void audiounit_stream_destroy(cubeb_stream * stm); +static void +audiounit_stream_destroy(cubeb_stream * stm); static int audio_stream_desc_init(AudioStreamBasicDescription * ss, @@ -1455,8 +1470,8 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss, break; case CUBEB_SAMPLE_S16BE: ss->mBitsPerChannel = 16; - ss->mFormatFlags = kAudioFormatFlagIsSignedInteger | - kAudioFormatFlagIsBigEndian; + ss->mFormatFlags = + kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian; break; case CUBEB_SAMPLE_FLOAT32LE: ss->mBitsPerChannel = 32; @@ -1464,8 +1479,7 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss, break; case CUBEB_SAMPLE_FLOAT32BE: ss->mBitsPerChannel = 32; - ss->mFormatFlags = kAudioFormatFlagIsFloat | - kAudioFormatFlagIsBigEndian; + ss->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsBigEndian; break; default: return CUBEB_ERROR_INVALID_FORMAT; @@ -1491,17 +1505,15 @@ audiounit_init_mixer(cubeb_stream * stm) // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio // data, it silently drop the channels so we need to remix the // audio data by ourselves to keep all the information. - stm->mixer.reset(cubeb_mixer_create(stm->output_stream_params.format, - stm->output_stream_params.channels, - stm->output_stream_params.layout, - stm->context->channels, - stm->context->layout)); + stm->mixer.reset(cubeb_mixer_create( + stm->output_stream_params.format, stm->output_stream_params.channels, + stm->output_stream_params.layout, stm->context->channels, + stm->context->layout)); assert(stm->mixer); } static int -audiounit_set_channel_layout(AudioUnit unit, - io_side side, +audiounit_set_channel_layout(AudioUnit unit, io_side side, cubeb_channel_layout layout) { if (side != io_side::OUTPUT) { @@ -1513,7 +1525,6 @@ audiounit_set_channel_layout(AudioUnit unit, return CUBEB_OK; } - OSStatus r; uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout); @@ -1531,21 +1542,20 @@ audiounit_set_channel_layout(AudioUnit unit, uint32_t channel = (channelMap & 1) << i; if (channel != 0) { au_layout->mChannelDescriptions[channels].mChannelLabel = - cubeb_channel_to_channel_label(static_cast(channel)); - au_layout->mChannelDescriptions[channels].mChannelFlags = kAudioChannelFlags_AllOff; + cubeb_channel_to_channel_label(static_cast(channel)); + au_layout->mChannelDescriptions[channels].mChannelFlags = + kAudioChannelFlags_AllOff; channels++; } channelMap = channelMap >> 1; } - r = AudioUnitSetProperty(unit, - kAudioUnitProperty_AudioChannelLayout, - kAudioUnitScope_Input, - AU_OUT_BUS, - au_layout.get(), + r = AudioUnitSetProperty(unit, kAudioUnitProperty_AudioChannelLayout, + kAudioUnitScope_Input, AU_OUT_BUS, au_layout.get(), size); if (r != noErr) { - LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", to_string(side), r); + LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", + to_string(side), r); return CUBEB_ERROR; } @@ -1562,22 +1572,20 @@ audiounit_layout_init(cubeb_stream * stm, io_side side) stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit); - audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT, stm->context->layout); + audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT, + stm->context->layout); } static vector audiounit_get_sub_devices(AudioDeviceID device_id) { vector sub_devices; - AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress property_address = { + kAudioAggregateDevicePropertyActiveSubDeviceList, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; UInt32 size = 0; - OSStatus rv = AudioObjectGetPropertyDataSize(device_id, - &property_address, - 0, - nullptr, - &size); + OSStatus rv = AudioObjectGetPropertyDataSize(device_id, &property_address, 0, + nullptr, &size); if (rv != noErr) { sub_devices.push_back(device_id); @@ -1586,12 +1594,8 @@ audiounit_get_sub_devices(AudioDeviceID device_id) uint32_t count = static_cast(size / sizeof(AudioObjectID)); sub_devices.resize(count); - rv = AudioObjectGetPropertyData(device_id, - &property_address, - 0, - nullptr, - &size, - sub_devices.data()); + rv = AudioObjectGetPropertyData(device_id, &property_address, 0, nullptr, + &size, sub_devices.data()); if (rv != noErr) { sub_devices.clear(); sub_devices.push_back(device_id); @@ -1602,18 +1606,19 @@ audiounit_get_sub_devices(AudioDeviceID device_id) } static int -audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID * aggregate_device_id) +audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, + AudioDeviceID * aggregate_device_id) { - AudioObjectPropertyAddress address_plugin_bundle_id = { kAudioHardwarePropertyPlugInForBundleID, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress address_plugin_bundle_id = { + kAudioHardwarePropertyPlugInForBundleID, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; UInt32 size = 0; - OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, - &address_plugin_bundle_id, - 0, NULL, - &size); + OSStatus r = AudioObjectGetPropertyDataSize( + kAudioObjectSystemObject, &address_plugin_bundle_id, 0, NULL, &size); if (r != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r); + LOG("AudioObjectGetPropertyDataSize/" + "kAudioHardwarePropertyPlugInForBundleID, rv=%d", + r); return CUBEB_ERROR; } @@ -1625,62 +1630,72 @@ audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID translation_value.mOutputDataSize = sizeof(*plugin_id); r = AudioObjectGetPropertyData(kAudioObjectSystemObject, - &address_plugin_bundle_id, - 0, - nullptr, - &size, + &address_plugin_bundle_id, 0, nullptr, &size, &translation_value); if (r != noErr) { - LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r); + LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, " + "rv=%d", + r); return CUBEB_ERROR; } - AudioObjectPropertyAddress create_aggregate_device_address = { kAudioPlugInCreateAggregateDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - r = AudioObjectGetPropertyDataSize(*plugin_id, - &create_aggregate_device_address, - 0, - nullptr, - &size); + AudioObjectPropertyAddress create_aggregate_device_address = { + kAudioPlugInCreateAggregateDevice, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; + r = AudioObjectGetPropertyDataSize( + *plugin_id, &create_aggregate_device_address, 0, nullptr, &size); if (r != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, rv=%d", r); + LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, " + "rv=%d", + r); return CUBEB_ERROR; } - CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, - &kCFTypeDictionaryKeyCallBacks, - &kCFTypeDictionaryValueCallBacks); + CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable( + kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); struct timeval timestamp; gettimeofday(×tamp, NULL); long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec; - CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id); - CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceNameKey), aggregate_device_name); + CFStringRef aggregate_device_name = CFStringCreateWithFormat( + NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id); + CFDictionaryAddValue(aggregate_device_dict, + CFSTR(kAudioAggregateDeviceNameKey), + aggregate_device_name); CFRelease(aggregate_device_name); - CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id); - CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceUIDKey), aggregate_device_UID); + CFStringRef aggregate_device_UID = + CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), + PRIVATE_AGGREGATE_DEVICE_NAME, time_id); + CFDictionaryAddValue(aggregate_device_dict, + CFSTR(kAudioAggregateDeviceUIDKey), + aggregate_device_UID); CFRelease(aggregate_device_UID); int private_value = 1; - CFNumberRef aggregate_device_private_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value); - CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsPrivateKey), aggregate_device_private_key); + CFNumberRef aggregate_device_private_key = + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value); + CFDictionaryAddValue(aggregate_device_dict, + CFSTR(kAudioAggregateDeviceIsPrivateKey), + aggregate_device_private_key); CFRelease(aggregate_device_private_key); int stacked_value = 0; - CFNumberRef aggregate_device_stacked_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value); - CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsStackedKey), aggregate_device_stacked_key); + CFNumberRef aggregate_device_stacked_key = + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value); + CFDictionaryAddValue(aggregate_device_dict, + CFSTR(kAudioAggregateDeviceIsStackedKey), + aggregate_device_stacked_key); CFRelease(aggregate_device_stacked_key); - r = AudioObjectGetPropertyData(*plugin_id, - &create_aggregate_device_address, + r = AudioObjectGetPropertyData(*plugin_id, &create_aggregate_device_address, sizeof(aggregate_device_dict), - &aggregate_device_dict, - &size, + &aggregate_device_dict, &size, aggregate_device_id); CFRelease(aggregate_device_dict); if (r != noErr) { - LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", r); + LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", + r); return CUBEB_ERROR; } LOG("New aggregate device %u", *aggregate_device_id); @@ -1696,10 +1711,11 @@ get_device_name(AudioDeviceID id) { UInt32 size = sizeof(CFStringRef); CFStringRef UIname = nullptr; - AudioObjectPropertyAddress address_uuid = { kAudioDevicePropertyDeviceUID, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus err = AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname); + AudioObjectPropertyAddress address_uuid = {kAudioDevicePropertyDeviceUID, + kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; + OSStatus err = + AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname); return (err == noErr) ? UIname : NULL; } @@ -1710,12 +1726,15 @@ audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id, { LOG("Add devices input %u and output %u into aggregate device %u", input_device_id, output_device_id, aggregate_device_id); - const vector output_sub_devices = audiounit_get_sub_devices(output_device_id); - const vector input_sub_devices = audiounit_get_sub_devices(input_device_id); + const vector output_sub_devices = + audiounit_get_sub_devices(output_device_id); + const vector input_sub_devices = + audiounit_get_sub_devices(input_device_id); - CFMutableArrayRef aggregate_sub_devices_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); - /* The order of the items in the array is significant and is used to determine the order of the streams - of the AudioAggregateDevice. */ + CFMutableArrayRef aggregate_sub_devices_array = + CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + /* The order of the items in the array is significant and is used to determine + the order of the streams of the AudioAggregateDevice. */ for (UInt32 i = 0; i < output_sub_devices.size(); i++) { CFStringRef ref = get_device_name(output_sub_devices[i]); if (ref == NULL) { @@ -1735,19 +1754,18 @@ audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id, CFRelease(ref); } - AudioObjectPropertyAddress aggregate_sub_device_list = { kAudioAggregateDevicePropertyFullSubDeviceList, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress aggregate_sub_device_list = { + kAudioAggregateDevicePropertyFullSubDeviceList, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; UInt32 size = sizeof(CFMutableArrayRef); - OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id, - &aggregate_sub_device_list, - 0, - nullptr, - size, - &aggregate_sub_devices_array); + OSStatus rv = AudioObjectSetPropertyData( + aggregate_device_id, &aggregate_sub_device_list, 0, nullptr, size, + &aggregate_sub_devices_array); CFRelease(aggregate_sub_devices_array); if (rv != noErr) { - LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", rv); + LOG("AudioObjectSetPropertyData/" + "kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", + rv); return CUBEB_ERROR; } @@ -1758,27 +1776,28 @@ static int audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id) { assert(aggregate_device_id != kAudioObjectUnknown); - AudioObjectPropertyAddress master_aggregate_sub_device = { kAudioAggregateDevicePropertyMasterSubDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress master_aggregate_sub_device = { + kAudioAggregateDevicePropertyMasterSubDevice, + kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; // Master become the 1st output sub device - AudioDeviceID output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - const vector output_sub_devices = audiounit_get_sub_devices(output_device_id); + AudioDeviceID output_device_id = + audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); + const vector output_sub_devices = + audiounit_get_sub_devices(output_device_id); CFStringRef master_sub_device = get_device_name(output_sub_devices[0]); UInt32 size = sizeof(CFStringRef); OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id, - &master_aggregate_sub_device, - 0, - NULL, - size, - &master_sub_device); + &master_aggregate_sub_device, 0, + NULL, size, &master_sub_device); if (master_sub_device) { CFRelease(master_sub_device); } if (rv != noErr) { - LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", rv); + LOG("AudioObjectSetPropertyData/" + "kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", + rv); return CUBEB_ERROR; } @@ -1786,24 +1805,25 @@ audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id) } static int -audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id) +audiounit_activate_clock_drift_compensation( + const AudioDeviceID aggregate_device_id) { assert(aggregate_device_id != kAudioObjectUnknown); - AudioObjectPropertyAddress address_owned = { kAudioObjectPropertyOwnedObjects, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress address_owned = { + kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; UInt32 qualifier_data_size = sizeof(AudioObjectID); AudioClassID class_id = kAudioSubDeviceClassID; void * qualifier_data = &class_id; UInt32 size = 0; - OSStatus rv = AudioObjectGetPropertyDataSize(aggregate_device_id, - &address_owned, - qualifier_data_size, - qualifier_data, - &size); + OSStatus rv = AudioObjectGetPropertyDataSize( + aggregate_device_id, &address_owned, qualifier_data_size, qualifier_data, + &size); if (rv != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, rv=%d", rv); + LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, " + "rv=%d", + rv); return CUBEB_ERROR; } @@ -1812,87 +1832,95 @@ audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device AudioObjectID sub_devices[subdevices_num]; size = sizeof(sub_devices); - rv = AudioObjectGetPropertyData(aggregate_device_id, - &address_owned, - qualifier_data_size, - qualifier_data, - &size, + rv = AudioObjectGetPropertyData(aggregate_device_id, &address_owned, + qualifier_data_size, qualifier_data, &size, sub_devices); if (rv != noErr) { - LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", rv); + LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", + rv); return CUBEB_ERROR; } - AudioObjectPropertyAddress address_drift = { kAudioSubDevicePropertyDriftCompensation, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress address_drift = { + kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; // Start from the second device since the first is the master clock for (UInt32 i = 1; i < subdevices_num; ++i) { UInt32 drift_compensation_value = 1; - rv = AudioObjectSetPropertyData(sub_devices[i], - &address_drift, - 0, - nullptr, - sizeof(UInt32), - &drift_compensation_value); + rv = AudioObjectSetPropertyData(sub_devices[i], &address_drift, 0, nullptr, + sizeof(UInt32), &drift_compensation_value); if (rv != noErr) { - LOG("AudioObjectSetPropertyData/kAudioSubDevicePropertyDriftCompensation, rv=%d", rv); + LOG("AudioObjectSetPropertyData/" + "kAudioSubDevicePropertyDriftCompensation, rv=%d", + rv); return CUBEB_OK; } } return CUBEB_OK; } -static int audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id); -static void audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope, - uint32_t * min, uint32_t * max, uint32_t * def); static int -audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type); -static void audiounit_device_destroy(cubeb_device_info * device); +audiounit_destroy_aggregate_device(AudioObjectID plugin_id, + AudioDeviceID * aggregate_device_id); +static void +audiounit_get_available_samplerate(AudioObjectID devid, + AudioObjectPropertyScope scope, + uint32_t * min, uint32_t * max, + uint32_t * def); +static int +audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, + AudioObjectID devid, cubeb_device_type type); +static void +audiounit_device_destroy(cubeb_device_info * device); static void audiounit_workaround_for_airpod(cubeb_stream * stm) { cubeb_device_info input_device_info; - audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id, CUBEB_DEVICE_TYPE_INPUT); + audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id, + CUBEB_DEVICE_TYPE_INPUT); cubeb_device_info output_device_info; - audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id, CUBEB_DEVICE_TYPE_OUTPUT); + audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id, + CUBEB_DEVICE_TYPE_OUTPUT); std::string input_name_str(input_device_info.friendly_name); std::string output_name_str(output_device_info.friendly_name); - if(input_name_str.find("AirPods") != std::string::npos && - output_name_str.find("AirPods") != std::string::npos) { + if (input_name_str.find("AirPods") != std::string::npos && + output_name_str.find("AirPods") != std::string::npos) { uint32_t input_min_rate = 0; uint32_t input_max_rate = 0; uint32_t input_nominal_rate = 0; - audiounit_get_available_samplerate(stm->input_device.id, kAudioObjectPropertyScopeGlobal, - &input_min_rate, &input_max_rate, &input_nominal_rate); - LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->input_device.id - , input_device_info.friendly_name, input_min_rate, input_max_rate, input_nominal_rate); + audiounit_get_available_samplerate( + stm->input_device.id, kAudioObjectPropertyScopeGlobal, &input_min_rate, + &input_max_rate, &input_nominal_rate); + LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u", + stm, stm->input_device.id, input_device_info.friendly_name, + input_min_rate, input_max_rate, input_nominal_rate); uint32_t output_min_rate = 0; uint32_t output_max_rate = 0; uint32_t output_nominal_rate = 0; - audiounit_get_available_samplerate(stm->output_device.id, kAudioObjectPropertyScopeGlobal, - &output_min_rate, &output_max_rate, &output_nominal_rate); - LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->output_device.id - , output_device_info.friendly_name, output_min_rate, output_max_rate, output_nominal_rate); + audiounit_get_available_samplerate( + stm->output_device.id, kAudioObjectPropertyScopeGlobal, + &output_min_rate, &output_max_rate, &output_nominal_rate); + LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u", + stm, stm->output_device.id, output_device_info.friendly_name, + output_min_rate, output_max_rate, output_nominal_rate); Float64 rate = input_nominal_rate; AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster}; - OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id, - &addr, - 0, - nullptr, - sizeof(Float64), - &rate); + OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id, &addr, 0, + nullptr, sizeof(Float64), &rate); if (rv != noErr) { - LOG("Non fatal error, AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, rv=%d", rv); + LOG("Non fatal error, " + "AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, " + "rv=%d", + rv); } } audiounit_device_destroy(&input_device_info); @@ -1900,10 +1928,11 @@ audiounit_workaround_for_airpod(cubeb_stream * stm) } /* - * Aggregate Device is a virtual audio interface which utilizes inputs and outputs - * of one or more physical audio interfaces. It is possible to use the clock of - * one of the devices as a master clock for all the combined devices and enable - * drift compensation for the devices that are not designated clock master. + * Aggregate Device is a virtual audio interface which utilizes inputs and + * outputs of one or more physical audio interfaces. It is possible to use the + * clock of one of the devices as a master clock for all the combined devices + * and enable drift compensation for the devices that are not designated clock + * master. * * Creating a new aggregate device programmatically requires [0][1]: * 1. Locate the base plug-in ("com.apple.audio.CoreAudio") @@ -1921,31 +1950,37 @@ audiounit_workaround_for_airpod(cubeb_stream * stm) static int audiounit_create_aggregate_device(cubeb_stream * stm) { - int r = audiounit_create_blank_aggregate_device(&stm->plugin_id, &stm->aggregate_device_id); + int r = audiounit_create_blank_aggregate_device(&stm->plugin_id, + &stm->aggregate_device_id); if (r != CUBEB_OK) { LOG("(%p) Failed to create blank aggregate device", stm); return CUBEB_ERROR; } - r = audiounit_set_aggregate_sub_device_list(stm->aggregate_device_id, stm->input_device.id, stm->output_device.id); + r = audiounit_set_aggregate_sub_device_list( + stm->aggregate_device_id, stm->input_device.id, stm->output_device.id); if (r != CUBEB_OK) { LOG("(%p) Failed to set aggregate sub-device list", stm); - audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id); + audiounit_destroy_aggregate_device(stm->plugin_id, + &stm->aggregate_device_id); return CUBEB_ERROR; } r = audiounit_set_master_aggregate_device(stm->aggregate_device_id); if (r != CUBEB_OK) { LOG("(%p) Failed to set master sub-device for aggregate device", stm); - audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id); - return CUBEB_ERROR; + audiounit_destroy_aggregate_device(stm->plugin_id, + &stm->aggregate_device_id); + return CUBEB_ERROR; } r = audiounit_activate_clock_drift_compensation(stm->aggregate_device_id); if (r != CUBEB_OK) { - LOG("(%p) Failed to activate clock drift compensation for aggregate device", stm); - audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id); - return CUBEB_ERROR; + LOG("(%p) Failed to activate clock drift compensation for aggregate device", + stm); + audiounit_destroy_aggregate_device(stm->plugin_id, + &stm->aggregate_device_id); + return CUBEB_ERROR; } audiounit_workaround_for_airpod(stm); @@ -1954,33 +1989,29 @@ audiounit_create_aggregate_device(cubeb_stream * stm) } static int -audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id) +audiounit_destroy_aggregate_device(AudioObjectID plugin_id, + AudioDeviceID * aggregate_device_id) { - assert(aggregate_device_id && - *aggregate_device_id != kAudioDeviceUnknown && + assert(aggregate_device_id && *aggregate_device_id != kAudioDeviceUnknown && plugin_id != kAudioObjectUnknown); - AudioObjectPropertyAddress destroy_aggregate_device_addr = { kAudioPlugInDestroyAggregateDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster}; + AudioObjectPropertyAddress destroy_aggregate_device_addr = { + kAudioPlugInDestroyAggregateDevice, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; UInt32 size; - OSStatus rv = AudioObjectGetPropertyDataSize(plugin_id, - &destroy_aggregate_device_addr, - 0, - NULL, - &size); + OSStatus rv = AudioObjectGetPropertyDataSize( + plugin_id, &destroy_aggregate_device_addr, 0, NULL, &size); if (rv != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, rv=%d", rv); + LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, " + "rv=%d", + rv); return CUBEB_ERROR; } - rv = AudioObjectGetPropertyData(plugin_id, - &destroy_aggregate_device_addr, - 0, - NULL, - &size, - aggregate_device_id); + rv = AudioObjectGetPropertyData(plugin_id, &destroy_aggregate_device_addr, 0, + NULL, &size, aggregate_device_id); if (rv != noErr) { - LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", rv); + LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", + rv); return CUBEB_ERROR; } @@ -2004,8 +2035,7 @@ audiounit_new_unit_instance(AudioUnit * unit, device_info * device) // so we retain automatic output device switching when the default // changes. Once we have complete support for device notifications // and switching, we can use the AUHAL for everything. - if ((device->flags & DEV_SYSTEM_DEFAULT) && - (device->flags & DEV_OUTPUT)) { + if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) { desc.componentSubType = kAudioUnitSubType_DefaultOutput; } else { desc.componentSubType = kAudioUnitSubType_HALOutput; @@ -2039,10 +2069,10 @@ audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state) OSStatus rv; UInt32 enable = state; rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, - (side == io_side::INPUT) ? kAudioUnitScope_Input : kAudioUnitScope_Output, + (side == io_side::INPUT) ? kAudioUnitScope_Input + : kAudioUnitScope_Output, (side == io_side::INPUT) ? AU_IN_BUS : AU_OUT_BUS, - &enable, - sizeof(UInt32)); + &enable, sizeof(UInt32)); if (rv != noErr) { LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv); return CUBEB_ERROR; @@ -2065,12 +2095,10 @@ audiounit_create_unit(AudioUnit * unit, device_info * device) } assert(*unit); - if ((device->flags & DEV_SYSTEM_DEFAULT) && - (device->flags & DEV_OUTPUT)) { + if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) { return CUBEB_OK; } - if (device->flags & DEV_INPUT) { r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE); if (r != CUBEB_OK) { @@ -2097,13 +2125,12 @@ audiounit_create_unit(AudioUnit * unit, device_info * device) assert(false); } - rv = AudioUnitSetProperty(*unit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &device->id, sizeof(AudioDeviceID)); + rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, 0, &device->id, + sizeof(AudioDeviceID)); if (rv != noErr) { - LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d", rv); + LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d", + rv); return CUBEB_ERROR; } @@ -2113,7 +2140,8 @@ audiounit_create_unit(AudioUnit * unit, device_info * device) static int audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity) { - uint32_t size = capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame; + uint32_t size = + capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame; if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) { stream->input_linear_buffer.reset(new auto_array_wrapper_impl(size)); } else { @@ -2131,7 +2159,7 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames) assert(audiounit_active_streams(stm->context) > 0); if (audiounit_active_streams(stm->context) == 1) { return max(min(latency_frames, SAFE_MAX_LATENCY_FRAMES), - SAFE_MIN_LATENCY_FRAMES); + SAFE_MIN_LATENCY_FRAMES); } assert(stm->output_unit); @@ -2141,36 +2169,36 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames) UInt32 output_buffer_size = 0; UInt32 size = sizeof(output_buffer_size); if (stm->output_unit) { - r = AudioUnitGetProperty(stm->output_unit, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Output, - AU_OUT_BUS, - &output_buffer_size, - &size); + r = AudioUnitGetProperty( + stm->output_unit, kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Output, AU_OUT_BUS, &output_buffer_size, &size); if (r != noErr) { - LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize rv=%d", r); + LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize " + "rv=%d", + r); return 0; } - output_buffer_size = max(min(output_buffer_size, SAFE_MAX_LATENCY_FRAMES), - SAFE_MIN_LATENCY_FRAMES); + output_buffer_size = + max(min(output_buffer_size, SAFE_MAX_LATENCY_FRAMES), + SAFE_MIN_LATENCY_FRAMES); } UInt32 input_buffer_size = 0; if (stm->input_unit) { - r = AudioUnitGetProperty(stm->input_unit, - kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Input, - AU_IN_BUS, - &input_buffer_size, - &size); + r = AudioUnitGetProperty( + stm->input_unit, kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Input, AU_IN_BUS, &input_buffer_size, &size); if (r != noErr) { - LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize rv=%d", r); + LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize " + "rv=%d", + r); return 0; } - input_buffer_size = max(min(input_buffer_size, SAFE_MAX_LATENCY_FRAMES), - SAFE_MIN_LATENCY_FRAMES); + input_buffer_size = + max(min(input_buffer_size, SAFE_MAX_LATENCY_FRAMES), + SAFE_MIN_LATENCY_FRAMES); } // Every following active streams can only set smaller latency @@ -2186,7 +2214,7 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames) } return max(min(latency_frames, upper_latency_limit), - SAFE_MIN_LATENCY_FRAMES); + SAFE_MIN_LATENCY_FRAMES); } /* @@ -2198,11 +2226,9 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames) * - property has changed, remove the listener * */ static void -buffer_size_changed_callback(void * inClientData, - AudioUnit inUnit, +buffer_size_changed_callback(void * inClientData, AudioUnit inUnit, AudioUnitPropertyID inPropertyID, - AudioUnitScope inScope, - AudioUnitElement inElement) + AudioUnitScope inScope, AudioUnitElement inElement) { cubeb_stream * stm = (cubeb_stream *)inClientData; @@ -2218,32 +2244,33 @@ buffer_size_changed_callback(void * inClientData, switch (inPropertyID) { - case kAudioDevicePropertyBufferFrameSize: { - if (inScope != au_scope) { - break; - } - UInt32 new_buffer_size; - UInt32 outSize = sizeof(UInt32); - OSStatus r = AudioUnitGetProperty(au, - kAudioDevicePropertyBufferFrameSize, - au_scope, - au_element, - &new_buffer_size, - &outSize); - if (r != noErr) { - LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current buffer size", stm); - } else { - LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size = %d for scope %d", stm, - au_type, new_buffer_size, inScope); - } - stm->buffer_size_change_state = true; + case kAudioDevicePropertyBufferFrameSize: { + if (inScope != au_scope) { break; } + UInt32 new_buffer_size; + UInt32 outSize = sizeof(UInt32); + OSStatus r = + AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, au_scope, + au_element, &new_buffer_size, &outSize); + if (r != noErr) { + LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current " + "buffer size", + stm); + } else { + LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size " + "= %d for scope %d", + stm, au_type, new_buffer_size, inScope); + } + stm->buffer_size_change_state = true; + break; + } } } static int -audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side side) +audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, + io_side side) { AudioUnit au = stm->output_unit; AudioUnitScope au_scope = kAudioUnitScope_Input; @@ -2257,48 +2284,45 @@ audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side uint32_t buffer_frames = 0; UInt32 size = sizeof(buffer_frames); - int r = AudioUnitGetProperty(au, - kAudioDevicePropertyBufferFrameSize, - au_scope, - au_element, - &buffer_frames, - &size); + int r = AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, + au_scope, au_element, &buffer_frames, &size); if (r != noErr) { - LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); + LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", + to_string(side), r); return CUBEB_ERROR; } if (new_size_frames == buffer_frames) { - LOG("(%p) No need to update %s buffer size already %u frames", stm, to_string(side), buffer_frames); + LOG("(%p) No need to update %s buffer size already %u frames", stm, + to_string(side), buffer_frames); return CUBEB_OK; } - r = AudioUnitAddPropertyListener(au, - kAudioDevicePropertyBufferFrameSize, - buffer_size_changed_callback, - stm); + r = AudioUnitAddPropertyListener(au, kAudioDevicePropertyBufferFrameSize, + buffer_size_changed_callback, stm); if (r != noErr) { - LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); + LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize " + "rv=%d", + to_string(side), r); return CUBEB_ERROR; } stm->buffer_size_change_state = false; - r = AudioUnitSetProperty(au, - kAudioDevicePropertyBufferFrameSize, - au_scope, - au_element, - &new_size_frames, + r = AudioUnitSetProperty(au, kAudioDevicePropertyBufferFrameSize, au_scope, + au_element, &new_size_frames, sizeof(new_size_frames)); if (r != noErr) { - LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); + LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", + to_string(side), r); - r = AudioUnitRemovePropertyListenerWithUserData(au, - kAudioDevicePropertyBufferFrameSize, - buffer_size_changed_callback, - stm); + r = AudioUnitRemovePropertyListenerWithUserData( + au, kAudioDevicePropertyBufferFrameSize, buffer_size_changed_callback, + stm); if (r != noErr) { - LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); + LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize " + "rv=%d", + to_string(side), r); } return CUBEB_ERROR; @@ -2309,18 +2333,21 @@ audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side struct timespec req, rem; req.tv_sec = 0; req.tv_nsec = 100000000L; // 0.1 sec - if (nanosleep(&req , &rem) < 0 ) { - LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time %ld nano secs \n", stm, rem.tv_nsec); + if (nanosleep(&req, &rem) < 0) { + LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time " + "%ld nano secs \n", + stm, rem.tv_nsec); } LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count); } - r = AudioUnitRemovePropertyListenerWithUserData(au, - kAudioDevicePropertyBufferFrameSize, - buffer_size_changed_callback, - stm); + r = AudioUnitRemovePropertyListenerWithUserData( + au, kAudioDevicePropertyBufferFrameSize, buffer_size_changed_callback, + stm); if (r != noErr) { - LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); + LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize " + "rv=%d", + to_string(side), r); return CUBEB_ERROR; } @@ -2329,7 +2356,8 @@ audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, io_side return CUBEB_ERROR; } - LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side), new_size_frames); + LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side), + new_size_frames); return CUBEB_OK; } @@ -2342,18 +2370,16 @@ audiounit_configure_input(cubeb_stream * stm) UInt32 size; AURenderCallbackStruct aurcbs_in; - LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.", + LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in " + "frames %u.", stm, stm->input_stream_params.rate, stm->input_stream_params.channels, stm->input_stream_params.format, stm->latency_frames); /* Get input device sample rate. */ AudioStreamBasicDescription input_hw_desc; size = sizeof(AudioStreamBasicDescription); - r = AudioUnitGetProperty(stm->input_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - AU_IN_BUS, - &input_hw_desc, + r = AudioUnitGetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, AU_IN_BUS, &input_hw_desc, &size); if (r != noErr) { LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r); @@ -2381,11 +2407,8 @@ audiounit_configure_input(cubeb_stream * stm) we will resample inside input callback. */ src_desc.mSampleRate = stm->input_hw_rate; - r = AudioUnitSetProperty(stm->input_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - AU_IN_BUS, - &src_desc, + r = AudioUnitSetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, AU_IN_BUS, &src_desc, sizeof(AudioStreamBasicDescription)); if (r != noErr) { LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r); @@ -2393,14 +2416,13 @@ audiounit_configure_input(cubeb_stream * stm) } /* Frames per buffer in the input callback. */ - r = AudioUnitSetProperty(stm->input_unit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - AU_IN_BUS, - &stm->latency_frames, - sizeof(UInt32)); + r = AudioUnitSetProperty( + stm->input_unit, kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Global, AU_IN_BUS, &stm->latency_frames, sizeof(UInt32)); if (r != noErr) { - LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r); + LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice " + "rv=%d", + r); return CUBEB_ERROR; } @@ -2417,14 +2439,13 @@ audiounit_configure_input(cubeb_stream * stm) aurcbs_in.inputProc = audiounit_input_callback; aurcbs_in.inputProcRefCon = stm; - r = AudioUnitSetProperty(stm->input_unit, - kAudioOutputUnitProperty_SetInputCallback, - kAudioUnitScope_Global, - AU_OUT_BUS, - &aurcbs_in, - sizeof(aurcbs_in)); + r = AudioUnitSetProperty( + stm->input_unit, kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_in, sizeof(aurcbs_in)); if (r != noErr) { - LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback rv=%d", r); + LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback " + "rv=%d", + r); return CUBEB_ERROR; } @@ -2444,8 +2465,8 @@ audiounit_configure_output(cubeb_stream * stm) AURenderCallbackStruct aurcbs_out; UInt32 size; - - LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.", + LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in " + "frames %u.", stm, stm->output_stream_params.rate, stm->output_stream_params.channels, stm->output_stream_params.format, stm->latency_frames); @@ -2459,18 +2480,16 @@ audiounit_configure_output(cubeb_stream * stm) AudioStreamBasicDescription output_hw_desc; size = sizeof(AudioStreamBasicDescription); memset(&output_hw_desc, 0, size); - r = AudioUnitGetProperty(stm->output_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Output, - AU_OUT_BUS, - &output_hw_desc, + r = AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, AU_OUT_BUS, &output_hw_desc, &size); if (r != noErr) { LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r); return CUBEB_ERROR; } stm->output_hw_rate = output_hw_desc.mSampleRate; - LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate); + LOG("(%p) Output device sampling rate: %.2f", stm, + output_hw_desc.mSampleRate); stm->context->channels = output_hw_desc.mChannelsPerFrame; // Set the input layout to match the output device layout. @@ -2486,16 +2505,13 @@ audiounit_configure_output(cubeb_stream * stm) stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) * stm->output_desc.mChannelsPerFrame; stm->output_desc.mBytesPerPacket = - stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket; + stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket; } else { stm->mixer = nullptr; } - r = AudioUnitSetProperty(stm->output_unit, - kAudioUnitProperty_StreamFormat, - kAudioUnitScope_Input, - AU_OUT_BUS, - &stm->output_desc, + r = AudioUnitSetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, AU_OUT_BUS, &stm->output_desc, sizeof(AudioStreamBasicDescription)); if (r != noErr) { LOG("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r); @@ -2509,27 +2525,25 @@ audiounit_configure_output(cubeb_stream * stm) } /* Frames per buffer in the input callback. */ - r = AudioUnitSetProperty(stm->output_unit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - AU_OUT_BUS, - &stm->latency_frames, - sizeof(UInt32)); + r = AudioUnitSetProperty( + stm->output_unit, kAudioUnitProperty_MaximumFramesPerSlice, + kAudioUnitScope_Global, AU_OUT_BUS, &stm->latency_frames, sizeof(UInt32)); if (r != noErr) { - LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r); + LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice " + "rv=%d", + r); return CUBEB_ERROR; } aurcbs_out.inputProc = audiounit_output_callback; aurcbs_out.inputProcRefCon = stm; - r = AudioUnitSetProperty(stm->output_unit, - kAudioUnitProperty_SetRenderCallback, - kAudioUnitScope_Global, - AU_OUT_BUS, - &aurcbs_out, - sizeof(aurcbs_out)); + r = AudioUnitSetProperty( + stm->output_unit, kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_out, sizeof(aurcbs_out)); if (r != noErr) { - LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback rv=%d", r); + LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback " + "rv=%d", + r); return CUBEB_ERROR; } @@ -2589,8 +2603,8 @@ audiounit_setup_stream(cubeb_stream * stm) } } - /* Latency cannot change if another stream is operating in parallel. In this case - * latency is set to the other stream value. */ + /* Latency cannot change if another stream is operating in parallel. In this + * case latency is set to the other stream value. */ if (audiounit_active_streams(stm->context) > 1) { LOG("(%p) More than one active stream, use global latency.", stm); stm->latency_frames = stm->context->global_latency_frames; @@ -2656,7 +2670,7 @@ audiounit_setup_stream(cubeb_stream * stm) return CUBEB_ERROR; } } -#else // TARGET_OS_IPHONE +#else // TARGET_OS_IPHONE //TODO: [[AVAudioSession sharedInstance] inputLatency] // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios #endif @@ -2683,13 +2697,10 @@ audiounit_setup_stream(cubeb_stream * stm) /* Create resampler. Output params are unchanged * because we do not need conversion on the output. */ - 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, - stm->data_callback, - stm->user_ptr, - CUBEB_RESAMPLER_QUALITY_DESKTOP)); + 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, + stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP)); if (!stm->resampler) { LOG("(%p) Could not create resampler.", stm); return CUBEB_ERROR; @@ -2710,20 +2721,25 @@ audiounit_setup_stream(cubeb_stream * stm) return CUBEB_ERROR; } - stm->current_latency_frames = audiounit_get_device_presentation_latency(stm->output_device.id, kAudioDevicePropertyScopeOutput); + stm->current_latency_frames = audiounit_get_device_presentation_latency( + stm->output_device.id, kAudioDevicePropertyScopeOutput); Float64 unit_s; UInt32 size = sizeof(unit_s); - if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, kAudioUnitScope_Global, 0, &unit_s, &size) == noErr) { - stm->current_latency_frames += static_cast(unit_s * stm->output_desc.mSampleRate); + if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency, + kAudioUnitScope_Global, 0, &unit_s, + &size) == noErr) { + stm->current_latency_frames += + static_cast(unit_s * stm->output_desc.mSampleRate); } } if (stm->input_unit && stm->output_unit) { - // According to the I/O hardware rate it is expected a specific pattern of callbacks - // for example is input is 44100 and output is 48000 we expected no more than 2 - // out callback in a row. - stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate); + // According to the I/O hardware rate it is expected a specific pattern of + // callbacks for example is input is 44100 and output is 48000 we expected + // no more than 2 out callback in a row. + stm->expected_output_callbacks_in_a_row = + ceilf(stm->output_hw_rate / stm->input_hw_rate); } r = audiounit_install_device_changed_callback(stm); @@ -2731,39 +2747,35 @@ audiounit_setup_stream(cubeb_stream * stm) LOG("(%p) Could not install all device change callback.", stm); } - return CUBEB_OK; } cubeb_stream::cubeb_stream(cubeb * context) - : context(context) - , resampler(nullptr, cubeb_resampler_destroy) - , mixer(nullptr, cubeb_mixer_destroy) + : context(context), resampler(nullptr, cubeb_resampler_destroy), + mixer(nullptr, cubeb_mixer_destroy) { PodZero(&input_desc, 1); PodZero(&output_desc, 1); } -static void audiounit_stream_destroy_internal(cubeb_stream * stm); +static void +audiounit_stream_destroy_internal(cubeb_stream * stm); static int -audiounit_stream_init(cubeb * context, - cubeb_stream ** stream, - char const * /* stream_name */, - cubeb_devid input_device, +audiounit_stream_init(cubeb * context, cubeb_stream ** stream, + char const * /* stream_name */, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int latency_frames, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + cubeb_state_callback state_callback, void * user_ptr) { assert(context); auto_lock context_lock(context->mutex); audiounit_increment_active_streams(context); - unique_ptr stm(new cubeb_stream(context), - audiounit_stream_destroy_internal); + unique_ptr stm( + new cubeb_stream(context), audiounit_stream_destroy_internal); int r; *stream = NULL; assert(latency_frames > 0); @@ -2781,7 +2793,8 @@ audiounit_stream_init(cubeb * context, } if (input_stream_params) { stm->input_stream_params = *input_stream_params; - r = audiounit_set_device_info(stm.get(), reinterpret_cast(input_device), io_side::INPUT); + r = audiounit_set_device_info( + stm.get(), reinterpret_cast(input_device), io_side::INPUT); if (r != CUBEB_OK) { LOG("(%p) Fail to set device info for input.", stm.get()); return r; @@ -2789,7 +2802,8 @@ audiounit_stream_init(cubeb * context, } if (output_stream_params) { stm->output_stream_params = *output_stream_params; - r = audiounit_set_device_info(stm.get(), reinterpret_cast(output_device), io_side::OUTPUT); + r = audiounit_set_device_info( + stm.get(), reinterpret_cast(output_device), io_side::OUTPUT); if (r != CUBEB_OK) { LOG("(%p) Fail to set device info for output.", stm.get()); return r; @@ -2821,7 +2835,7 @@ audiounit_stream_init(cubeb * context, } static void -audiounit_close_stream(cubeb_stream *stm) +audiounit_close_stream(cubeb_stream * stm) { stm->mutex.assert_current_thread_owns(); @@ -2843,13 +2857,14 @@ audiounit_close_stream(cubeb_stream *stm) stm->mixer.reset(); if (stm->aggregate_device_id != kAudioObjectUnknown) { - audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id); + audiounit_destroy_aggregate_device(stm->plugin_id, + &stm->aggregate_device_id); stm->aggregate_device_id = kAudioObjectUnknown; } } static void -audiounit_stream_destroy_internal(cubeb_stream *stm) +audiounit_stream_destroy_internal(cubeb_stream * stm) { stm->context->mutex.assert_current_thread_owns(); @@ -2880,7 +2895,7 @@ audiounit_stream_destroy(cubeb_stream * stm) LOG("(%p) Could not uninstall all device change listeners", stm); } - if (!stm->shutdown.load()){ + if (!stm->shutdown.load()) { auto_lock context_lock(stm->context->mutex); audiounit_stream_stop_internal(stm); stm->shutdown = true; @@ -2981,7 +2996,7 @@ int audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { #if TARGET_OS_IPHONE - //TODO + // TODO return CUBEB_ERROR_NOT_SUPPORTED; #else *latency = stm->total_output_latency_frames; @@ -2993,10 +3008,8 @@ static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume) { assert(stm->output_unit); - OSStatus r = AudioUnitGetParameter(stm->output_unit, - kHALOutputParam_Volume, - kAudioUnitScope_Global, - 0, volume); + OSStatus r = AudioUnitGetParameter(stm->output_unit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, volume); if (r != noErr) { LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r); return CUBEB_ERROR; @@ -3009,10 +3022,8 @@ audiounit_stream_set_volume(cubeb_stream * stm, float volume) { assert(stm->output_unit); OSStatus r; - r = AudioUnitSetParameter(stm->output_unit, - kHALOutputParam_Volume, - kAudioUnitScope_Global, - 0, volume, 0); + r = AudioUnitSetParameter(stm->output_unit, kHALOutputParam_Volume, + kAudioUnitScope_Global, 0, volume, 0); if (r != noErr) { LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r); @@ -3021,11 +3032,12 @@ audiounit_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } -unique_ptr convert_uint32_into_string(UInt32 data) +unique_ptr +convert_uint32_into_string(UInt32 data) { // Simply create an empty string if no data. - size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32. - auto str = unique_ptr { new char[size + 1] }; // + 1 for '\0'. + size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32. + auto str = unique_ptr{new char[size + 1]}; // + 1 for '\0'. str[size] = '\0'; if (size < 4) { return str; @@ -3039,8 +3051,8 @@ unique_ptr convert_uint32_into_string(UInt32 data) return str; } -int audiounit_get_default_device_datasource(cubeb_device_type type, - UInt32 * data) +int +audiounit_get_default_device_datasource(cubeb_device_type type, UInt32 * data) { AudioDeviceID id = audiounit_get_default_device_id(type); if (id == kAudioObjectUnknown) { @@ -3049,11 +3061,11 @@ int audiounit_get_default_device_datasource(cubeb_device_type type, UInt32 size = sizeof(*data); /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */ - OSStatus r = AudioObjectGetPropertyData(id, - type == CUBEB_DEVICE_TYPE_INPUT ? - &INPUT_DATA_SOURCE_PROPERTY_ADDRESS : - &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS, - 0, NULL, &size, data); + OSStatus r = AudioObjectGetPropertyData( + id, + type == CUBEB_DEVICE_TYPE_INPUT ? &INPUT_DATA_SOURCE_PROPERTY_ADDRESS + : &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS, + 0, NULL, &size, data); if (r != noErr) { *data = 0; } @@ -3061,9 +3073,10 @@ int audiounit_get_default_device_datasource(cubeb_device_type type, return CUBEB_OK; } -int audiounit_get_default_device_name(cubeb_stream * stm, - cubeb_device * const device, - cubeb_device_type type) +int +audiounit_get_default_device_name(cubeb_stream * stm, + cubeb_device * const device, + cubeb_device_type type) { assert(stm); assert(device); @@ -3073,8 +3086,8 @@ int audiounit_get_default_device_name(cubeb_stream * stm, if (r != CUBEB_OK) { return r; } - char ** name = type == CUBEB_DEVICE_TYPE_INPUT ? - &device->input_name : &device->output_name; + char ** name = type == CUBEB_DEVICE_TYPE_INPUT ? &device->input_name + : &device->output_name; *name = convert_uint32_into_string(data).release(); if (!strlen(*name)) { // empty string. LOG("(%p) name of %s device is empty!", stm, @@ -3083,12 +3096,12 @@ int audiounit_get_default_device_name(cubeb_stream * stm, return CUBEB_OK; } - -int audiounit_stream_get_current_device(cubeb_stream * stm, - cubeb_device ** const device) +int +audiounit_stream_get_current_device(cubeb_stream * stm, + cubeb_device ** const device) { #if TARGET_OS_IPHONE - //TODO + // TODO return CUBEB_ERROR_NOT_SUPPORTED; #else *device = new cubeb_device; @@ -3097,14 +3110,13 @@ int audiounit_stream_get_current_device(cubeb_stream * stm, } PodZero(*device, 1); - int r = audiounit_get_default_device_name(stm, *device, - CUBEB_DEVICE_TYPE_OUTPUT); + int r = + audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_OUTPUT); if (r != CUBEB_OK) { return r; } - r = audiounit_get_default_device_name(stm, *device, - CUBEB_DEVICE_TYPE_INPUT); + r = audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_INPUT); if (r != CUBEB_OK) { return r; } @@ -3113,17 +3125,20 @@ int audiounit_stream_get_current_device(cubeb_stream * stm, #endif } -int audiounit_stream_device_destroy(cubeb_stream * /* stream */, - cubeb_device * device) +int +audiounit_stream_device_destroy(cubeb_stream * /* stream */, + cubeb_device * device) { - delete [] device->output_name; - delete [] device->input_name; + delete[] device->output_name; + delete[] device->input_name; delete device; return CUBEB_OK; } -int audiounit_stream_register_device_changed_callback(cubeb_stream * stream, - cubeb_device_changed_callback device_changed_callback) +int +audiounit_stream_register_device_changed_callback( + cubeb_stream * stream, + cubeb_device_changed_callback device_changed_callback) { auto_lock dev_cb_lock(stream->device_changed_callback_lock); /* Note: second register without unregister first causes 'nope' error. @@ -3148,7 +3163,7 @@ audiounit_strref_to_cstr_utf8(CFStringRef strref) ret = new char[size]; if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) { - delete [] ret; + delete[] ret; ret = NULL; } @@ -3158,15 +3173,18 @@ audiounit_strref_to_cstr_utf8(CFStringRef strref) static uint32_t audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope) { - AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress adr = {0, scope, + kAudioObjectPropertyElementMaster}; UInt32 size = 0; uint32_t i, ret = 0; adr.mSelector = kAudioDevicePropertyStreamConfiguration; - if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) { + if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && + size > 0) { AudioBufferList * list = static_cast(alloca(size)); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) { + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == + noErr) { for (i = 0; i < list->mNumberBuffers; i++) ret += list->mBuffers[i].mNumberChannels; } @@ -3176,16 +3194,20 @@ audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope) } static void -audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope, - uint32_t * min, uint32_t * max, uint32_t * def) +audiounit_get_available_samplerate(AudioObjectID devid, + AudioObjectPropertyScope scope, + uint32_t * min, uint32_t * max, + uint32_t * def) { - AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress adr = {0, scope, + kAudioObjectPropertyElementMaster}; adr.mSelector = kAudioDevicePropertyNominalSampleRate; if (AudioObjectHasProperty(devid, &adr)) { UInt32 size = sizeof(Float64); Float64 fvalue = 0.0; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) { + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == + noErr) { *def = fvalue; } } @@ -3199,7 +3221,8 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope vector ranges(count); range.mMinimum = 9999999999.0; range.mMaximum = 0.0; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges.data()) == noErr) { + if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, + ranges.data()) == noErr) { for (uint32_t i = 0; i < count; i++) { if (ranges[i].mMaximum > range.mMaximum) range.mMaximum = ranges[i].mMaximum; @@ -3212,13 +3235,14 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope } else { *min = *max = 0; } - } static UInt32 -audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope) +audiounit_get_device_presentation_latency(AudioObjectID devid, + AudioObjectPropertyScope scope) { - AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress adr = {0, scope, + kAudioObjectPropertyElementMaster}; UInt32 size, dev, stream = 0; AudioStreamID sid[1]; @@ -3240,9 +3264,10 @@ audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectProper } static int -audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type) +audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, + AudioObjectID devid, cubeb_device_type type) { - AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster }; + AudioObjectPropertyAddress adr = {0, 0, kAudioObjectPropertyElementMaster}; UInt32 size; if (type == CUBEB_DEVICE_TYPE_OUTPUT) { @@ -3263,10 +3288,12 @@ audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID d CFStringRef device_id_str = nullptr; size = sizeof(CFStringRef); adr.mSelector = kAudioDevicePropertyDeviceUID; - OSStatus ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str); - if ( ret == noErr && device_id_str != NULL) { + OSStatus ret = + AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str); + if (ret == noErr && device_id_str != NULL) { dev_info->device_id = audiounit_strref_to_cstr_utf8(device_id_str); - static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)), "cubeb_devid can't represent devid"); + static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)), + "cubeb_devid can't represent devid"); dev_info->devid = reinterpret_cast(devid); dev_info->group_id = dev_info->device_id; CFRelease(device_id_str); @@ -3278,7 +3305,8 @@ audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID d adr.mSelector = kAudioDevicePropertyDataSource; ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds); if (ret == noErr) { - AudioValueTranslation trl = { &ds, sizeof(ds), &friendly_name_str, sizeof(CFStringRef) }; + AudioValueTranslation trl = {&ds, sizeof(ds), &friendly_name_str, + sizeof(CFStringRef)}; adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString; size = sizeof(AudioValueTranslation); AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl); @@ -3306,7 +3334,8 @@ audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID d CFStringRef vendor_name_str = nullptr; size = sizeof(CFStringRef); adr.mSelector = kAudioObjectPropertyManufacturer; - ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str); + ret = + AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str); if (ret == noErr && vendor_name_str != NULL) { dev_info->vendor_name = audiounit_strref_to_cstr_utf8(vendor_name_str); CFRelease(vendor_name_str); @@ -3314,15 +3343,18 @@ audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID d dev_info->type = type; dev_info->state = CUBEB_DEVICE_STATE_ENABLED; - dev_info->preferred = (devid == audiounit_get_default_device_id(type)) ? - CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + dev_info->preferred = (devid == audiounit_get_default_device_id(type)) + ? CUBEB_DEVICE_PREF_ALL + : CUBEB_DEVICE_PREF_NONE; dev_info->max_channels = ch; - dev_info->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */ + dev_info->format = + (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */ /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */ dev_info->default_format = CUBEB_DEVICE_FMT_F32NE; - audiounit_get_available_samplerate(devid, adr.mScope, - &dev_info->min_rate, &dev_info->max_rate, &dev_info->default_rate); + audiounit_get_available_samplerate(devid, adr.mScope, &dev_info->min_rate, + &dev_info->max_rate, + &dev_info->default_rate); UInt32 latency = audiounit_get_device_presentation_latency(devid, adr.mScope); @@ -3334,8 +3366,10 @@ audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID d dev_info->latency_lo = latency + range.mMinimum; dev_info->latency_hi = latency + range.mMaximum; } else { - dev_info->latency_lo = 10 * dev_info->default_rate / 1000; /* Default to 10ms */ - dev_info->latency_hi = 100 * dev_info->default_rate / 1000; /* Default to 100ms */ + dev_info->latency_lo = + 10 * dev_info->default_rate / 1000; /* Default to 10ms */ + dev_info->latency_hi = + 100 * dev_info->default_rate / 1000; /* Default to 100ms */ } return CUBEB_OK; @@ -3374,9 +3408,10 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type, collection->count = 0; if (type & CUBEB_DEVICE_TYPE_OUTPUT) { - for (auto dev: output_devs) { + for (auto dev : output_devs) { auto device = &devices[collection->count]; - auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT); + auto err = audiounit_create_device_from_hwdev(device, dev, + CUBEB_DEVICE_TYPE_OUTPUT); if (err != CUBEB_OK || is_aggregate_device(device)) { continue; } @@ -3385,9 +3420,10 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type, } if (type & CUBEB_DEVICE_TYPE_INPUT) { - for (auto dev: input_devs) { + for (auto dev : input_devs) { auto device = &devices[collection->count]; - auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT); + auto err = audiounit_create_device_from_hwdev(device, dev, + CUBEB_DEVICE_TYPE_INPUT); if (err != CUBEB_OK || is_aggregate_device(device)) { continue; } @@ -3398,7 +3434,7 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type, if (collection->count > 0) { collection->device = devices; } else { - delete [] devices; + delete[] devices; collection->device = NULL; } @@ -3408,9 +3444,9 @@ audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type, static void audiounit_device_destroy(cubeb_device_info * device) { - delete [] device->device_id; - delete [] device->friendly_name; - delete [] device->vendor_name; + delete[] device->device_id; + delete[] device->friendly_name; + delete[] device->vendor_name; } static int @@ -3420,7 +3456,7 @@ audiounit_device_collection_destroy(cubeb * /* context */, for (size_t i = 0; i < collection->count; i++) { audiounit_device_destroy(&collection->device[i]); } - delete [] collection->device; + delete[] collection->device; return CUBEB_OK; } @@ -3429,9 +3465,8 @@ static vector audiounit_get_devices_of_type(cubeb_device_type devtype) { UInt32 size = 0; - OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, - &DEVICES_PROPERTY_ADDRESS, 0, - NULL, &size); + OSStatus ret = AudioObjectGetPropertyDataSize( + kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size); if (ret != noErr) { return vector(); } @@ -3447,7 +3482,7 @@ audiounit_get_devices_of_type(cubeb_device_type devtype) for (auto it = devices.begin(); it != devices.end();) { CFStringRef name = get_device_name(*it); if (name && CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location != - kCFNotFound) { + kCFNotFound) { it = devices.erase(it); } else { it++; @@ -3458,17 +3493,16 @@ audiounit_get_devices_of_type(cubeb_device_type devtype) } /* Expected sorted but did not find anything in the docs. */ - sort(devices.begin(), devices.end(), [](AudioObjectID a, AudioObjectID b) { - return a < b; - }); + sort(devices.begin(), devices.end(), + [](AudioObjectID a, AudioObjectID b) { return a < b; }); if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) { return devices; } - AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) ? - kAudioDevicePropertyScopeInput : - kAudioDevicePropertyScopeOutput; + AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) + ? kAudioDevicePropertyScopeInput + : kAudioDevicePropertyScopeOutput; vector devices_in_scope; for (uint32_t i = 0; i < devices.size(); ++i) { @@ -3482,35 +3516,39 @@ audiounit_get_devices_of_type(cubeb_device_type devtype) } static OSStatus -audiounit_collection_changed_callback(AudioObjectID /* inObjectID */, - UInt32 /* inNumberAddresses */, - const AudioObjectPropertyAddress * /* inAddresses */, - void * inClientData) +audiounit_collection_changed_callback( + AudioObjectID /* inObjectID */, UInt32 /* inNumberAddresses */, + const AudioObjectPropertyAddress * /* inAddresses */, void * inClientData) { cubeb * context = static_cast(inClientData); - // This can be called from inside an AudioUnit function, dispatch to another queue. + // This can be called from inside an AudioUnit function, dispatch to another + // queue. dispatch_async(context->serial_queue, ^() { auto_lock lock(context->mutex); if (!context->input_collection_changed_callback && - !context->output_collection_changed_callback) { + !context->output_collection_changed_callback) { /* Listener removed while waiting in mutex, abort. */ return; } if (context->input_collection_changed_callback) { - vector devices = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); + vector devices = + audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); /* Elements in the vector expected sorted. */ if (context->input_device_array != devices) { context->input_device_array = devices; - context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr); + context->input_collection_changed_callback( + context, context->input_collection_changed_user_ptr); } } if (context->output_collection_changed_callback) { - vector devices = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); + vector devices = + audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); /* Elements in the vector expected sorted. */ if (context->output_device_array != devices) { context->output_device_array = devices; - context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr); + context->output_collection_changed_callback( + context, context->output_collection_changed_user_ptr); } } }); @@ -3518,24 +3556,25 @@ audiounit_collection_changed_callback(AudioObjectID /* inObjectID */, } static OSStatus -audiounit_add_device_listener(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) +audiounit_add_device_listener( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback collection_changed_callback, + void * user_ptr) { context->mutex.assert_current_thread_owns(); assert(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)); /* Note: second register without unregister first causes 'nope' error. * Current implementation requires unregister before register a new cb. */ - assert((devtype & CUBEB_DEVICE_TYPE_INPUT) && !context->input_collection_changed_callback || - (devtype & CUBEB_DEVICE_TYPE_OUTPUT) && !context->output_collection_changed_callback); + assert((devtype & CUBEB_DEVICE_TYPE_INPUT) && + !context->input_collection_changed_callback || + (devtype & CUBEB_DEVICE_TYPE_OUTPUT) && + !context->output_collection_changed_callback); if (!context->input_collection_changed_callback && !context->output_collection_changed_callback) { - OSStatus ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, - &DEVICES_PROPERTY_ADDRESS, - audiounit_collection_changed_callback, - context); + OSStatus ret = AudioObjectAddPropertyListener( + kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, + audiounit_collection_changed_callback, context); if (ret != noErr) { return ret; } @@ -3543,14 +3582,16 @@ audiounit_add_device_listener(cubeb * context, if (devtype & CUBEB_DEVICE_TYPE_INPUT) { /* Expected empty after unregister. */ assert(context->input_device_array.empty()); - context->input_device_array = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); + context->input_device_array = + audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); context->input_collection_changed_callback = collection_changed_callback; context->input_collection_changed_user_ptr = user_ptr; } if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) { /* Expected empty after unregister. */ assert(context->output_device_array.empty()); - context->output_device_array = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); + context->output_device_array = + audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); context->output_collection_changed_callback = collection_changed_callback; context->output_collection_changed_user_ptr = user_ptr; } @@ -3578,16 +3619,16 @@ audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype) return noErr; } /* Note: unregister a non registered cb is not a problem, not checking. */ - return AudioObjectRemovePropertyListener(kAudioObjectSystemObject, - &DEVICES_PROPERTY_ADDRESS, - audiounit_collection_changed_callback, - context); + return AudioObjectRemovePropertyListener( + kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, + audiounit_collection_changed_callback, context); } -int audiounit_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) +int +audiounit_register_device_collection_changed( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback collection_changed_callback, + void * user_ptr) { if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) { return CUBEB_ERROR_INVALID_PARAMETER; @@ -3595,10 +3636,8 @@ int audiounit_register_device_collection_changed(cubeb * context, OSStatus ret; auto_lock lock(context->mutex); if (collection_changed_callback) { - ret = audiounit_add_device_listener(context, - devtype, - collection_changed_callback, - user_ptr); + ret = audiounit_add_device_listener(context, devtype, + collection_changed_callback, user_ptr); } else { ret = audiounit_remove_device_listener(context, devtype); } @@ -3606,26 +3645,26 @@ int audiounit_register_device_collection_changed(cubeb * context, } cubeb_ops const audiounit_ops = { - /*.init =*/ audiounit_init, - /*.get_backend_id =*/ audiounit_get_backend_id, - /*.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, - /*.enumerate_devices =*/ audiounit_enumerate_devices, - /*.device_collection_destroy =*/ audiounit_device_collection_destroy, - /*.destroy =*/ audiounit_destroy, - /*.stream_init =*/ audiounit_stream_init, - /*.stream_destroy =*/ audiounit_stream_destroy, - /*.stream_start =*/ audiounit_stream_start, - /*.stream_stop =*/ audiounit_stream_stop, - /*.stream_reset_default_device =*/ nullptr, - /*.stream_get_position =*/ audiounit_stream_get_position, - /*.stream_get_latency =*/ audiounit_stream_get_latency, - /*.stream_get_input_latency =*/ NULL, - /*.stream_set_volume =*/ audiounit_stream_set_volume, - /*.stream_set_name =*/ NULL, - /*.stream_get_current_device =*/ audiounit_stream_get_current_device, - /*.stream_device_destroy =*/ audiounit_stream_device_destroy, - /*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback, - /*.register_device_collection_changed =*/ audiounit_register_device_collection_changed -}; + /*.init =*/audiounit_init, + /*.get_backend_id =*/audiounit_get_backend_id, + /*.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, + /*.enumerate_devices =*/audiounit_enumerate_devices, + /*.device_collection_destroy =*/audiounit_device_collection_destroy, + /*.destroy =*/audiounit_destroy, + /*.stream_init =*/audiounit_stream_init, + /*.stream_destroy =*/audiounit_stream_destroy, + /*.stream_start =*/audiounit_stream_start, + /*.stream_stop =*/audiounit_stream_stop, + /*.stream_get_position =*/audiounit_stream_get_position, + /*.stream_get_latency =*/audiounit_stream_get_latency, + /*.stream_get_input_latency =*/NULL, + /*.stream_set_volume =*/audiounit_stream_set_volume, + /*.stream_set_name =*/NULL, + /*.stream_get_current_device =*/audiounit_stream_get_current_device, + /*.stream_device_destroy =*/audiounit_stream_device_destroy, + /*.stream_register_device_changed_callback =*/ + audiounit_stream_register_device_changed_callback, + /*.register_device_collection_changed =*/ + audiounit_register_device_collection_changed}; diff --git a/externals/cubeb/src/cubeb_jack.cpp b/externals/cubeb/src/cubeb_jack.cpp index 5c1567268..e6d948f1a 100755 --- a/externals/cubeb/src/cubeb_jack.cpp +++ b/externals/cubeb/src/cubeb_jack.cpp @@ -11,50 +11,56 @@ #ifndef __FreeBSD__ #define _POSIX_SOURCE #endif -#include -#include -#include -#include -#include -#include -#include -#include "cubeb/cubeb.h" #include "cubeb-internal.h" +#include "cubeb/cubeb.h" #include "cubeb_resampler.h" #include "cubeb_utils.h" +#include +#include +#include +#include +#include +#include +#include #include #include -#define JACK_API_VISIT(X) \ - X(jack_activate) \ - X(jack_client_close) \ - X(jack_client_open) \ - X(jack_connect) \ - X(jack_free) \ - X(jack_get_ports) \ - X(jack_get_sample_rate) \ - X(jack_get_xrun_delayed_usecs) \ - X(jack_get_buffer_size) \ - X(jack_port_get_buffer) \ - X(jack_port_name) \ - X(jack_port_register) \ - X(jack_port_unregister) \ - X(jack_port_get_latency_range) \ - X(jack_set_process_callback) \ - X(jack_set_xrun_callback) \ - X(jack_set_graph_order_callback) \ - X(jack_set_error_function) \ +#ifdef DISABLE_LIBJACK_DLOPEN +#define WRAP(x) x +#else +#define WRAP(x) (*api_##x) +#define JACK_API_VISIT(X) \ + X(jack_activate) \ + X(jack_client_close) \ + X(jack_client_open) \ + X(jack_connect) \ + X(jack_free) \ + X(jack_get_ports) \ + X(jack_get_sample_rate) \ + X(jack_get_xrun_delayed_usecs) \ + X(jack_get_buffer_size) \ + X(jack_port_get_buffer) \ + X(jack_port_name) \ + X(jack_port_register) \ + X(jack_port_unregister) \ + X(jack_port_get_latency_range) \ + X(jack_set_process_callback) \ + X(jack_set_xrun_callback) \ + X(jack_set_graph_order_callback) \ + X(jack_set_error_function) \ X(jack_set_info_function) #define IMPORT_FUNC(x) static decltype(x) * api_##x; JACK_API_VISIT(IMPORT_FUNC); +#undef IMPORT_FUNC +#endif #define JACK_DEFAULT_IN "JACK capture" #define JACK_DEFAULT_OUT "JACK playback" static const int MAX_STREAMS = 16; -static const int MAX_CHANNELS = 8; +static const int MAX_CHANNELS = 8; static const int FIFO_SIZE = 4096 * sizeof(float); enum devstream { @@ -64,6 +70,12 @@ enum devstream { DUPLEX, }; +enum cbjack_connect_ports_options { + CBJACK_CP_OPTIONS_NONE = 0x0, + CBJACK_CP_OPTIONS_SKIP_OUTPUT = 0x1, + CBJACK_CP_OPTIONS_SKIP_INPUT = 0x2, +}; + static void s16ne_to_float(float * dst, const int16_t * src, size_t n) { @@ -75,71 +87,95 @@ static void float_to_s16ne(int16_t * dst, float * src, size_t n) { for (size_t i = 0; i < n; i++) { - if (*src > 1.f) *src = 1.f; - if (*src < -1.f) *src = -1.f; + if (*src > 1.f) + *src = 1.f; + if (*src < -1.f) + *src = -1.f; *(dst++) = (int16_t)((int16_t)(*(src++) * 32767)); } } -extern "C" -{ -/*static*/ int jack_init (cubeb ** context, char const * context_name); +extern "C" { +/*static*/ int +jack_init(cubeb ** context, char const * context_name); } -static char const * cbjack_get_backend_id(cubeb * context); -static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels); -static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames); -static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames); -static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate); -static void cbjack_destroy(cubeb * context); -static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch); -static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes); -static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes); -static int cbjack_stream_device_destroy(cubeb_stream * stream, - cubeb_device * device); -static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device); -static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection); -static int cbjack_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection); -static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, - cubeb_stream_params * input_stream_params, - cubeb_devid output_device, - cubeb_stream_params * output_stream_params, - unsigned int latency_frames, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr); -static void cbjack_stream_destroy(cubeb_stream * stream); -static int cbjack_stream_start(cubeb_stream * stream); -static int cbjack_stream_stop(cubeb_stream * stream); -static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position); -static int cbjack_stream_set_volume(cubeb_stream * stm, float volume); +static char const * +cbjack_get_backend_id(cubeb * context); +static int +cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels); +static int +cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_frames); +static int +cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames); +static int +cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate); +static void +cbjack_destroy(cubeb * context); +static void +cbjack_interleave_capture(cubeb_stream * stream, float ** in, + jack_nframes_t nframes, bool format_mismatch); +static void +cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, + short ** bufs_in, float ** bufs_out, + jack_nframes_t nframes); +static void +cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, + float ** bufs_in, float ** bufs_out, + jack_nframes_t nframes); +static int +cbjack_stream_device_destroy(cubeb_stream * stream, cubeb_device * device); +static int +cbjack_stream_get_current_device(cubeb_stream * stm, + cubeb_device ** const device); +static int +cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection); +static int +cbjack_device_collection_destroy(cubeb * context, + cubeb_device_collection * collection); +static int +cbjack_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, + cubeb_stream_params * input_stream_params, + cubeb_devid output_device, + cubeb_stream_params * output_stream_params, + unsigned int latency_frames, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr); +static void +cbjack_stream_destroy(cubeb_stream * stream); +static int +cbjack_stream_start(cubeb_stream * stream); +static int +cbjack_stream_stop(cubeb_stream * stream); +static int +cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position); +static int +cbjack_stream_set_volume(cubeb_stream * stm, float volume); static struct cubeb_ops const cbjack_ops = { - .init = jack_init, - .get_backend_id = cbjack_get_backend_id, - .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, - .enumerate_devices = cbjack_enumerate_devices, - .device_collection_destroy = cbjack_device_collection_destroy, - .destroy = cbjack_destroy, - .stream_init = cbjack_stream_init, - .stream_destroy = cbjack_stream_destroy, - .stream_start = cbjack_stream_start, - .stream_stop = cbjack_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = cbjack_stream_get_position, - .stream_get_latency = cbjack_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = cbjack_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = cbjack_stream_get_current_device, - .stream_device_destroy = cbjack_stream_device_destroy, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; + .init = jack_init, + .get_backend_id = cbjack_get_backend_id, + .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, + .enumerate_devices = cbjack_enumerate_devices, + .device_collection_destroy = cbjack_device_collection_destroy, + .destroy = cbjack_destroy, + .stream_init = cbjack_stream_init, + .stream_destroy = cbjack_stream_destroy, + .stream_start = cbjack_stream_start, + .stream_stop = cbjack_stream_stop, + .stream_get_position = cbjack_stream_get_position, + .stream_get_latency = cbjack_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = cbjack_stream_set_volume, + .stream_set_name = NULL, + .stream_get_current_device = cbjack_stream_get_current_device, + .stream_device_destroy = cbjack_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; struct cubeb_stream { /* Note: Must match cubeb_stream layout in cubeb.c. */ @@ -150,7 +186,7 @@ struct cubeb_stream { /**< Mutex for each stream */ pthread_mutex_t mutex; - bool in_use; /**< Set to false iff the stream is free */ + bool in_use; /**< Set to false iff the stream is free */ bool ports_ready; /**< Set to true iff the JACK ports are ready */ cubeb_data_callback data_callback; @@ -205,15 +241,16 @@ struct cubeb { static int load_jack_lib(cubeb * context) { +#ifndef DISABLE_LIBJACK_DLOPEN #ifdef __APPLE__ context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY); context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY); #elif defined(__WIN32__) -# ifdef _WIN64 - context->libjack = LoadLibrary("libjack64.dll"); -# else - context->libjack = LoadLibrary("libjack.dll"); -# endif +#ifdef _WIN64 + context->libjack = LoadLibrary("libjack64.dll"); +#else + context->libjack = LoadLibrary("libjack.dll"); +#endif #else context->libjack = dlopen("libjack.so.0", RTLD_LAZY); if (!context->libjack) { @@ -224,56 +261,59 @@ load_jack_lib(cubeb * context) return CUBEB_ERROR; } -#define LOAD(x) \ - { \ - api_##x = (decltype(x)*)dlsym(context->libjack, #x); \ - if (!api_##x) { \ - dlclose(context->libjack); \ - return CUBEB_ERROR; \ - } \ +#define LOAD(x) \ + { \ + api_##x = (decltype(x) *)dlsym(context->libjack, #x); \ + if (!api_##x) { \ + dlclose(context->libjack); \ + return CUBEB_ERROR; \ + } \ } JACK_API_VISIT(LOAD); #undef LOAD - +#endif return CUBEB_OK; } static void -cbjack_connect_port_out (cubeb_stream * stream, const size_t out_port, const char * const phys_in_port) +cbjack_connect_port_out(cubeb_stream * stream, const size_t out_port, + const char * const phys_in_port) { - const char *src_port = api_jack_port_name (stream->output_ports[out_port]); + const char * src_port = WRAP(jack_port_name)(stream->output_ports[out_port]); - api_jack_connect (stream->context->jack_client, src_port, phys_in_port); + WRAP(jack_connect)(stream->context->jack_client, src_port, phys_in_port); } static void -cbjack_connect_port_in (cubeb_stream * stream, const char * const phys_out_port, size_t in_port) +cbjack_connect_port_in(cubeb_stream * stream, const char * const phys_out_port, + size_t in_port) { - const char *src_port = api_jack_port_name (stream->input_ports[in_port]); + const char * src_port = WRAP(jack_port_name)(stream->input_ports[in_port]); - api_jack_connect (stream->context->jack_client, phys_out_port, src_port); + WRAP(jack_connect)(stream->context->jack_client, phys_out_port, src_port); } static int -cbjack_connect_ports (cubeb_stream * stream) +cbjack_connect_ports(cubeb_stream * stream, + enum cbjack_connect_ports_options options) { int r = CUBEB_ERROR; - const char ** phys_in_ports = api_jack_get_ports (stream->context->jack_client, - NULL, NULL, - JackPortIsInput - | JackPortIsPhysical); - const char ** phys_out_ports = api_jack_get_ports (stream->context->jack_client, - NULL, NULL, - JackPortIsOutput - | JackPortIsPhysical); + const char ** phys_in_ports = + WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL, + JackPortIsInput | JackPortIsPhysical); + const char ** phys_out_ports = + WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL, + JackPortIsOutput | JackPortIsPhysical); - if (phys_in_ports == NULL || *phys_in_ports == NULL) { + if (phys_in_ports == NULL || *phys_in_ports == NULL || + options & CBJACK_CP_OPTIONS_SKIP_OUTPUT) { goto skipplayback; } // Connect outputs to playback - for (unsigned int c = 0; c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) { + for (unsigned int c = 0; + c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) { cbjack_connect_port_out(stream, c, phys_in_ports[c]); } @@ -285,20 +325,22 @@ cbjack_connect_ports (cubeb_stream * stream) r = CUBEB_OK; skipplayback: - if (phys_out_ports == NULL || *phys_out_ports == NULL) { + if (phys_out_ports == NULL || *phys_out_ports == NULL || + options & CBJACK_CP_OPTIONS_SKIP_INPUT) { goto end; } // Connect inputs to capture - for (unsigned int c = 0; c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) { + for (unsigned int c = 0; + c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) { cbjack_connect_port_in(stream, phys_out_ports[c], c); } r = CUBEB_OK; end: if (phys_out_ports) { - api_jack_free(phys_out_ports); + WRAP(jack_free)(phys_out_ports); } if (phys_in_ports) { - api_jack_free(phys_in_ports); + WRAP(jack_free)(phys_in_ports); } return r; } @@ -308,8 +350,9 @@ cbjack_xrun_callback(void * arg) { cubeb * ctx = (cubeb *)arg; - float delay = api_jack_get_xrun_delayed_usecs(ctx->jack_client); - float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) / ctx->jack_buffer_size); + float delay = WRAP(jack_get_xrun_delayed_usecs)(ctx->jack_client); + float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) / + ctx->jack_buffer_size); ctx->jack_xruns += (unsigned int)fragments; return 0; @@ -324,7 +367,7 @@ cbjack_graph_order_callback(void * arg) jack_nframes_t port_latency, max_latency = 0; for (int j = 0; j < MAX_STREAMS; j++) { - cubeb_stream *stm = &ctx->streams[j]; + cubeb_stream * stm = &ctx->streams[j]; if (!stm->in_use) continue; @@ -332,10 +375,11 @@ cbjack_graph_order_callback(void * arg) continue; for (i = 0; i < (int)stm->out_params.channels; ++i) { - api_jack_port_get_latency_range(stm->output_ports[i], JackPlaybackLatency, &latency_range); + WRAP(jack_port_get_latency_range) + (stm->output_ports[i], JackPlaybackLatency, &latency_range); port_latency = latency_range.max; if (port_latency > max_latency) - max_latency = port_latency; + max_latency = port_latency; } /* Cap minimum latency to 128 frames */ if (max_latency < 128) @@ -357,9 +401,9 @@ cbjack_process(jack_nframes_t nframes, void * arg) ctx->jack_xruns = 0; for (int j = 0; j < MAX_STREAMS; j++) { - cubeb_stream *stm = &ctx->streams[j]; - float *bufs_out[stm->out_params.channels]; - float *bufs_in[stm->in_params.channels]; + cubeb_stream * stm = &ctx->streams[j]; + float * bufs_out[stm->out_params.channels]; + float * bufs_in[stm->in_params.channels]; if (!stm->in_use) continue; @@ -373,18 +417,20 @@ cbjack_process(jack_nframes_t nframes, void * arg) if (stm->devs & OUT_ONLY) { // get jack output buffers for (i = 0; i < (int)stm->out_params.channels; i++) - bufs_out[i] = (float*)api_jack_port_get_buffer(stm->output_ports[i], nframes); + bufs_out[i] = + (float *)WRAP(jack_port_get_buffer)(stm->output_ports[i], nframes); } if (stm->devs & IN_ONLY) { // get jack input buffers for (i = 0; i < (int)stm->in_params.channels; i++) - bufs_in[i] = (float*)api_jack_port_get_buffer(stm->input_ports[i], nframes); + bufs_in[i] = + (float *)WRAP(jack_port_get_buffer)(stm->input_ports[i], nframes); } if (stm->pause) { // paused, play silence on output if (stm->devs & OUT_ONLY) { for (unsigned int c = 0; c < stm->out_params.channels; c++) { - float* buffer_out = bufs_out[c]; + float * buffer_out = bufs_out[c]; for (long f = 0; f < nframes; f++) { buffer_out[f] = 0.f; } @@ -393,7 +439,7 @@ cbjack_process(jack_nframes_t nframes, void * arg) if (stm->devs & IN_ONLY) { // paused, capture silence for (unsigned int c = 0; c < stm->in_params.channels; c++) { - float* buffer_in = bufs_in[c]; + float * buffer_in = bufs_in[c]; for (long f = 0; f < nframes; f++) { buffer_in[f] = 0.f; } @@ -404,31 +450,38 @@ cbjack_process(jack_nframes_t nframes, void * arg) // try to lock stream mutex if (pthread_mutex_trylock(&stm->mutex) == 0) { - int16_t *in_s16ne = stm->context->in_resampled_interleaved_buffer_s16ne; - float *in_float = stm->context->in_resampled_interleaved_buffer_float; + int16_t * in_s16ne = + stm->context->in_resampled_interleaved_buffer_s16ne; + float * in_float = stm->context->in_resampled_interleaved_buffer_float; // unpaused, play audio if (stm->devs == DUPLEX) { if (stm->out_params.format == CUBEB_SAMPLE_S16NE) { cbjack_interleave_capture(stm, bufs_in, nframes, true); - cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out, nframes); + cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out, + nframes); } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { cbjack_interleave_capture(stm, bufs_in, nframes, false); - cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out, nframes); + cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out, + nframes); } } else if (stm->devs == IN_ONLY) { if (stm->in_params.format == CUBEB_SAMPLE_S16NE) { cbjack_interleave_capture(stm, bufs_in, nframes, true); - cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr, nframes); + cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr, + nframes); } else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) { cbjack_interleave_capture(stm, bufs_in, nframes, false); - cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr, nframes); + cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr, + nframes); } } else if (stm->devs == OUT_ONLY) { if (stm->out_params.format == CUBEB_SAMPLE_S16NE) { - cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out, nframes); + cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out, + nframes); } else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { - cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, nframes); + cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, + nframes); } } // unlock stream mutex @@ -439,7 +492,7 @@ cbjack_process(jack_nframes_t nframes, void * arg) // output silence if (stm->devs & OUT_ONLY) { for (unsigned int c = 0; c < stm->out_params.channels; c++) { - float* buffer_out = bufs_out[c]; + float * buffer_out = bufs_out[c]; for (long f = 0; f < nframes; f++) { buffer_out[f] = 0.f; } @@ -448,7 +501,7 @@ cbjack_process(jack_nframes_t nframes, void * arg) if (stm->devs & IN_ONLY) { // capture silence for (unsigned int c = 0; c < stm->in_params.channels; c++) { - float* buffer_in = bufs_in[c]; + float * buffer_in = bufs_in[c]; for (long f = 0; f < nframes; f++) { buffer_in[f] = 0.f; } @@ -461,7 +514,9 @@ cbjack_process(jack_nframes_t nframes, void * arg) } static void -cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes) +cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, + float ** bufs_out, + jack_nframes_t nframes) { float * out_interleaved_buffer = nullptr; @@ -472,20 +527,24 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl long done_frames = 0; long input_frames_count = (in != NULL) ? nframes : 0; - done_frames = cubeb_resampler_fill(stream->resampler, - inptr, - &input_frames_count, - (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_float : NULL, - needed_frames); + done_frames = cubeb_resampler_fill( + stream->resampler, inptr, &input_frames_count, + (bufs_out != NULL) + ? stream->context->out_resampled_interleaved_buffer_float + : NULL, + needed_frames); - out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float; + out_interleaved_buffer = + stream->context->out_resampled_interleaved_buffer_float; if (outptr) { // convert interleaved output buffers to contiguous buffers for (unsigned int c = 0; c < stream->out_params.channels; c++) { - float* buffer = bufs_out[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; + buffer[f] = + out_interleaved_buffer[(f * stream->out_params.channels) + c] * + stream->volume; } if (done_frames < needed_frames) { // draining @@ -519,7 +578,9 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl } static void -cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes) +cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, + float ** bufs_out, + jack_nframes_t nframes) { float * out_interleaved_buffer = nullptr; @@ -530,22 +591,28 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl long done_frames = 0; long input_frames_count = (in != NULL) ? nframes : 0; - done_frames = cubeb_resampler_fill(stream->resampler, - inptr, - &input_frames_count, - (bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_s16ne : NULL, - needed_frames); + done_frames = cubeb_resampler_fill( + stream->resampler, inptr, &input_frames_count, + (bufs_out != NULL) + ? stream->context->out_resampled_interleaved_buffer_s16ne + : NULL, + needed_frames); - s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, stream->context->out_resampled_interleaved_buffer_s16ne, done_frames * stream->out_params.channels); + s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, + stream->context->out_resampled_interleaved_buffer_s16ne, + done_frames * stream->out_params.channels); - out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float; + out_interleaved_buffer = + stream->context->out_resampled_interleaved_buffer_float; if (outptr) { // convert interleaved output buffers to contiguous buffers for (unsigned int c = 0; c < stream->out_params.channels; c++) { - float* buffer = bufs_out[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; + buffer[f] = + out_interleaved_buffer[(f * stream->out_params.channels) + c] * + stream->volume; } if (done_frames < needed_frames) { // draining @@ -579,20 +646,25 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl } static void -cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch) +cbjack_interleave_capture(cubeb_stream * stream, float ** in, + jack_nframes_t nframes, bool format_mismatch) { - float *in_buffer = stream->context->in_float_interleaved_buffer; + float * in_buffer = stream->context->in_float_interleaved_buffer; for (unsigned int c = 0; c < stream->in_params.channels; c++) { for (long f = 0; f < nframes; f++) { - in_buffer[(f * stream->in_params.channels) + c] = in[c][f] * stream->volume; + in_buffer[(f * stream->in_params.channels) + c] = + in[c][f] * stream->volume; } } if (format_mismatch) { - float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne, in_buffer, nframes * stream->in_params.channels); + float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne, + in_buffer, nframes * stream->in_params.channels); } else { - memset(stream->context->in_resampled_interleaved_buffer_float, 0, (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float)); - memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer, (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float)); + memset(stream->context->in_resampled_interleaved_buffer_float, 0, + (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float)); + memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer, + (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float)); } } @@ -602,7 +674,7 @@ silent_jack_error_callback(char const * /*msg*/) } /*static*/ int -jack_init (cubeb ** context, char const * context_name) +jack_init(cubeb ** context, char const * context_name) { int r; @@ -619,8 +691,8 @@ jack_init (cubeb ** context, char const * context_name) return CUBEB_ERROR; } - api_jack_set_error_function(silent_jack_error_callback); - api_jack_set_info_function(silent_jack_error_callback); + WRAP(jack_set_error_function)(silent_jack_error_callback); + WRAP(jack_set_info_function)(silent_jack_error_callback); ctx->ops = &cbjack_ops; @@ -633,9 +705,8 @@ jack_init (cubeb ** context, char const * context_name) if (context_name) jack_client_name = context_name; - ctx->jack_client = api_jack_client_open(jack_client_name, - JackNoStartServer, - NULL); + ctx->jack_client = + WRAP(jack_client_open)(jack_client_name, JackNoStartServer, NULL); if (ctx->jack_client == NULL) { cbjack_destroy(ctx); @@ -644,16 +715,17 @@ jack_init (cubeb ** context, char const * context_name) ctx->jack_xruns = 0; - api_jack_set_process_callback (ctx->jack_client, cbjack_process, ctx); - api_jack_set_xrun_callback (ctx->jack_client, cbjack_xrun_callback, ctx); - api_jack_set_graph_order_callback (ctx->jack_client, cbjack_graph_order_callback, ctx); + WRAP(jack_set_process_callback)(ctx->jack_client, cbjack_process, ctx); + WRAP(jack_set_xrun_callback)(ctx->jack_client, cbjack_xrun_callback, ctx); + WRAP(jack_set_graph_order_callback) + (ctx->jack_client, cbjack_graph_order_callback, ctx); - if (api_jack_activate (ctx->jack_client)) { + if (WRAP(jack_activate)(ctx->jack_client)) { cbjack_destroy(ctx); return CUBEB_ERROR; } - ctx->jack_sample_rate = api_jack_get_sample_rate(ctx->jack_client); + ctx->jack_sample_rate = WRAP(jack_get_sample_rate)(ctx->jack_client); ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate; ctx->active = true; @@ -683,7 +755,8 @@ cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms) } static int -cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, uint32_t * latency_ms) +cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, + uint32_t * latency_ms) { *latency_ms = ctx->jack_latency; return CUBEB_OK; @@ -693,18 +766,17 @@ static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { if (!ctx->jack_client) { - jack_client_t * testclient = api_jack_client_open("test-samplerate", - JackNoStartServer, - NULL); + jack_client_t * testclient = + WRAP(jack_client_open)("test-samplerate", JackNoStartServer, NULL); if (!testclient) { return CUBEB_ERROR; } - *rate = api_jack_get_sample_rate(testclient); - api_jack_client_close(testclient); + *rate = WRAP(jack_get_sample_rate)(testclient); + WRAP(jack_client_close)(testclient); } else { - *rate = api_jack_get_sample_rate(ctx->jack_client); + *rate = WRAP(jack_get_sample_rate)(ctx->jack_client); } return CUBEB_OK; } @@ -715,7 +787,7 @@ cbjack_destroy(cubeb * context) context->active = false; if (context->jack_client != NULL) - api_jack_client_close (context->jack_client); + WRAP(jack_client_close)(context->jack_client); if (context->libjack) dlclose(context->libjack); @@ -738,30 +810,27 @@ context_alloc_stream(cubeb * context, char const * stream_name) } static int -cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, +cbjack_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int /*latency_frames*/, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + cubeb_state_callback state_callback, void * user_ptr) { int stream_actual_rate = 0; - int jack_rate = api_jack_get_sample_rate(context->jack_client); + int jack_rate = WRAP(jack_get_sample_rate)(context->jack_client); - if (output_stream_params - && (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && - output_stream_params->format != CUBEB_SAMPLE_S16NE) - ) { + if (output_stream_params && + (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && + output_stream_params->format != CUBEB_SAMPLE_S16NE)) { return CUBEB_ERROR_INVALID_FORMAT; } - if (input_stream_params - && (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && - input_stream_params->format != CUBEB_SAMPLE_S16NE) - ) { + if (input_stream_params && + (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && + input_stream_params->format != CUBEB_SAMPLE_S16NE)) { return CUBEB_ERROR_INVALID_FORMAT; } @@ -771,8 +840,10 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ } // Loopback is unsupported - if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) || - (output_stream_params && (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) { + if ((input_stream_params && + (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) || + (output_stream_params && + (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) { return CUBEB_ERROR_NOT_SUPPORTED; } @@ -841,7 +912,7 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ stm->state_callback = state_callback; stm->position = 0; stm->volume = 1.0f; - context->jack_buffer_size = api_jack_get_buffer_size(context->jack_client); + context->jack_buffer_size = WRAP(jack_get_buffer_size)(context->jack_client); context->fragment_size = context->jack_buffer_size; if (stm->devs == NONE) { @@ -852,29 +923,17 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ stm->resampler = NULL; if (stm->devs == DUPLEX) { - stm->resampler = cubeb_resampler_create(stm, - &stm->in_params, - &stm->out_params, - stream_actual_rate, - stm->data_callback, - stm->user_ptr, - CUBEB_RESAMPLER_QUALITY_DESKTOP); + stm->resampler = cubeb_resampler_create( + stm, &stm->in_params, &stm->out_params, stream_actual_rate, + stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP); } else if (stm->devs == IN_ONLY) { - stm->resampler = cubeb_resampler_create(stm, - &stm->in_params, - nullptr, - stream_actual_rate, - stm->data_callback, - stm->user_ptr, - CUBEB_RESAMPLER_QUALITY_DESKTOP); + stm->resampler = cubeb_resampler_create( + stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback, + stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP); } else if (stm->devs == OUT_ONLY) { - stm->resampler = cubeb_resampler_create(stm, - nullptr, - &stm->out_params, - stream_actual_rate, - stm->data_callback, - stm->user_ptr, - CUBEB_RESAMPLER_QUALITY_DESKTOP); + stm->resampler = cubeb_resampler_create( + stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback, + stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP); } if (!stm->resampler) { @@ -887,11 +946,18 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ for (unsigned int c = 0; c < stm->out_params.channels; c++) { char portname[256]; snprintf(portname, 255, "%s_out_%d", stm->stream_name, c); - stm->output_ports[c] = api_jack_port_register(stm->context->jack_client, - portname, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsOutput, - 0); + stm->output_ports[c] = WRAP(jack_port_register)( + stm->context->jack_client, portname, JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + if (!(output_stream_params->prefs & + CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) { + if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_INPUT) != + CUBEB_OK) { + pthread_mutex_unlock(&stm->mutex); + cbjack_stream_destroy(stm); + return CUBEB_ERROR; + } + } } } @@ -899,19 +965,18 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_ for (unsigned int c = 0; c < stm->in_params.channels; c++) { char portname[256]; snprintf(portname, 255, "%s_in_%d", stm->stream_name, c); - stm->input_ports[c] = api_jack_port_register(stm->context->jack_client, - portname, - JACK_DEFAULT_AUDIO_TYPE, - JackPortIsInput, - 0); - } - } - - if (!input_stream_params->prefs & CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT) { - if (cbjack_connect_ports(stm) != CUBEB_OK) { - pthread_mutex_unlock(&stm->mutex); - cbjack_stream_destroy(stm); - return CUBEB_ERROR; + stm->input_ports[c] = + WRAP(jack_port_register)(stm->context->jack_client, portname, + JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + if (!(input_stream_params->prefs & + CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) { + if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_OUTPUT) != + CUBEB_OK) { + pthread_mutex_unlock(&stm->mutex); + cbjack_stream_destroy(stm); + return CUBEB_ERROR; + } + } } } @@ -933,7 +998,8 @@ cbjack_stream_destroy(cubeb_stream * stream) if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) { for (unsigned int c = 0; c < stream->out_params.channels; c++) { if (stream->output_ports[c]) { - api_jack_port_unregister (stream->context->jack_client, stream->output_ports[c]); + WRAP(jack_port_unregister) + (stream->context->jack_client, stream->output_ports[c]); stream->output_ports[c] = NULL; } } @@ -942,7 +1008,8 @@ cbjack_stream_destroy(cubeb_stream * stream) if (stream->devs == DUPLEX || stream->devs == IN_ONLY) { for (unsigned int c = 0; c < stream->in_params.channels; c++) { if (stream->input_ports[c]) { - api_jack_port_unregister (stream->context->jack_client, stream->input_ports[c]); + WRAP(jack_port_unregister) + (stream->context->jack_client, stream->input_ports[c]); stream->input_ports[c] = NULL; } } @@ -987,7 +1054,8 @@ cbjack_stream_set_volume(cubeb_stream * stm, float volume) } static int -cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device) +cbjack_stream_get_current_device(cubeb_stream * stm, + cubeb_device ** const device) { *device = (cubeb_device *)calloc(1, sizeof(cubeb_device)); if (*device == NULL) @@ -1012,8 +1080,7 @@ cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const devic } static int -cbjack_stream_device_destroy(cubeb_stream * /*stream*/, - cubeb_device * device) +cbjack_stream_device_destroy(cubeb_stream * /*stream*/, cubeb_device * device) { if (device->input_name) free(device->input_name); @@ -1042,7 +1109,7 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, if (type & CUBEB_DEVICE_TYPE_OUTPUT) { cubeb_device_info * cur = &devices[collection->count]; cur->device_id = JACK_DEFAULT_OUT; - cur->devid = (cubeb_devid) cur->device_id; + cur->devid = (cubeb_devid)cur->device_id; cur->friendly_name = JACK_DEFAULT_OUT; cur->group_id = JACK_DEFAULT_OUT; cur->vendor_name = JACK_DEFAULT_OUT; @@ -1057,13 +1124,13 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, cur->default_rate = rate; cur->latency_lo = 0; cur->latency_hi = 0; - collection->count +=1 ; + collection->count += 1; } if (type & CUBEB_DEVICE_TYPE_INPUT) { cubeb_device_info * cur = &devices[collection->count]; cur->device_id = JACK_DEFAULT_IN; - cur->devid = (cubeb_devid) cur->device_id; + cur->devid = (cubeb_devid)cur->device_id; cur->friendly_name = JACK_DEFAULT_IN; cur->group_id = JACK_DEFAULT_IN; cur->vendor_name = JACK_DEFAULT_IN; @@ -1091,6 +1158,6 @@ cbjack_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * collection) { XASSERT(collection); - delete [] collection->device; + delete[] collection->device; return CUBEB_OK; } diff --git a/externals/cubeb/src/cubeb_kai.c b/externals/cubeb/src/cubeb_kai.c index 36eaf126a..0a0d67661 100755 --- a/externals/cubeb/src/cubeb_kai.c +++ b/externals/cubeb/src/cubeb_kai.c @@ -4,15 +4,15 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ +#include #include #include -#include #include #include -#include "cubeb/cubeb.h" #include "cubeb-internal.h" +#include "cubeb/cubeb.h" /* We don't support more than 2 channels in KAI */ #define MAX_CHANNELS 2 @@ -59,7 +59,8 @@ bytes_to_frames(long bytes, cubeb_stream_params params) return bytes / 2 / params.channels; /* 2 bytes per frame */ } -static void kai_destroy(cubeb * ctx); +static void +kai_destroy(cubeb * ctx); /*static*/ int kai_init(cubeb ** context, char const * context_name) @@ -97,7 +98,7 @@ kai_destroy(cubeb * ctx) } static void -float_to_s16ne(int16_t *dst, float *src, size_t n) +float_to_s16ne(int16_t * dst, float * src, size_t n) { long l; @@ -115,14 +116,13 @@ static ULONG APIENTRY kai_callback(PVOID cbdata, PVOID buffer, ULONG len) { cubeb_stream * stm = cbdata; - void *p; + void * p; long wanted_frames; long frames; float soft_volume; int elements = len / sizeof(int16_t); - p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE - ? stm->float_buffer : buffer; + p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE ? stm->float_buffer : buffer; wanted_frames = bytes_to_frames(len, stm->params); frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames); @@ -139,7 +139,7 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len) float_to_s16ne(buffer, p, elements); if (soft_volume != -1.0f) { - int16_t *b = buffer; + int16_t * b = buffer; int i; for (i = 0; i < elements; i++) @@ -149,12 +149,12 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len) return frames_to_bytes(frames, stm->params); } -static void kai_stream_destroy(cubeb_stream * stm); +static void +kai_stream_destroy(cubeb_stream * stm); static int kai_stream_init(cubeb * context, cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, @@ -202,17 +202,17 @@ kai_stream_init(cubeb * context, cubeb_stream ** stream, return CUBEB_ERROR; } - wanted_spec.usDeviceIndex = 0; - wanted_spec.ulType = KAIT_PLAY; + wanted_spec.usDeviceIndex = 0; + wanted_spec.ulType = KAIT_PLAY; wanted_spec.ulBitsPerSample = BPS_16; - wanted_spec.ulSamplingRate = stm->params.rate; - wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM; - wanted_spec.ulChannels = stm->params.channels; - wanted_spec.ulNumBuffers = NBUFS; - wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, stm->params); - wanted_spec.fShareable = TRUE; - wanted_spec.pfnCallBack = kai_callback; - wanted_spec.pCallBackData = stm; + wanted_spec.ulSamplingRate = stm->params.rate; + wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM; + wanted_spec.ulChannels = stm->params.channels; + wanted_spec.ulNumBuffers = NBUFS; + wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, stm->params); + wanted_spec.fShareable = TRUE; + wanted_spec.pfnCallBack = kai_callback; + wanted_spec.pCallBackData = stm; if (kaiOpen(&wanted_spec, &stm->spec, &stm->hkai)) { _fmutex_close(&stm->mutex); @@ -265,17 +265,17 @@ kai_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) params.rate = 48000; params.channels = 2; - wanted_spec.usDeviceIndex = 0; - wanted_spec.ulType = KAIT_PLAY; + wanted_spec.usDeviceIndex = 0; + wanted_spec.ulType = KAIT_PLAY; wanted_spec.ulBitsPerSample = BPS_16; - wanted_spec.ulSamplingRate = params.rate; - wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM; - wanted_spec.ulChannels = params.channels; - wanted_spec.ulNumBuffers = NBUFS; - wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, params); - wanted_spec.fShareable = TRUE; - wanted_spec.pfnCallBack = kai_callback; - wanted_spec.pCallBackData = NULL; + wanted_spec.ulSamplingRate = params.rate; + wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM; + wanted_spec.ulChannels = params.channels; + wanted_spec.ulNumBuffers = NBUFS; + wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, params); + wanted_spec.fShareable = TRUE; + wanted_spec.pfnCallBack = kai_callback; + wanted_spec.pCallBackData = NULL; /* Test 48KHz */ if (kaiOpen(&wanted_spec, &spec, &hkai)) { @@ -328,8 +328,8 @@ kai_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { /* Out of buffers, one is being played, the others are being filled. So there is as much latency as total buffers - 1. */ - *latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params) - * (stm->spec.ulNumBuffers - 1); + *latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params) * + (stm->spec.ulNumBuffers - 1); return CUBEB_OK; } @@ -345,27 +345,25 @@ kai_stream_set_volume(cubeb_stream * stm, float volume) } static struct cubeb_ops const kai_ops = { - /*.init =*/ kai_init, - /*.get_backend_id =*/ kai_get_backend_id, - /*.get_max_channel_count=*/ kai_get_max_channel_count, - /*.get_min_latency=*/ kai_get_min_latency, - /*.get_preferred_sample_rate =*/ kai_get_preferred_sample_rate, - /*.get_preferred_channel_layout =*/ NULL, - /*.enumerate_devices =*/ NULL, - /*.device_collection_destroy =*/ NULL, - /*.destroy =*/ kai_destroy, - /*.stream_init =*/ kai_stream_init, - /*.stream_destroy =*/ kai_stream_destroy, - /*.stream_start =*/ kai_stream_start, - /*.stream_stop =*/ kai_stream_stop, - /*.stream_reset_default_device =*/ NULL, - /*.stream_get_position =*/ kai_stream_get_position, - /*.stream_get_latency = */ kai_stream_get_latency, - /*.stream_get_input_latency = */ NULL, - /*.stream_set_volume =*/ kai_stream_set_volume, - /*.stream_set_name =*/ NULL, - /*.stream_get_current_device =*/ NULL, - /*.stream_device_destroy =*/ NULL, - /*.stream_register_device_changed_callback=*/ NULL, - /*.register_device_collection_changed=*/ NULL -}; + /*.init =*/kai_init, + /*.get_backend_id =*/kai_get_backend_id, + /*.get_max_channel_count=*/kai_get_max_channel_count, + /*.get_min_latency=*/kai_get_min_latency, + /*.get_preferred_sample_rate =*/kai_get_preferred_sample_rate, + /*.get_preferred_channel_layout =*/NULL, + /*.enumerate_devices =*/NULL, + /*.device_collection_destroy =*/NULL, + /*.destroy =*/kai_destroy, + /*.stream_init =*/kai_stream_init, + /*.stream_destroy =*/kai_stream_destroy, + /*.stream_start =*/kai_stream_start, + /*.stream_stop =*/kai_stream_stop, + /*.stream_get_position =*/kai_stream_get_position, + /*.stream_get_latency = */ kai_stream_get_latency, + /*.stream_get_input_latency = */ NULL, + /*.stream_set_volume =*/kai_stream_set_volume, + /*.stream_set_name =*/NULL, + /*.stream_get_current_device =*/NULL, + /*.stream_device_destroy =*/NULL, + /*.stream_register_device_changed_callback=*/NULL, + /*.register_device_collection_changed=*/NULL}; diff --git a/externals/cubeb/src/cubeb_log.cpp b/externals/cubeb/src/cubeb_log.cpp index 54c7f4a15..ff72e0e87 100755 --- a/externals/cubeb/src/cubeb_log.cpp +++ b/externals/cubeb/src/cubeb_log.cpp @@ -27,17 +27,13 @@ const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40; #define CUBEB_LOG_BATCH_PRINT_INTERVAL_MS 10 /** - * This wraps an inline buffer, that represents a log message, that must be - * null-terminated. - * This class should not use system calls or other potentially blocking code. - */ -class cubeb_log_message -{ + * This wraps an inline buffer, that represents a log message, that must be + * null-terminated. + * This class should not use system calls or other potentially blocking code. + */ +class cubeb_log_message { public: - cubeb_log_message() - { - *storage = '\0'; - } + cubeb_log_message() { *storage = '\0'; } cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE]) { size_t length = strlen(str); @@ -49,20 +45,19 @@ public: PodCopy(storage, str, length); storage[length] = '\0'; } - char const * get() { - return storage; - } + char const * get() { return storage; } + private: char storage[CUBEB_LOG_MESSAGE_MAX_SIZE]; }; /** Lock-free asynchronous logger, made so that logging from a * real-time audio callback does not block the audio thread. */ -class cubeb_async_logger -{ +class cubeb_async_logger { public: /* This is thread-safe since C++11 */ - static cubeb_async_logger & get() { + static cubeb_async_logger & get() + { static cubeb_async_logger instance; return instance; } @@ -85,8 +80,7 @@ public: timespec sleep_duration = sleep_for; timespec remainder; do { - if (nanosleep(&sleep_duration, &remainder) == 0 || - errno != EINTR) { + if (nanosleep(&sleep_duration, &remainder) == 0 || errno != EINTR) { break; } sleep_duration = remainder; @@ -97,29 +91,22 @@ public: } // Tell the underlying queue the producer thread has changed, so it does not // assert in debug. This should be called with the thread stopped. - void reset_producer_thread() - { - msg_queue.reset_thread_ids(); - } + void reset_producer_thread() { msg_queue.reset_thread_ids(); } + private: #ifndef _WIN32 const struct timespec sleep_for = { - CUBEB_LOG_BATCH_PRINT_INTERVAL_MS/1000, - (CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000 - }; + CUBEB_LOG_BATCH_PRINT_INTERVAL_MS / 1000, + (CUBEB_LOG_BATCH_PRINT_INTERVAL_MS % 1000) * 1000 * 1000}; #endif - cubeb_async_logger() - : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH) - { - run(); - } + cubeb_async_logger() : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH) { run(); } /** This is quite a big data structure, but is only instantiated if the * asynchronous logger is used.*/ lock_free_queue msg_queue; }; - -void cubeb_async_log(char const * fmt, ...) +void +cubeb_async_log(char const * fmt, ...) { if (!g_cubeb_log_callback) { return; @@ -135,7 +122,8 @@ void cubeb_async_log(char const * fmt, ...) va_end(args); } -void cubeb_async_log_reset_threads() +void +cubeb_async_log_reset_threads() { if (!g_cubeb_log_callback) { return; diff --git a/externals/cubeb/src/cubeb_log.h b/externals/cubeb/src/cubeb_log.h index 446e29a5c..aee31805e 100755 --- a/externals/cubeb/src/cubeb_log.h +++ b/externals/cubeb/src/cubeb_log.h @@ -19,18 +19,23 @@ extern "C" { #if defined(__FILE_NAME__) #define __FILENAME__ __FILE_NAME__ #else -#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__) +#define __FILENAME__ \ + (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \ + : __FILE__) #endif #else #define PRINTF_FORMAT(fmt, args) #include -#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#define __FILENAME__ \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) #endif extern cubeb_log_level g_cubeb_log_level; extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2); -void cubeb_async_log(const char * fmt, ...); -void cubeb_async_log_reset_threads(); +void +cubeb_async_log(const char * fmt, ...); +void +cubeb_async_log_reset_threads(); #ifdef __cplusplus } @@ -39,16 +44,19 @@ void cubeb_async_log_reset_threads(); #define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__) #define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__) -#define LOG_INTERNAL(level, fmt, ...) do { \ - if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \ - g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, ##__VA_ARGS__); \ - } \ - } while(0) +#define LOG_INTERNAL(level, fmt, ...) \ + do { \ + if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \ + g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, \ + ##__VA_ARGS__); \ + } \ + } while (0) /* Asynchronous verbose logging, to log in real-time callbacks. */ -#define ALOGV(fmt, ...) \ -do { \ - cubeb_async_log(fmt, ##__VA_ARGS__); \ -} while(0) +/* Should not be used on android due to the use of global/static variables. */ +#define ALOGV(fmt, ...) \ + do { \ + cubeb_async_log(fmt, ##__VA_ARGS__); \ + } while (0) #endif // CUBEB_LOG diff --git a/externals/cubeb/src/cubeb_mixer.cpp b/externals/cubeb/src/cubeb_mixer.cpp index 2ab7f673a..74bab7139 100755 --- a/externals/cubeb/src/cubeb_mixer.cpp +++ b/externals/cubeb/src/cubeb_mixer.cpp @@ -9,6 +9,9 @@ #define NOMINMAX +#include "cubeb_mixer.h" +#include "cubeb-internal.h" +#include "cubeb_utils.h" #include #include #include @@ -16,67 +19,67 @@ #include #include #include -#include "cubeb-internal.h" -#include "cubeb_mixer.h" -#include "cubeb_utils.h" #ifndef FF_ARRAY_ELEMS #define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0])) #endif #define CHANNELS_MAX 32 -#define FRONT_LEFT 0 -#define FRONT_RIGHT 1 -#define FRONT_CENTER 2 -#define LOW_FREQUENCY 3 -#define BACK_LEFT 4 -#define BACK_RIGHT 5 -#define FRONT_LEFT_OF_CENTER 6 -#define FRONT_RIGHT_OF_CENTER 7 -#define BACK_CENTER 8 -#define SIDE_LEFT 9 -#define SIDE_RIGHT 10 -#define TOP_CENTER 11 -#define TOP_FRONT_LEFT 12 -#define TOP_FRONT_CENTER 13 -#define TOP_FRONT_RIGHT 14 -#define TOP_BACK_LEFT 15 -#define TOP_BACK_CENTER 16 -#define TOP_BACK_RIGHT 17 -#define NUM_NAMED_CHANNELS 18 +#define FRONT_LEFT 0 +#define FRONT_RIGHT 1 +#define FRONT_CENTER 2 +#define LOW_FREQUENCY 3 +#define BACK_LEFT 4 +#define BACK_RIGHT 5 +#define FRONT_LEFT_OF_CENTER 6 +#define FRONT_RIGHT_OF_CENTER 7 +#define BACK_CENTER 8 +#define SIDE_LEFT 9 +#define SIDE_RIGHT 10 +#define TOP_CENTER 11 +#define TOP_FRONT_LEFT 12 +#define TOP_FRONT_CENTER 13 +#define TOP_FRONT_RIGHT 14 +#define TOP_BACK_LEFT 15 +#define TOP_BACK_CENTER 16 +#define TOP_BACK_RIGHT 17 +#define NUM_NAMED_CHANNELS 18 #ifndef M_SQRT1_2 -#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ +#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */ #endif #ifndef M_SQRT2 -#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ #endif -#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ +#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */ -#define C30DB M_SQRT2 -#define C15DB 1.189207115 -#define C__0DB 1.0 -#define C_15DB 0.840896415 -#define C_30DB M_SQRT1_2 -#define C_45DB 0.594603558 -#define C_60DB 0.5 +#define C30DB M_SQRT2 +#define C15DB 1.189207115 +#define C__0DB 1.0 +#define C_15DB 0.840896415 +#define C_30DB M_SQRT1_2 +#define C_45DB 0.594603558 +#define C_60DB 0.5 static cubeb_channel_layout cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c) { - if (l == CUBEB_LAYOUT_UNDEFINED) { - switch (c) { - case 1: return CUBEB_LAYOUT_MONO; - case 2: return CUBEB_LAYOUT_STEREO; - } + if (l == CUBEB_LAYOUT_UNDEFINED) { + switch (c) { + case 1: + return CUBEB_LAYOUT_MONO; + case 2: + return CUBEB_LAYOUT_STEREO; } - return l; + } + return l; } -unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x) +unsigned int +cubeb_channel_layout_nb_channels(cubeb_channel_layout x) { #if __GNUC__ || __clang__ - return __builtin_popcount (x); + return __builtin_popcount(x); #else x -= (x >> 1) & 0x55555555; x = (x & 0x33333333) + ((x >> 2) & 0x33333333); @@ -87,16 +90,12 @@ unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x) } struct MixerContext { - MixerContext(cubeb_sample_format f, - uint32_t in_channels, - cubeb_channel_layout in, - uint32_t out_channels, + MixerContext(cubeb_sample_format f, uint32_t in_channels, + cubeb_channel_layout in, uint32_t out_channels, cubeb_channel_layout out) - : _format(f) - , _in_ch_layout(cubeb_channel_layout_check(in, in_channels)) - , _out_ch_layout(cubeb_channel_layout_check(out, out_channels)) - , _in_ch_count(in_channels) - , _out_ch_count(out_channels) + : _format(f), _in_ch_layout(cubeb_channel_layout_check(in, in_channels)), + _out_ch_layout(cubeb_channel_layout_check(out, out_channels)), + _in_ch_count(in_channels), _out_ch_count(out_channels) { if (in_channels != cubeb_channel_layout_nb_channels(in) || out_channels != cubeb_channel_layout_nb_channels(out)) { @@ -159,24 +158,30 @@ struct MixerContext { int init(); const cubeb_sample_format _format; - const cubeb_channel_layout _in_ch_layout; ///< input channel layout - const cubeb_channel_layout _out_ch_layout; ///< output channel layout - const uint32_t _in_ch_count; ///< input channel count - const uint32_t _out_ch_count; ///< output channel count - const float _surround_mix_level = C_30DB; ///< surround mixing level - const float _center_mix_level = C_30DB; ///< center mixing level - const float _lfe_mix_level = 1; ///< LFE mixing level - double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< floating point rematrixing coefficients - float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< single precision floating point rematrixing coefficients - int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< 17.15 fixed point rematrixing coefficients - uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }}; ///< Lists of input channels per output channel that have non zero rematrixing coefficients - bool _clipping = false; ///< Set to true if clipping detection is required - bool _valid = false; ///< Set to true if context is valid. + const cubeb_channel_layout _in_ch_layout; ///< input channel layout + const cubeb_channel_layout _out_ch_layout; ///< output channel layout + const uint32_t _in_ch_count; ///< input channel count + const uint32_t _out_ch_count; ///< output channel count + const float _surround_mix_level = C_30DB; ///< surround mixing level + const float _center_mix_level = C_30DB; ///< center mixing level + const float _lfe_mix_level = 1; ///< LFE mixing level + double _matrix[CHANNELS_MAX][CHANNELS_MAX] = { + {0}}; ///< floating point rematrixing coefficients + float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = { + {0}}; ///< single precision floating point rematrixing coefficients + int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = { + {0}}; ///< 17.15 fixed point rematrixing coefficients + uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX + 1] = { + {0}}; ///< Lists of input channels per output channel that have non zero + ///< rematrixing coefficients + bool _clipping = false; ///< Set to true if clipping detection is required + bool _valid = false; ///< Set to true if context is valid. }; -int MixerContext::auto_matrix() +int +MixerContext::auto_matrix() { - double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } }; + double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}}; double maxcoef = 0; float maxval; @@ -239,8 +244,7 @@ int MixerContext::auto_matrix() matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2; matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2; } else if (out_ch_layout & CHANNEL_FRONT_CENTER) { - matrix[FRONT_CENTER][BACK_CENTER] += - _surround_mix_level * M_SQRT1_2; + matrix[FRONT_CENTER][BACK_CENTER] += _surround_mix_level * M_SQRT1_2; } } if (unaccounted & CHANNEL_BACK_LEFT) { @@ -321,7 +325,7 @@ int MixerContext::auto_matrix() _matrix[out_i][in_i] = matrix[i][j]; } else { _matrix[out_i][in_i] = - i == j && (in_ch_layout & out_ch_layout & (1U << i)); + i == j && (in_ch_layout & out_ch_layout & (1U << i)); } sum += fabs(_matrix[out_i][in_i]); in_i++; @@ -356,7 +360,8 @@ int MixerContext::auto_matrix() return 0; } -int MixerContext::init() +int +MixerContext::init() { int r = auto_matrix(); if (r) { @@ -398,22 +403,15 @@ int MixerContext::init() return 0; } -template +template void -sum2(TYPE_SAMPLE * out, - uint32_t stride_out, - const TYPE_SAMPLE * in1, - const TYPE_SAMPLE * in2, - uint32_t stride_in, - TYPE_COEFF coeff1, - TYPE_COEFF coeff2, - F&& operand, - uint32_t frames) +sum2(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in1, + const TYPE_SAMPLE * in2, uint32_t stride_in, TYPE_COEFF coeff1, + TYPE_COEFF coeff2, F && operand, uint32_t frames) { static_assert( - std::is_same::type>::value, - "function must return the same type as used by matrix_coeff"); + std::is_same::value, + "function must return the same type as used by coeff1 and coeff2"); for (uint32_t i = 0; i < frames; i++) { *out = operand(coeff1 * *in1 + coeff2 * *in2); out += stride_out; @@ -422,20 +420,13 @@ sum2(TYPE_SAMPLE * out, } } -template +template void -copy(TYPE_SAMPLE * out, - uint32_t stride_out, - const TYPE_SAMPLE * in, - uint32_t stride_in, - TYPE_COEFF coeff, - F&& operand, - uint32_t frames) +copy(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in, + uint32_t stride_in, TYPE_COEFF coeff, F && operand, uint32_t frames) { - static_assert( - std::is_same::type>::value, - "function must return the same type as used by matrix_coeff"); + static_assert(std::is_same::value, + "function must return the same type as used by coeff"); for (uint32_t i = 0; i < frames; i++) { *out = operand(coeff * *in); out += stride_out; @@ -444,74 +435,58 @@ copy(TYPE_SAMPLE * out, } template -static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn, - const TYPE_COEFF (&matrix_coeff)[COLS][COLS], - F&& aF, uint32_t frames) +static int +rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn, + const TYPE_COEFF (&matrix_coeff)[COLS][COLS], F && aF, uint32_t frames) { static_assert( - std::is_same::type>::value, - "function must return the same type as used by matrix_coeff"); + std::is_same::value, + "function must return the same type as used by matrix_coeff"); for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) { - TYPE* out = aOut + out_i; + TYPE * out = aOut + out_i; switch (s->_matrix_ch[out_i][0]) { - case 0: - for (uint32_t i = 0; i < frames; i++) { - out[i * s->_out_ch_count] = 0; + case 0: + for (uint32_t i = 0; i < frames; i++) { + out[i * s->_out_ch_count] = 0; + } + break; + case 1: { + int in_i = s->_matrix_ch[out_i][1]; + copy(out, s->_out_ch_count, aIn + in_i, s->_in_ch_count, + matrix_coeff[out_i][in_i], aF, frames); + } break; + case 2: + sum2(out, s->_out_ch_count, aIn + s->_matrix_ch[out_i][1], + aIn + s->_matrix_ch[out_i][2], s->_in_ch_count, + matrix_coeff[out_i][s->_matrix_ch[out_i][1]], + matrix_coeff[out_i][s->_matrix_ch[out_i][2]], aF, frames); + break; + default: + for (uint32_t i = 0; i < frames; i++) { + TYPE_COEFF v = 0; + for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) { + uint32_t in_i = s->_matrix_ch[out_i][1 + j]; + v += *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i]; } - break; - case 1: { - int in_i = s->_matrix_ch[out_i][1]; - copy(out, - s->_out_ch_count, - aIn + in_i, - s->_in_ch_count, - matrix_coeff[out_i][in_i], - aF, - frames); - } break; - case 2: - sum2(out, - s->_out_ch_count, - aIn + s->_matrix_ch[out_i][1], - aIn + s->_matrix_ch[out_i][2], - s->_in_ch_count, - matrix_coeff[out_i][s->_matrix_ch[out_i][1]], - matrix_coeff[out_i][s->_matrix_ch[out_i][2]], - aF, - frames); - break; - default: - for (uint32_t i = 0; i < frames; i++) { - TYPE_COEFF v = 0; - for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) { - uint32_t in_i = s->_matrix_ch[out_i][1 + j]; - v += - *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i]; - } - out[i * s->_out_ch_count] = aF(v); - } - break; + out[i * s->_out_ch_count] = aF(v); + } + break; } } return 0; } -struct cubeb_mixer -{ - cubeb_mixer(cubeb_sample_format format, - uint32_t in_channels, - cubeb_channel_layout in_layout, - uint32_t out_channels, +struct cubeb_mixer { + cubeb_mixer(cubeb_sample_format format, uint32_t in_channels, + cubeb_channel_layout in_layout, uint32_t out_channels, cubeb_channel_layout out_layout) - : _context(format, in_channels, in_layout, out_channels, out_layout) + : _context(format, in_channels, in_layout, out_channels, out_layout) { } - template - void copy_and_trunc(size_t frames, - const T * input_buffer, + template + void copy_and_trunc(size_t frames, const T * input_buffer, T * output_buffer) const { if (_context._in_ch_count <= _context._out_ch_count) { @@ -545,11 +520,8 @@ struct cubeb_mixer } } - int mix(size_t frames, - const void * input_buffer, - size_t input_buffer_size, - void * output_buffer, - size_t output_buffer_size) const + int mix(size_t frames, const void * input_buffer, size_t input_buffer_size, + void * output_buffer, size_t output_buffer_size) const { if (frames <= 0 || _context._out_ch_count == 0) { return 0; @@ -557,7 +529,7 @@ struct cubeb_mixer // Check if output buffer is of sufficient size. size_t size_read_needed = - frames * _context._in_ch_count * cubeb_sample_size(_context._format); + frames * _context._in_ch_count * cubeb_sample_size(_context._format); if (input_buffer_size < size_read_needed) { // We don't have enough data to read! return -1; @@ -571,58 +543,46 @@ struct cubeb_mixer // The channel layouts were invalid or unsupported, instead we will simply // either drop the extra channels, or fill with silence the missing ones if (_context._format == CUBEB_SAMPLE_FLOAT32NE) { - copy_and_trunc(frames, - static_cast(input_buffer), - static_cast(output_buffer)); + copy_and_trunc(frames, static_cast(input_buffer), + static_cast(output_buffer)); } else { assert(_context._format == CUBEB_SAMPLE_S16NE); - copy_and_trunc(frames, - static_cast(input_buffer), - reinterpret_cast(output_buffer)); + copy_and_trunc(frames, static_cast(input_buffer), + reinterpret_cast(output_buffer)); } return 0; } - switch (_context._format) - { - case CUBEB_SAMPLE_FLOAT32NE: { - auto f = [](float x) { return x; }; - return rematrix(&_context, - static_cast(output_buffer), - static_cast(input_buffer), - _context._matrix_flt, - f, - frames); + switch (_context._format) { + case CUBEB_SAMPLE_FLOAT32NE: { + auto f = [](float x) { return x; }; + return rematrix(&_context, static_cast(output_buffer), + static_cast(input_buffer), + _context._matrix_flt, f, frames); + } + case CUBEB_SAMPLE_S16NE: + if (_context._clipping) { + auto f = [](int x) { + int y = (x + 16384) >> 15; + // clip the signed integer value into the -32768,32767 range. + if ((y + 0x8000U) & ~0xFFFF) { + return (y >> 31) ^ 0x7FFF; + } + return y; + }; + return rematrix(&_context, static_cast(output_buffer), + static_cast(input_buffer), + _context._matrix32, f, frames); + } else { + auto f = [](int x) { return (x + 16384) >> 15; }; + return rematrix(&_context, static_cast(output_buffer), + static_cast(input_buffer), + _context._matrix32, f, frames); } - case CUBEB_SAMPLE_S16NE: - if (_context._clipping) { - auto f = [](int x) { - int y = (x + 16384) >> 15; - // clip the signed integer value into the -32768,32767 range. - if ((y + 0x8000U) & ~0xFFFF) { - return (y >> 31) ^ 0x7FFF; - } - return y; - }; - return rematrix(&_context, - static_cast(output_buffer), - static_cast(input_buffer), - _context._matrix32, - f, - frames); - } else { - auto f = [](int x) { return (x + 16384) >> 15; }; - return rematrix(&_context, - static_cast(output_buffer), - static_cast(input_buffer), - _context._matrix32, - f, - frames); - } - break; - default: - assert(false); - break; + break; + default: + assert(false); + break; } return -1; @@ -636,28 +596,26 @@ struct cubeb_mixer MixerContext _context; }; -cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format, - uint32_t in_channels, - cubeb_channel_layout in_layout, - uint32_t out_channels, - cubeb_channel_layout out_layout) +cubeb_mixer * +cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels, + cubeb_channel_layout in_layout, uint32_t out_channels, + cubeb_channel_layout out_layout) { - return new cubeb_mixer( - format, in_channels, in_layout, out_channels, out_layout); + return new cubeb_mixer(format, in_channels, in_layout, out_channels, + out_layout); } -void cubeb_mixer_destroy(cubeb_mixer * mixer) +void +cubeb_mixer_destroy(cubeb_mixer * mixer) { delete mixer; } -int cubeb_mixer_mix(cubeb_mixer * mixer, - size_t frames, - const void * input_buffer, - size_t input_buffer_size, - void * output_buffer, - size_t output_buffer_size) +int +cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer, + size_t input_buffer_size, void * output_buffer, + size_t output_buffer_size) { - return mixer->mix( - frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size); + return mixer->mix(frames, input_buffer, input_buffer_size, output_buffer, + output_buffer_size); } diff --git a/externals/cubeb/src/cubeb_mixer.h b/externals/cubeb/src/cubeb_mixer.h index d43a237f9..1859dab46 100755 --- a/externals/cubeb/src/cubeb_mixer.h +++ b/externals/cubeb/src/cubeb_mixer.h @@ -15,20 +15,19 @@ extern "C" { #endif typedef struct cubeb_mixer cubeb_mixer; -cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format, - uint32_t in_channels, - cubeb_channel_layout in_layout, - uint32_t out_channels, - cubeb_channel_layout out_layout); -void cubeb_mixer_destroy(cubeb_mixer * mixer); -int cubeb_mixer_mix(cubeb_mixer * mixer, - size_t frames, - const void * input_buffer, - size_t input_buffer_size, - void * output_buffer, - size_t output_buffer_size); +cubeb_mixer * +cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels, + cubeb_channel_layout in_layout, uint32_t out_channels, + cubeb_channel_layout out_layout); +void +cubeb_mixer_destroy(cubeb_mixer * mixer); +int +cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer, + size_t input_buffer_size, void * output_buffer, + size_t output_buffer_size); -unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout); +unsigned int +cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout); #if defined(__cplusplus) } diff --git a/externals/cubeb/src/cubeb_opensl.c b/externals/cubeb/src/cubeb_opensl.c index f34ab7ac5..78096d5dd 100755 --- a/externals/cubeb/src/cubeb_opensl.c +++ b/externals/cubeb/src/cubeb_opensl.c @@ -5,28 +5,29 @@ * accompanying file LICENSE for details. */ #undef NDEBUG +#include #include #include -#include -#include #include -#include #include +#include +#include #include #if defined(__ANDROID__) -#include -#include #include "android/sles_definitions.h" #include -#include #include +#include +#include +#include #endif -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "cubeb_resampler.h" -#include "cubeb-sles.h" -#include "cubeb_array_queue.h" #include "android/cubeb-output-latency.h" +#include "cubeb-internal.h" +#include "cubeb-sles.h" +#include "cubeb/cubeb.h" +#include "cubeb_android.h" +#include "cubeb_array_queue.h" +#include "cubeb_resampler.h" #if defined(__ANDROID__) #ifdef LOG @@ -34,24 +35,32 @@ #endif //#define LOGGING_ENABLED #ifdef LOGGING_ENABLED -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args) +#define LOG(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL", ##args) #else #define LOG(...) #endif //#define TIMESTAMP_ENABLED #ifdef TIMESTAMP_ENABLED -#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) -#define LOG_TS(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)" , ## args) -#define TIMESTAMP(msg) do { \ - struct timeval timestamp; \ - int ts_ret = gettimeofday(×tamp, NULL); \ - if (ts_ret == 0) { \ - LOG_TS("%lld: %s (%s %s:%d)", timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, __FUNCTION__, FILENAME, __LINE__);\ - } else { \ - LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, __LINE__);\ - } \ -} while(0) +#define FILENAME \ + (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__) +#define LOG_TS(args...) \ + __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)", \ + ##args) +#define TIMESTAMP(msg) \ + do { \ + struct timeval timestamp; \ + int ts_ret = gettimeofday(×tamp, NULL); \ + if (ts_ret == 0) { \ + LOG_TS("%lld: %s (%s %s:%d)", \ + timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, \ + __FUNCTION__, FILENAME, __LINE__); \ + } else { \ + LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, \ + __LINE__); \ + } \ + } while (0) #else #define TIMESTAMP(...) #endif @@ -65,11 +74,6 @@ #define DEFAULT_SAMPLE_RATE 48000 #define DEFAULT_NUM_OF_FRAMES 480 -// If the latency requested is above this threshold, this stream is considered -// intended for playback (vs. real-time). Tell Android it should favor saving -// power over performance or latency. -// This is around 100ms at 44100 or 48000 -#define POWERSAVE_LATENCY_FRAMES_THRESHOLD 4000 static struct cubeb_ops const opensl_ops; @@ -173,15 +177,18 @@ struct cubeb_stream { }; /* Forward declaration. */ -static int opensl_stop_player(cubeb_stream * stm); -static int opensl_stop_recorder(cubeb_stream * stm); +static int +opensl_stop_player(cubeb_stream * stm); +static int +opensl_stop_recorder(cubeb_stream * stm); static int opensl_get_draining(cubeb_stream * stm) { #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex); - assert((r == EDEADLK || r == EBUSY) && "get_draining: mutex should be locked but it's not."); + assert((r == EDEADLK || r == EBUSY) && + "get_draining: mutex should be locked but it's not."); #endif return stm->draining; } @@ -192,7 +199,8 @@ opensl_set_draining(cubeb_stream * stm, int value) #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex); LOG("set draining try r = %d", r); - assert((r == EDEADLK || r == EBUSY) && "set_draining: mutex should be locked but it's not."); + assert((r == EDEADLK || r == EBUSY) && + "set_draining: mutex should be locked but it's not."); #endif assert(value == 0 || value == 1); stm->draining = value; @@ -226,7 +234,8 @@ opensl_get_shutdown(cubeb_stream * stm) { #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex); - assert((r == EDEADLK || r == EBUSY) && "get_shutdown: mutex should be locked but it's not."); + assert((r == EDEADLK || r == EBUSY) && + "get_shutdown: mutex should be locked but it's not."); #endif return stm->shutdown; } @@ -237,7 +246,8 @@ opensl_set_shutdown(cubeb_stream * stm, uint32_t value) #ifdef DEBUG int r = pthread_mutex_trylock(&stm->mutex); LOG("set shutdown try r = %d", r); - assert((r == EDEADLK || r == EBUSY) && "set_shutdown: mutex should be locked but it's not."); + assert((r == EDEADLK || r == EBUSY) && + "set_shutdown: mutex should be locked but it's not."); #endif assert(value == 0 || value == 1); stm->shutdown = value; @@ -249,8 +259,8 @@ play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event) cubeb_stream * stm = user_ptr; assert(stm); switch (event) { - case SL_PLAYEVENT_HEADATMARKER: - opensl_notify_drained(stm); + case SL_PLAYEVENT_HEADATMARKER: + opensl_notify_drained(stm); break; default: break; @@ -258,7 +268,7 @@ play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event) } static void -recorder_marker_callback (SLRecordItf caller, void * pContext, SLuint32 event) +recorder_marker_callback(SLRecordItf caller, void * pContext, SLuint32 event) { cubeb_stream * stm = pContext; assert(stm); @@ -299,7 +309,7 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) return; } - uint8_t *buf = stm->queuebuf[stm->queuebuf_idx]; + uint8_t * buf = stm->queuebuf[stm->queuebuf_idx]; written = 0; int r = pthread_mutex_lock(&stm->mutex); assert(r == 0); @@ -308,9 +318,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) r = pthread_mutex_unlock(&stm->mutex); assert(r == 0); if (!draining && !shutdown) { - written = cubeb_resampler_fill(stm->resampler, - NULL, NULL, - buf, stm->queuebuf_len / stm->framesize); + written = cubeb_resampler_fill(stm->resampler, NULL, NULL, buf, + stm->queuebuf_len / stm->framesize); LOG("bufferqueue_callback: resampler fill returned %ld frames", written); if (written < 0 || written * stm->framesize > stm->queuebuf_len) { r = pthread_mutex_lock(&stm->mutex); @@ -327,7 +336,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) // Keep sending silent data even in draining mode to prevent the audio // back-end from being stopped automatically by OpenSL/ES. assert(stm->queuebuf_len >= written * stm->framesize); - memset(buf + written * stm->framesize, 0, stm->queuebuf_len - written * stm->framesize); + memset(buf + written * stm->framesize, 0, + stm->queuebuf_len - written * stm->framesize); res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len); assert(res == SL_RESULT_SUCCESS); stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity; @@ -342,7 +352,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) LOG("bufferqueue_callback draining"); r = pthread_mutex_lock(&stm->mutex); assert(r == 0); - int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec; + int64_t written_duration = + INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec; opensl_set_draining(stm, 1); r = pthread_mutex_unlock(&stm->mutex); assert(r == 0); @@ -354,7 +365,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) } else { // Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf // to make sure all the data has been processed. - (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration); + (*stm->play) + ->SetMarkerPosition(stm->play, (SLmillisecond)written_duration); } return; } @@ -372,16 +384,18 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer) // This is the first enqueue current_index = 0; } else { - // The current index hold the last filled buffer get it before advance index. + // The current index hold the last filled buffer get it before advance + // index. last_buffer = stm->input_buffer_array[current_index]; // Advance to get next available buffer current_index = (current_index + 1) % stm->input_array_capacity; } // enqueue next empty buffer to be filled by the recorder - SLresult res = (*stm->recorderBufferQueueItf)->Enqueue(stm->recorderBufferQueueItf, - stm->input_buffer_array[current_index], - stm->input_buffer_length); - if (res != SL_RESULT_SUCCESS ) { + SLresult res = (*stm->recorderBufferQueueItf) + ->Enqueue(stm->recorderBufferQueueItf, + stm->input_buffer_array[current_index], + stm->input_buffer_length); + if (res != SL_RESULT_SUCCESS) { LOG("Enqueue recorder failed. Error code: %lu", res); return CUBEB_ERROR; } @@ -394,7 +408,8 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer) } // input data callback -void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context) +void +recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context) { assert(context); cubeb_stream * stm = context; @@ -424,11 +439,8 @@ void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context) assert(input_buffer); // Fill resampler with last input long input_frame_count = stm->input_buffer_length / stm->input_frame_size; - long got = cubeb_resampler_fill(stm->resampler, - input_buffer, - &input_frame_count, - NULL, - 0); + long got = cubeb_resampler_fill(stm->resampler, input_buffer, + &input_frame_count, NULL, 0); // Error case if (got < 0 || got > input_frame_count) { r = pthread_mutex_lock(&stm->mutex); @@ -450,13 +462,16 @@ void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context) opensl_set_draining(stm, 1); r = pthread_mutex_unlock(&stm->mutex); assert(r == 0); - int64_t duration = INT64_C(1000) * stm->input_total_frames / stm->input_device_rate; - (*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration); + int64_t duration = + INT64_C(1000) * stm->input_total_frames / stm->input_device_rate; + (*stm->recorderItf) + ->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration); return; } } -void recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context) +void +recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context) { assert(context); cubeb_stream * stm = context; @@ -471,10 +486,10 @@ void recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * conte if (shutdown || draining) { /* On draining and shutdown the recorder should have been stoped from - * the one set the flags. Accordint to the doc, on transition to - * the SL_RECORDSTATE_STOPPED state, the application should - * continue to enqueue buffers onto the queue to retrieve the residual - * recorded data in the system. */ + * the one set the flags. Accordint to the doc, on transition to + * the SL_RECORDSTATE_STOPPED state, the application should + * continue to enqueue buffers onto the queue to retrieve the residual + * recorded data in the system. */ LOG("Input shutdown %d or drain %d", shutdown, draining); int r = opensl_enqueue_recorder(stm, NULL); assert(r == CUBEB_OK); @@ -529,9 +544,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr) memset(output_buffer, 0, stm->queuebuf_len); // Enqueue data in player buffer queue - res = (*stm->bufq)->Enqueue(stm->bufq, - output_buffer, - stm->queuebuf_len); + res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len); assert(res == SL_RESULT_SUCCESS); return; } @@ -547,16 +560,14 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr) long written = 0; // Trigger user callback through resampler - written = cubeb_resampler_fill(stm->resampler, - input_buffer, - &input_frame_count, - output_buffer, - frames_needed); + written = + cubeb_resampler_fill(stm->resampler, input_buffer, &input_frame_count, + output_buffer, frames_needed); - LOG("Fill: written %ld, frames_needed %ld, input array size %zu", - written, frames_needed, array_queue_get_size(stm->input_queue)); + LOG("Fill: written %ld, frames_needed %ld, input array size %zu", written, + frames_needed, array_queue_get_size(stm->input_queue)); - if (written < 0 || written > frames_needed) { + if (written < 0 || written > frames_needed) { // Error case r = pthread_mutex_lock(&stm->mutex); assert(r == 0); @@ -569,9 +580,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr) memset(output_buffer, 0, stm->queuebuf_len); // Enqueue data in player buffer queue - res = (*stm->bufq)->Enqueue(stm->bufq, - output_buffer, - stm->queuebuf_len); + res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len); assert(res == SL_RESULT_SUCCESS); return; } @@ -583,10 +592,11 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr) r = pthread_mutex_unlock(&stm->mutex); assert(r == 0); - if ( written < frames_needed) { + if (written < frames_needed) { r = pthread_mutex_lock(&stm->mutex); assert(r == 0); - int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec; + int64_t written_duration = + INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec; opensl_set_draining(stm, 1); r = pthread_mutex_unlock(&stm->mutex); assert(r == 0); @@ -602,29 +612,28 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr) stm->queuebuf_len - written * stm->framesize); // Enqueue data in player buffer queue - res = (*stm->bufq)->Enqueue(stm->bufq, - output_buffer, - stm->queuebuf_len); + res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len); assert(res == SL_RESULT_SUCCESS); TIMESTAMP("EXIT"); } -static void opensl_destroy(cubeb * ctx); +static void +opensl_destroy(cubeb * ctx); #if defined(__ANDROID__) #if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP) -typedef int (system_property_get)(const char*, char*); +typedef int(system_property_get)(const char *, char *); static int -wrap_system_property_get(const char* name, char* value) +wrap_system_property_get(const char * name, char * value) { - void* libc = dlopen("libc.so", RTLD_LAZY); + void * libc = dlopen("libc.so", RTLD_LAZY); if (!libc) { LOG("Failed to open libc.so"); return -1; } - system_property_get* func = (system_property_get*) - dlsym(libc, "__system_property_get"); + system_property_get * func = + (system_property_get *)dlsym(libc, "__system_property_get"); int ret = -1; if (func) { ret = func(name, value); @@ -664,7 +673,8 @@ opensl_init(cubeb ** context, char const * context_name) #if defined(__ANDROID__) int android_version = get_android_version(); - if (android_version > 0 && android_version <= ANDROID_VERSION_GINGERBREAD_MR1) { + if (android_version > 0 && + android_version <= ANDROID_VERSION_GINGERBREAD_MR1) { // Don't even attempt to run on Gingerbread and lower return CUBEB_ERROR; } @@ -683,35 +693,34 @@ opensl_init(cubeb ** context, char const * context_name) return CUBEB_ERROR; } - typedef SLresult (*slCreateEngine_t)(SLObjectItf *, - SLuint32, - const SLEngineOption *, - SLuint32, - const SLInterfaceID *, - const SLboolean *); + typedef SLresult (*slCreateEngine_t)( + SLObjectItf *, SLuint32, const SLEngineOption *, SLuint32, + const SLInterfaceID *, const SLboolean *); slCreateEngine_t f_slCreateEngine = - (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine"); - SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE"); - SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX"); + (slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine"); + SLInterfaceID SL_IID_ENGINE = + *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE"); + SLInterfaceID SL_IID_OUTPUTMIX = + *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX"); ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME"); - ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE"); + ctx->SL_IID_BUFFERQUEUE = + *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE"); #if defined(__ANDROID__) - ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION"); - ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); + ctx->SL_IID_ANDROIDCONFIGURATION = + *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION"); + ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE = + *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); #endif ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY"); ctx->SL_IID_RECORD = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_RECORD"); - if (!f_slCreateEngine || - !SL_IID_ENGINE || - !SL_IID_OUTPUTMIX || + if (!f_slCreateEngine || !SL_IID_ENGINE || !SL_IID_OUTPUTMIX || !ctx->SL_IID_BUFFERQUEUE || #if defined(__ANDROID__) !ctx->SL_IID_ANDROIDCONFIGURATION || !ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE || #endif - !ctx->SL_IID_PLAY || - !ctx->SL_IID_RECORD) { + !ctx->SL_IID_PLAY || !ctx->SL_IID_RECORD) { opensl_destroy(ctx); return CUBEB_ERROR; } @@ -740,7 +749,8 @@ opensl_init(cubeb ** context, char const * context_name) const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX}; const SLboolean reqom[] = {SL_BOOLEAN_TRUE}; - res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom); + res = + (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom); if (res != SL_RESULT_SUCCESS) { opensl_destroy(ctx); return CUBEB_ERROR; @@ -752,9 +762,11 @@ opensl_init(cubeb ** context, char const * context_name) return CUBEB_ERROR; } - ctx->p_output_latency_function = cubeb_output_latency_load_method(android_version); + ctx->p_output_latency_function = + cubeb_output_latency_load_method(android_version); if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) { - LOG("Warning: output latency is not available, cubeb_stream_get_position() is not supported"); + LOG("Warning: output latency is not available, cubeb_stream_get_position() " + "is not supported"); } *context = ctx; @@ -774,7 +786,8 @@ opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) { assert(ctx && max_channels); /* The android mixer handles up to two channels, see - http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */ + http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 + */ *max_channels = 2; return CUBEB_OK; @@ -793,11 +806,13 @@ opensl_destroy(cubeb * ctx) free(ctx); } -static void opensl_stream_destroy(cubeb_stream * stm); +static void +opensl_stream_destroy(cubeb_stream * stm); #if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP) static int -opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params * params) +opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, + cubeb_stream_params * params) { assert(format); assert(params); @@ -806,37 +821,37 @@ opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params * format->numChannels = params->channels; // sampleRate is in milliHertz format->sampleRate = params->rate * 1000; - format->channelMask = params->channels == 1 ? - SL_SPEAKER_FRONT_CENTER : - SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + format->channelMask = params->channels == 1 + ? SL_SPEAKER_FRONT_CENTER + : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; switch (params->format) { - case CUBEB_SAMPLE_S16LE: - format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; - format->endianness = SL_BYTEORDER_LITTLEENDIAN; - break; - case CUBEB_SAMPLE_S16BE: - format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; - format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; - format->endianness = SL_BYTEORDER_BIGENDIAN; - break; - case CUBEB_SAMPLE_FLOAT32LE: - format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; - format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32; - format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - format->endianness = SL_BYTEORDER_LITTLEENDIAN; - break; - case CUBEB_SAMPLE_FLOAT32BE: - format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; - format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32; - format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; - format->endianness = SL_BYTEORDER_BIGENDIAN; - break; - default: - return CUBEB_ERROR_INVALID_FORMAT; + case CUBEB_SAMPLE_S16LE: + format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; + format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; + format->endianness = SL_BYTEORDER_LITTLEENDIAN; + break; + case CUBEB_SAMPLE_S16BE: + format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; + format->representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT; + format->endianness = SL_BYTEORDER_BIGENDIAN; + break; + case CUBEB_SAMPLE_FLOAT32LE: + format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; + format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32; + format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; + format->endianness = SL_BYTEORDER_LITTLEENDIAN; + break; + case CUBEB_SAMPLE_FLOAT32BE: + format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32; + format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_32; + format->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT; + format->endianness = SL_BYTEORDER_BIGENDIAN; + break; + default: + return CUBEB_ERROR_INVALID_FORMAT; } return CUBEB_OK; } @@ -854,19 +869,19 @@ opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params) format->samplesPerSec = params->rate * 1000; format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; - format->channelMask = params->channels == 1 ? - SL_SPEAKER_FRONT_CENTER : - SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + format->channelMask = params->channels == 1 + ? SL_SPEAKER_FRONT_CENTER + : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; switch (params->format) { - case CUBEB_SAMPLE_S16LE: - format->endianness = SL_BYTEORDER_LITTLEENDIAN; - break; - case CUBEB_SAMPLE_S16BE: - format->endianness = SL_BYTEORDER_BIGENDIAN; - break; - default: - return CUBEB_ERROR_INVALID_FORMAT; + case CUBEB_SAMPLE_S16LE: + format->endianness = SL_BYTEORDER_LITTLEENDIAN; + break; + case CUBEB_SAMPLE_S16BE: + format->endianness = SL_BYTEORDER_BIGENDIAN; + break; + default: + return CUBEB_ERROR_INVALID_FORMAT; } return CUBEB_OK; } @@ -904,38 +919,36 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) lDataSource.pLocator = &lDataLocatorIn; lDataSource.pFormat = NULL; - const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD, - stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - stm->context->SL_IID_ANDROIDCONFIGURATION }; + const SLInterfaceID lSoundRecorderIIDs[] = { + stm->context->SL_IID_RECORD, + stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + stm->context->SL_IID_ANDROIDCONFIGURATION}; - const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; + const SLboolean lSoundRecorderReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, + SL_BOOLEAN_TRUE}; // create the audio recorder abstract object - SLresult res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng, - &stm->recorderObj, - &lDataSource, - &lDataSink, - NELEMS(lSoundRecorderIIDs), - lSoundRecorderIIDs, - lSoundRecorderReqs); + SLresult res = (*stm->context->eng) + ->CreateAudioRecorder( + stm->context->eng, &stm->recorderObj, &lDataSource, + &lDataSink, NELEMS(lSoundRecorderIIDs), + lSoundRecorderIIDs, lSoundRecorderReqs); // Sample rate not supported. Try again with default sample rate! if (res == SL_RESULT_CONTENT_UNSUPPORTED) { if (stm->output_enabled && stm->output_configured_rate != 0) { // Set the same with the player. Since there is no // api for input device this is a safe choice. stm->input_device_rate = stm->output_configured_rate; - } else { + } else { // The output preferred rate is used for an input only scenario. // The default rate expected to be supported from all android devices. stm->input_device_rate = DEFAULT_SAMPLE_RATE; } lDataFormat.samplesPerSec = stm->input_device_rate * 1000; - res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng, - &stm->recorderObj, - &lDataSource, - &lDataSink, - NELEMS(lSoundRecorderIIDs), - lSoundRecorderIIDs, - lSoundRecorderReqs); + res = (*stm->context->eng) + ->CreateAudioRecorder(stm->context->eng, &stm->recorderObj, + &lDataSource, &lDataSink, + NELEMS(lSoundRecorderIIDs), + lSoundRecorderIIDs, lSoundRecorderReqs); if (res != SL_RESULT_SUCCESS) { LOG("Failed to create recorder. Error code: %lu", res); @@ -943,7 +956,6 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) } } - if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) { SLAndroidConfigurationItf recorderConfig; res = (*stm->recorderObj) @@ -952,7 +964,8 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) &recorderConfig); if (res != SL_RESULT_SUCCESS) { - LOG("Failed to get the android configuration interface for recorder. Error " + LOG("Failed to get the android configuration interface for recorder. " + "Error " "code: %lu", res); return CUBEB_ERROR; @@ -960,16 +973,19 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) // Voice recognition is the lowest latency, according to the docs. Camcorder // uses a microphone that is in the same direction as the camera. - SLint32 streamType = stm->voice_input ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION - : SL_ANDROID_RECORDING_PRESET_CAMCORDER; + SLint32 streamType = stm->voice_input + ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION + : SL_ANDROID_RECORDING_PRESET_CAMCORDER; - res = (*recorderConfig) - ->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, - &streamType, sizeof(SLint32)); + res = + (*recorderConfig) + ->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, + &streamType, sizeof(SLint32)); if (res != SL_RESULT_SUCCESS) { LOG("Failed to set the android configuration to VOICE for the recorder. " - "Error code: %lu", res); + "Error code: %lu", + res); return CUBEB_ERROR; } } @@ -980,15 +996,16 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) return CUBEB_ERROR; } // get the record interface - res = (*stm->recorderObj)->GetInterface(stm->recorderObj, - stm->context->SL_IID_RECORD, - &stm->recorderItf); + res = (*stm->recorderObj) + ->GetInterface(stm->recorderObj, stm->context->SL_IID_RECORD, + &stm->recorderItf); if (res != SL_RESULT_SUCCESS) { LOG("Failed to get recorder interface. Error code: %lu", res); return CUBEB_ERROR; } - res = (*stm->recorderItf)->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm); + res = (*stm->recorderItf) + ->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm); if (res != SL_RESULT_SUCCESS) { LOG("Failed to register recorder marker callback. Error code: %lu", res); return CUBEB_ERROR; @@ -996,17 +1013,22 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) (*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)0); - res = (*stm->recorderItf)->SetCallbackEventsMask(stm->recorderItf, (SLuint32)SL_RECORDEVENT_HEADATMARKER); + res = (*stm->recorderItf) + ->SetCallbackEventsMask(stm->recorderItf, + (SLuint32)SL_RECORDEVENT_HEADATMARKER); if (res != SL_RESULT_SUCCESS) { LOG("Failed to set headatmarker event mask. Error code: %lu", res); return CUBEB_ERROR; } // get the simple android buffer queue interface - res = (*stm->recorderObj)->GetInterface(stm->recorderObj, - stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, - &stm->recorderBufferQueueItf); + res = (*stm->recorderObj) + ->GetInterface(stm->recorderObj, + stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE, + &stm->recorderBufferQueueItf); if (res != SL_RESULT_SUCCESS) { - LOG("Failed to get recorder (android) buffer queue interface. Error code: %lu", res); + LOG("Failed to get recorder (android) buffer queue interface. Error code: " + "%lu", + res); return CUBEB_ERROR; } @@ -1016,11 +1038,11 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) // Register full duplex callback instead. rec_callback = recorder_fullduplex_callback; } - res = (*stm->recorderBufferQueueItf)->RegisterCallback(stm->recorderBufferQueueItf, - rec_callback, - stm); + res = (*stm->recorderBufferQueueItf) + ->RegisterCallback(stm->recorderBufferQueueItf, rec_callback, stm); if (res != SL_RESULT_SUCCESS) { - LOG("Failed to register recorder buffer queue callback. Error code: %lu", res); + LOG("Failed to register recorder buffer queue callback. Error code: %lu", + res); return CUBEB_ERROR; } @@ -1032,14 +1054,16 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) stm->input_array_capacity = NBUFS; if (stm->output_enabled) { // Full duplex, update capacity to hold 1 sec of data - stm->input_array_capacity = 1 * stm->input_device_rate / stm->input_buffer_length; + stm->input_array_capacity = + 1 * stm->input_device_rate / stm->input_buffer_length; } // Allocate input array - stm->input_buffer_array = (void**)calloc(1, sizeof(void*)*stm->input_array_capacity); + stm->input_buffer_array = + (void **)calloc(1, sizeof(void *) * stm->input_array_capacity); // Buffering has not started yet. stm->input_buffer_index = -1; // Prepare input buffers - for(uint32_t i = 0; i < stm->input_array_capacity; ++i) { + for (uint32_t i = 0; i < stm->input_array_capacity; ++i) { stm->input_buffer_array[i] = calloc(1, stm->input_buffer_length); } @@ -1063,22 +1087,25 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) } static int -opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { +opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) +{ assert(stm); assert(params); stm->user_output_rate = params->rate; - if(params->format == CUBEB_SAMPLE_S16NE || params->format == CUBEB_SAMPLE_S16BE) { + if (params->format == CUBEB_SAMPLE_S16NE || + params->format == CUBEB_SAMPLE_S16BE) { stm->framesize = params->channels * sizeof(int16_t); - } else if(params->format == CUBEB_SAMPLE_FLOAT32NE || params->format == CUBEB_SAMPLE_FLOAT32BE) { + } else if (params->format == CUBEB_SAMPLE_FLOAT32NE || + params->format == CUBEB_SAMPLE_FLOAT32BE) { stm->framesize = params->channels * sizeof(float); } stm->lastPosition = -1; stm->lastPositionTimeStamp = 0; stm->lastCompensativePosition = -1; - void* format = NULL; - SLuint32* format_sample_rate = NULL; + void * format = NULL; + SLuint32 * format_sample_rate = NULL; #if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP) SLAndroidDataFormat_PCM_EX pcm_ext_format; @@ -1092,8 +1119,8 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { #endif SLDataFormat_PCM pcm_format; - if(!format) { - if(opensl_set_format(&pcm_format, params) != CUBEB_OK) { + if (!format) { + if (opensl_set_format(&pcm_format, params) != CUBEB_OK) { return CUBEB_ERROR_INVALID_FORMAT; } format = &pcm_format; @@ -1128,13 +1155,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { uint32_t preferred_sampling_rate = stm->user_output_rate; SLresult res = SL_RESULT_CONTENT_UNSUPPORTED; if (preferred_sampling_rate) { - res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng, - &stm->playerObj, - &source, - &sink, - NELEMS(ids), - ids, - req); + res = (*stm->context->eng) + ->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source, + &sink, NELEMS(ids), ids, req); } // Sample rate not supported? Try again with primary sample rate! @@ -1142,13 +1165,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { preferred_sampling_rate != DEFAULT_SAMPLE_RATE) { preferred_sampling_rate = DEFAULT_SAMPLE_RATE; *format_sample_rate = preferred_sampling_rate * 1000; - res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng, - &stm->playerObj, - &source, - &sink, - NELEMS(ids), - ids, - req); + res = (*stm->context->eng) + ->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source, + &sink, NELEMS(ids), ids, req); } if (res != SL_RESULT_SUCCESS) { @@ -1164,10 +1183,11 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { stm->queuebuf_capacity = NBUFS; if (stm->output_enabled) { // Full duplex, update capacity to hold 1 sec of data - stm->queuebuf_capacity = 1 * stm->output_configured_rate / stm->queuebuf_len; + stm->queuebuf_capacity = + 1 * stm->output_configured_rate / stm->queuebuf_len; } // Allocate input array - stm->queuebuf = (void**)calloc(1, sizeof(void*) * stm->queuebuf_capacity); + stm->queuebuf = (void **)calloc(1, sizeof(void *) * stm->queuebuf_capacity); for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) { stm->queuebuf[i] = calloc(1, stm->queuebuf_len); assert(stm->queuebuf[i]); @@ -1181,7 +1201,8 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { stm->context->SL_IID_ANDROIDCONFIGURATION, &playerConfig); if (res != SL_RESULT_SUCCESS) { - LOG("Failed to get Android configuration interface. Error code: %lu", res); + LOG("Failed to get Android configuration interface. Error code: %lu", + res); return CUBEB_ERROR; } @@ -1189,10 +1210,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { if (stm->voice_output) { streamType = SL_ANDROID_STREAM_VOICE; } - res = (*playerConfig)->SetConfiguration(playerConfig, - SL_ANDROID_KEY_STREAM_TYPE, - &streamType, - sizeof(streamType)); + res = (*playerConfig) + ->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE, + &streamType, sizeof(streamType)); if (res != SL_RESULT_SUCCESS) { LOG("Failed to set Android configuration to %d Error code: %lu", streamType, res); @@ -1203,13 +1223,14 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING; } - res = (*playerConfig)->SetConfiguration(playerConfig, - SL_ANDROID_KEY_PERFORMANCE_MODE, - &performanceMode, - sizeof(performanceMode)); + res = (*playerConfig) + ->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, + &performanceMode, sizeof(performanceMode)); if (res != SL_RESULT_SUCCESS) { - LOG("Failed to set Android performance mode to %d Error code: %lu. This is" - " not fatal", performanceMode, res); + LOG("Failed to set Android performance mode to %d Error code: %lu. This " + "is" + " not fatal", + performanceMode, res); } } @@ -1233,10 +1254,10 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { SLuint32 paramSize = sizeof(SLuint32); // The reported latency is in milliseconds. if (playerConfig) { - res = (*playerConfig)->GetConfiguration(playerConfig, - (const SLchar *)"androidGetAudioLatency", - ¶mSize, - &audioLatency); + res = (*playerConfig) + ->GetConfiguration(playerConfig, + (const SLchar *)"androidGetAudioLatency", + ¶mSize, &audioLatency); if (res == SL_RESULT_SUCCESS) { LOG("Got playback latency using android configuration extension"); stm->output_latency_ms = audioLatency; @@ -1245,11 +1266,12 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { // `playerConfig` is available, but the above failed, or `playerConfig` is not // available. In both cases, we need to acquire the output latency by an other // mean. - if ((playerConfig && res != SL_RESULT_SUCCESS) || - !playerConfig) { - if (cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) { + if ((playerConfig && res != SL_RESULT_SUCCESS) || !playerConfig) { + if (cubeb_output_latency_method_is_loaded( + stm->context->p_output_latency_function)) { LOG("Got playback latency using JNI"); - stm->output_latency_ms = cubeb_get_output_latency(stm->context->p_output_latency_function); + stm->output_latency_ms = + cubeb_get_output_latency(stm->context->p_output_latency_function); } else { LOG("No alternate latency querying method loaded, A/V sync will be off."); stm->output_latency_ms = 0; @@ -1258,25 +1280,25 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { LOG("Audio output latency: %dms", stm->output_latency_ms); - res = (*stm->playerObj)->GetInterface(stm->playerObj, - stm->context->SL_IID_PLAY, - &stm->play); + res = + (*stm->playerObj) + ->GetInterface(stm->playerObj, stm->context->SL_IID_PLAY, &stm->play); if (res != SL_RESULT_SUCCESS) { LOG("Failed to get play interface. Error code: %lu", res); return CUBEB_ERROR; } - res = (*stm->playerObj)->GetInterface(stm->playerObj, - stm->context->SL_IID_BUFFERQUEUE, - &stm->bufq); + res = (*stm->playerObj) + ->GetInterface(stm->playerObj, stm->context->SL_IID_BUFFERQUEUE, + &stm->bufq); if (res != SL_RESULT_SUCCESS) { LOG("Failed to get bufferqueue interface. Error code: %lu", res); return CUBEB_ERROR; } - res = (*stm->playerObj)->GetInterface(stm->playerObj, - stm->context->SL_IID_VOLUME, - &stm->volume); + res = (*stm->playerObj) + ->GetInterface(stm->playerObj, stm->context->SL_IID_VOLUME, + &stm->volume); if (res != SL_RESULT_SUCCESS) { LOG("Failed to get volume interface. Error code: %lu", res); return CUBEB_ERROR; @@ -1291,7 +1313,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { // Work around wilhelm/AudioTrack badness, bug 1221228 (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0); - res = (*stm->play)->SetCallbackEventsMask(stm->play, (SLuint32)SL_PLAYEVENT_HEADATMARKER); + res = (*stm->play) + ->SetCallbackEventsMask(stm->play, + (SLuint32)SL_PLAYEVENT_HEADATMARKER); if (res != SL_RESULT_SUCCESS) { LOG("Failed to set headatmarker event mask. Error code: %lu", res); return CUBEB_ERROR; @@ -1312,7 +1336,7 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { // will be consumed and kick off the buffer queue callback. // Note the duration of a single frame is less than 1ms. We don't bother // adjusting the playback position. - uint8_t *buf = stm->queuebuf[stm->queuebuf_idx++]; + uint8_t * buf = stm->queuebuf[stm->queuebuf_idx++]; memset(buf, 0, stm->framesize); res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->framesize); assert(res == SL_RESULT_SUCCESS); @@ -1329,48 +1353,48 @@ opensl_validate_stream_param(cubeb_stream_params * stream_params) (stream_params->channels < 1 || stream_params->channels > 32))) { return CUBEB_ERROR_INVALID_FORMAT; } - if ((stream_params && - (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) { + if ((stream_params && (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) { LOG("Loopback is not supported"); return CUBEB_ERROR_NOT_SUPPORTED; } return CUBEB_OK; } -int has_pref_set(cubeb_stream_params* input_params, - cubeb_stream_params* output_params, - cubeb_stream_prefs pref) +int +has_pref_set(cubeb_stream_params * input_params, + cubeb_stream_params * output_params, cubeb_stream_prefs pref) { return (input_params && input_params->prefs & pref) || (output_params && output_params->prefs & pref); } static int -opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, +opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int latency_frames, - cubeb_data_callback data_callback, cubeb_state_callback state_callback, - void * user_ptr) + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; assert(ctx); if (input_device || output_device) { - LOG("Device selection is not supported in Android. The default will be used"); + LOG("Device selection is not supported in Android. The default will be " + "used"); } *stream = NULL; int r = opensl_validate_stream_param(output_stream_params); - if(r != CUBEB_OK) { + if (r != CUBEB_OK) { LOG("Output stream params not valid"); return r; } r = opensl_validate_stream_param(input_stream_params); - if(r != CUBEB_OK) { + if (r != CUBEB_OK) { LOG("Input stream params not valid"); return r; } @@ -1382,15 +1406,19 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name stm->data_callback = data_callback; stm->state_callback = state_callback; stm->user_ptr = user_ptr; - stm->buffer_size_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES; + stm->buffer_size_frames = + latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES; stm->input_enabled = (input_stream_params) ? 1 : 0; stm->output_enabled = (output_stream_params) ? 1 : 0; stm->shutdown = 1; - stm->voice_input = has_pref_set(input_stream_params, NULL, CUBEB_STREAM_PREF_VOICE); - stm->voice_output = has_pref_set(NULL, output_stream_params, CUBEB_STREAM_PREF_VOICE); + stm->voice_input = + has_pref_set(input_stream_params, NULL, CUBEB_STREAM_PREF_VOICE); + stm->voice_output = + has_pref_set(NULL, output_stream_params, CUBEB_STREAM_PREF_VOICE); - LOG("cubeb stream prefs: voice_input: %s voice_output: %s", stm->voice_input ? "true" : "false", - stm->voice_output ? "true" : "false"); + LOG("cubeb stream prefs: voice_input: %s voice_output: %s", + stm->voice_input ? "true" : "false", + stm->voice_output ? "true" : "false"); #ifdef DEBUG pthread_mutexattr_t attr; @@ -1403,7 +1431,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name assert(r == 0); if (output_stream_params) { - LOG("Playback params: Rate %d, channels %d, format %d, latency in frames %d.", + LOG("Playback params: Rate %d, channels %d, format %d, latency in frames " + "%d.", output_stream_params->rate, output_stream_params->channels, output_stream_params->format, stm->buffer_size_frames); r = opensl_configure_playback(stm, output_stream_params); @@ -1414,7 +1443,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name } if (input_stream_params) { - LOG("Capture params: Rate %d, channels %d, format %d, latency in frames %d.", + LOG("Capture params: Rate %d, channels %d, format %d, latency in frames " + "%d.", input_stream_params->rate, input_stream_params->channels, input_stream_params->format, stm->buffer_size_frames); r = opensl_configure_capture(stm, input_stream_params); @@ -1446,13 +1476,10 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name output_params.rate = stm->output_configured_rate; } - stm->resampler = cubeb_resampler_create(stm, - input_stream_params ? &input_params : NULL, - output_stream_params ? &output_params : NULL, - target_sample_rate, - data_callback, - user_ptr, - CUBEB_RESAMPLER_QUALITY_DEFAULT); + stm->resampler = cubeb_resampler_create( + stm, input_stream_params ? &input_params : NULL, + output_stream_params ? &output_params : NULL, target_sample_rate, + data_callback, user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT); if (!stm->resampler) { LOG("Failed to create resampler"); opensl_stream_destroy(stm); @@ -1472,7 +1499,7 @@ opensl_start_player(cubeb_stream * stm) (*stm->playerObj)->GetState(stm->playerObj, &playerState); if (playerState == SL_OBJECT_STATE_REALIZED) { SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PLAYING); - if(res != SL_RESULT_SUCCESS) { + if (res != SL_RESULT_SUCCESS) { LOG("Failed to start player. Error code: %lu", res); return CUBEB_ERROR; } @@ -1487,8 +1514,10 @@ opensl_start_recorder(cubeb_stream * stm) SLuint32 recorderState; (*stm->recorderObj)->GetState(stm->recorderObj, &recorderState); if (recorderState == SL_OBJECT_STATE_REALIZED) { - SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING); - if(res != SL_RESULT_SUCCESS) { + SLresult res = + (*stm->recorderItf) + ->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING); + if (res != SL_RESULT_SUCCESS) { LOG("Failed to start recorder. Error code: %lu", res); return CUBEB_ERROR; } @@ -1548,7 +1577,8 @@ opensl_stop_recorder(cubeb_stream * stm) assert(stm->recorderObj); assert(stm->shutdown || stm->draining); - SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED); + SLresult res = (*stm->recorderItf) + ->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED); if (res != SL_RESULT_SUCCESS) { LOG("Failed to stop recorder. Error code: %lu", res); return CUBEB_ERROR; @@ -1594,7 +1624,8 @@ opensl_destroy_recorder(cubeb_stream * stm) assert(stm->recorderObj); if (stm->recorderBufferQueueItf) { - SLresult res = (*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf); + SLresult res = + (*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf); if (res != SL_RESULT_SUCCESS) { LOG("Failed to clear recorder buffer queue. Error code: %lu", res); return CUBEB_ERROR; @@ -1660,11 +1691,12 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position) struct timespec t; clock_gettime(CLOCK_MONOTONIC, &t); - if(stm->lastPosition == msec) { + if (stm->lastPosition == msec) { compensation_msec = - (t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000; + (t.tv_sec * 1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / + 1000000; } else { - stm->lastPositionTimeStamp = t.tv_sec*1000000000LL + t.tv_nsec; + stm->lastPositionTimeStamp = t.tv_sec * 1000000000LL + t.tv_nsec; stm->lastPosition = msec; } @@ -1672,7 +1704,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position) uint32_t output_latency = stm->output_latency_ms; pthread_mutex_lock(&stm->mutex); - int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate; + int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / + stm->output_configured_rate; pthread_mutex_unlock(&stm->mutex); assert(maximum_position >= 0); @@ -1681,14 +1714,14 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position) if (stm->lastCompensativePosition > msec + compensation_msec) { // Over compensation, use lastCompensativePosition. unadjusted_position = - samplerate * (stm->lastCompensativePosition - output_latency) / 1000; + samplerate * (stm->lastCompensativePosition - output_latency) / 1000; } else { unadjusted_position = - samplerate * (msec - output_latency + compensation_msec) / 1000; + samplerate * (msec - output_latency + compensation_msec) / 1000; stm->lastCompensativePosition = msec + compensation_msec; } - *position = unadjusted_position < maximum_position ? - unadjusted_position : maximum_position; + *position = unadjusted_position < maximum_position ? unadjusted_position + : maximum_position; } else { *position = 0; } @@ -1702,7 +1735,7 @@ opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency) assert(latency); uint32_t stream_latency_frames = - stm->user_output_rate * (stm->output_latency_ms / 1000); + stm->user_output_rate * stm->output_latency_ms / 1000; return stream_latency_frames + cubeb_resampler_latency(stm->resampler); } @@ -1739,26 +1772,24 @@ opensl_stream_set_volume(cubeb_stream * stm, float volume) } static struct cubeb_ops const opensl_ops = { - .init = opensl_init, - .get_backend_id = opensl_get_backend_id, - .get_max_channel_count = opensl_get_max_channel_count, - .get_min_latency = NULL, - .get_preferred_sample_rate = NULL, - .enumerate_devices = NULL, - .device_collection_destroy = NULL, - .destroy = opensl_destroy, - .stream_init = opensl_stream_init, - .stream_destroy = opensl_stream_destroy, - .stream_start = opensl_stream_start, - .stream_stop = opensl_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = opensl_stream_get_position, - .stream_get_latency = opensl_stream_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = opensl_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = NULL, - .stream_device_destroy = NULL, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; + .init = opensl_init, + .get_backend_id = opensl_get_backend_id, + .get_max_channel_count = opensl_get_max_channel_count, + .get_min_latency = NULL, + .get_preferred_sample_rate = NULL, + .enumerate_devices = NULL, + .device_collection_destroy = NULL, + .destroy = opensl_destroy, + .stream_init = opensl_stream_init, + .stream_destroy = opensl_stream_destroy, + .stream_start = opensl_stream_start, + .stream_stop = opensl_stream_stop, + .stream_get_position = opensl_stream_get_position, + .stream_get_latency = opensl_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = opensl_stream_set_volume, + .stream_set_name = NULL, + .stream_get_current_device = NULL, + .stream_device_destroy = NULL, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; diff --git a/externals/cubeb/src/cubeb_oss.c b/externals/cubeb/src/cubeb_oss.c index 3348cdc3d..c85aec554 100755 --- a/externals/cubeb/src/cubeb_oss.c +++ b/externals/cubeb/src/cubeb_oss.c @@ -10,25 +10,25 @@ * accompanying file LICENSE for details. */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "cubeb-internal.h" #include "cubeb/cubeb.h" #include "cubeb_mixer.h" #include "cubeb_strings.h" -#include "cubeb-internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* Supported well by most hardware. */ #ifndef OSS_PREFER_RATE @@ -55,25 +55,25 @@ #define ENV_AUDIO_DEVICE "AUDIO_DEVICE" #ifndef OSS_MAX_CHANNELS -# if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) /* * The current maximum number of channels supported * on FreeBSD is 8. * * Reference: FreeBSD 12.1-RELEASE */ -# define OSS_MAX_CHANNELS (8) -# elif defined(__sun__) +#define OSS_MAX_CHANNELS (8) +#elif defined(__sun__) /* * The current maximum number of channels supported * on Illumos is 16. * * Reference: PSARC 2008/318 */ -# define OSS_MAX_CHANNELS (16) -# else -# define OSS_MAX_CHANNELS (2) -# endif +#define OSS_MAX_CHANNELS (16) +#else +#define OSS_MAX_CHANNELS (2) +#endif #endif #if defined(__FreeBSD__) || defined(__DragonFly__) @@ -89,13 +89,16 @@ struct cubeb { /* Our intern string store */ pthread_mutex_t mutex; /* protects devid_strs */ - cubeb_strings *devid_strs; + cubeb_strings * devid_strs; }; struct oss_stream { oss_devnode_t name; int fd; void * buf; + unsigned int nfr; /* Number of frames allocated */ + unsigned int nfrags; + unsigned int bufframes; struct stream_info { int channels; @@ -112,29 +115,26 @@ struct cubeb_stream { struct cubeb * context; void * user_ptr; pthread_t thread; - bool doorbell; /* (m) */ + bool doorbell; /* (m) */ pthread_cond_t doorbell_cv; /* (m) */ - pthread_cond_t stopped_cv; /* (m) */ + pthread_cond_t stopped_cv; /* (m) */ pthread_mutex_t mtx; /* Members protected by this should be marked (m) */ bool thread_created; /* (m) */ - bool running; /* (m) */ - bool destroying; /* (m) */ - cubeb_state state; /* (m) */ + bool running; /* (m) */ + bool destroying; /* (m) */ + cubeb_state state; /* (m) */ float volume /* (m) */; struct oss_stream play; struct oss_stream record; cubeb_data_callback data_cb; cubeb_state_callback state_cb; uint64_t frames_written /* (m) */; - unsigned int nfr; /* Number of frames allocated */ - unsigned int nfrags; - unsigned int bufframes; }; static char const * -oss_cubeb_devid_intern(cubeb *context, char const * devid) +oss_cubeb_devid_intern(cubeb * context, char const * devid) { - char const *is; + char const * is; pthread_mutex_lock(&context->mutex); is = cubeb_strings_intern(context->devid_strs, devid); pthread_mutex_unlock(&context->mutex); @@ -142,7 +142,8 @@ oss_cubeb_devid_intern(cubeb *context, char const * devid) } int -oss_init(cubeb **context, char const *context_name) { +oss_init(cubeb ** context, char const * context_name) +{ cubeb * c; (void)context_name; @@ -211,7 +212,7 @@ oss_get_min_latency(cubeb * context, cubeb_stream_params params, } static void -oss_free_cubeb_device_info_strings(cubeb_device_info *cdi) +oss_free_cubeb_device_info_strings(cubeb_device_info * cdi) { free((char *)cdi->device_id); free((char *)cdi->friendly_name); @@ -230,8 +231,8 @@ oss_free_cubeb_device_info_strings(cubeb_device_info *cdi) * Return 0 if OK, otherwise 1. */ static int -oss_probe_open(const char *dsppath, cubeb_device_type type, - int *fdp, oss_audioinfo *resai) +oss_probe_open(const char * dsppath, cubeb_device_type type, int * fdp, + oss_audioinfo * resai) { oss_audioinfo ai; int error; @@ -258,81 +259,81 @@ oss_probe_open(const char *dsppath, cubeb_device_type type, struct sndstat_info { oss_devnode_t devname; - const char *desc; + const char * desc; cubeb_device_type type; int preferred; }; static int -oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo) +oss_sndstat_line_parse(char * line, int is_ud, struct sndstat_info * sinfo) { - char *matchptr = line, *n = NULL; - struct sndstat_info res; + char *matchptr = line, *n = NULL; + struct sndstat_info res; - memset(&res, 0, sizeof(res)); + memset(&res, 0, sizeof(res)); - n = strchr(matchptr, ':'); - if (n == NULL) + n = strchr(matchptr, ':'); + if (n == NULL) + goto fail; + if (is_ud == 0) { + unsigned int devunit; + + if (sscanf(matchptr, "pcm%u: ", &devunit) < 1) goto fail; - if (is_ud == 0) { - unsigned int devunit; - if (sscanf(matchptr, "pcm%u: ", &devunit) < 1) - goto fail; - - if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1) - goto fail; - } else { - if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/"))) - goto fail; - - strlcpy(res.devname, "/dev/", sizeof(res.devname)); - strncat(res.devname, matchptr, n - matchptr); - } - matchptr = n + 1; - - n = strchr(matchptr, '<'); - if (n == NULL) + if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1) goto fail; - matchptr = n + 1; - n = strrchr(matchptr, '>'); - if (n == NULL) + } else { + if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/"))) goto fail; - *n = 0; - res.desc = matchptr; - matchptr = n + 1; - n = strchr(matchptr, '('); - if (n == NULL) - goto fail; - matchptr = n + 1; - n = strrchr(matchptr, ')'); - if (n == NULL) - goto fail; - *n = 0; - if (!isdigit(matchptr[0])) { - if (strstr(matchptr, "play") != NULL) - res.type |= CUBEB_DEVICE_TYPE_OUTPUT; - if (strstr(matchptr, "rec") != NULL) - res.type |= CUBEB_DEVICE_TYPE_INPUT; - } else { - int p, r; - if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2) - goto fail; - if (p > 0) - res.type |= CUBEB_DEVICE_TYPE_OUTPUT; - if (r > 0) - res.type |= CUBEB_DEVICE_TYPE_INPUT; - } - matchptr = n + 1; - if (strstr(matchptr, "default") != NULL) - res.preferred = 1; + strlcpy(res.devname, "/dev/", sizeof(res.devname)); + strncat(res.devname, matchptr, n - matchptr); + } + matchptr = n + 1; - *sinfo = res; - return 0; + n = strchr(matchptr, '<'); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, '>'); + if (n == NULL) + goto fail; + *n = 0; + res.desc = matchptr; + matchptr = n + 1; + + n = strchr(matchptr, '('); + if (n == NULL) + goto fail; + matchptr = n + 1; + n = strrchr(matchptr, ')'); + if (n == NULL) + goto fail; + *n = 0; + if (!isdigit(matchptr[0])) { + if (strstr(matchptr, "play") != NULL) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (strstr(matchptr, "rec") != NULL) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } else { + int p, r; + if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2) + goto fail; + if (p > 0) + res.type |= CUBEB_DEVICE_TYPE_OUTPUT; + if (r > 0) + res.type |= CUBEB_DEVICE_TYPE_INPUT; + } + matchptr = n + 1; + if (strstr(matchptr, "default") != NULL) + res.preferred = 1; + + *sinfo = res; + return 0; fail: - return 1; + return 1; } /* @@ -344,10 +345,10 @@ static int oss_enumerate_devices(cubeb * context, cubeb_device_type type, cubeb_device_collection * collection) { - cubeb_device_info *devinfop = NULL; - char *line = NULL; + cubeb_device_info * devinfop = NULL; + char * line = NULL; size_t linecap = 0; - FILE *sndstatfp = NULL; + FILE * sndstatfp = NULL; int collection_cnt = 0; int is_ud = 0; int skipall = 0; @@ -360,7 +361,7 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type, if (sndstatfp == NULL) goto fail; while (getline(&line, &linecap, sndstatfp) > 0) { - const char *devid = NULL; + const char * devid = NULL; struct sndstat_info sinfo; oss_audioinfo ai; @@ -373,7 +374,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type, skipall = 0; continue; } - if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) { + if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, + strlen(SNDSTAT_USER_BEGIN_STR))) { is_ud = 1; skipall = 0; continue; @@ -433,8 +435,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type, collection_cnt++; - void *newp = reallocarray(devinfop, collection_cnt + 1, - sizeof(cubeb_device_info)); + void * newp = + reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info)); if (newp == NULL) goto fail; devinfop = newp; @@ -464,7 +466,7 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type, { oss_sysinfo si; int error, i; - cubeb_device_info *devinfop = NULL; + cubeb_device_info * devinfop = NULL; int collection_cnt = 0; int mixer_fd = -1; @@ -476,7 +478,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type, error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si); if (error) { - LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno); + LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", + OSS_DEFAULT_MIXER, errno); goto fail; } @@ -487,8 +490,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type, collection->count = 0; for (i = 0; i < si.numaudios; i++) { oss_audioinfo ai; - cubeb_device_info cdi = { 0 }; - const char *devid = NULL; + cubeb_device_info cdi = {0}; + const char * devid = NULL; ai.dev = i; error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai); @@ -574,24 +577,24 @@ static unsigned int oss_chn_from_cubeb(cubeb_channel chn) { switch (chn) { - case CHANNEL_FRONT_LEFT: - return CHID_L; - case CHANNEL_FRONT_RIGHT: - return CHID_R; - case CHANNEL_FRONT_CENTER: - return CHID_C; - case CHANNEL_LOW_FREQUENCY: - return CHID_LFE; - case CHANNEL_BACK_LEFT: - return CHID_LR; - case CHANNEL_BACK_RIGHT: - return CHID_RR; - case CHANNEL_SIDE_LEFT: - return CHID_LS; - case CHANNEL_SIDE_RIGHT: - return CHID_RS; - default: - return CHID_UNDEF; + case CHANNEL_FRONT_LEFT: + return CHID_L; + case CHANNEL_FRONT_RIGHT: + return CHID_R; + case CHANNEL_FRONT_CENTER: + return CHID_C; + case CHANNEL_LOW_FREQUENCY: + return CHID_LFE; + case CHANNEL_BACK_LEFT: + return CHID_LR; + case CHANNEL_BACK_RIGHT: + return CHID_RR; + case CHANNEL_SIDE_LEFT: + return CHID_LS; + case CHANNEL_SIDE_RIGHT: + return CHID_RS; + default: + return CHID_UNDEF; } } @@ -648,7 +651,8 @@ oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params, return CUBEB_ERROR; } /* Mono layout is an exception */ - if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) { + if (params->layout != CUBEB_LAYOUT_UNDEFINED && + params->layout != CUBEB_LAYOUT_MONO) { chnorder = oss_cubeb_layout_to_chnorder(params->layout); if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1) LOG("Non-fatal error %d occured when setting channel order.", errno); @@ -741,73 +745,191 @@ oss_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol) } } +static int +oss_get_rec_frames(cubeb_stream * s, unsigned int nframes) +{ + size_t rem = nframes * s->record.frame_size; + size_t read_ofs = 0; + while (rem > 0) { + ssize_t n; + if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) < + 0) { + if (errno == EINTR) + continue; + return CUBEB_ERROR; + } + read_ofs += n; + rem -= n; + } + return 0; +} + +static int +oss_put_play_frames(cubeb_stream * s, unsigned int nframes) +{ + size_t rem = nframes * s->play.frame_size; + size_t write_ofs = 0; + while (rem > 0) { + ssize_t n; + if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, rem)) < 0) { + if (errno == EINTR) + continue; + return CUBEB_ERROR; + } + pthread_mutex_lock(&s->mtx); + s->frames_written += n / s->play.frame_size; + pthread_mutex_unlock(&s->mtx); + write_ofs += n; + rem -= n; + } + return 0; +} + +static int +oss_wait_fds_for_space(cubeb_stream * s, long * nfrp) +{ + audio_buf_info bi; + struct pollfd pfds[2]; + long nfr, tnfr; + int i; + + assert(s->play.fd != -1 || s->record.fd != -1); + pfds[0].events = POLLOUT | POLLHUP; + pfds[0].revents = 0; + pfds[0].fd = s->play.fd; + pfds[1].events = POLLIN | POLLHUP; + pfds[1].revents = 0; + pfds[1].fd = s->record.fd; + +retry: + nfr = LONG_MAX; + + if (poll(pfds, 2, 1000) == -1) { + return CUBEB_ERROR; + } + + for (i = 0; i < 2; i++) { + if (pfds[i].revents & POLLHUP) { + return CUBEB_ERROR; + } + } + + if (s->play.fd != -1) { + if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) { + return CUBEB_STATE_ERROR; + } + tnfr = bi.bytes / s->play.frame_size; + if (tnfr <= 0) { + /* too little space - stop polling record, if any */ + pfds[0].fd = s->play.fd; + pfds[1].fd = -1; + goto retry; + } else if (tnfr > (long)s->play.bufframes) { + /* too many frames available - limit */ + tnfr = (long)s->play.bufframes; + } + if (nfr > tnfr) { + nfr = tnfr; + } + } + if (s->record.fd != -1) { + if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == -1) { + return CUBEB_STATE_ERROR; + } + tnfr = bi.bytes / s->record.frame_size; + if (tnfr <= 0) { + /* too little space - stop polling playback, if any */ + pfds[0].fd = -1; + pfds[1].fd = s->record.fd; + goto retry; + } else if (tnfr > (long)s->record.bufframes) { + /* too many frames available - limit */ + tnfr = (long)s->record.bufframes; + } + if (nfr > tnfr) { + nfr = tnfr; + } + } + + *nfrp = nfr; + return 0; +} + /* 1 - Stopped by cubeb_stream_stop, otherwise 0 */ static int -oss_audio_loop(cubeb_stream * s, cubeb_state *new_state) +oss_audio_loop(cubeb_stream * s, cubeb_state * new_state) { cubeb_state state = CUBEB_STATE_STOPPED; - int trig = 0; - int drain = 0; - struct pollfd pfds[2]; - unsigned int ppending, rpending; + int trig = 0, drain = 0; + const bool play_on = s->play.fd != -1, record_on = s->record.fd != -1; + long nfr = 0; - pfds[0].fd = s->play.fd; - pfds[0].events = POLLOUT; - pfds[1].fd = s->record.fd; - pfds[1].events = POLLIN; - - ppending = 0; - rpending = s->bufframes; - - if (s->record.fd != -1) { + if (record_on) { if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { LOG("Error %d occured when setting trigger on record fd", errno); state = CUBEB_STATE_ERROR; goto breakdown; } + trig |= PCM_ENABLE_INPUT; - if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) { + memset(s->record.buf, 0, s->record.bufframes * s->record.frame_size); + + if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) { LOG("Error %d occured when setting trigger on record fd", errno); state = CUBEB_STATE_ERROR; goto breakdown; } - memset(s->record.buf, 0, s->bufframes * s->record.frame_size); + } + + if (!play_on && !record_on) { + /* + * Stop here if the stream is not play & record stream, + * play-only stream or record-only stream + */ + + goto breakdown; } while (1) { - long nfr = 0; - pthread_mutex_lock(&s->mtx); if (!s->running || s->destroying) { pthread_mutex_unlock(&s->mtx); break; } pthread_mutex_unlock(&s->mtx); - if (s->play.fd == -1 && s->record.fd == -1) { - /* - * Stop here if the stream is not play & record stream, - * play-only stream or record-only stream - */ - goto breakdown; - } - - while ((s->bufframes - ppending) >= s->nfr && rpending >= s->nfr) { - long n = ((s->bufframes - ppending) < rpending) ? s->bufframes - ppending : rpending; - char *rptr = NULL, *pptr = NULL; - if (s->record.fd != -1) - rptr = (char *)s->record.buf; - if (s->play.fd != -1) - pptr = (char *)s->play.buf + ppending * s->play.frame_size; - if (s->record.fd != -1 && s->record.floating) { - oss_linear32_to_float(s->record.buf, s->record.info.channels * n); + long got = 0; + if (nfr > 0) { + if (record_on) { + if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) { + state = CUBEB_STATE_ERROR; + goto breakdown; + } + if (s->record.floating) { + oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr); + } } - nfr = s->data_cb(s, s->user_ptr, rptr, pptr, n); - if (nfr == CUBEB_ERROR) { + + got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr); + if (got == CUBEB_ERROR) { state = CUBEB_STATE_ERROR; goto breakdown; } - if (pptr) { + if (got < nfr) { + if (s->play.fd != -1) { + drain = 1; + } else { + /* + * This is a record-only stream and number of frames + * returned from data_cb() is smaller than number + * of frames required to read. Stop here. + */ + state = CUBEB_STATE_STOPPED; + goto breakdown; + } + } + + if (got > 0 && play_on) { float vol; pthread_mutex_lock(&s->mtx); @@ -815,102 +937,24 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state) pthread_mutex_unlock(&s->mtx); if (s->play.floating) { - oss_float_to_linear32(pptr, s->play.info.channels * nfr, vol); + oss_float_to_linear32(s->play.buf, s->play.info.channels * got, vol); } else { - oss_linear16_set_vol((int16_t *)pptr, s->play.info.channels * nfr, vol); + oss_linear16_set_vol((int16_t *)s->play.buf, + s->play.info.channels * got, vol); } - } - if (pptr) { - ppending += nfr; - assert(ppending <= s->bufframes); - } - if (rptr) { - assert(rpending >= nfr); - rpending -= nfr; - memmove(rptr, rptr + nfr * s->record.frame_size, - (s->bufframes - nfr) * s->record.frame_size); - } - if (nfr < n) { - if (s->play.fd != -1) { - drain = 1; - break; - } else { - /* - * This is a record-only stream and number of frames - * returned from data_cb() is smaller than number - * of frames required to read. Stop here. - */ - - state = CUBEB_STATE_STOPPED; - goto breakdown; - } - } - } - - ssize_t n, frames; - int nfds; - - pfds[0].revents = 0; - pfds[1].revents = 0; - - nfds = poll(pfds, 2, 1000); - if (nfds == -1) { - if (errno == EINTR) - continue; - LOG("Error %d occured when polling playback and record fd", errno); - state = CUBEB_STATE_ERROR; - goto breakdown; - } else if (nfds == 0) - continue; - - if ((pfds[0].revents & (POLLERR | POLLHUP)) || - (pfds[1].revents & (POLLERR | POLLHUP))) { - LOG("Error occured on playback, record fds"); - state = CUBEB_STATE_ERROR; - goto breakdown; - } - - if (pfds[0].revents) { - while (ppending > 0) { - size_t bytes = ppending * s->play.frame_size; - if ((n = write(s->play.fd, (uint8_t *)s->play.buf, bytes)) < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) { - if (drain) - continue; - break; - } + if (oss_put_play_frames(s, got) == CUBEB_ERROR) { state = CUBEB_STATE_ERROR; goto breakdown; } - frames = n / s->play.frame_size; - pthread_mutex_lock(&s->mtx); - s->frames_written += frames; - pthread_mutex_unlock(&s->mtx); - ppending -= frames; - memmove(s->play.buf, (uint8_t *)s->play.buf + n, - (s->bufframes - frames) * s->play.frame_size); + } + if (drain) { + state = CUBEB_STATE_DRAINED; + goto breakdown; } } - if (pfds[1].revents) { - while (s->bufframes - rpending > 0) { - size_t bytes = (s->bufframes - rpending) * s->record.frame_size; - size_t read_ofs = rpending * s->record.frame_size; - if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) - break; - state = CUBEB_STATE_ERROR; - goto breakdown; - } - frames = n / s->record.frame_size; - rpending += frames; - } - } - if (drain) { - state = CUBEB_STATE_DRAINED; + + if (oss_wait_fds_for_space(s, &nfr) != 0) { + state = CUBEB_STATE_ERROR; goto breakdown; } } @@ -926,9 +970,9 @@ breakdown: } static void * -oss_io_routine(void *arg) +oss_io_routine(void * arg) { - cubeb_stream *s = arg; + cubeb_stream * s = arg; cubeb_state new_state; int stopped; @@ -969,9 +1013,10 @@ static inline int oss_calc_frag_shift(unsigned int frames, unsigned int frame_size) { int n = 4; - int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS; - while ((1 << n) < blksize) + int blksize = frames * frame_size; + while ((1 << n) < blksize) { n++; + } return n; } @@ -982,22 +1027,17 @@ oss_get_frag_params(unsigned int shift) } static int -oss_stream_init(cubeb * context, - cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, +oss_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency_frames, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + unsigned int latency_frames, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) { int ret = CUBEB_OK; - unsigned int playnfr = 0, recnfr = 0; - cubeb_stream *s = NULL; - const char *defdsp; + cubeb_stream * s = NULL; + const char * defdsp; if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0') defdsp = OSS_DEFAULT_DEVICE; @@ -1009,7 +1049,6 @@ oss_stream_init(cubeb * context, } s->state = CUBEB_STATE_STOPPED; s->record.fd = s->play.fd = -1; - s->nfr = latency_frames; if (input_device != NULL) { strlcpy(s->record.name, input_device, sizeof(s->record.name)); } else { @@ -1030,26 +1069,35 @@ oss_stream_init(cubeb * context, nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout); if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && nb_channels != input_stream_params->channels) { - LOG("input_stream_params->layout does not match input_stream_params->channels"); + LOG("input_stream_params->layout does not match " + "input_stream_params->channels"); ret = CUBEB_ERROR_INVALID_PARAMETER; goto error; } - if (s->record.fd == -1) { - if ((s->record.fd = open(s->record.name, O_RDONLY | O_NONBLOCK)) == -1) { - LOG("Audio device \"%s\" could not be opened as read-only", - s->record.name); - ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; - goto error; - } + if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) { + LOG("Audio device \"%s\" could not be opened as read-only", + s->record.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; } if ((ret = oss_copy_params(s->record.fd, s, input_stream_params, &s->record.info)) != CUBEB_OK) { LOG("Setting record params failed"); goto error; } - s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); - s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8); - recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size; + s->record.floating = + (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->record.frame_size = + s->record.info.channels * (s->record.info.precision / 8); + s->record.nfrags = OSS_NFRAGS; + s->record.nfr = latency_frames / OSS_NFRAGS; + s->record.bufframes = s->record.nfrags * s->record.nfr; + uint32_t minnfr; + oss_get_min_latency(context, *input_stream_params, &minnfr); + if (s->record.nfr < minnfr) { + s->record.nfr = minnfr; + s->record.nfrags = latency_frames / minnfr; + } } if (output_stream_params != NULL) { unsigned int nb_channels; @@ -1058,20 +1106,20 @@ oss_stream_init(cubeb * context, ret = CUBEB_ERROR_NOT_SUPPORTED; goto error; } - nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout); + nb_channels = + cubeb_channel_layout_nb_channels(output_stream_params->layout); if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED && nb_channels != output_stream_params->channels) { - LOG("output_stream_params->layout does not match output_stream_params->channels"); + LOG("output_stream_params->layout does not match " + "output_stream_params->channels"); ret = CUBEB_ERROR_INVALID_PARAMETER; goto error; } - if (s->play.fd == -1) { - if ((s->play.fd = open(s->play.name, O_WRONLY | O_NONBLOCK)) == -1) { - LOG("Audio device \"%s\" could not be opened as write-only", - s->play.name); - ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; - goto error; - } + if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) { + LOG("Audio device \"%s\" could not be opened as write-only", + s->play.name); + ret = CUBEB_ERROR_DEVICE_UNAVAILABLE; + goto error; } if ((ret = oss_copy_params(s->play.fd, s, output_stream_params, &s->play.info)) != CUBEB_OK) { @@ -1080,23 +1128,55 @@ oss_stream_init(cubeb * context, } s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8); - playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size; + s->play.nfrags = OSS_NFRAGS; + s->play.nfr = latency_frames / OSS_NFRAGS; + uint32_t minnfr; + oss_get_min_latency(context, *output_stream_params, &minnfr); + if (s->play.nfr < minnfr) { + s->play.nfr = minnfr; + s->play.nfrags = latency_frames / minnfr; + } + s->play.bufframes = s->play.nfrags * s->play.nfr; } - /* Use the largest nframes among playing and recording streams */ - s->nfr = (playnfr > recnfr) ? playnfr : recnfr; - s->nfrags = OSS_NFRAGS; - s->bufframes = s->nfr * s->nfrags; if (s->play.fd != -1) { - int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->play.frame_size)); - if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) - LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", + int frag = oss_get_frag_params( + oss_calc_frag_shift(s->play.nfr, s->play.frame_size)); + if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) + LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", frag); + audio_buf_info bi; + if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi)) + LOG("Failed to get play fd's buffer info."); + else { + s->play.nfr = bi.fragsize / s->play.frame_size; + s->play.nfrags = bi.fragments; + s->play.bufframes = s->play.nfr * s->play.nfrags; + } + + int lw = s->play.frame_size; + if (ioctl(s->play.fd, SNDCTL_DSP_LOW_WATER, &lw)) + LOG("Audio device \"%s\" (play) could not set trigger threshold", + s->play.name); } if (s->record.fd != -1) { - int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->record.frame_size)); + int frag = oss_get_frag_params( + oss_calc_frag_shift(s->record.nfr, s->record.frame_size)); if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag)) LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x", frag); + audio_buf_info bi; + if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi)) + LOG("Failed to get record fd's buffer info."); + else { + s->record.nfr = bi.fragsize / s->record.frame_size; + s->record.nfrags = bi.fragments; + s->record.bufframes = s->record.nfr * s->record.nfrags; + } + + int lw = s->record.frame_size; + if (ioctl(s->record.fd, SNDCTL_DSP_LOW_WATER, &lw)) + LOG("Audio device \"%s\" (record) could not set trigger threshold", + s->record.name); } s->context = context; s->volume = 1.0; @@ -1119,13 +1199,14 @@ oss_stream_init(cubeb * context, s->doorbell = false; if (s->play.fd != -1) { - if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) { + if ((s->play.buf = calloc(s->play.bufframes, s->play.frame_size)) == NULL) { ret = CUBEB_ERROR; goto error; } } if (s->record.fd != -1) { - if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) { + if ((s->record.buf = calloc(s->record.bufframes, s->record.frame_size)) == + NULL) { ret = CUBEB_ERROR; goto error; } @@ -1219,10 +1300,10 @@ oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device) if (*device == NULL) { return CUBEB_ERROR; } - (*device)->input_name = stream->record.fd != -1 ? - strdup(stream->record.name) : NULL; - (*device)->output_name = stream->play.fd != -1 ? - strdup(stream->play.name) : NULL; + (*device)->input_name = + stream->record.fd != -1 ? strdup(stream->record.name) : NULL; + (*device)->output_name = + stream->play.fd != -1 ? strdup(stream->play.name) : NULL; return CUBEB_OK; } @@ -1249,7 +1330,6 @@ static struct cubeb_ops const oss_ops = { .stream_destroy = oss_stream_destroy, .stream_start = oss_stream_start, .stream_stop = oss_stream_stop, - .stream_reset_default_device = NULL, .stream_get_position = oss_stream_get_position, .stream_get_latency = oss_stream_get_latency, .stream_get_input_latency = NULL, diff --git a/externals/cubeb/src/cubeb_osx_run_loop.cpp b/externals/cubeb/src/cubeb_osx_run_loop.cpp index de4e43970..5828db6e9 100755 --- a/externals/cubeb/src/cubeb_osx_run_loop.cpp +++ b/externals/cubeb/src/cubeb_osx_run_loop.cpp @@ -5,31 +5,29 @@ * accompanying file LICENSE for details. */ -#include #include "cubeb_osx_run_loop.h" #include "cubeb_log.h" #include #include #include #include +#include -void cubeb_set_coreaudio_notification_runloop() +void +cubeb_set_coreaudio_notification_runloop() { /* This is needed so that AudioUnit listeners get called on this thread, and * not the main thread. If we don't do that, they are not called, or a crash * occur, depending on the OSX version. */ AudioObjectPropertyAddress runloop_address = { - kAudioHardwarePropertyRunLoop, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; + kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal, + kAudioObjectPropertyElementMaster}; CFRunLoopRef run_loop = nullptr; OSStatus r; - r = AudioObjectSetPropertyData(kAudioObjectSystemObject, - &runloop_address, - 0, NULL, sizeof(CFRunLoopRef), &run_loop); + r = AudioObjectSetPropertyData(kAudioObjectSystemObject, &runloop_address, 0, + NULL, sizeof(CFRunLoopRef), &run_loop); if (r != noErr) { LOG("Could not make global CoreAudio notifications use their own thread."); } diff --git a/externals/cubeb/src/cubeb_osx_run_loop.h b/externals/cubeb/src/cubeb_osx_run_loop.h index 78cd68d09..8d88a3714 100755 --- a/externals/cubeb/src/cubeb_osx_run_loop.h +++ b/externals/cubeb/src/cubeb_osx_run_loop.h @@ -15,7 +15,8 @@ extern "C" { #endif -void cubeb_set_coreaudio_notification_runloop(); +void +cubeb_set_coreaudio_notification_runloop(); #if defined(__cplusplus) } diff --git a/externals/cubeb/src/cubeb_pulse.c b/externals/cubeb/src/cubeb_pulse.c index c7e9d1ca1..438a695e1 100755 --- a/externals/cubeb/src/cubeb_pulse.c +++ b/externals/cubeb/src/cubeb_pulse.c @@ -5,88 +5,88 @@ * accompanying file LICENSE for details. */ #undef NDEBUG +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" +#include "cubeb_mixer.h" +#include "cubeb_strings.h" #include #include #include #include #include #include -#include "cubeb-internal.h" -#include "cubeb/cubeb.h" -#include "cubeb_mixer.h" -#include "cubeb_strings.h" #ifdef DISABLE_LIBPULSE_DLOPEN #define WRAP(x) x #else -#define WRAP(x) cubeb_##x -#define LIBPULSE_API_VISIT(X) \ - X(pa_channel_map_can_balance) \ - X(pa_channel_map_init) \ - X(pa_context_connect) \ - X(pa_context_disconnect) \ - X(pa_context_drain) \ - X(pa_context_get_server_info) \ - X(pa_context_get_sink_info_by_name) \ - X(pa_context_get_sink_info_list) \ - X(pa_context_get_sink_input_info) \ - X(pa_context_get_source_info_list) \ - X(pa_context_get_state) \ - X(pa_context_new) \ - X(pa_context_rttime_new) \ - X(pa_context_set_sink_input_volume) \ - X(pa_context_set_state_callback) \ - X(pa_context_unref) \ - X(pa_cvolume_set) \ - X(pa_cvolume_set_balance) \ - X(pa_frame_size) \ - X(pa_operation_get_state) \ - X(pa_operation_unref) \ - X(pa_proplist_gets) \ - X(pa_rtclock_now) \ - X(pa_stream_begin_write) \ - X(pa_stream_cancel_write) \ - X(pa_stream_connect_playback) \ - X(pa_stream_cork) \ - X(pa_stream_disconnect) \ - X(pa_stream_get_channel_map) \ - X(pa_stream_get_index) \ - X(pa_stream_get_latency) \ - X(pa_stream_get_sample_spec) \ - X(pa_stream_get_state) \ - X(pa_stream_get_time) \ - X(pa_stream_new) \ - X(pa_stream_set_state_callback) \ - X(pa_stream_set_write_callback) \ - X(pa_stream_unref) \ - X(pa_stream_update_timing_info) \ - X(pa_stream_write) \ - X(pa_sw_volume_from_linear) \ - X(pa_threaded_mainloop_free) \ - X(pa_threaded_mainloop_get_api) \ - X(pa_threaded_mainloop_in_thread) \ - X(pa_threaded_mainloop_lock) \ - X(pa_threaded_mainloop_new) \ - X(pa_threaded_mainloop_signal) \ - X(pa_threaded_mainloop_start) \ - X(pa_threaded_mainloop_stop) \ - X(pa_threaded_mainloop_unlock) \ - X(pa_threaded_mainloop_wait) \ - X(pa_usec_to_bytes) \ - X(pa_stream_set_read_callback) \ - X(pa_stream_connect_record) \ - X(pa_stream_readable_size) \ - X(pa_stream_writable_size) \ - X(pa_stream_peek) \ - X(pa_stream_drop) \ - X(pa_stream_get_buffer_attr) \ - X(pa_stream_get_device_name) \ - X(pa_context_set_subscribe_callback) \ - X(pa_context_subscribe) \ - X(pa_mainloop_api_once) \ - X(pa_get_library_version) \ - X(pa_channel_map_init_auto) \ - X(pa_stream_set_name) \ +#define WRAP(x) (*cubeb_##x) +#define LIBPULSE_API_VISIT(X) \ + X(pa_channel_map_can_balance) \ + X(pa_channel_map_init) \ + X(pa_context_connect) \ + X(pa_context_disconnect) \ + X(pa_context_drain) \ + X(pa_context_get_server_info) \ + X(pa_context_get_sink_info_by_name) \ + X(pa_context_get_sink_info_list) \ + X(pa_context_get_sink_input_info) \ + X(pa_context_get_source_info_list) \ + X(pa_context_get_state) \ + X(pa_context_new) \ + X(pa_context_rttime_new) \ + X(pa_context_set_sink_input_volume) \ + X(pa_context_set_state_callback) \ + X(pa_context_unref) \ + X(pa_cvolume_set) \ + X(pa_cvolume_set_balance) \ + X(pa_frame_size) \ + X(pa_operation_get_state) \ + X(pa_operation_unref) \ + X(pa_proplist_gets) \ + X(pa_rtclock_now) \ + X(pa_stream_begin_write) \ + X(pa_stream_cancel_write) \ + X(pa_stream_connect_playback) \ + X(pa_stream_cork) \ + X(pa_stream_disconnect) \ + X(pa_stream_get_channel_map) \ + X(pa_stream_get_index) \ + X(pa_stream_get_latency) \ + X(pa_stream_get_sample_spec) \ + X(pa_stream_get_state) \ + X(pa_stream_get_time) \ + X(pa_stream_new) \ + X(pa_stream_set_state_callback) \ + X(pa_stream_set_write_callback) \ + X(pa_stream_unref) \ + X(pa_stream_update_timing_info) \ + X(pa_stream_write) \ + X(pa_sw_volume_from_linear) \ + X(pa_threaded_mainloop_free) \ + X(pa_threaded_mainloop_get_api) \ + X(pa_threaded_mainloop_in_thread) \ + X(pa_threaded_mainloop_lock) \ + X(pa_threaded_mainloop_new) \ + X(pa_threaded_mainloop_signal) \ + X(pa_threaded_mainloop_start) \ + X(pa_threaded_mainloop_stop) \ + X(pa_threaded_mainloop_unlock) \ + X(pa_threaded_mainloop_wait) \ + X(pa_usec_to_bytes) \ + X(pa_stream_set_read_callback) \ + X(pa_stream_connect_record) \ + X(pa_stream_readable_size) \ + X(pa_stream_writable_size) \ + X(pa_stream_peek) \ + X(pa_stream_drop) \ + X(pa_stream_get_buffer_attr) \ + X(pa_stream_get_device_name) \ + X(pa_context_set_subscribe_callback) \ + X(pa_context_subscribe) \ + X(pa_mainloop_api_once) \ + X(pa_get_library_version) \ + X(pa_channel_map_init_auto) \ + X(pa_stream_set_name) #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; LIBPULSE_API_VISIT(MAKE_TYPEDEF); @@ -139,11 +139,7 @@ struct cubeb_stream { static const float PULSE_NO_GAIN = -1.0; -enum cork_state { - UNCORK = 0, - CORK = 1 << 0, - NOTIFY = 1 << 1 -}; +enum cork_state { UNCORK = 0, CORK = 1 << 0, NOTIFY = 1 << 1 }; static int intern_device_id(cubeb * ctx, char const ** id) @@ -164,14 +160,16 @@ intern_device_id(cubeb * ctx, char const ** id) } static void -sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u) +sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, + void * u) { (void)context; cubeb * ctx = u; if (!eol) { free(ctx->default_sink_info); ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info)); - memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, sizeof(pa_channel_map)); + memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, + sizeof(pa_channel_map)); ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate; ctx->default_sink_info->flags = info->flags; } @@ -179,10 +177,12 @@ sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, voi } static void -server_info_callback(pa_context * context, const pa_server_info * info, void * u) +server_info_callback(pa_context * context, const pa_server_info * info, + void * u) { pa_operation * o; - o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u); + o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, + sink_info_callback, u); if (o) { WRAP(pa_operation_unref)(o); } @@ -223,7 +223,8 @@ stream_state_change_callback(cubeb_stream * stm, cubeb_state s) } static void -stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u) +stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, + struct timeval const * tv, void * u) { (void)a; (void)tv; @@ -247,7 +248,8 @@ stream_state_callback(pa_stream * s, void * u) } static void -trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cubeb_stream * stm) +trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, + cubeb_stream * stm) { void * buffer; size_t size; @@ -264,13 +266,17 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub while (towrite) { size = towrite; r = WRAP(pa_stream_begin_write)(s, &buffer, &size); - // Note: this has failed running under rr on occassion - needs investigation. + // Note: this has failed running under rr on occassion - needs + // investigation. assert(r == 0); assert(size > 0); assert(size % frame_size == 0); - LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", size, read_offset); - got = stm->data_callback(stm, stm->user_ptr, (uint8_t const *)input_data + read_offset, buffer, size / frame_size); + LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", + size, read_offset); + got = stm->data_callback(stm, stm->user_ptr, + (uint8_t const *)input_data + read_offset, buffer, + size / frame_size); if (got < 0) { WRAP(pa_stream_cancel_write)(s); stm->shutdown = 1; @@ -283,7 +289,7 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub } if (stm->volume != PULSE_NO_GAIN) { - uint32_t samples = size * stm->output_sample_spec.channels / frame_size ; + uint32_t samples = size * stm->output_sample_spec.channels / frame_size; if (stm->output_sample_spec.format == PA_SAMPLE_S16BE || stm->output_sample_spec.format == PA_SAMPLE_S16LE) { @@ -299,10 +305,11 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub } } - r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE); + r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, + PA_SEEK_RELATIVE); assert(r == 0); - if ((size_t) got < size / frame_size) { + if ((size_t)got < size / frame_size) { pa_usec_t latency = 0; r = WRAP(pa_stream_get_latency)(s, &latency, NULL); if (r == -PA_ERR_NODATA) { @@ -313,7 +320,9 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub /* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */ /* arbitrary safety margin: double the current latency. */ assert(!stm->drain_timer); - stm->drain_timer = WRAP(pa_context_rttime_new)(stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, stream_drain_callback, stm); + stm->drain_timer = WRAP(pa_context_rttime_new)( + stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, + stream_drain_callback, stm); stm->shutdown = 1; return; } @@ -341,12 +350,11 @@ stream_write_callback(pa_stream * s, size_t nbytes, void * u) { LOGV("Output callback to be written buffer size %zd", nbytes); cubeb_stream * stm = u; - if (stm->shutdown || - stm->state != CUBEB_STATE_STARTED) { + if (stm->shutdown || stm->state != CUBEB_STATE_STARTED) { return; } - if (!stm->input_stream){ + if (!stm->input_stream) { // Output/playback only operation. // Write directly to output assert(!stm->input_stream && stm->output_stream); @@ -379,8 +387,9 @@ stream_read_callback(pa_stream * s, size_t nbytes, void * u) trigger_user_callback(stm->output_stream, read_data, write_size, stm); } else { // input/capture only operation. Call callback directly - long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, read_frames); - if (got < 0 || (size_t) got != read_frames) { + long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, + read_frames); + if (got < 0 || (size_t)got != read_frames) { WRAP(pa_stream_cancel_write)(s); stm->shutdown = 1; break; @@ -414,7 +423,7 @@ wait_until_context_ready(cubeb * ctx) static int wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop) { - if (!stream || !mainloop){ + if (!stream || !mainloop) { return -1; } for (;;) { @@ -432,11 +441,13 @@ static int wait_until_stream_ready(cubeb_stream * stm) { if (stm->output_stream && - wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == -1) { + wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == + -1) { return -1; } - if(stm->input_stream && - wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) { + if (stm->input_stream && + wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == + -1) { return -1; } return 0; @@ -464,7 +475,8 @@ cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state) if (!io_stream) { return; } - o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm); + o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, + stm); if (o) { operation_wait(stm->context, io_stream, o); WRAP(pa_operation_unref)(o); @@ -491,7 +503,8 @@ stream_update_timing_info(cubeb_stream * stm) int r = -1; pa_operation * o = NULL; if (stm->output_stream) { - o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm); + o = WRAP(pa_stream_update_timing_info)(stm->output_stream, + stream_success_callback, stm); if (o) { r = operation_wait(stm->context, stm->output_stream, o); WRAP(pa_operation_unref)(o); @@ -502,7 +515,8 @@ stream_update_timing_info(cubeb_stream * stm) } if (stm->input_stream) { - o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm); + o = WRAP(pa_stream_update_timing_info)(stm->input_stream, + stream_success_callback, stm); if (o) { r = operation_wait(stm->context, stm->input_stream, o); WRAP(pa_operation_unref)(o); @@ -516,44 +530,44 @@ static pa_channel_position_t cubeb_channel_to_pa_channel(cubeb_channel channel) { switch (channel) { - case CHANNEL_FRONT_LEFT: - return PA_CHANNEL_POSITION_FRONT_LEFT; - case CHANNEL_FRONT_RIGHT: - return PA_CHANNEL_POSITION_FRONT_RIGHT; - case CHANNEL_FRONT_CENTER: - return PA_CHANNEL_POSITION_FRONT_CENTER; - case CHANNEL_LOW_FREQUENCY: - return PA_CHANNEL_POSITION_LFE; - case CHANNEL_BACK_LEFT: - return PA_CHANNEL_POSITION_REAR_LEFT; - case CHANNEL_BACK_RIGHT: - return PA_CHANNEL_POSITION_REAR_RIGHT; - case CHANNEL_FRONT_LEFT_OF_CENTER: - return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; - case CHANNEL_FRONT_RIGHT_OF_CENTER: - return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; - case CHANNEL_BACK_CENTER: - return PA_CHANNEL_POSITION_REAR_CENTER; - case CHANNEL_SIDE_LEFT: - return PA_CHANNEL_POSITION_SIDE_LEFT; - case CHANNEL_SIDE_RIGHT: - return PA_CHANNEL_POSITION_SIDE_RIGHT; - case CHANNEL_TOP_CENTER: - return PA_CHANNEL_POSITION_TOP_CENTER; - case CHANNEL_TOP_FRONT_LEFT: - return PA_CHANNEL_POSITION_TOP_FRONT_LEFT; - case CHANNEL_TOP_FRONT_CENTER: - return PA_CHANNEL_POSITION_TOP_FRONT_CENTER; - case CHANNEL_TOP_FRONT_RIGHT: - return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; - case CHANNEL_TOP_BACK_LEFT: - return PA_CHANNEL_POSITION_TOP_REAR_LEFT; - case CHANNEL_TOP_BACK_CENTER: - return PA_CHANNEL_POSITION_TOP_REAR_CENTER; - case CHANNEL_TOP_BACK_RIGHT: - return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; - default: - return PA_CHANNEL_POSITION_INVALID; + case CHANNEL_FRONT_LEFT: + return PA_CHANNEL_POSITION_FRONT_LEFT; + case CHANNEL_FRONT_RIGHT: + return PA_CHANNEL_POSITION_FRONT_RIGHT; + case CHANNEL_FRONT_CENTER: + return PA_CHANNEL_POSITION_FRONT_CENTER; + case CHANNEL_LOW_FREQUENCY: + return PA_CHANNEL_POSITION_LFE; + case CHANNEL_BACK_LEFT: + return PA_CHANNEL_POSITION_REAR_LEFT; + case CHANNEL_BACK_RIGHT: + return PA_CHANNEL_POSITION_REAR_RIGHT; + case CHANNEL_FRONT_LEFT_OF_CENTER: + return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER; + case CHANNEL_FRONT_RIGHT_OF_CENTER: + return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; + case CHANNEL_BACK_CENTER: + return PA_CHANNEL_POSITION_REAR_CENTER; + case CHANNEL_SIDE_LEFT: + return PA_CHANNEL_POSITION_SIDE_LEFT; + case CHANNEL_SIDE_RIGHT: + return PA_CHANNEL_POSITION_SIDE_RIGHT; + case CHANNEL_TOP_CENTER: + return PA_CHANNEL_POSITION_TOP_CENTER; + case CHANNEL_TOP_FRONT_LEFT: + return PA_CHANNEL_POSITION_TOP_FRONT_LEFT; + case CHANNEL_TOP_FRONT_CENTER: + return PA_CHANNEL_POSITION_TOP_FRONT_CENTER; + case CHANNEL_TOP_FRONT_RIGHT: + return PA_CHANNEL_POSITION_TOP_FRONT_RIGHT; + case CHANNEL_TOP_BACK_LEFT: + return PA_CHANNEL_POSITION_TOP_REAR_LEFT; + case CHANNEL_TOP_BACK_CENTER: + return PA_CHANNEL_POSITION_TOP_REAR_CENTER; + case CHANNEL_TOP_BACK_RIGHT: + return PA_CHANNEL_POSITION_TOP_REAR_RIGHT; + default: + return PA_CHANNEL_POSITION_INVALID; } } @@ -566,7 +580,7 @@ layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm) uint32_t channels = 0; cubeb_channel_layout channelMap = layout; - for (uint32_t i = 0 ; channelMap != 0; ++i) { + for (uint32_t i = 0; channelMap != 0; ++i) { uint32_t channel = (channelMap & 1) << i; if (channel != 0) { cm->map[channels] = cubeb_channel_to_pa_channel(channel); @@ -576,7 +590,7 @@ layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm) } unsigned int channels_from_layout = cubeb_channel_layout_nb_channels(layout); assert(channels_from_layout <= UINT8_MAX); - cm->channels = (uint8_t) channels_from_layout; + cm->channels = (uint8_t)channels_from_layout; // Special case single channel center mapping as mono. if (cm->channels == 1 && cm->map[0] == PA_CHANNEL_POSITION_FRONT_CENTER) { @@ -584,8 +598,10 @@ layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm) } } -static void pulse_context_destroy(cubeb * ctx); -static void pulse_destroy(cubeb * ctx); +static void +pulse_context_destroy(cubeb * ctx); +static void +pulse_destroy(cubeb * ctx); static int pulse_context_init(cubeb * ctx) @@ -597,12 +613,13 @@ pulse_context_init(cubeb * ctx) pulse_context_destroy(ctx); } - ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), - ctx->context_name); + ctx->context = WRAP(pa_context_new)( + WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), ctx->context_name); if (!ctx->context) { return -1; } - WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx); + WRAP(pa_context_set_state_callback) + (ctx->context, context_state_callback, ctx); WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL); @@ -621,8 +638,8 @@ pulse_context_init(cubeb * ctx) return 0; } -static int pulse_subscribe_notifications(cubeb * context, - pa_subscription_mask_t mask); +static int +pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask); /*static*/ int pulse_init(cubeb ** context, char const * context_name) @@ -642,12 +659,13 @@ pulse_init(cubeb ** context, char const * context_name) } } -#define LOAD(x) { \ - cubeb_##x = dlsym(libpulse, #x); \ - if (!cubeb_##x) { \ - dlclose(libpulse); \ - return CUBEB_ERROR; \ - } \ +#define LOAD(x) \ + { \ + cubeb_##x = dlsym(libpulse, #x); \ + if (!cubeb_##x) { \ + dlclose(libpulse); \ + return CUBEB_ERROR; \ + } \ } LIBPULSE_API_VISIT(LOAD); @@ -655,7 +673,7 @@ pulse_init(cubeb ** context, char const * context_name) #endif #if PA_CHECK_VERSION(2, 0, 0) - const char* version = WRAP(pa_get_library_version)(); + const char * version = WRAP(pa_get_library_version)(); has_pulse_v2 = strtol(version, NULL, 10) >= 2; #endif @@ -735,7 +753,8 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) } static int -pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) +pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_frames) { (void)ctx; // According to PulseAudio developers, this is a safe minimum. @@ -785,7 +804,8 @@ pulse_destroy(cubeb * ctx) free(ctx); } -static void pulse_stream_destroy(cubeb_stream * stm); +static void +pulse_stream_destroy(cubeb_stream * stm); static pa_sample_format_t to_pulse_format(cubeb_sample_format format) @@ -807,33 +827,40 @@ to_pulse_format(cubeb_sample_format format) static cubeb_channel_layout pulse_default_layout_for_channels(uint32_t ch) { - assert (ch > 0 && ch <= 8); + assert(ch > 0 && ch <= 8); switch (ch) { - case 1: return CUBEB_LAYOUT_MONO; - case 2: return CUBEB_LAYOUT_STEREO; - case 3: return CUBEB_LAYOUT_3F; - case 4: return CUBEB_LAYOUT_QUAD; - case 5: return CUBEB_LAYOUT_3F2; - case 6: return CUBEB_LAYOUT_3F_LFE | - CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT; - case 7: return CUBEB_LAYOUT_3F3R_LFE; - case 8: return CUBEB_LAYOUT_3F4_LFE; + case 1: + return CUBEB_LAYOUT_MONO; + case 2: + return CUBEB_LAYOUT_STEREO; + case 3: + return CUBEB_LAYOUT_3F; + case 4: + return CUBEB_LAYOUT_QUAD; + case 5: + return CUBEB_LAYOUT_3F2; + case 6: + return CUBEB_LAYOUT_3F_LFE | CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT; + case 7: + return CUBEB_LAYOUT_3F3R_LFE; + case 8: + return CUBEB_LAYOUT_3F4_LFE; } // Never get here! return CUBEB_LAYOUT_UNDEFINED; } static int -create_pa_stream(cubeb_stream * stm, - pa_stream ** pa_stm, - cubeb_stream_params * stream_params, - char const * stream_name) +create_pa_stream(cubeb_stream * stm, pa_stream ** pa_stm, + cubeb_stream_params * stream_params, char const * stream_name) { assert(stm && stream_params); - assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm && - (stream_params->layout == CUBEB_LAYOUT_UNDEFINED || - (stream_params->layout != CUBEB_LAYOUT_UNDEFINED && - cubeb_channel_layout_nb_channels(stream_params->layout) == stream_params->channels)))); + assert(&stm->input_stream == pa_stm || + (&stm->output_stream == pa_stm && + (stream_params->layout == CUBEB_LAYOUT_UNDEFINED || + (stream_params->layout != CUBEB_LAYOUT_UNDEFINED && + cubeb_channel_layout_nb_channels(stream_params->layout) == + stream_params->channels)))); if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { return CUBEB_ERROR_NOT_SUPPORTED; } @@ -845,18 +872,23 @@ create_pa_stream(cubeb_stream * stm, ss.rate = stream_params->rate; if (stream_params->channels > UINT8_MAX) return CUBEB_ERROR_INVALID_FORMAT; - ss.channels = (uint8_t) stream_params->channels; + ss.channels = (uint8_t)stream_params->channels; if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) { pa_channel_map cm; if (stream_params->channels <= 8 && - !WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, PA_CHANNEL_MAP_DEFAULT)) { - LOG("Layout undefined and PulseAudio's default layout has not been configured, guess one."); - layout_to_channel_map(pulse_default_layout_for_channels(stream_params->channels), &cm); - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); + !WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, + PA_CHANNEL_MAP_DEFAULT)) { + LOG("Layout undefined and PulseAudio's default layout has not been " + "configured, guess one."); + layout_to_channel_map( + pulse_default_layout_for_channels(stream_params->channels), &cm); + *pa_stm = + WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); } else { LOG("Layout undefined, PulseAudio will use its default."); - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); + *pa_stm = + WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); } } else { pa_channel_map cm; @@ -867,33 +899,33 @@ create_pa_stream(cubeb_stream * stm, } static pa_buffer_attr -set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec) +set_buffering_attribute(unsigned int latency_frames, + pa_sample_spec * sample_spec) { pa_buffer_attr battr; battr.maxlength = -1; - battr.prebuf = -1; - battr.tlength = latency_frames * WRAP(pa_frame_size)(sample_spec); - battr.minreq = battr.tlength / 4; - battr.fragsize = battr.minreq; + battr.prebuf = -1; + battr.tlength = latency_frames * WRAP(pa_frame_size)(sample_spec); + battr.minreq = battr.tlength / 4; + battr.fragsize = battr.minreq; - LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u", - battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize); + LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq " + "%u, fragsize %u", + battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, + battr.fragsize); return battr; } static int -pulse_stream_init(cubeb * context, - cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, +pulse_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int latency_frames, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + cubeb_state_callback state_callback, void * user_ptr) { cubeb_stream * stm; pa_buffer_attr battr; @@ -921,47 +953,53 @@ pulse_stream_init(cubeb * context, WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); if (output_stream_params) { - r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name); + r = create_pa_stream(stm, &stm->output_stream, output_stream_params, + stream_name); if (r != CUBEB_OK) { WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); pulse_stream_destroy(stm); return r; } - stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream)); + stm->output_sample_spec = + *(WRAP(pa_stream_get_sample_spec)(stm->output_stream)); - WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm); - WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm); + WRAP(pa_stream_set_state_callback) + (stm->output_stream, stream_state_callback, stm); + WRAP(pa_stream_set_write_callback) + (stm->output_stream, stream_write_callback, stm); battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec); - WRAP(pa_stream_connect_playback)(stm->output_stream, - (char const *) output_device, - &battr, - PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | - PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY, - NULL, NULL); + WRAP(pa_stream_connect_playback) + (stm->output_stream, (char const *)output_device, &battr, + PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | + PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY, + NULL, NULL); } // Set up input stream if (input_stream_params) { - r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name); + r = create_pa_stream(stm, &stm->input_stream, input_stream_params, + stream_name); if (r != CUBEB_OK) { WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); pulse_stream_destroy(stm); return r; } - stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream)); + stm->input_sample_spec = + *(WRAP(pa_stream_get_sample_spec)(stm->input_stream)); - WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm); - WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm); + WRAP(pa_stream_set_state_callback) + (stm->input_stream, stream_state_callback, stm); + WRAP(pa_stream_set_read_callback) + (stm->input_stream, stream_read_callback, stm); battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec); - WRAP(pa_stream_connect_record)(stm->input_stream, - (char const *) input_device, - &battr, - PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | - PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY); + WRAP(pa_stream_connect_record) + (stm->input_stream, (char const *)input_device, &battr, + PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING | + PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY); } r = wait_until_stream_ready(stm); @@ -979,18 +1017,22 @@ pulse_stream_init(cubeb * context, } if (g_cubeb_log_level) { - if (output_stream_params){ + if (output_stream_params) { const pa_buffer_attr * output_att; output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream); - LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",output_att->maxlength, output_att->tlength, - output_att->prebuf, output_att->minreq, output_att->fragsize); + LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, " + "minreq %u, fragsize %u", + output_att->maxlength, output_att->tlength, output_att->prebuf, + output_att->minreq, output_att->fragsize); } - if (input_stream_params){ + if (input_stream_params) { const pa_buffer_attr * input_att; input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream); - LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",input_att->maxlength, input_att->tlength, - input_att->prebuf, input_att->minreq, input_att->fragsize); + LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq " + "%u, fragsize %u", + input_att->maxlength, input_att->tlength, input_att->prebuf, + input_att->minreq, input_att->fragsize); } } @@ -1010,7 +1052,8 @@ pulse_stream_destroy(cubeb_stream * stm) if (stm->drain_timer) { /* there's no pa_rttime_free, so use this instead. */ - WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer); + WRAP(pa_threaded_mainloop_get_api) + (stm->context->mainloop)->time_free(stm->drain_timer); } WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL); @@ -1054,8 +1097,9 @@ pulse_stream_start(cubeb_stream * stm) * things roll. This is done via a defer event in order to execute it * from PA server thread. */ WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - WRAP(pa_mainloop_api_once)(WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop), - pulse_defer_event_cb, stm); + WRAP(pa_mainloop_api_once) + (WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop), + pulse_defer_event_cb, stm); WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); } @@ -1131,7 +1175,7 @@ pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency) } static void -volume_success(pa_context *c, int success, void *userdata) +volume_success(pa_context * c, int success, void * userdata) { (void)success; (void)c; @@ -1141,7 +1185,7 @@ volume_success(pa_context *c, int success, void *userdata) } static void -rename_success(pa_stream *s, int success, void *userdata) +rename_success(pa_stream * s, int success, void * userdata) { cubeb_stream * stream = userdata; assert(success); @@ -1178,9 +1222,8 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume) index = WRAP(pa_stream_get_index)(stm->output_stream); - op = WRAP(pa_context_set_sink_input_volume)(ctx->context, - index, &cvol, volume_success, - stm); + op = WRAP(pa_context_set_sink_input_volume)(ctx->context, index, &cvol, + volume_success, stm); if (op) { operation_wait(ctx, stm->output_stream, op); WRAP(pa_operation_unref)(op); @@ -1201,8 +1244,8 @@ pulse_stream_set_name(cubeb_stream * stm, char const * stream_name) WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - pa_operation * op = - WRAP(pa_stream_set_name)(stm->output_stream, stream_name, rename_success, stm); + pa_operation * op = WRAP(pa_stream_set_name)(stm->output_stream, stream_name, + rename_success, stm); if (op) { operation_wait(stm->context, stm->output_stream, op); @@ -1242,12 +1285,12 @@ pulse_format_to_cubeb_format(pa_sample_format_t format) } static void -pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data) +pulse_ensure_dev_list_data_list_size(pulse_dev_list_data * list_data) { if (list_data->count == list_data->max) { list_data->max += 8; - list_data->devinfo = realloc(list_data->devinfo, - sizeof(cubeb_device_info) * list_data->max); + list_data->devinfo = + realloc(list_data->devinfo, sizeof(cubeb_device_info) * list_data->max); } } @@ -1267,8 +1310,8 @@ pulse_get_state_from_sink_port(pa_sink_port_info * info) } static void -pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, - int eol, void * user_data) +pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, int eol, + void * user_data) { pulse_dev_list_data * list_data = user_data; cubeb_device_info * devinfo; @@ -1296,7 +1339,7 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, memset(devinfo, 0, sizeof(cubeb_device_info)); devinfo->device_id = device_id; - devinfo->devid = (cubeb_devid) devinfo->device_id; + devinfo->devid = (cubeb_devid)devinfo->device_id; devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) @@ -1307,11 +1350,13 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT; devinfo->state = pulse_get_state_from_sink_port(info->active_port); - devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ? - CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) + ? CUBEB_DEVICE_PREF_ALL + : CUBEB_DEVICE_PREF_NONE; devinfo->format = CUBEB_DEVICE_FMT_ALL; - devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); + devinfo->default_format = + pulse_format_to_cubeb_format(info->sample_spec.format); devinfo->max_channels = info->channel_map.channels; devinfo->min_rate = 1; devinfo->max_rate = PA_RATE_MAX; @@ -1339,8 +1384,8 @@ pulse_get_state_from_source_port(pa_source_port_info * info) } static void -pulse_source_info_cb(pa_context * context, const pa_source_info * info, - int eol, void * user_data) +pulse_source_info_cb(pa_context * context, const pa_source_info * info, int eol, + void * user_data) { pulse_dev_list_data * list_data = user_data; cubeb_device_info * devinfo; @@ -1365,7 +1410,7 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, memset(devinfo, 0, sizeof(cubeb_device_info)); devinfo->device_id = device_id; - devinfo->devid = (cubeb_devid) devinfo->device_id; + devinfo->devid = (cubeb_devid)devinfo->device_id; devinfo->friendly_name = strdup(info->description); prop = WRAP(pa_proplist_gets)(info->proplist, "sysfs.path"); if (prop) @@ -1376,11 +1421,13 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info, devinfo->type = CUBEB_DEVICE_TYPE_INPUT; devinfo->state = pulse_get_state_from_source_port(info->active_port); - devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ? - CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) + ? CUBEB_DEVICE_PREF_ALL + : CUBEB_DEVICE_PREF_NONE; devinfo->format = CUBEB_DEVICE_FMT_ALL; - devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format); + devinfo->default_format = + pulse_format_to_cubeb_format(info->sample_spec.format); devinfo->max_channels = info->channel_map.channels; devinfo->min_rate = 1; devinfo->max_rate = PA_RATE_MAX; @@ -1402,9 +1449,9 @@ pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata) free(list_data->default_sink_name); free(list_data->default_source_name); list_data->default_sink_name = - i->default_sink_name ? strdup(i->default_sink_name) : NULL; + i->default_sink_name ? strdup(i->default_sink_name) : NULL; list_data->default_source_name = - i->default_source_name ? strdup(i->default_source_name) : NULL; + i->default_source_name ? strdup(i->default_source_name) : NULL; WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); } @@ -1413,13 +1460,13 @@ static int pulse_enumerate_devices(cubeb * context, cubeb_device_type type, cubeb_device_collection * collection) { - pulse_dev_list_data user_data = { NULL, NULL, NULL, 0, 0, context }; + pulse_dev_list_data user_data = {NULL, NULL, NULL, 0, 0, context}; pa_operation * o; WRAP(pa_threaded_mainloop_lock)(context->mainloop); - o = WRAP(pa_context_get_server_info)(context->context, - pulse_server_info_cb, &user_data); + o = WRAP(pa_context_get_server_info)(context->context, pulse_server_info_cb, + &user_data); if (o) { operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); @@ -1427,7 +1474,7 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type, if (type & CUBEB_DEVICE_TYPE_OUTPUT) { o = WRAP(pa_context_get_sink_info_list)(context->context, - pulse_sink_info_cb, &user_data); + pulse_sink_info_cb, &user_data); if (o) { operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); @@ -1436,7 +1483,7 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type, if (type & CUBEB_DEVICE_TYPE_INPUT) { o = WRAP(pa_context_get_source_info_list)(context->context, - pulse_source_info_cb, &user_data); + pulse_source_info_cb, &user_data); if (o) { operation_wait(context, NULL, o); WRAP(pa_operation_unref)(o); @@ -1454,14 +1501,15 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type, } static int -pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection) +pulse_device_collection_destroy(cubeb * ctx, + cubeb_device_collection * collection) { size_t n; for (n = 0; n < collection->count; n++) { - free((void *) collection->device[n].friendly_name); - free((void *) collection->device[n].vendor_name); - free((void *) collection->device[n].group_id); + free((void *)collection->device[n].friendly_name); + free((void *)collection->device[n].vendor_name); + free((void *)collection->device[n].group_id); } free(collection->device); @@ -1469,7 +1517,8 @@ pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collectio } static int -pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device) +pulse_stream_get_current_device(cubeb_stream * stm, + cubeb_device ** const device) { #if PA_CHECK_VERSION(0, 9, 8) *device = calloc(1, sizeof(cubeb_device)); @@ -1493,8 +1542,7 @@ pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device } static int -pulse_stream_device_destroy(cubeb_stream * stream, - cubeb_device * device) +pulse_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) { (void)stream; free(device->input_name); @@ -1504,8 +1552,7 @@ pulse_stream_device_destroy(cubeb_stream * stream, } static void -pulse_subscribe_callback(pa_context * ctx, - pa_subscription_event_type_t t, +pulse_subscribe_callback(pa_context * ctx, pa_subscription_event_type_t t, uint32_t index, void * userdata) { (void)ctx; @@ -1515,36 +1562,49 @@ pulse_subscribe_callback(pa_context * ctx, case PA_SUBSCRIPTION_EVENT_SERVER: if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) { LOG("Server changed %d", index); - WRAP(pa_context_get_server_info)(context->context, server_info_callback, context); + WRAP(pa_context_get_server_info) + (context->context, server_info_callback, context); } break; case PA_SUBSCRIPTION_EVENT_SOURCE: case PA_SUBSCRIPTION_EVENT_SINK: if (g_cubeb_log_level) { - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == + PA_SUBSCRIPTION_EVENT_SOURCE && + (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == + PA_SUBSCRIPTION_EVENT_REMOVE) { LOG("Removing source index %d", index); - } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == + PA_SUBSCRIPTION_EVENT_SOURCE && + (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == + PA_SUBSCRIPTION_EVENT_NEW) { LOG("Adding source index %d", index); } - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) { + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == + PA_SUBSCRIPTION_EVENT_SINK && + (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == + PA_SUBSCRIPTION_EVENT_REMOVE) { LOG("Removing sink index %d", index); - } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { + } else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == + PA_SUBSCRIPTION_EVENT_SINK && + (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == + PA_SUBSCRIPTION_EVENT_NEW) { LOG("Adding sink index %d", index); } } if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE || (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) { - context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr); + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == + PA_SUBSCRIPTION_EVENT_SOURCE) { + context->input_collection_changed_callback( + context, context->input_collection_changed_user_ptr); } - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) { - context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr); + if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == + PA_SUBSCRIPTION_EVENT_SINK) { + context->output_collection_changed_callback( + context, context->output_collection_changed_user_ptr); } } break; @@ -1552,7 +1612,7 @@ pulse_subscribe_callback(pa_context * ctx, } static void -subscribe_success(pa_context *c, int success, void *userdata) +subscribe_success(pa_context * c, int success, void * userdata) { (void)c; cubeb * context = userdata; @@ -1561,13 +1621,16 @@ subscribe_success(pa_context *c, int success, void *userdata) } static int -pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) { +pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) +{ WRAP(pa_threaded_mainloop_lock)(context->mainloop); - WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context); + WRAP(pa_context_set_subscribe_callback) + (context->context, pulse_subscribe_callback, context); pa_operation * o; - o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context); + o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, + context); if (o == NULL) { WRAP(pa_threaded_mainloop_unlock)(context->mainloop); LOG("Context subscribe failed"); @@ -1582,10 +1645,10 @@ pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) { } static int -pulse_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) +pulse_register_device_collection_changed( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback collection_changed_callback, + void * user_ptr) { if (devtype & CUBEB_DEVICE_TYPE_INPUT) { context->input_collection_changed_callback = collection_changed_callback; @@ -1613,26 +1676,25 @@ pulse_register_device_collection_changed(cubeb * context, } static struct cubeb_ops const pulse_ops = { - .init = pulse_init, - .get_backend_id = pulse_get_backend_id, - .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, - .enumerate_devices = pulse_enumerate_devices, - .device_collection_destroy = pulse_device_collection_destroy, - .destroy = pulse_destroy, - .stream_init = pulse_stream_init, - .stream_destroy = pulse_stream_destroy, - .stream_start = pulse_stream_start, - .stream_stop = pulse_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = pulse_stream_get_position, - .stream_get_latency = pulse_stream_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = pulse_stream_set_volume, - .stream_set_name = pulse_stream_set_name, - .stream_get_current_device = pulse_stream_get_current_device, - .stream_device_destroy = pulse_stream_device_destroy, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = pulse_register_device_collection_changed -}; + .init = pulse_init, + .get_backend_id = pulse_get_backend_id, + .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, + .enumerate_devices = pulse_enumerate_devices, + .device_collection_destroy = pulse_device_collection_destroy, + .destroy = pulse_destroy, + .stream_init = pulse_stream_init, + .stream_destroy = pulse_stream_destroy, + .stream_start = pulse_stream_start, + .stream_stop = pulse_stream_stop, + .stream_get_position = pulse_stream_get_position, + .stream_get_latency = pulse_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = pulse_stream_set_volume, + .stream_set_name = pulse_stream_set_name, + .stream_get_current_device = pulse_stream_get_current_device, + .stream_device_destroy = pulse_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = + pulse_register_device_collection_changed}; diff --git a/externals/cubeb/src/cubeb_resampler.cpp b/externals/cubeb/src/cubeb_resampler.cpp index 29ad7365f..d61b1daf2 100755 --- a/externals/cubeb/src/cubeb_resampler.cpp +++ b/externals/cubeb/src/cubeb_resampler.cpp @@ -8,21 +8,21 @@ #define NOMINMAX #endif // NOMINMAX -#include -#include -#include -#include -#include -#include #include "cubeb_resampler.h" #include "cubeb-speex-resampler.h" #include "cubeb_resampler_internal.h" #include "cubeb_utils.h" +#include +#include +#include +#include +#include +#include int to_speex_quality(cubeb_resampler_quality q) { - switch(q) { + switch (q) { case CUBEB_RESAMPLER_QUALITY_VOIP: return SPEEX_RESAMPLER_QUALITY_VOIP; case CUBEB_RESAMPLER_QUALITY_DEFAULT: @@ -35,34 +35,34 @@ to_speex_quality(cubeb_resampler_quality q) } } -uint32_t min_buffered_audio_frame(uint32_t sample_rate) +uint32_t +min_buffered_audio_frame(uint32_t sample_rate) { return sample_rate / 20; } -template +template passthrough_resampler::passthrough_resampler(cubeb_stream * s, cubeb_data_callback cb, void * ptr, uint32_t input_channels, uint32_t sample_rate) - : processor(input_channels) - , stream(s) - , data_callback(cb) - , user_ptr(ptr) - , sample_rate(sample_rate) + : processor(input_channels), stream(s), data_callback(cb), user_ptr(ptr), + sample_rate(sample_rate) { } -template -long passthrough_resampler::fill(void * input_buffer, long * input_frames_count, - void * output_buffer, long output_frames) +template +long +passthrough_resampler::fill(void * input_buffer, long * input_frames_count, + void * output_buffer, long output_frames) { if (input_buffer) { assert(input_frames_count); } assert((input_buffer && output_buffer) || - (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) || + (output_buffer && !input_buffer && + (!input_frames_count || *input_frames_count == 0)) || (input_buffer && !output_buffer && output_frames == 0)); // When we have no pending input data and exactly as much input @@ -71,41 +71,44 @@ long passthrough_resampler::fill(void * input_buffer, long * input_frames_cou void * in_buf = input_buffer; unsigned long pop_input_count = 0u; if (input_buffer && !output_buffer) { - output_frames = *input_frames_count; - } else if(input_buffer) { + output_frames = *input_frames_count; + } else if (input_buffer) { if (internal_input_buffer.length() != 0 || *input_frames_count < output_frames) { // If we have pending input data left and have to first append the input // so we can pass it as one pointer to the callback. Or this is a glitch. // It can happen when system's performance is poor. Audible silence is // being pushed at the end of the short input buffer. An improvement for - // the future is to resample to the output number of frames, when that happens. - internal_input_buffer.push(static_cast(input_buffer), + // the future is to resample to the output number of frames, when that + // happens. + internal_input_buffer.push(static_cast(input_buffer), frames_to_samples(*input_frames_count)); if (internal_input_buffer.length() < frames_to_samples(output_frames)) { // This is unxpected but it can happen when a glitch occurs. Fill the // buffer with silence. First keep the actual number of input samples // used without the silence. pop_input_count = internal_input_buffer.length(); - internal_input_buffer.push_silence( - frames_to_samples(output_frames) - internal_input_buffer.length()); + internal_input_buffer.push_silence(frames_to_samples(output_frames) - + internal_input_buffer.length()); } else { pop_input_count = frames_to_samples(output_frames); } in_buf = internal_input_buffer.data(); - } else if(*input_frames_count > output_frames) { + } else if (*input_frames_count > output_frames) { // In this case we have more input that we need output and // fill the overflowing input into internal_input_buffer // Since we have no other pending data, we can nonetheless // pass the current input data directly to the callback assert(pop_input_count == 0); unsigned long samples_off = frames_to_samples(output_frames); - internal_input_buffer.push(static_cast(input_buffer) + samples_off, - frames_to_samples(*input_frames_count - output_frames)); + internal_input_buffer.push( + static_cast(input_buffer) + samples_off, + frames_to_samples(*input_frames_count - output_frames)); } } - long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames); + long rv = + data_callback(stream, user_ptr, in_buf, output_buffer, output_frames); if (input_buffer) { if (pop_input_count) { @@ -124,51 +127,47 @@ long passthrough_resampler::fill(void * input_buffer, long * input_frames_cou template class passthrough_resampler; template class passthrough_resampler; -template -cubeb_resampler_speex - ::cubeb_resampler_speex(InputProcessor * input_processor, - OutputProcessor * output_processor, - cubeb_stream * s, - cubeb_data_callback cb, - void * ptr) - : input_processor(input_processor) - , output_processor(output_processor) - , stream(s) - , data_callback(cb) - , user_ptr(ptr) +template +cubeb_resampler_speex:: + cubeb_resampler_speex(InputProcessor * input_processor, + OutputProcessor * output_processor, cubeb_stream * s, + cubeb_data_callback cb, void * ptr) + : input_processor(input_processor), output_processor(output_processor), + stream(s), data_callback(cb), user_ptr(ptr) { if (input_processor && output_processor) { fill_internal = &cubeb_resampler_speex::fill_internal_duplex; - } else if (input_processor) { + } else if (input_processor) { fill_internal = &cubeb_resampler_speex::fill_internal_input; - } else if (output_processor) { + } else if (output_processor) { fill_internal = &cubeb_resampler_speex::fill_internal_output; } } -template -cubeb_resampler_speex - ::~cubeb_resampler_speex() -{ } - -template -long -cubeb_resampler_speex -::fill(void * input_buffer, long * input_frames_count, - void * output_buffer, long output_frames_needed) +template +cubeb_resampler_speex::~cubeb_resampler_speex() { - /* Input and output buffers, typed */ - T * in_buffer = reinterpret_cast(input_buffer); - T * out_buffer = reinterpret_cast(output_buffer); - return (this->*fill_internal)(in_buffer, input_frames_count, - out_buffer, output_frames_needed); } -template +template long -cubeb_resampler_speex -::fill_internal_output(T * input_buffer, long * input_frames_count, - T * output_buffer, long output_frames_needed) +cubeb_resampler_speex::fill( + void * input_buffer, long * input_frames_count, void * output_buffer, + long output_frames_needed) +{ + /* Input and output buffers, typed */ + T * in_buffer = reinterpret_cast(input_buffer); + T * out_buffer = reinterpret_cast(output_buffer); + return (this->*fill_internal)(in_buffer, input_frames_count, out_buffer, + output_frames_needed); +} + +template +long +cubeb_resampler_speex::fill_internal_output( + T * input_buffer, long * input_frames_count, T * output_buffer, + long output_frames_needed) { assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) && output_buffer && output_frames_needed); @@ -180,13 +179,12 @@ cubeb_resampler_speex /* fill directly the input buffer of the output processor to save a copy */ output_frames_before_processing = - output_processor->input_needed_for_output(output_frames_needed); + output_processor->input_needed_for_output(output_frames_needed); out_unprocessed = - output_processor->input_buffer(output_frames_before_processing); + output_processor->input_buffer(output_frames_before_processing); - got = data_callback(stream, user_ptr, - nullptr, out_unprocessed, + got = data_callback(stream, user_ptr, nullptr, out_unprocessed, output_frames_before_processing); if (got < output_frames_before_processing) { @@ -201,52 +199,62 @@ cubeb_resampler_speex } /* Process the output. If not enough frames have been returned from the - * callback, drain the processors. */ + * callback, drain the processors. */ return output_processor->output(output_buffer, output_frames_needed); } -template +template long -cubeb_resampler_speex -::fill_internal_input(T * input_buffer, long * input_frames_count, - T * output_buffer, long /*output_frames_needed*/) +cubeb_resampler_speex::fill_internal_input( + T * input_buffer, long * input_frames_count, T * output_buffer, + long /*output_frames_needed*/) { assert(input_buffer && input_frames_count && *input_frames_count && !output_buffer); - /* The input data, after eventual resampling. This is passed to the callback. */ + /* The input data, after eventual resampling. This is passed to the callback. + */ T * resampled_input = nullptr; - uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count); + uint32_t resampled_frame_count = + input_processor->output_for_input(*input_frames_count); /* process the input, and present exactly `output_frames_needed` in the - * callback. */ + * callback. */ input_processor->input(input_buffer, *input_frames_count); + /* resampled_frame_count == 0 happens if the resampler + * doesn't have enough input frames buffered to produce 1 resampled frame. */ + if (resampled_frame_count == 0) { + return *input_frames_count; + } + size_t frames_resampled = 0; - resampled_input = input_processor->output(resampled_frame_count, &frames_resampled); + resampled_input = + input_processor->output(resampled_frame_count, &frames_resampled); *input_frames_count = frames_resampled; - long got = data_callback(stream, user_ptr, - resampled_input, nullptr, resampled_frame_count); + long got = data_callback(stream, user_ptr, resampled_input, nullptr, + resampled_frame_count); /* Return the number of initial input frames or part of it. - * Since output_frames_needed == 0 in input scenario, the only - * available number outside resampler is the initial number of frames. */ + * Since output_frames_needed == 0 in input scenario, the only + * available number outside resampler is the initial number of frames. */ return (*input_frames_count) * (got / resampled_frame_count); } -template +template long -cubeb_resampler_speex -::fill_internal_duplex(T * in_buffer, long * input_frames_count, - T * out_buffer, long output_frames_needed) +cubeb_resampler_speex::fill_internal_duplex( + T * in_buffer, long * input_frames_count, T * out_buffer, + long output_frames_needed) { if (draining) { // discard input and drain any signal remaining in the resampler. return output_processor->output(out_buffer, output_frames_needed); } - /* The input data, after eventual resampling. This is passed to the callback. */ + /* The input data, after eventual resampling. This is passed to the callback. + */ T * resampled_input = nullptr; /* The output buffer passed down in the callback, that might be resampled. */ T * out_unprocessed = nullptr; @@ -266,26 +274,25 @@ cubeb_resampler_speex * caller. */ output_frames_before_processing = - output_processor->input_needed_for_output(output_frames_needed); - /* fill directly the input buffer of the output processor to save a copy */ + output_processor->input_needed_for_output(output_frames_needed); + /* fill directly the input buffer of the output processor to save a copy */ out_unprocessed = - output_processor->input_buffer(output_frames_before_processing); + output_processor->input_buffer(output_frames_before_processing); if (in_buffer) { /* process the input, and present exactly `output_frames_needed` in the - * callback. */ + * callback. */ input_processor->input(in_buffer, *input_frames_count); size_t frames_resampled = 0; - resampled_input = - input_processor->output(output_frames_before_processing, &frames_resampled); + resampled_input = input_processor->output(output_frames_before_processing, + &frames_resampled); *input_frames_count = frames_resampled; } else { resampled_input = nullptr; } - got = data_callback(stream, user_ptr, - resampled_input, out_unprocessed, + got = data_callback(stream, user_ptr, resampled_input, out_unprocessed, output_frames_before_processing); if (got < output_frames_before_processing) { @@ -315,10 +322,8 @@ cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream, cubeb_stream_params * input_params, cubeb_stream_params * output_params, - unsigned int target_rate, - cubeb_data_callback callback, - void * user_ptr, - cubeb_resampler_quality quality) + unsigned int target_rate, cubeb_data_callback callback, + void * user_ptr, cubeb_resampler_quality quality) { cubeb_sample_format format; @@ -330,38 +335,28 @@ cubeb_resampler_create(cubeb_stream * stream, format = output_params->format; } - switch(format) { - case CUBEB_SAMPLE_S16NE: - return cubeb_resampler_create_internal(stream, - input_params, - output_params, - target_rate, - callback, - user_ptr, - quality); - case CUBEB_SAMPLE_FLOAT32NE: - return cubeb_resampler_create_internal(stream, - input_params, - output_params, - target_rate, - callback, - user_ptr, - quality); - default: - assert(false); - return nullptr; + switch (format) { + case CUBEB_SAMPLE_S16NE: + return cubeb_resampler_create_internal(stream, input_params, + output_params, target_rate, + callback, user_ptr, quality); + case CUBEB_SAMPLE_FLOAT32NE: + return cubeb_resampler_create_internal(stream, input_params, + output_params, target_rate, + callback, user_ptr, quality); + default: + assert(false); + return nullptr; } } long -cubeb_resampler_fill(cubeb_resampler * resampler, - void * input_buffer, - long * input_frames_count, - void * output_buffer, +cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer, + long * input_frames_count, void * output_buffer, long output_frames_needed) { - return resampler->fill(input_buffer, input_frames_count, - output_buffer, output_frames_needed); + return resampler->fill(input_buffer, input_frames_count, output_buffer, + output_frames_needed); } void diff --git a/externals/cubeb/src/cubeb_resampler.h b/externals/cubeb/src/cubeb_resampler.h index f6b551373..e9b95324e 100755 --- a/externals/cubeb/src/cubeb_resampler.h +++ b/externals/cubeb/src/cubeb_resampler.h @@ -39,13 +39,12 @@ typedef enum { * @param quality Quality of the resampler. * @retval A non-null pointer if success. */ -cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream, - cubeb_stream_params * input_params, - cubeb_stream_params * output_params, - unsigned int target_rate, - cubeb_data_callback callback, - void * user_ptr, - cubeb_resampler_quality quality); +cubeb_resampler * +cubeb_resampler_create(cubeb_stream * stream, + cubeb_stream_params * input_params, + cubeb_stream_params * output_params, + unsigned int target_rate, cubeb_data_callback callback, + void * user_ptr, cubeb_resampler_quality quality); /** * Fill the buffer with frames acquired using the data callback. Resampling will @@ -59,24 +58,25 @@ cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream, * @retval Number of frames that are actually produced. * @retval CUBEB_ERROR on error. */ -long cubeb_resampler_fill(cubeb_resampler * resampler, - void * input_buffer, - long * input_frame_count, - void * output_buffer, - long output_frames_needed); +long +cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer, + long * input_frame_count, void * output_buffer, + long output_frames_needed); /** * Destroy a cubeb_resampler. * @param resampler A cubeb_resampler instance. */ -void cubeb_resampler_destroy(cubeb_resampler * resampler); +void +cubeb_resampler_destroy(cubeb_resampler * resampler); /** * Returns the latency, in frames, of the resampler. * @param resampler A cubeb resampler instance. * @retval The latency, in frames, induced by the resampler. */ -long cubeb_resampler_latency(cubeb_resampler * resampler); +long +cubeb_resampler_latency(cubeb_resampler * resampler); #if defined(__cplusplus) } diff --git a/externals/cubeb/src/cubeb_resampler_internal.h b/externals/cubeb/src/cubeb_resampler_internal.h index ed5685796..2eabb7c4c 100755 --- a/externals/cubeb/src/cubeb_resampler_internal.h +++ b/externals/cubeb/src/cubeb_resampler_internal.h @@ -8,9 +8,9 @@ #if !defined(CUBEB_RESAMPLER_INTERNAL) #define CUBEB_RESAMPLER_INTERNAL -#include -#include #include +#include +#include #include #ifdef CUBEB_GECKO_BUILD #include "mozilla/UniquePtr.h" @@ -25,29 +25,32 @@ #define MOZ_END_STD_NAMESPACE } #endif MOZ_BEGIN_STD_NAMESPACE - using mozilla::DefaultDelete; - using mozilla::UniquePtr; - #define default_delete DefaultDelete - #define unique_ptr UniquePtr +using mozilla::DefaultDelete; +using mozilla::UniquePtr; +#define default_delete DefaultDelete +#define unique_ptr UniquePtr MOZ_END_STD_NAMESPACE #endif -#include "cubeb/cubeb.h" -#include "cubeb_utils.h" #include "cubeb-speex-resampler.h" -#include "cubeb_resampler.h" +#include "cubeb/cubeb.h" #include "cubeb_log.h" +#include "cubeb_resampler.h" +#include "cubeb_utils.h" #include -/* This header file contains the internal C++ API of the resamplers, for testing. */ +/* This header file contains the internal C++ API of the resamplers, for + * testing. */ // When dropping audio input frames to prevent building // an input delay, this function returns the number of frames // to keep in the buffer. // @parameter sample_rate The sample rate of the stream. // @return A number of frames to keep. -uint32_t min_buffered_audio_frame(uint32_t sample_rate); +uint32_t +min_buffered_audio_frame(uint32_t sample_rate); -int to_speex_quality(cubeb_resampler_quality q); +int +to_speex_quality(cubeb_resampler_quality q); struct cubeb_resampler { virtual long fill(void * input_buffer, long * input_frames_count, @@ -59,14 +62,10 @@ struct cubeb_resampler { /** Base class for processors. This is just used to share methods for now. */ class processor { public: - explicit processor(uint32_t channels) - : channels(channels) - {} + explicit processor(uint32_t channels) : channels(channels) {} + protected: - size_t frames_to_samples(size_t frames) const - { - return frames * channels; - } + size_t frames_to_samples(size_t frames) const { return frames * channels; } size_t samples_to_frames(size_t samples) const { assert(!(samples % channels)); @@ -76,30 +75,24 @@ protected: const uint32_t channels; }; -template -class passthrough_resampler : public cubeb_resampler - , public processor { +template +class passthrough_resampler : public cubeb_resampler, public processor { public: - passthrough_resampler(cubeb_stream * s, - cubeb_data_callback cb, - void * ptr, - uint32_t input_channels, - uint32_t sample_rate); + passthrough_resampler(cubeb_stream * s, cubeb_data_callback cb, void * ptr, + uint32_t input_channels, uint32_t sample_rate); virtual long fill(void * input_buffer, long * input_frames_count, void * output_buffer, long output_frames); - virtual long latency() - { - return 0; - } + virtual long latency() { return 0; } void drop_audio_if_needed() { uint32_t to_keep = min_buffered_audio_frame(sample_rate); uint32_t available = samples_to_frames(internal_input_buffer.length()); if (available > to_keep) { - internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep)); + internal_input_buffer.pop(nullptr, + frames_to_samples(available - to_keep)); } } @@ -116,14 +109,12 @@ private: /** Bidirectional resampler, can resample an input and an output stream, or just * an input stream or output stream. In this case a delay is inserted in the * opposite direction to keep the streams synchronized. */ -template +template class cubeb_resampler_speex : public cubeb_resampler { public: cubeb_resampler_speex(InputProcessing * input_processor, - OutputProcessing * output_processor, - cubeb_stream * s, - cubeb_data_callback cb, - void * ptr); + OutputProcessing * output_processor, cubeb_stream * s, + cubeb_data_callback cb, void * ptr); virtual ~cubeb_resampler_speex(); @@ -143,7 +134,9 @@ public: } private: - typedef long(cubeb_resampler_speex::*processing_callback)(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed); + typedef long (cubeb_resampler_speex::*processing_callback)( + T * input_buffer, long * input_frames_count, T * output_buffer, + long output_frames_needed); long fill_internal_duplex(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed); @@ -165,8 +158,7 @@ private: * audio buffers of type T. This class is designed so that the number of frames * coming out of the resampler can be precisely controled. It manages its own * input buffer, and can use the caller's output buffer, or allocate its own. */ -template -class cubeb_resampler_speex_one_way : public processor { +template class cubeb_resampler_speex_one_way : public processor { public: /** The sample type of this resampler, either 16-bit integers or 32-bit * floats. */ @@ -178,19 +170,15 @@ public: * @parameter target_rate The sample-rate of the audio output. * @parameter quality A number between 0 (fast, low quality) and 10 (slow, * high quality). */ - cubeb_resampler_speex_one_way(uint32_t channels, - uint32_t source_rate, - uint32_t target_rate, - int quality) - : processor(channels) - , resampling_ratio(static_cast(source_rate) / target_rate) - , source_rate(source_rate) - , additional_latency(0) - , leftover_samples(0) + cubeb_resampler_speex_one_way(uint32_t channels, uint32_t source_rate, + uint32_t target_rate, int quality) + : processor(channels), + resampling_ratio(static_cast(source_rate) / target_rate), + source_rate(source_rate), additional_latency(0), leftover_samples(0) { int r; - speex_resampler = speex_resampler_init(channels, source_rate, - target_rate, quality, &r); + speex_resampler = + speex_resampler_init(channels, source_rate, target_rate, quality, &r); assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure"); uint32_t input_latency = speex_resampler_get_input_latency(speex_resampler); @@ -200,11 +188,8 @@ public: uint32_t input_frame_count = input_latency; uint32_t output_frame_count = LATENCY_SAMPLES; assert(input_latency * channels <= LATENCY_SAMPLES); - speex_resample( - input_buffer, - &input_frame_count, - output_buffer, - &output_frame_count); + speex_resample(input_buffer, &input_frame_count, output_buffer, + &output_frame_count); } /** Destructor, deallocate the resampler */ @@ -221,14 +206,14 @@ public: } /** Outputs exactly `output_frame_count` into `output_buffer`. - * `output_buffer` has to be at least `output_frame_count` long. */ + * `output_buffer` has to be at least `output_frame_count` long. */ size_t output(T * output_buffer, size_t output_frame_count) { uint32_t in_len = samples_to_frames(resampling_in_buffer.length()); uint32_t out_len = output_frame_count; - speex_resample(resampling_in_buffer.data(), &in_len, - output_buffer, &out_len); + speex_resample(resampling_in_buffer.data(), &in_len, output_buffer, + &out_len); /* This shifts back any unresampled samples to the beginning of the input buffer. */ @@ -239,15 +224,17 @@ public: size_t output_for_input(uint32_t input_frames) { - return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length())) - / resampling_ratio); + return (size_t)floorf( + (input_frames + samples_to_frames(resampling_in_buffer.length())) / + resampling_ratio); } /** Returns a buffer containing exactly `output_frame_count` resampled frames. - * The consumer should not hold onto the pointer. */ + * The consumer should not hold onto the pointer. */ T * output(size_t output_frame_count, size_t * input_frames_used) { - if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) { + if (resampling_out_buffer.capacity() < + frames_to_samples(output_frame_count)) { resampling_out_buffer.reserve(frames_to_samples(output_frame_count)); } @@ -258,10 +245,12 @@ public: resampling_out_buffer.data(), &out_len); if (out_len < output_frame_count) { - LOGV("underrun during resampling: got %u frames, expected %zu", (unsigned)out_len, output_frame_count); + LOGV("underrun during resampling: got %u frames, expected %zu", + (unsigned)out_len, output_frame_count); // silence the rightmost part - T* data = resampling_out_buffer.data(); - for (uint32_t i = frames_to_samples(out_len); i < frames_to_samples(output_frame_count); i++) { + T * data = resampling_out_buffer.data(); + for (uint32_t i = frames_to_samples(out_len); + i < frames_to_samples(output_frame_count); i++) { data[i] = 0; } } @@ -281,8 +270,8 @@ public: * only consider a single channel here so it's the same number of frames. */ int latency = 0; - latency = - speex_resampler_get_output_latency(speex_resampler) + additional_latency; + latency = speex_resampler_get_output_latency(speex_resampler) + + additional_latency; assert(latency >= 0); @@ -296,11 +285,13 @@ public: uint32_t input_needed_for_output(int32_t output_frame_count) const { assert(output_frame_count >= 0); // Check overflow - int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length()); - int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length()); + int32_t unresampled_frames_left = + samples_to_frames(resampling_in_buffer.length()); + int32_t resampled_frames_left = + samples_to_frames(resampling_out_buffer.length()); float input_frames_needed = - (output_frame_count - unresampled_frames_left) * resampling_ratio - - resampled_frames_left; + (output_frame_count - unresampled_frames_left) * resampling_ratio - + resampled_frames_left; if (input_frames_needed < 0) { return 0; } @@ -337,9 +328,10 @@ public: resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep)); } } + private: /** Wrapper for the speex resampling functions to have a typed - * interface. */ + * interface. */ void speex_resample(float * input_buffer, uint32_t * input_frame_count, float * output_buffer, uint32_t * output_frame_count) { @@ -347,11 +339,9 @@ private: int rv; rv = #endif - speex_resampler_process_interleaved_float(speex_resampler, - input_buffer, - input_frame_count, - output_buffer, - output_frame_count); + speex_resampler_process_interleaved_float( + speex_resampler, input_buffer, input_frame_count, output_buffer, + output_frame_count); assert(rv == RESAMPLER_ERR_SUCCESS); } @@ -362,11 +352,9 @@ private: int rv; rv = #endif - speex_resampler_process_interleaved_int(speex_resampler, - input_buffer, - input_frame_count, - output_buffer, - output_frame_count); + speex_resampler_process_interleaved_int( + speex_resampler, input_buffer, input_frame_count, output_buffer, + output_frame_count); assert(rv == RESAMPLER_ERR_SUCCESS); } /** The state for the speex resampler used internaly. */ @@ -387,18 +375,16 @@ private: }; /** This class allows delaying an audio stream by `frames` frames. */ -template -class delay_line : public processor { +template class delay_line : public processor { public: /** Constructor * @parameter frames the number of frames of delay. * @parameter channels the number of channels of this delay line. - * @parameter sample_rate sample-rate of the audio going through this delay line */ + * @parameter sample_rate sample-rate of the audio going through this delay + * line */ delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate) - : processor(channels) - , length(frames) - , leftover_samples(0) - , sample_rate(sample_rate) + : processor(channels), length(frames), leftover_samples(0), + sample_rate(sample_rate) { /* Fill the delay line with some silent frames to add latency. */ delay_input_buffer.push_silence(frames * channels); @@ -436,7 +422,8 @@ public: T * input_buffer(uint32_t frames_needed) { leftover_samples = delay_input_buffer.length(); - delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed)); + delay_input_buffer.reserve(leftover_samples + + frames_to_samples(frames_needed)); return delay_input_buffer.data() + leftover_samples; } /** This method works with `input_buffer`, and allows to inform the processor @@ -471,17 +458,12 @@ public: assert(frames_needed >= 0); // Check overflow return frames_needed; } - /** Returns the number of frames produces for `input_frames` frames in input */ - size_t output_for_input(uint32_t input_frames) - { - return input_frames; - } + /** Returns the number of frames produces for `input_frames` frames in input + */ + size_t output_for_input(uint32_t input_frames) { return input_frames; } /** The number of frames this delay line delays the stream by. * @returns The number of frames of delay. */ - size_t latency() - { - return length; - } + size_t latency() { return length; } void drop_audio_if_needed() { @@ -491,6 +473,7 @@ public: delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep)); } } + private: /** The length, in frames, of this delay line */ uint32_t length; @@ -506,14 +489,13 @@ private: }; /** This sits behind the C API and is more typed. */ -template +template cubeb_resampler * cubeb_resampler_create_internal(cubeb_stream * stream, cubeb_stream_params * input_params, cubeb_stream_params * output_params, unsigned int target_rate, - cubeb_data_callback callback, - void * user_ptr, + cubeb_data_callback callback, void * user_ptr, cubeb_resampler_quality quality) { std::unique_ptr> input_resampler = nullptr; @@ -528,35 +510,31 @@ cubeb_resampler_create_internal(cubeb_stream * stream, sample rate, use a no-op resampler, that simply forwards the buffers to the callback. */ if (((input_params && input_params->rate == target_rate) && - (output_params && output_params->rate == target_rate)) || + (output_params && output_params->rate == target_rate)) || (input_params && !output_params && (input_params->rate == target_rate)) || - (output_params && !input_params && (output_params->rate == target_rate))) { + (output_params && !input_params && + (output_params->rate == target_rate))) { LOG("Input and output sample-rate match, target rate of %dHz", target_rate); - return new passthrough_resampler(stream, callback, - user_ptr, - input_params ? input_params->channels : 0, - target_rate); + return new passthrough_resampler( + stream, callback, user_ptr, input_params ? input_params->channels : 0, + target_rate); } /* Determine if we need to resampler one or both directions, and create the resamplers. */ if (output_params && (output_params->rate != target_rate)) { - output_resampler.reset( - new cubeb_resampler_speex_one_way(output_params->channels, - target_rate, - output_params->rate, - to_speex_quality(quality))); + output_resampler.reset(new cubeb_resampler_speex_one_way( + output_params->channels, target_rate, output_params->rate, + to_speex_quality(quality))); if (!output_resampler) { return NULL; } } if (input_params && (input_params->rate != target_rate)) { - input_resampler.reset( - new cubeb_resampler_speex_one_way(input_params->channels, - input_params->rate, - target_rate, - to_speex_quality(quality))); + input_resampler.reset(new cubeb_resampler_speex_one_way( + input_params->channels, input_params->rate, target_rate, + to_speex_quality(quality))); if (!input_resampler) { return NULL; } @@ -572,7 +550,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream, if (!output_delay) { return NULL; } - } else if (output_resampler && !input_resampler && input_params && output_params) { + } else if (output_resampler && !input_resampler && input_params && + output_params) { input_delay.reset(new delay_line(output_resampler->latency(), input_params->channels, output_params->rate)); @@ -582,29 +561,26 @@ cubeb_resampler_create_internal(cubeb_stream * stream, } if (input_resampler && output_resampler) { - LOG("Resampling input (%d) and output (%d) to target rate of %dHz", input_params->rate, output_params->rate, target_rate); - return new cubeb_resampler_speex, - cubeb_resampler_speex_one_way> - (input_resampler.release(), - output_resampler.release(), - stream, callback, user_ptr); + LOG("Resampling input (%d) and output (%d) to target rate of %dHz", + input_params->rate, output_params->rate, target_rate); + return new cubeb_resampler_speex, + cubeb_resampler_speex_one_way>( + input_resampler.release(), output_resampler.release(), stream, callback, + user_ptr); } else if (input_resampler) { - LOG("Resampling input (%d) to target and output rate of %dHz", input_params->rate, target_rate); - return new cubeb_resampler_speex, - delay_line> - (input_resampler.release(), - output_delay.release(), - stream, callback, user_ptr); + LOG("Resampling input (%d) to target and output rate of %dHz", + input_params->rate, target_rate); + return new cubeb_resampler_speex, + delay_line>(input_resampler.release(), + output_delay.release(), + stream, callback, user_ptr); } else { - LOG("Resampling output (%dHz) to target and input rate of %dHz", output_params->rate, target_rate); - return new cubeb_resampler_speex, - cubeb_resampler_speex_one_way> - (input_delay.release(), - output_resampler.release(), - stream, callback, user_ptr); + LOG("Resampling output (%dHz) to target and input rate of %dHz", + output_params->rate, target_rate); + return new cubeb_resampler_speex, + cubeb_resampler_speex_one_way>( + input_delay.release(), output_resampler.release(), stream, callback, + user_ptr); } } diff --git a/externals/cubeb/src/cubeb_ring_array.h b/externals/cubeb/src/cubeb_ring_array.h index 51b3b321a..05a8fe962 100755 --- a/externals/cubeb/src/cubeb_ring_array.h +++ b/externals/cubeb/src/cubeb_ring_array.h @@ -16,17 +16,16 @@ them in the correct order. */ typedef struct { - AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated space for the buffers. */ - unsigned int tail; /**< Index of the last element (first to deliver). */ - unsigned int count; /**< Number of elements in the array. */ - unsigned int capacity; /**< Total length of the array. */ + AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated + space for the buffers. */ + unsigned int tail; /**< Index of the last element (first to deliver). */ + unsigned int count; /**< Number of elements in the array. */ + unsigned int capacity; /**< Total length of the array. */ } ring_array; static int -single_audiobuffer_init(AudioBuffer * buffer, - uint32_t bytesPerFrame, - uint32_t channelsPerFrame, - uint32_t frames) +single_audiobuffer_init(AudioBuffer * buffer, uint32_t bytesPerFrame, + uint32_t channelsPerFrame, uint32_t frames) { assert(buffer); assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0); @@ -36,7 +35,7 @@ single_audiobuffer_init(AudioBuffer * buffer, if (buffer->mData == NULL) { return CUBEB_ERROR; } - PodZero(static_cast(buffer->mData), size); + PodZero(static_cast(buffer->mData), size); buffer->mNumberChannels = channelsPerFrame; buffer->mDataByteSize = size; @@ -48,15 +47,12 @@ single_audiobuffer_init(AudioBuffer * buffer, @param ra The ring_array pointer of allocated structure. @retval 0 on success. */ int -ring_array_init(ring_array * ra, - uint32_t capacity, - uint32_t bytesPerFrame, - uint32_t channelsPerFrame, - uint32_t framesPerBuffer) +ring_array_init(ring_array * ra, uint32_t capacity, uint32_t bytesPerFrame, + uint32_t channelsPerFrame, uint32_t framesPerBuffer) { assert(ra); - if (capacity == 0 || bytesPerFrame == 0 || - channelsPerFrame == 0 || framesPerBuffer == 0) { + if (capacity == 0 || bytesPerFrame == 0 || channelsPerFrame == 0 || + framesPerBuffer == 0) { return CUBEB_ERROR_INVALID_PARAMETER; } ra->capacity = capacity; @@ -70,8 +66,7 @@ ring_array_init(ring_array * ra, } for (unsigned int i = 0; i < ra->capacity; ++i) { - if (single_audiobuffer_init(&ra->buffer_array[i], - bytesPerFrame, + if (single_audiobuffer_init(&ra->buffer_array[i], bytesPerFrame, channelsPerFrame, framesPerBuffer) != CUBEB_OK) { return CUBEB_ERROR; @@ -87,7 +82,7 @@ void ring_array_destroy(ring_array * ra) { assert(ra); - if (ra->buffer_array == NULL){ + if (ra->buffer_array == NULL) { return; } for (unsigned int i = 0; i < ra->capacity; ++i) { @@ -95,12 +90,13 @@ ring_array_destroy(ring_array * ra) operator delete(ra->buffer_array[i].mData); } } - delete [] ra->buffer_array; + delete[] ra->buffer_array; } /** Get the allocated buffer to be stored with fresh data. @param ra The ring_array pointer. - @retval Pointer of the allocated space to be stored with fresh data or NULL if full. */ + @retval Pointer of the allocated space to be stored with fresh data or NULL + if full. */ AudioBuffer * ring_array_get_free_buffer(ring_array * ra) { @@ -156,4 +152,4 @@ ring_array_get_dummy_buffer(ring_array * ra) return &ra->buffer_array[0]; } -#endif //CUBEB_RING_ARRAY_H +#endif // CUBEB_RING_ARRAY_H diff --git a/externals/cubeb/src/cubeb_ringbuffer.h b/externals/cubeb/src/cubeb_ringbuffer.h index b6696e886..28381849f 100755 --- a/externals/cubeb/src/cubeb_ringbuffer.h +++ b/externals/cubeb/src/cubeb_ringbuffer.h @@ -18,10 +18,10 @@ /** * Single producer single consumer lock-free and wait-free ring buffer. * - * This data structure allows producing data from one thread, and consuming it on - * another thread, safely and without explicit synchronization. If used on two - * threads, this data structure uses atomics for thread safety. It is possible - * to disable the use of atomics at compile time and only use this data + * This data structure allows producing data from one thread, and consuming it + * on another thread, safely and without explicit synchronization. If used on + * two threads, this data structure uses atomics for thread safety. It is + * possible to disable the use of atomics at compile time and only use this data * structure on one thread. * * The role for the producer and the consumer must be constant, i.e., the @@ -48,9 +48,7 @@ * providing an external buffer to copy into is an easy way to have linear * data for further processing. */ -template -class ring_buffer_base -{ +template class ring_buffer_base { public: /** * Constructor for a ring buffer. @@ -61,11 +59,10 @@ public: * @param capacity The maximum number of element this ring buffer will hold. */ ring_buffer_base(int capacity) - /* One more element to distinguish from empty and full buffer. */ - : capacity_(capacity + 1) + /* One more element to distinguish from empty and full buffer. */ + : capacity_(capacity + 1) { - assert(storage_capacity() < - std::numeric_limits::max() / 2 && + assert(storage_capacity() < std::numeric_limits::max() / 2 && "buffer too large for the type of index used."); assert(capacity_ > 0); @@ -84,10 +81,7 @@ public: * @param count The number of elements to enqueue. * @return The number of element enqueued. */ - int enqueue_default(int count) - { - return enqueue(nullptr, count); - } + int enqueue_default(int count) { return enqueue(nullptr, count); } /** * @brief Put an element in the queue * @@ -97,20 +91,18 @@ public: * * @return 1 if the element was inserted, 0 otherwise. */ - int enqueue(T& element) - { - return enqueue(&element, 1); - } + int enqueue(T & element) { return enqueue(&element, 1); } /** * Push `count` elements in the ring buffer. * * Only safely called on the producer thread. * * @param elements a pointer to a buffer containing at least `count` elements. - * If `elements` is nullptr, zero or default constructed elements are enqueued. + * If `elements` is nullptr, zero or default constructed elements are + * enqueued. * @param count The number of elements to read from `elements` - * @return The number of elements successfully coped from `elements` and inserted - * into the ring buffer. + * @return The number of elements successfully coped from `elements` and + * inserted into the ring buffer. */ int enqueue(T * elements, int count) { @@ -118,19 +110,17 @@ public: assert_correct_thread(producer_id); #endif - int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed); - int wr_idx = write_index_.load(std::memory_order::memory_order_relaxed); + int rd_idx = read_index_.load(std::memory_order_relaxed); + int wr_idx = write_index_.load(std::memory_order_relaxed); if (full_internal(rd_idx, wr_idx)) { return 0; } - int to_write = - std::min(available_write_internal(rd_idx, wr_idx), count); + int to_write = std::min(available_write_internal(rd_idx, wr_idx), count); /* First part, from the write index to the end of the array. */ - int first_part = std::min(storage_capacity() - wr_idx, - to_write); + int first_part = std::min(storage_capacity() - wr_idx, to_write); /* Second part, from the beginning of the array */ int second_part = to_write - first_part; @@ -142,7 +132,8 @@ public: ConstructDefault(data_.get(), second_part); } - write_index_.store(increment_index(wr_idx, to_write), std::memory_order::memory_order_release); + write_index_.store(increment_index(wr_idx, to_write), + std::memory_order_release); return to_write; } @@ -163,15 +154,14 @@ public: assert_correct_thread(consumer_id); #endif - int wr_idx = write_index_.load(std::memory_order::memory_order_acquire); - int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed); + int wr_idx = write_index_.load(std::memory_order_acquire); + int rd_idx = read_index_.load(std::memory_order_relaxed); if (empty_internal(rd_idx, wr_idx)) { return 0; } - int to_read = - std::min(available_read_internal(rd_idx, wr_idx), count); + int to_read = std::min(available_read_internal(rd_idx, wr_idx), count); int first_part = std::min(storage_capacity() - rd_idx, to_read); int second_part = to_read - first_part; @@ -181,7 +171,8 @@ public: Copy(elements + first_part, data_.get(), second_part); } - read_index_.store(increment_index(rd_idx, to_read), std::memory_order::memory_order_relaxed); + read_index_.store(increment_index(rd_idx, to_read), + std::memory_order_relaxed); return to_read; } @@ -197,8 +188,9 @@ public: #ifndef NDEBUG assert_correct_thread(consumer_id); #endif - return available_read_internal(read_index_.load(std::memory_order::memory_order_relaxed), - write_index_.load(std::memory_order::memory_order_relaxed)); + return available_read_internal( + read_index_.load(std::memory_order_relaxed), + write_index_.load(std::memory_order_relaxed)); } /** * Get the number of available elements for consuming. @@ -212,8 +204,9 @@ public: #ifndef NDEBUG assert_correct_thread(producer_id); #endif - return available_write_internal(read_index_.load(std::memory_order::memory_order_relaxed), - write_index_.load(std::memory_order::memory_order_relaxed)); + return available_write_internal( + read_index_.load(std::memory_order_relaxed), + write_index_.load(std::memory_order_relaxed)); } /** * Get the total capacity, for this ring buffer. @@ -222,10 +215,7 @@ public: * * @return The maximum capacity of this ring buffer. */ - int capacity() const - { - return storage_capacity() - 1; - } + int capacity() const { return storage_capacity() - 1; } /** * Reset the consumer and producer thread identifier, in case the thread are * being changed. This has to be externally synchronized. This is no-op when @@ -237,6 +227,7 @@ public: consumer_id = producer_id = std::thread::id(); #endif } + private: /** Return true if the ring buffer is empty. * @@ -244,8 +235,7 @@ private: * @param write_index the write index to consider * @return true if the ring buffer is empty, false otherwise. **/ - bool empty_internal(int read_index, - int write_index) const + bool empty_internal(int read_index, int write_index) const { return write_index == read_index; } @@ -258,8 +248,7 @@ private: * @param write_index the write index to consider * @return true if the ring buffer is full, false otherwise. **/ - bool full_internal(int read_index, - int write_index) const + bool full_internal(int read_index, int write_index) const { return (write_index + 1) % storage_capacity() == read_index; } @@ -269,18 +258,13 @@ private: * * @return the number of elements that can be stored in the buffer. */ - int storage_capacity() const - { - return capacity_; - } + int storage_capacity() const { return capacity_; } /** * Returns the number of elements available for reading. * * @return the number of available elements for reading. */ - int - available_read_internal(int read_index, - int write_index) const + int available_read_internal(int read_index, int write_index) const { if (write_index >= read_index) { return write_index - read_index; @@ -293,9 +277,7 @@ private: * * @return the number of elements that can be written into the array. */ - int - available_write_internal(int read_index, - int write_index) const + int available_write_internal(int read_index, int write_index) const { /* We substract one element here to always keep at least one sample * free in the buffer, to distinguish between full and empty array. */ @@ -312,8 +294,7 @@ private: * @param increment the number by which `index` is incremented. * @return the new index. */ - int - increment_index(int index, int increment) const + int increment_index(int index, int increment) const { assert(increment >= 0); return (index + increment) % storage_capacity(); @@ -325,7 +306,7 @@ private: * @param id the id of the thread that has called the calling method first. */ #ifndef NDEBUG - static void assert_correct_thread(std::thread::id& id) + static void assert_correct_thread(std::thread::id & id) { if (id == std::thread::id()) { id = std::this_thread::get_id(); @@ -354,9 +335,7 @@ private: /** * Adapter for `ring_buffer_base` that exposes an interface in frames. */ -template -class audio_ring_buffer_base -{ +template class audio_ring_buffer_base { public: /** * @brief Constructor. @@ -365,8 +344,8 @@ public: * @param capacity_in_frames The capacity in frames. */ audio_ring_buffer_base(int channel_count, int capacity_in_frames) - : channel_count(channel_count) - , ring_buffer(frames_to_samples(capacity_in_frames)) + : channel_count(channel_count), + ring_buffer(frames_to_samples(capacity_in_frames)) { assert(channel_count > 0); } @@ -380,7 +359,8 @@ public: */ int enqueue_default(int frame_count) { - return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count))); + return samples_to_frames( + ring_buffer.enqueue(nullptr, frames_to_samples(frame_count))); } /** * @brief Enqueue `frames_count` frames of audio. @@ -396,7 +376,8 @@ public: int enqueue(T * frames, int frame_count) { - return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count))); + return samples_to_frames( + ring_buffer.enqueue(frames, frames_to_samples(frame_count))); } /** @@ -413,7 +394,8 @@ public: */ int dequeue(T * frames, int frame_count) { - return samples_to_frames(ring_buffer.dequeue(frames, frames_to_samples(frame_count))); + return samples_to_frames( + ring_buffer.dequeue(frames, frames_to_samples(frame_count))); } /** * Get the number of available frames of audio for consuming. @@ -444,10 +426,8 @@ public: * * @return The maximum capacity of this ring buffer. */ - int capacity() const - { - return samples_to_frames(ring_buffer.capacity()); - } + int capacity() const { return samples_to_frames(ring_buffer.capacity()); } + private: /** * @brief Frames to samples conversion. @@ -456,10 +436,7 @@ private: * * @return A number of samples. */ - int frames_to_samples(int frames) const - { - return frames * channel_count; - } + int frames_to_samples(int frames) const { return frames * channel_count; } /** * @brief Samples to frames conversion. * @@ -467,10 +444,7 @@ private: * * @return A number of frames. */ - int samples_to_frames(int samples) const - { - return samples / channel_count; - } + int samples_to_frames(int samples) const { return samples / channel_count; } /** Number of channels of audio that will stream through this ring buffer. */ int channel_count; /** The underlying ring buffer that is used to store the data. */ @@ -482,14 +456,13 @@ private: * from two threads, one producer, one consumer (that never change role), * without explicit synchronization. */ -template -using lock_free_queue = ring_buffer_base; +template using lock_free_queue = ring_buffer_base; /** * Lock-free instantiation of the `audio_ring_buffer` type. This is safe to use * from two threads, one producer, one consumer (that never change role), * without explicit synchronization. */ -template +template using lock_free_audio_ring_buffer = audio_ring_buffer_base; #endif // CUBEB_RING_BUFFER_H diff --git a/externals/cubeb/src/cubeb_sndio.c b/externals/cubeb/src/cubeb_sndio.c index 41fa59afa..5e11725ec 100755 --- a/externals/cubeb/src/cubeb_sndio.c +++ b/externals/cubeb/src/cubeb_sndio.c @@ -4,44 +4,46 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" +#include +#include #include #include #include #include #include #include -#include #include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" +#include #if defined(CUBEB_SNDIO_DEBUG) #define DPR(...) fprintf(stderr, __VA_ARGS__); #else -#define DPR(...) do {} while(0) +#define DPR(...) \ + do { \ + } while (0) #endif #ifdef DISABLE_LIBSNDIO_DLOPEN #define WRAP(x) x #else -#define WRAP(x) cubeb_##x -#define LIBSNDIO_API_VISIT(X) \ - X(sio_close) \ - X(sio_eof) \ - X(sio_getpar) \ - X(sio_initpar) \ - X(sio_nfds) \ - X(sio_onmove) \ - X(sio_open) \ - X(sio_pollfd) \ - X(sio_read) \ - X(sio_revents) \ - X(sio_setpar) \ - X(sio_start) \ - X(sio_stop) \ - X(sio_write) \ +#define WRAP(x) (*cubeb_##x) +#define LIBSNDIO_API_VISIT(X) \ + X(sio_close) \ + X(sio_eof) \ + X(sio_getpar) \ + X(sio_initpar) \ + X(sio_nfds) \ + X(sio_onmove) \ + X(sio_open) \ + X(sio_pollfd) \ + X(sio_read) \ + X(sio_revents) \ + X(sio_setpar) \ + X(sio_start) \ + X(sio_stop) \ + X(sio_write) #define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; LIBSNDIO_API_VISIT(MAKE_TYPEDEF); @@ -58,33 +60,33 @@ struct cubeb { struct cubeb_stream { /* Note: Must match cubeb_stream layout in cubeb.c. */ cubeb * context; - void * arg; /* user arg to {data,state}_cb */ + void * arg; /* user arg to {data,state}_cb */ /**/ - pthread_t th; /* to run real-time audio i/o */ - pthread_mutex_t mtx; /* protects hdl and pos */ - struct sio_hdl *hdl; /* link us to sndio */ - int mode; /* bitmap of SIO_{PLAY,REC} */ - int active; /* cubec_start() called */ - int conv; /* need float->s16 conversion */ - unsigned char *rbuf; /* rec data consumed from here */ - unsigned char *pbuf; /* play data is prepared here */ - unsigned int nfr; /* number of frames in ibuf and obuf */ - unsigned int rbpf; /* rec bytes per frame */ - unsigned int pbpf; /* play bytes per frame */ - unsigned int rchan; /* number of rec channels */ - unsigned int pchan; /* number of play channels */ - unsigned int nblks; /* number of blocks in the buffer */ - uint64_t hwpos; /* frame number Joe hears right now */ - uint64_t swpos; /* number of frames produced/consumed */ - cubeb_data_callback data_cb; /* cb to preapare data */ - cubeb_state_callback state_cb; /* cb to notify about state changes */ - float volume; /* current volume */ + pthread_t th; /* to run real-time audio i/o */ + pthread_mutex_t mtx; /* protects hdl and pos */ + struct sio_hdl * hdl; /* link us to sndio */ + int mode; /* bitmap of SIO_{PLAY,REC} */ + int active; /* cubec_start() called */ + int conv; /* need float->s16 conversion */ + unsigned char * rbuf; /* rec data consumed from here */ + unsigned char * pbuf; /* play data is prepared here */ + unsigned int nfr; /* number of frames in ibuf and obuf */ + unsigned int rbpf; /* rec bytes per frame */ + unsigned int pbpf; /* play bytes per frame */ + unsigned int rchan; /* number of rec channels */ + unsigned int pchan; /* number of play channels */ + unsigned int nblks; /* number of blocks in the buffer */ + uint64_t hwpos; /* frame number Joe hears right now */ + uint64_t swpos; /* number of frames produced/consumed */ + cubeb_data_callback data_cb; /* cb to preapare data */ + cubeb_state_callback state_cb; /* cb to notify about state changes */ + float volume; /* current volume */ }; static void -s16_setvol(void *ptr, long nsamp, float volume) +s16_setvol(void * ptr, long nsamp, float volume) { - int16_t *dst = ptr; + int16_t * dst = ptr; int32_t mult = volume * 32768; int32_t s; @@ -96,10 +98,10 @@ s16_setvol(void *ptr, long nsamp, float volume) } static void -float_to_s16(void *ptr, long nsamp, float volume) +float_to_s16(void * ptr, long nsamp, float volume) { - int16_t *dst = ptr; - float *src = ptr; + int16_t * dst = ptr; + float * src = ptr; float mult = volume * 32768; int s; @@ -114,10 +116,10 @@ float_to_s16(void *ptr, long nsamp, float volume) } static void -s16_to_float(void *ptr, long nsamp) +s16_to_float(void * ptr, long nsamp) { - int16_t *src = ptr; - float *dst = ptr; + int16_t * src = ptr; + float * dst = ptr; src += nsamp; dst += nsamp; @@ -133,9 +135,9 @@ sndio_get_device() * On other platforms default to sndio devices, * so cubebs other backends can be used instead. */ - const char *dev = getenv("AUDIODEVICE"); + const char * dev = getenv("AUDIODEVICE"); if (dev == NULL || *dev == '\0') - return "snd/0"; + return "snd/0"; return dev; #else return SIO_DEVANY; @@ -143,26 +145,26 @@ sndio_get_device() } static void -sndio_onmove(void *arg, int delta) +sndio_onmove(void * arg, int delta) { - cubeb_stream *s = (cubeb_stream *)arg; + cubeb_stream * s = (cubeb_stream *)arg; s->hwpos += delta; } static void * -sndio_mainloop(void *arg) +sndio_mainloop(void * arg) { - struct pollfd *pfds; - cubeb_stream *s = arg; + struct pollfd * pfds; + cubeb_stream * s = arg; int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED; size_t pstart = 0, pend = 0, rstart = 0, rend = 0; long nfr; nfds = WRAP(sio_nfds)(s->hdl); - pfds = calloc(nfds, sizeof (struct pollfd)); + pfds = calloc(nfds, sizeof(struct pollfd)); if (pfds == NULL) - return NULL; + return NULL; DPR("sndio_mainloop()\n"); s->state_cb(s, s->arg, CUBEB_STATE_STARTED); @@ -196,7 +198,7 @@ sndio_mainloop(void *arg) /* do we have a complete block? */ if ((!(s->mode & SIO_PLAY) || pstart == pend) && - (!(s->mode & SIO_REC) || rstart == rend)) { + (!(s->mode & SIO_REC) || rstart == rend)) { if (eof) { DPR("sndio_mainloop() drained\n"); @@ -224,7 +226,7 @@ sndio_mainloop(void *arg) if (!(s->mode & SIO_PLAY) || nfr == 0) { state = CUBEB_STATE_DRAINED; break; - } + } /* need to write (aka drain) the partial play block we got */ pend = nfr * s->pbpf; @@ -302,10 +304,10 @@ sndio_mainloop(void *arg) } /*static*/ int -sndio_init(cubeb **context, char const *context_name) +sndio_init(cubeb ** context, char const * context_name) { void * libsndio = NULL; - struct sio_hdl *hdl; + struct sio_hdl * hdl; assert(context); @@ -319,13 +321,14 @@ sndio_init(cubeb **context, char const *context_name) } } -#define LOAD(x) { \ - cubeb_##x = dlsym(libsndio, #x); \ - if (!cubeb_##x) { \ - DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \ - dlclose(libsndio); \ - return CUBEB_ERROR; \ - } \ +#define LOAD(x) \ + { \ + cubeb_##x = dlsym(libsndio, #x); \ + if (!cubeb_##x) { \ + DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \ + dlclose(libsndio); \ + return CUBEB_ERROR; \ + } \ } LIBSNDIO_API_VISIT(LOAD); @@ -342,7 +345,7 @@ sndio_init(cubeb **context, char const *context_name) DPR("sndio_init(%s)\n", context_name); *context = malloc(sizeof(**context)); if (*context == NULL) - return CUBEB_ERROR; + return CUBEB_ERROR; (*context)->libsndio = libsndio; (*context)->ops = &sndio_ops; (void)context_name; @@ -350,13 +353,13 @@ sndio_init(cubeb **context, char const *context_name) } static char const * -sndio_get_backend_id(cubeb *context) +sndio_get_backend_id(cubeb * context) { return "sndio"; } static void -sndio_destroy(cubeb *context) +sndio_destroy(cubeb * context) { DPR("sndio_destroy()\n"); if (context->libsndio) @@ -365,19 +368,16 @@ sndio_destroy(cubeb *context) } static int -sndio_stream_init(cubeb * context, - cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, +sndio_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int latency_frames, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void *user_ptr) + cubeb_state_callback state_callback, void * user_ptr) { - cubeb_stream *s; + cubeb_stream * s; struct sio_par wpar, rpar; cubeb_sample_format format; int rate; @@ -445,8 +445,8 @@ sndio_stream_init(cubeb * context, DPR("sndio_stream_init(), sio_setpar() failed\n"); goto err; } - if (rpar.bits != wpar.bits || rpar.le != wpar.le || - rpar.sig != wpar.sig || rpar.rate != wpar.rate || + if (rpar.bits != wpar.bits || rpar.le != wpar.le || rpar.sig != wpar.sig || + rpar.rate != wpar.rate || ((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) || ((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) { DPR("sndio_stream_init() unsupported params\n"); @@ -522,7 +522,8 @@ sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) } static int -sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) +sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_frames) { /* * We've no device-independent minimum latency. @@ -533,7 +534,7 @@ sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latenc } static void -sndio_stream_destroy(cubeb_stream *s) +sndio_stream_destroy(cubeb_stream * s) { DPR("sndio_stream_destroy()\n"); WRAP(sio_close)(s->hdl); @@ -545,7 +546,7 @@ sndio_stream_destroy(cubeb_stream *s) } static int -sndio_stream_start(cubeb_stream *s) +sndio_stream_start(cubeb_stream * s) { int err; @@ -560,9 +561,9 @@ sndio_stream_start(cubeb_stream *s) } static int -sndio_stream_stop(cubeb_stream *s) +sndio_stream_stop(cubeb_stream * s) { - void *dummy; + void * dummy; DPR("sndio_stream_stop()\n"); if (s->active) { @@ -573,7 +574,7 @@ sndio_stream_stop(cubeb_stream *s) } static int -sndio_stream_get_position(cubeb_stream *s, uint64_t *p) +sndio_stream_get_position(cubeb_stream * s, uint64_t * p) { pthread_mutex_lock(&s->mtx); DPR("sndio_stream_get_position() %" PRId64 "\n", s->hwpos); @@ -583,7 +584,7 @@ sndio_stream_get_position(cubeb_stream *s, uint64_t *p) } static int -sndio_stream_set_volume(cubeb_stream *s, float volume) +sndio_stream_set_volume(cubeb_stream * s, float volume) { DPR("sndio_stream_set_volume(%f)\n", volume); pthread_mutex_lock(&s->mtx); @@ -606,27 +607,27 @@ sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency) } static int -sndio_enumerate_devices(cubeb *context, cubeb_device_type type, - cubeb_device_collection *collection) +sndio_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * collection) { static char dev[] = SIO_DEVANY; - cubeb_device_info *device; + cubeb_device_info * device; device = malloc(sizeof(cubeb_device_info)); if (device == NULL) return CUBEB_ERROR; - device->devid = dev; /* passed to stream_init() */ - device->device_id = dev; /* printable in UI */ - device->friendly_name = dev; /* same, but friendly */ - device->group_id = dev; /* actual device if full-duplex */ - device->vendor_name = NULL; /* may be NULL */ - device->type = type; /* Input/Output */ + device->devid = dev; /* passed to stream_init() */ + device->device_id = dev; /* printable in UI */ + device->friendly_name = dev; /* same, but friendly */ + device->group_id = dev; /* actual device if full-duplex */ + device->vendor_name = NULL; /* may be NULL */ + device->type = type; /* Input/Output */ device->state = CUBEB_DEVICE_STATE_ENABLED; device->preferred = CUBEB_DEVICE_PREF_ALL; device->format = CUBEB_DEVICE_FMT_S16NE; device->default_format = CUBEB_DEVICE_FMT_S16NE; - device->max_channels = 16; + device->max_channels = (type == CUBEB_DEVICE_TYPE_INPUT) ? 2 : 8; device->default_rate = 48000; device->min_rate = 4000; device->max_rate = 192000; @@ -639,32 +640,30 @@ sndio_enumerate_devices(cubeb *context, cubeb_device_type type, static int sndio_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection) + cubeb_device_collection * collection) { free(collection->device); return CUBEB_OK; } static struct cubeb_ops const sndio_ops = { - .init = sndio_init, - .get_backend_id = sndio_get_backend_id, - .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, - .enumerate_devices = sndio_enumerate_devices, - .device_collection_destroy = sndio_device_collection_destroy, - .destroy = sndio_destroy, - .stream_init = sndio_stream_init, - .stream_destroy = sndio_stream_destroy, - .stream_start = sndio_stream_start, - .stream_stop = sndio_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = sndio_stream_get_position, - .stream_get_latency = sndio_stream_get_latency, - .stream_set_volume = sndio_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = NULL, - .stream_device_destroy = NULL, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; + .init = sndio_init, + .get_backend_id = sndio_get_backend_id, + .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, + .enumerate_devices = sndio_enumerate_devices, + .device_collection_destroy = sndio_device_collection_destroy, + .destroy = sndio_destroy, + .stream_init = sndio_stream_init, + .stream_destroy = sndio_stream_destroy, + .stream_start = sndio_stream_start, + .stream_stop = sndio_stream_stop, + .stream_get_position = sndio_stream_get_position, + .stream_get_latency = sndio_stream_get_latency, + .stream_set_volume = sndio_stream_set_volume, + .stream_set_name = NULL, + .stream_get_current_device = NULL, + .stream_device_destroy = NULL, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; diff --git a/externals/cubeb/src/cubeb_strings.c b/externals/cubeb/src/cubeb_strings.c index 79d7d21b3..5fe5b791e 100755 --- a/externals/cubeb/src/cubeb_strings.c +++ b/externals/cubeb/src/cubeb_strings.c @@ -23,7 +23,7 @@ struct cubeb_strings { int cubeb_strings_init(cubeb_strings ** strings) { - cubeb_strings* strs = NULL; + cubeb_strings * strs = NULL; if (!strings) { return CUBEB_ERROR; @@ -58,7 +58,7 @@ cubeb_strings_destroy(cubeb_strings * strings) sp = strings->data; se = sp + strings->count; - for ( ; sp != se; sp++) { + for (; sp != se; sp++) { if (*sp) { free(*sp); } @@ -88,7 +88,7 @@ cubeb_strings_lookup(cubeb_strings * strings, char const * s) sp = strings->data; se = sp + strings->count; - for ( ; sp != se; sp++) { + for (; sp != se; sp++) { if (*sp && strcmp(*sp, s) == 0) { return *sp; } @@ -152,4 +152,3 @@ cubeb_strings_intern(cubeb_strings * strings, char const * s) return cubeb_strings_push(strings, s); } - diff --git a/externals/cubeb/src/cubeb_strings.h b/externals/cubeb/src/cubeb_strings.h index a918a01c5..cfffbbc68 100755 --- a/externals/cubeb/src/cubeb_strings.h +++ b/externals/cubeb/src/cubeb_strings.h @@ -22,12 +22,14 @@ typedef struct cubeb_strings cubeb_strings; interned string storage will be returned. @retval CUBEB_OK in case of success. @retval CUBEB_ERROR in case of error. */ -CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings); +CUBEB_EXPORT int +cubeb_strings_init(cubeb_strings ** strings); /** Destroy an interned string structure freeing all associated memory. @param strings An opaque pointer to the interned string storage to destroy. */ -CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings); +CUBEB_EXPORT void +cubeb_strings_destroy(cubeb_strings * strings); /** Add string to internal storage. @param strings Opaque pointer to interned string storage. @@ -35,7 +37,8 @@ CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings); @retval CUBEB_OK @retval CUBEB_ERROR */ -CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s); +CUBEB_EXPORT char const * +cubeb_strings_intern(cubeb_strings * strings, char const * s); #if defined(__cplusplus) } diff --git a/externals/cubeb/src/cubeb_sun.c b/externals/cubeb/src/cubeb_sun.c index 206de447f..3b7bef71d 100755 --- a/externals/cubeb/src/cubeb_sun.c +++ b/externals/cubeb/src/cubeb_sun.c @@ -4,18 +4,18 @@ * This program is made available under an ISC-style license. See the * accompanying file LICENSE for details. */ -#include -#include +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" #include -#include +#include #include #include -#include #include +#include #include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" +#include +#include +#include /* Default to 4 + 1 for the default device. */ #ifndef SUN_DEVICE_COUNT @@ -45,11 +45,11 @@ */ #ifndef SUN_MAX_CHANNELS -# ifdef __NetBSD__ -# define SUN_MAX_CHANNELS (12) -# else -# define SUN_MAX_CHANNELS (2) -# endif +#ifdef __NetBSD__ +#define SUN_MAX_CHANNELS (12) +#else +#define SUN_MAX_CHANNELS (2) +#endif #endif #ifndef SUN_MIN_RATE @@ -145,8 +145,8 @@ sun_get_min_latency(cubeb * context, cubeb_stream_params params, } static int -sun_get_hwinfo(const char * device, struct audio_info * format, - int * props, struct audio_device * dev) +sun_get_hwinfo(const char * device, struct audio_info * format, int * props, + struct audio_device * dev) { int fd = -1; @@ -181,9 +181,10 @@ error: static int sun_prinfo_verify_sanity(struct audio_prinfo * prinfo) { - return prinfo->precision >= 8 && prinfo->precision <= 32 && - prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS && - prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE; + return prinfo->precision >= 8 && prinfo->precision <= 32 && + prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS && + prinfo->sample_rate < SUN_MAX_RATE && + prinfo->sample_rate > SUN_MIN_RATE; } static int @@ -196,7 +197,7 @@ sun_enumerate_devices(cubeb * context, cubeb_device_type type, char dev_friendly[64]; struct audio_info hwfmt; struct audio_device hwname; - struct audio_prinfo *prinfo = NULL; + struct audio_prinfo * prinfo = NULL; int hwprops; collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info)); @@ -262,7 +263,8 @@ sun_enumerate_devices(cubeb * context, cubeb_device_type type, device.vendor_name = strdup(hwname.name); device.type = type; device.state = CUBEB_DEVICE_STATE_ENABLED; - device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; + device.preferred = + (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; #ifdef AUDIO_GETFORMAT device.max_channels = prinfo->channels; device.default_rate = prinfo->sample_rate; @@ -393,7 +395,7 @@ sun_float_to_linear32(void * buf, unsigned sample_count, float vol) } static void -sun_linear32_to_float(void * buf, unsigned sample_count) +sun_linear32_to_float(void * buf, unsigned sample_count) { int32_t * in = buf; float * out = buf; @@ -418,7 +420,7 @@ sun_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol) static void * sun_io_routine(void * arg) { - cubeb_stream *s = arg; + cubeb_stream * s = arg; cubeb_state state = CUBEB_STATE_STARTED; size_t to_read = 0; long to_write = 0; @@ -439,8 +441,8 @@ sun_io_routine(void * arg) sun_linear32_to_float(s->record.buf, s->record.info.record.channels * SUN_BUFFER_FRAMES); } - to_write = s->data_cb(s, s->user_ptr, - s->record.buf, s->play.buf, SUN_BUFFER_FRAMES); + to_write = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, + SUN_BUFFER_FRAMES); if (to_write == CUBEB_ERROR) { state = CUBEB_STATE_ERROR; break; @@ -456,8 +458,8 @@ sun_io_routine(void * arg) sun_float_to_linear32(s->play.buf, s->play.info.play.channels * to_write, vol); } else { - sun_linear16_set_vol(s->play.buf, - s->play.info.play.channels * to_write, vol); + sun_linear16_set_vol(s->play.buf, s->play.info.play.channels * to_write, + vol); } } if (to_write < SUN_BUFFER_FRAMES) { @@ -473,7 +475,8 @@ sun_io_routine(void * arg) if (to_write > 0) { bytes = to_write * s->play.frame_size; - if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) { + if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < + 0) { state = CUBEB_STATE_ERROR; break; } @@ -486,7 +489,8 @@ sun_io_routine(void * arg) } if (to_read > 0) { bytes = to_read * s->record.frame_size; - if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) { + if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, + bytes)) < 0) { state = CUBEB_STATE_ERROR; break; } @@ -505,20 +509,16 @@ sun_io_routine(void * arg) } static int -sun_stream_init(cubeb * context, - cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, +sun_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned latency_frames, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + unsigned latency_frames, cubeb_data_callback data_callback, + cubeb_state_callback state_callback, void * user_ptr) { int ret = CUBEB_OK; - cubeb_stream *s = NULL; + cubeb_stream * s = NULL; (void)stream_name; (void)latency_frames; @@ -529,14 +529,14 @@ sun_stream_init(cubeb * context, s->record.fd = -1; s->play.fd = -1; if (input_device != 0) { - snprintf(s->record.name, sizeof(s->record.name), - "/dev/audio%zu", (uintptr_t)input_device - 1); + snprintf(s->record.name, sizeof(s->record.name), "/dev/audio%zu", + (uintptr_t)input_device - 1); } else { snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE); } if (output_device != 0) { - snprintf(s->play.name, sizeof(s->play.name), - "/dev/audio%zu", (uintptr_t)output_device - 1); + snprintf(s->play.name, sizeof(s->play.name), "/dev/audio%zu", + (uintptr_t)output_device - 1); } else { snprintf(s->play.name, sizeof(s->play.name), "%s", SUN_DEFAULT_DEVICE); } @@ -558,11 +558,13 @@ sun_stream_init(cubeb * context, s->record.info.mode = AUMODE_RECORD; #endif if ((ret = sun_copy_params(s->record.fd, s, input_stream_params, - &s->record.info, &s->record.info.record)) != CUBEB_OK) { + &s->record.info, &s->record.info.record)) != + CUBEB_OK) { LOG("Setting record params failed"); goto error; } - s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); + s->record.floating = + (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE); } if (output_stream_params != NULL) { if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) { @@ -582,7 +584,8 @@ sun_stream_init(cubeb * context, s->play.info.mode = AUMODE_PLAY; #endif if ((ret = sun_copy_params(s->play.fd, s, output_stream_params, - &s->play.info, &s->play.info.play)) != CUBEB_OK) { + &s->play.info, &s->play.info.play)) != + CUBEB_OK) { LOG("Setting play params failed"); goto error; } @@ -597,17 +600,18 @@ sun_stream_init(cubeb * context, LOG("Failed to create mutex"); goto error; } - s->play.frame_size = s->play.info.play.channels * - (s->play.info.play.precision / 8); + s->play.frame_size = + s->play.info.play.channels * (s->play.info.play.precision / 8); if (s->play.fd != -1 && - (s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) { + (s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) { ret = CUBEB_ERROR; goto error; } - s->record.frame_size = s->record.info.record.channels * - (s->record.info.record.precision / 8); + s->record.frame_size = + s->record.info.record.channels * (s->record.info.record.precision / 8); if (s->record.fd != -1 && - (s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == NULL) { + (s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == + NULL) { ret = CUBEB_ERROR; goto error; } @@ -688,10 +692,10 @@ sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device) if (*device == NULL) { return CUBEB_ERROR; } - (*device)->input_name = stream->record.fd != -1 ? - strdup(stream->record.name) : NULL; - (*device)->output_name = stream->play.fd != -1 ? - strdup(stream->play.name) : NULL; + (*device)->input_name = + stream->record.fd != -1 ? strdup(stream->record.name) : NULL; + (*device)->output_name = + stream->play.fd != -1 ? strdup(stream->play.name) : NULL; return CUBEB_OK; } @@ -706,26 +710,24 @@ sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device) } static struct cubeb_ops const sun_ops = { - .init = sun_init, - .get_backend_id = sun_get_backend_id, - .get_max_channel_count = sun_get_max_channel_count, - .get_min_latency = sun_get_min_latency, - .get_preferred_sample_rate = sun_get_preferred_sample_rate, - .enumerate_devices = sun_enumerate_devices, - .device_collection_destroy = sun_device_collection_destroy, - .destroy = sun_destroy, - .stream_init = sun_stream_init, - .stream_destroy = sun_stream_destroy, - .stream_start = sun_stream_start, - .stream_stop = sun_stream_stop, - .stream_reset_default_device = NULL, - .stream_get_position = sun_stream_get_position, - .stream_get_latency = sun_stream_get_latency, - .stream_get_input_latency = NULL, - .stream_set_volume = sun_stream_set_volume, - .stream_set_name = NULL, - .stream_get_current_device = sun_get_current_device, - .stream_device_destroy = sun_stream_device_destroy, - .stream_register_device_changed_callback = NULL, - .register_device_collection_changed = NULL -}; + .init = sun_init, + .get_backend_id = sun_get_backend_id, + .get_max_channel_count = sun_get_max_channel_count, + .get_min_latency = sun_get_min_latency, + .get_preferred_sample_rate = sun_get_preferred_sample_rate, + .enumerate_devices = sun_enumerate_devices, + .device_collection_destroy = sun_device_collection_destroy, + .destroy = sun_destroy, + .stream_init = sun_stream_init, + .stream_destroy = sun_stream_destroy, + .stream_start = sun_stream_start, + .stream_stop = sun_stream_stop, + .stream_get_position = sun_stream_get_position, + .stream_get_latency = sun_stream_get_latency, + .stream_get_input_latency = NULL, + .stream_set_volume = sun_stream_set_volume, + .stream_set_name = NULL, + .stream_get_current_device = sun_get_current_device, + .stream_device_destroy = sun_stream_device_destroy, + .stream_register_device_changed_callback = NULL, + .register_device_collection_changed = NULL}; diff --git a/externals/cubeb/src/cubeb_utils.cpp b/externals/cubeb/src/cubeb_utils.cpp index d42e951e4..dd1aef7b8 100755 --- a/externals/cubeb/src/cubeb_utils.cpp +++ b/externals/cubeb/src/cubeb_utils.cpp @@ -7,18 +7,19 @@ #include "cubeb_utils.h" -size_t cubeb_sample_size(cubeb_sample_format format) +size_t +cubeb_sample_size(cubeb_sample_format format) { switch (format) { - case CUBEB_SAMPLE_S16LE: - case CUBEB_SAMPLE_S16BE: - return sizeof(int16_t); - case CUBEB_SAMPLE_FLOAT32LE: - case CUBEB_SAMPLE_FLOAT32BE: - return sizeof(float); - default: - // should never happen as all cases are handled above. - assert(false); - return 0; + case CUBEB_SAMPLE_S16LE: + case CUBEB_SAMPLE_S16BE: + return sizeof(int16_t); + case CUBEB_SAMPLE_FLOAT32LE: + case CUBEB_SAMPLE_FLOAT32BE: + return sizeof(float); + default: + // should never happen as all cases are handled above. + assert(false); + return 0; } } diff --git a/externals/cubeb/src/cubeb_utils.h b/externals/cubeb/src/cubeb_utils.h index df6751155..fd7a3d740 100755 --- a/externals/cubeb/src/cubeb_utils.h +++ b/externals/cubeb/src/cubeb_utils.h @@ -12,10 +12,10 @@ #ifdef __cplusplus -#include -#include #include #include +#include +#include #include #if defined(_WIN32) #include "cubeb_utils_win.h" @@ -24,8 +24,9 @@ #endif /** Similar to memcpy, but accounts for the size of an element. */ -template -void PodCopy(T * destination, const T * source, size_t count) +template +void +PodCopy(T * destination, const T * source, size_t count) { static_assert(std::is_trivial::value, "Requires trivial type"); assert(destination && source); @@ -33,8 +34,9 @@ void PodCopy(T * destination, const T * source, size_t count) } /** Similar to memmove, but accounts for the size of an element. */ -template -void PodMove(T * destination, const T * source, size_t count) +template +void +PodMove(T * destination, const T * source, size_t count) { static_assert(std::is_trivial::value, "Requires trivial type"); assert(destination && source); @@ -42,133 +44,118 @@ void PodMove(T * destination, const T * source, size_t count) } /** Similar to a memset to zero, but accounts for the size of an element. */ -template -void PodZero(T * destination, size_t count) +template +void +PodZero(T * destination, size_t count) { static_assert(std::is_trivial::value, "Requires trivial type"); assert(destination); - memset(destination, 0, count * sizeof(T)); + memset(destination, 0, count * sizeof(T)); } namespace { -template -void Copy(T * destination, const T * source, size_t count, Trait) +template +void +Copy(T * destination, const T * source, size_t count, Trait) { for (size_t i = 0; i < count; i++) { destination[i] = source[i]; } } -template -void Copy(T * destination, const T * source, size_t count, std::true_type) +template +void +Copy(T * destination, const T * source, size_t count, std::true_type) { PodCopy(destination, source, count); } -} +} // namespace /** * This allows copying a number of elements from a `source` pointer to a * `destination` pointer, using `memcpy` if it is safe to do so, or a loop that * calls the constructors and destructors otherwise. */ -template -void Copy(T * destination, const T * source, size_t count) +template +void +Copy(T * destination, const T * source, size_t count) { assert(destination && source); Copy(destination, source, count, typename std::is_trivial::type()); } namespace { -template -void ConstructDefault(T * destination, size_t count, Trait) +template +void +ConstructDefault(T * destination, size_t count, Trait) { for (size_t i = 0; i < count; i++) { destination[i] = T(); } } -template -void ConstructDefault(T * destination, - size_t count, std::true_type) +template +void +ConstructDefault(T * destination, size_t count, std::true_type) { PodZero(destination, count); } -} +} // namespace /** * This allows zeroing (using memset) or default-constructing a number of * elements calling the constructors and destructors if necessary. */ -template -void ConstructDefault(T * destination, size_t count) +template +void +ConstructDefault(T * destination, size_t count) { assert(destination); - ConstructDefault(destination, count, - typename std::is_arithmetic::type()); + ConstructDefault(destination, count, typename std::is_arithmetic::type()); } -template -class auto_array -{ +template class auto_array { public: explicit auto_array(uint32_t capacity = 0) - : data_(capacity ? new T[capacity] : nullptr) - , capacity_(capacity) - , length_(0) - {} - - ~auto_array() + : data_(capacity ? new T[capacity] : nullptr), capacity_(capacity), + length_(0) { - delete [] data_; } + ~auto_array() { delete[] data_; } + /** Get a constant pointer to the underlying data. */ - T * data() const - { - return data_; - } + T * data() const { return data_; } - T * end() const - { - return data_ + length_; - } + T * end() const { return data_ + length_; } - const T& at(size_t index) const + const T & at(size_t index) const { assert(index < length_ && "out of range"); return data_[index]; } - T& at(size_t index) + T & at(size_t index) { assert(index < length_ && "out of range"); return data_[index]; } /** Get how much underlying storage this auto_array has. */ - size_t capacity() const - { - return capacity_; - } + size_t capacity() const { return capacity_; } /** Get how much elements this auto_array contains. */ - size_t length() const - { - return length_; - } + size_t length() const { return length_; } /** Keeps the storage, but removes all the elements from the array. */ - void clear() - { - length_ = 0; - } + void clear() { length_ = 0; } - /** Change the storage of this auto array, copying the elements to the new - * storage. - * @returns true in case of success - * @returns false if the new capacity is not big enough to accomodate for the - * elements in the array. - */ + /** Change the storage of this auto array, copying the elements to the new + * storage. + * @returns true in case of success + * @returns false if the new capacity is not big enough to accomodate for the + * elements in the array. + */ bool reserve(size_t new_capacity) { if (new_capacity < length_) { @@ -179,17 +166,17 @@ public: PodCopy(new_data, data_, length_); } capacity_ = new_capacity; - delete [] data_; + delete[] data_; data_ = new_data; return true; } - /** Append `length` elements to the end of the array, resizing the array if - * needed. - * @parameter elements the elements to append to the array. - * @parameter length the number of elements to append to the array. - */ + /** Append `length` elements to the end of the array, resizing the array if + * needed. + * @parameter elements the elements to append to the array. + * @parameter length the number of elements to append to the array. + */ void push(const T * elements, size_t length) { if (length_ + length > capacity_) { @@ -227,17 +214,14 @@ public: } /** Return the number of free elements in the array. */ - size_t available() const - { - return capacity_ - length_; - } + size_t available() const { return capacity_ - length_; } /** Copies `length` elements to `elements` if it is not null, and shift - * the remaining elements of the `auto_array` to the beginning. - * @parameter elements a buffer to copy the elements to, or nullptr. - * @parameter length the number of elements to copy. - * @returns true in case of success. - * @returns false if the auto_array contains less than `length` elements. */ + * the remaining elements of the `auto_array` to the beginning. + * @parameter elements a buffer to copy the elements to, or nullptr. + * @parameter length the number of elements to copy. + * @returns true in case of success. + * @returns false if the auto_array contains less than `length` elements. */ bool pop(T * elements, size_t length) { if (length > length_) { @@ -285,56 +269,38 @@ template struct auto_array_wrapper_impl : public auto_array_wrapper { auto_array_wrapper_impl() {} - explicit auto_array_wrapper_impl(uint32_t size) - : ar(size) - {} + explicit auto_array_wrapper_impl(uint32_t size) : ar(size) {} - void push(void * elements, size_t length) override { + void push(void * elements, size_t length) override + { ar.push(static_cast(elements), length); } - size_t length() override { - return ar.length(); - } + size_t length() override { return ar.length(); } - void push_silence(size_t length) override { - ar.push_silence(length); - } + void push_silence(size_t length) override { ar.push_silence(length); } - bool pop(size_t length) override { - return ar.pop(nullptr, length); - } + bool pop(size_t length) override { return ar.pop(nullptr, length); } - void * data() override { - return ar.data(); - } + void * data() override { return ar.data(); } - void * end() override { - return ar.end(); - } + void * end() override { return ar.end(); } - void clear() override { - ar.clear(); - } + void clear() override { ar.clear(); } - bool reserve(size_t capacity) override { - return ar.reserve(capacity); - } + bool reserve(size_t capacity) override { return ar.reserve(capacity); } - void set_length(size_t length) override { - ar.set_length(length); - } + void set_length(size_t length) override { ar.set_length(length); } - ~auto_array_wrapper_impl() { - ar.clear(); - } + ~auto_array_wrapper_impl() { ar.clear(); } private: auto_array ar; }; extern "C" { - size_t cubeb_sample_size(cubeb_sample_format format); +size_t +cubeb_sample_size(cubeb_sample_format format); } using auto_lock = std::lock_guard; diff --git a/externals/cubeb/src/cubeb_utils_unix.h b/externals/cubeb/src/cubeb_utils_unix.h index 4876d015f..b6618ca45 100755 --- a/externals/cubeb/src/cubeb_utils_unix.h +++ b/externals/cubeb/src/cubeb_utils_unix.h @@ -8,13 +8,12 @@ #if !defined(CUBEB_UTILS_UNIX) #define CUBEB_UTILS_UNIX -#include #include +#include #include /* This wraps a critical section to track the owner in debug mode. */ -class owned_critical_section -{ +class owned_critical_section { public: owned_critical_section() { @@ -29,7 +28,7 @@ public: #ifndef NDEBUG int r = #endif - pthread_mutex_init(&mutex, &attr); + pthread_mutex_init(&mutex, &attr); #ifndef NDEBUG assert(r == 0); #endif @@ -42,7 +41,7 @@ public: #ifndef NDEBUG int r = #endif - pthread_mutex_destroy(&mutex); + pthread_mutex_destroy(&mutex); #ifndef NDEBUG assert(r == 0); #endif @@ -53,7 +52,7 @@ public: #ifndef NDEBUG int r = #endif - pthread_mutex_lock(&mutex); + pthread_mutex_lock(&mutex); #ifndef NDEBUG assert(r == 0 && "Deadlock"); #endif @@ -64,7 +63,7 @@ public: #ifndef NDEBUG int r = #endif - pthread_mutex_unlock(&mutex); + pthread_mutex_unlock(&mutex); #ifndef NDEBUG assert(r == 0 && "Unlocking unlocked mutex"); #endif @@ -82,8 +81,8 @@ private: pthread_mutex_t mutex; // Disallow copy and assignment because pthread_mutex_t cannot be copied. - owned_critical_section(const owned_critical_section&); - owned_critical_section& operator=(const owned_critical_section&); + owned_critical_section(const owned_critical_section &); + owned_critical_section & operator=(const owned_critical_section &); }; #endif /* CUBEB_UTILS_UNIX */ diff --git a/externals/cubeb/src/cubeb_utils_win.h b/externals/cubeb/src/cubeb_utils_win.h index 0112ad6d3..4c47f454c 100755 --- a/externals/cubeb/src/cubeb_utils_win.h +++ b/externals/cubeb/src/cubeb_utils_win.h @@ -8,26 +8,23 @@ #if !defined(CUBEB_UTILS_WIN) #define CUBEB_UTILS_WIN -#include #include "cubeb-internal.h" +#include /* This wraps a critical section to track the owner in debug mode, adapted from - NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */ -class owned_critical_section -{ + NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx + */ +class owned_critical_section { public: owned_critical_section() #ifndef NDEBUG - : owner(0) + : owner(0) #endif { InitializeCriticalSection(&critical_section); } - ~owned_critical_section() - { - DeleteCriticalSection(&critical_section); - } + ~owned_critical_section() { DeleteCriticalSection(&critical_section); } void lock() { @@ -64,8 +61,8 @@ private: #endif // Disallow copy and assignment because CRICICAL_SECTION cannot be copied. - owned_critical_section(const owned_critical_section&); - owned_critical_section& operator=(const owned_critical_section&); + owned_critical_section(const owned_critical_section &); + owned_critical_section & operator=(const owned_critical_section &); }; #endif /* CUBEB_UTILS_WIN */ diff --git a/externals/cubeb/src/cubeb_wasapi.cpp b/externals/cubeb/src/cubeb_wasapi.cpp index 1194eb942..9cc8a262e 100755 --- a/externals/cubeb/src/cubeb_wasapi.cpp +++ b/externals/cubeb/src/cubeb_wasapi.cpp @@ -7,85 +7,89 @@ #define _WIN32_WINNT 0x0603 #define NOMINMAX -#include -#include -#include -#include +#include +#include #include -#include -#include #include +#include +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include #include +#include +#include -#include "cubeb/cubeb.h" #include "cubeb-internal.h" +#include "cubeb/cubeb.h" #include "cubeb_mixer.h" #include "cubeb_resampler.h" #include "cubeb_strings.h" #include "cubeb_utils.h" // 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 compiled -// against an older SDK. +// 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 +// compiled against an older SDK. #ifndef __IAudioClient3_INTERFACE_DEFINED__ #define __IAudioClient3_INTERFACE_DEFINED__ MIDL_INTERFACE("7ED4EE07-8E67-4CD4-8C1A-2B7A5987AD42") IAudioClient3 : public IAudioClient { public: - virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod( - /* [annotation][in] */ - _In_ const WAVEFORMATEX *pFormat, - /* [annotation][out] */ - _Out_ UINT32 *pDefaultPeriodInFrames, - /* [annotation][out] */ - _Out_ UINT32 *pFundamentalPeriodInFrames, - /* [annotation][out] */ - _Out_ UINT32 *pMinPeriodInFrames, - /* [annotation][out] */ - _Out_ UINT32 *pMaxPeriodInFrames) = 0; + virtual HRESULT STDMETHODCALLTYPE GetSharedModeEnginePeriod( + /* [annotation][in] */ + _In_ const WAVEFORMATEX * pFormat, + /* [annotation][out] */ + _Out_ UINT32 * pDefaultPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 * pFundamentalPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 * pMinPeriodInFrames, + /* [annotation][out] */ + _Out_ UINT32 * pMaxPeriodInFrames) = 0; - virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod( - /* [unique][annotation][out] */ - _Out_ WAVEFORMATEX **ppFormat, - /* [annotation][out] */ - _Out_ UINT32 *pCurrentPeriodInFrames) = 0; + virtual HRESULT STDMETHODCALLTYPE GetCurrentSharedModeEnginePeriod( + /* [unique][annotation][out] */ + _Out_ WAVEFORMATEX * *ppFormat, + /* [annotation][out] */ + _Out_ UINT32 * pCurrentPeriodInFrames) = 0; - virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream( - /* [annotation][in] */ - _In_ DWORD StreamFlags, - /* [annotation][in] */ - _In_ UINT32 PeriodInFrames, - /* [annotation][in] */ - _In_ const WAVEFORMATEX *pFormat, - /* [annotation][in] */ - _In_opt_ LPCGUID AudioSessionGuid) = 0; + virtual HRESULT STDMETHODCALLTYPE InitializeSharedAudioStream( + /* [annotation][in] */ + _In_ DWORD StreamFlags, + /* [annotation][in] */ + _In_ UINT32 PeriodInFrames, + /* [annotation][in] */ + _In_ const WAVEFORMATEX * pFormat, + /* [annotation][in] */ + _In_opt_ LPCGUID AudioSessionGuid) = 0; }; #ifdef __CRT_UUID_DECL // Required for MinGW -__CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42) +__CRT_UUID_DECL(IAudioClient3, 0x7ED4EE07, 0x8E67, 0x4CD4, 0x8C, 0x1A, 0x2B, + 0x7A, 0x59, 0x87, 0xAD, 0x42) #endif #endif // Copied from audioclient.h in the Windows 10 SDK #ifndef AUDCLNT_E_ENGINE_PERIODICITY_LOCKED -#define AUDCLNT_E_ENGINE_PERIODICITY_LOCKED AUDCLNT_ERR(0x028) +#define AUDCLNT_E_ENGINE_PERIODICITY_LOCKED AUDCLNT_ERR(0x028) #endif #ifndef PKEY_Device_FriendlyName -DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14); // DEVPROP_TYPE_STRING +DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, + 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, + 14); // DEVPROP_TYPE_STRING #endif #ifndef PKEY_Device_InstanceId -DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, 0x00000100); // VT_LPWSTR +DEFINE_PROPERTYKEY(PKEY_Device_InstanceId, 0x78c34fc8, 0x104a, 0x4aca, 0x9e, + 0xa4, 0x52, 0x4d, 0x52, 0x99, 0x6e, 0x57, + 0x00000100); // VT_LPWSTR #endif namespace { @@ -93,29 +97,23 @@ namespace { const int64_t LATENCY_NOT_AVAILABLE_YET = -1; struct com_heap_ptr_deleter { - void operator()(void * ptr) const noexcept { - CoTaskMemFree(ptr); - } + void operator()(void * ptr) const noexcept { CoTaskMemFree(ptr); } }; template using com_heap_ptr = std::unique_ptr; -template -constexpr size_t -ARRAY_LENGTH(T(&)[N]) +template constexpr size_t ARRAY_LENGTH(T (&)[N]) { return N; } -template -class no_addref_release : public T { +template class no_addref_release : public T { ULONG STDMETHODCALLTYPE AddRef() = 0; ULONG STDMETHODCALLTYPE Release() = 0; }; -template -class com_ptr { +template class com_ptr { public: com_ptr() noexcept = default; @@ -123,17 +121,12 @@ public: com_ptr & operator=(com_ptr const & other) noexcept = delete; T ** operator&() const noexcept = delete; - ~com_ptr() noexcept { - release(); - } + ~com_ptr() noexcept { release(); } - com_ptr(com_ptr && other) noexcept - : ptr(other.ptr) + com_ptr(com_ptr && other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } + + com_ptr & operator=(com_ptr && other) noexcept { - other.ptr = nullptr; - } - - com_ptr & operator=(com_ptr && other) noexcept { if (ptr != other.ptr) { release(); ptr = other.ptr; @@ -142,39 +135,41 @@ public: return *this; } - explicit operator bool() const noexcept { - return nullptr != ptr; - } + explicit operator bool() const noexcept { return nullptr != ptr; } - no_addref_release * operator->() const noexcept { + no_addref_release * operator->() const noexcept + { return static_cast *>(ptr); } - T * get() const noexcept { - return ptr; - } + T * get() const noexcept { return ptr; } - T ** receive() noexcept { + T ** receive() noexcept + { XASSERT(ptr == nullptr); return &ptr; } - void ** receive_vpp() noexcept { + void ** receive_vpp() noexcept + { return reinterpret_cast(receive()); } - com_ptr & operator=(std::nullptr_t) noexcept { + com_ptr & operator=(std::nullptr_t) noexcept + { release(); return *this; } - void reset(T * p = nullptr) noexcept { + void reset(T * p = nullptr) noexcept + { release(); ptr = p; } private: - void release() noexcept { + void release() noexcept + { T * temp = ptr; if (temp) { @@ -188,19 +183,33 @@ private: extern cubeb_ops const wasapi_ops; -int wasapi_stream_stop(cubeb_stream * stm); -int wasapi_stream_start(cubeb_stream * stm); -void close_wasapi_stream(cubeb_stream * stm); -int setup_wasapi_stream(cubeb_stream * stm); -ERole pref_to_role(cubeb_stream_prefs param); -int wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev); -void wasapi_destroy_device(cubeb_device_info * device_info); -static int wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, cubeb_device_collection * out); -static int wasapi_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection); -static char const * wstr_to_utf8(wchar_t const * str); -static std::unique_ptr utf8_to_wstr(char const * str); +int +wasapi_stream_stop(cubeb_stream * stm); +int +wasapi_stream_start(cubeb_stream * stm); +void +close_wasapi_stream(cubeb_stream * stm); +int +setup_wasapi_stream(cubeb_stream * stm); +ERole +pref_to_role(cubeb_stream_prefs param); +int +wasapi_create_device(cubeb * ctx, cubeb_device_info & ret, + IMMDeviceEnumerator * enumerator, IMMDevice * dev); +void +wasapi_destroy_device(cubeb_device_info * device_info); +static int +wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, + cubeb_device_collection * out); +static int +wasapi_device_collection_destroy(cubeb * ctx, + cubeb_device_collection * collection); +static char const * +wstr_to_utf8(wchar_t const * str); +static std::unique_ptr +utf8_to_wstr(char const * str); -} +} // namespace class wasapi_collection_notification_client; class monitor_device_notifications; @@ -213,10 +222,12 @@ struct cubeb { com_ptr device_collection_enumerator; com_ptr collection_notification_client; /* Collection changed for input (capture) devices. */ - cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr; + cubeb_device_collection_changed_callback input_collection_changed_callback = + nullptr; void * input_collection_changed_user_ptr = nullptr; /* Collection changed for output (render) devices. */ - cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr; + cubeb_device_collection_changed_callback output_collection_changed_callback = + nullptr; void * output_collection_changed_user_ptr = nullptr; UINT64 performance_counter_frequency; }; @@ -241,12 +252,20 @@ struct cubeb_stream { /* Mixer pameters. We need to convert the input stream to this samplerate/channel layout, as WASAPI does not resample nor upmix itself. */ - cubeb_stream_params input_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE }; - cubeb_stream_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE }; + cubeb_stream_params input_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, + CUBEB_LAYOUT_UNDEFINED, + CUBEB_STREAM_PREF_NONE}; + cubeb_stream_params output_mix_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, + CUBEB_LAYOUT_UNDEFINED, + CUBEB_STREAM_PREF_NONE}; /* Stream parameters. This is what the client requested, * and what will be presented in the callback. */ - cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE }; - cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE }; + cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, + CUBEB_LAYOUT_UNDEFINED, + CUBEB_STREAM_PREF_NONE}; + cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0, + CUBEB_LAYOUT_UNDEFINED, + CUBEB_STREAM_PREF_NONE}; /* A MMDevice role for this stream: either communication or console here. */ ERole role; /* True if this stream will transport voice-data. */ @@ -277,8 +296,10 @@ struct cubeb_stream { com_ptr output_client; /* Interface pointer to use the event-driven interface. */ com_ptr render_client; +#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME /* Interface pointer to use the volume facilities. */ com_ptr audio_stream_volume; +#endif /* Interface pointer to use the stream audio clock. */ com_ptr audio_clock; /* Frames written to the stream since it was opened. Reset on device @@ -324,10 +345,13 @@ struct cubeb_stream { /* Maximum number of frames that can be requested in a callback. */ uint32_t output_buffer_frame_count = 0; /* Resampler instance. Resampling will only happen if necessary. */ - std::unique_ptr resampler = { nullptr, cubeb_resampler_destroy }; + std::unique_ptr + resampler = {nullptr, cubeb_resampler_destroy}; /* Mixer interfaces */ - std::unique_ptr output_mixer = { nullptr, cubeb_mixer_destroy }; - std::unique_ptr input_mixer = { nullptr, cubeb_mixer_destroy }; + std::unique_ptr output_mixer = { + nullptr, cubeb_mixer_destroy}; + std::unique_ptr input_mixer = { + nullptr, cubeb_mixer_destroy}; /* A buffer for up/down mixing multi-channel audio output. */ std::vector mix_buffer; /* WASAPI input works in "packets". We re-linearize the audio packets @@ -345,18 +369,23 @@ struct cubeb_stream { bool draining = false; /* True when we've destroyed the stream. This pointer is leaked on stream * destruction if we could not join the thread. */ - std::atomic*> emergency_bailout { nullptr }; - /* Synchronizes render thread start to ensure safe access to emergency_bailout. */ + std::atomic *> emergency_bailout{nullptr}; + /* Synchronizes render thread start to ensure safe access to + * emergency_bailout. */ HANDLE thread_ready_event = 0; /* This needs an active audio input stream to be known, and is updated in the * first audio input callback. */ - std::atomic input_latency_hns { LATENCY_NOT_AVAILABLE_YET }; + std::atomic input_latency_hns{LATENCY_NOT_AVAILABLE_YET}; + + /* Those attributes count the number of frames requested (resp. received) by + the OS, to be able to detect drifts. This is only used for logging for now. */ + size_t total_input_frames = 0; + size_t total_output_frames = 0; }; class monitor_device_notifications { public: - monitor_device_notifications(cubeb * context) - : cubeb_context(context) + monitor_device_notifications(cubeb * context) : cubeb_context(context) { create_thread(); } @@ -390,12 +419,12 @@ public: } } } + private: - static unsigned int __stdcall - thread_proc(LPVOID args) + static unsigned int __stdcall thread_proc(LPVOID args) { XASSERT(args); - auto mdn = static_cast(args); + auto mdn = static_cast(args); mdn->notification_thread_loop(); SetEvent(mdn->shutdown_complete); return 0; @@ -404,34 +433,31 @@ private: void notification_thread_loop() { struct auto_com { - auto_com() { + auto_com() + { HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); XASSERT(SUCCEEDED(hr)); } - ~auto_com() { - CoUninitialize(); - } + ~auto_com() { CoUninitialize(); } } com; HANDLE wait_array[3] = { - input_changed, - output_changed, - begin_shutdown, + input_changed, + output_changed, + begin_shutdown, }; while (true) { Sleep(200); DWORD wait_result = WaitForMultipleObjects(ARRAY_LENGTH(wait_array), - wait_array, - FALSE, - INFINITE); + wait_array, FALSE, INFINITE); if (wait_result == WAIT_OBJECT_0) { // input changed - cubeb_context->input_collection_changed_callback(cubeb_context, - cubeb_context->input_collection_changed_user_ptr); + cubeb_context->input_collection_changed_callback( + cubeb_context, cubeb_context->input_collection_changed_user_ptr); } else if (wait_result == WAIT_OBJECT_0 + 1) { // output changed - cubeb_context->output_collection_changed_callback(cubeb_context, - cubeb_context->output_collection_changed_user_ptr); + cubeb_context->output_collection_changed_callback( + cubeb_context, cubeb_context->output_collection_changed_user_ptr); } else if (wait_result == WAIT_OBJECT_0 + 2) { // shutdown break; } else { @@ -466,12 +492,8 @@ private: return; } - thread = (HANDLE) _beginthreadex(nullptr, - 256 * 1024, - thread_proc, - this, - STACK_SIZE_PARAM_IS_A_RESERVATION, - nullptr); + thread = (HANDLE)_beginthreadex(nullptr, 256 * 1024, thread_proc, this, + STACK_SIZE_PARAM_IS_A_RESERVATION, nullptr); if (!thread) { LOG("Failed to create thread."); return; @@ -487,18 +509,12 @@ private: cubeb * cubeb_context = nullptr; }; -class wasapi_collection_notification_client : public IMMNotificationClient -{ +class wasapi_collection_notification_client : public IMMNotificationClient { public: /* The implementation of MSCOM was copied from MSDN. */ - ULONG STDMETHODCALLTYPE - AddRef() - { - return InterlockedIncrement(&ref_count); - } + ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&ref_count); } - ULONG STDMETHODCALLTYPE - Release() + ULONG STDMETHODCALLTYPE Release() { ULONG ulRef = InterlockedDecrement(&ref_count); if (0 == ulRef) { @@ -507,15 +523,14 @@ public: return ulRef; } - HRESULT STDMETHODCALLTYPE - QueryInterface(REFIID riid, VOID **ppvInterface) + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID ** ppvInterface) { if (__uuidof(IUnknown) == riid) { AddRef(); - *ppvInterface = (IUnknown*)this; + *ppvInterface = (IUnknown *)this; } else if (__uuidof(IMMNotificationClient) == riid) { AddRef(); - *ppvInterface = (IMMNotificationClient*)this; + *ppvInterface = (IMMNotificationClient *)this; } else { *ppvInterface = NULL; return E_NOINTERFACE; @@ -524,18 +539,15 @@ public: } wasapi_collection_notification_client(cubeb * context) - : ref_count(1) - , cubeb_context(context) - , monitor_notifications(context) + : ref_count(1), cubeb_context(context), monitor_notifications(context) { XASSERT(cubeb_context); } - virtual ~wasapi_collection_notification_client() - { } + virtual ~wasapi_collection_notification_client() {} - HRESULT STDMETHODCALLTYPE - OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, + LPCWSTR device_id) { LOG("collection: Audio device default changed, id = %S.", device_id); return S_OK; @@ -555,12 +567,13 @@ public: return S_OK; } - HRESULT STDMETHODCALLTYPE - OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR device_id, + DWORD new_state) { XASSERT(cubeb_context->output_collection_changed_callback || cubeb_context->input_collection_changed_callback); - LOG("collection: Audio device state changed, id = %S, state = %lu.", device_id, new_state); + LOG("collection: Audio device state changed, id = %S, state = %lu.", + device_id, new_state); EDataFlow flow; HRESULT hr = GetDataFlow(device_id, &flow); if (FAILED(hr)) { @@ -570,10 +583,10 @@ public: return S_OK; } - HRESULT STDMETHODCALLTYPE - OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR device_id, + const PROPERTYKEY key) { - //Audio device property value changed. + // Audio device property value changed. return S_OK; } @@ -583,8 +596,8 @@ private: com_ptr device; com_ptr endpoint; - HRESULT hr = cubeb_context->device_collection_enumerator - ->GetDevice(device_id, device.receive()); + HRESULT hr = cubeb_context->device_collection_enumerator->GetDevice( + device_id, device.receive()); if (FAILED(hr)) { LOG("collection: Could not get device: %lx", hr); return hr; @@ -606,18 +619,12 @@ private: monitor_device_notifications monitor_notifications; }; -class wasapi_endpoint_notification_client : public IMMNotificationClient -{ +class wasapi_endpoint_notification_client : public IMMNotificationClient { public: /* The implementation of MSCOM was copied from MSDN. */ - ULONG STDMETHODCALLTYPE - AddRef() - { - return InterlockedIncrement(&ref_count); - } + ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&ref_count); } - ULONG STDMETHODCALLTYPE - Release() + ULONG STDMETHODCALLTYPE Release() { ULONG ulRef = InterlockedDecrement(&ref_count); if (0 == ulRef) { @@ -626,15 +633,14 @@ public: return ulRef; } - HRESULT STDMETHODCALLTYPE - QueryInterface(REFIID riid, VOID **ppvInterface) + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID ** ppvInterface) { if (__uuidof(IUnknown) == riid) { AddRef(); - *ppvInterface = (IUnknown*)this; + *ppvInterface = (IUnknown *)this; } else if (__uuidof(IMMNotificationClient) == riid) { AddRef(); - *ppvInterface = (IMMNotificationClient*)this; + *ppvInterface = (IMMNotificationClient *)this; } else { *ppvInterface = NULL; return E_NOINTERFACE; @@ -643,16 +649,14 @@ public: } wasapi_endpoint_notification_client(HANDLE event, ERole role) - : ref_count(1) - , reconfigure_event(event) - , role(role) - { } + : ref_count(1), reconfigure_event(event), role(role) + { + } - virtual ~wasapi_endpoint_notification_client() - { } + virtual ~wasapi_endpoint_notification_client() {} - HRESULT STDMETHODCALLTYPE - OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) + HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, + LPCWSTR device_id) { LOG("endpoint: Audio device default changed."); @@ -663,7 +667,8 @@ public: BOOL ok = SetEvent(reconfigure_event); if (!ok) { - LOG("endpoint: SetEvent on reconfigure_event failed: %lx", GetLastError()); + LOG("endpoint: SetEvent on reconfigure_event failed: %lx", + GetLastError()); } return S_OK; @@ -683,19 +688,20 @@ public: return S_OK; } - HRESULT STDMETHODCALLTYPE - OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) + HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR device_id, + DWORD new_state) { LOG("endpoint: Audio device state changed."); return S_OK; } - HRESULT STDMETHODCALLTYPE - OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) + HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR device_id, + const PROPERTYKEY key) { - //Audio device property value changed. + // Audio device property value changed. return S_OK; } + private: /* refcount for this instance, necessary to implement MSCOM semantics. */ LONG ref_count; @@ -717,28 +723,34 @@ intern_device_id(cubeb * ctx, wchar_t const * id) char const * interned = cubeb_strings_intern(ctx->device_ids, tmp); - free((void *) tmp); + free((void *)tmp); return interned; } -bool has_input(cubeb_stream * stm) +bool +has_input(cubeb_stream * stm) { return stm->input_stream_params.rate != 0; } -bool has_output(cubeb_stream * stm) +bool +has_output(cubeb_stream * stm) { return stm->output_stream_params.rate != 0; } -double stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, cubeb_stream_params & mixer) +double +stream_to_mix_samplerate_ratio(cubeb_stream_params & stream, + cubeb_stream_params & mixer) { return double(stream.rate) / mixer.rate; } /* Convert the channel layout into the corresponding KSAUDIO_CHANNEL_CONFIG. - See more: https://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx */ + See more: + https://msdn.microsoft.com/en-us/library/windows/hardware/ff537083(v=vs.85).aspx + */ cubeb_channel_layout mask_to_channel_layout(WAVEFORMATEX const * fmt) @@ -746,7 +758,8 @@ mask_to_channel_layout(WAVEFORMATEX const * fmt) cubeb_channel_layout mask = 0; if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE const * ext = reinterpret_cast(fmt); + WAVEFORMATEXTENSIBLE const * ext = + reinterpret_cast(fmt); mask = ext->dwChannelMask; } else if (fmt->wFormatTag == WAVE_FORMAT_PCM || fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { @@ -778,12 +791,6 @@ hns_to_frames(cubeb_stream * stm, REFERENCE_TIME hns) return hns_to_frames(get_rate(stm), hns); } -REFERENCE_TIME -frames_to_hns(cubeb_stream * stm, uint32_t frames) -{ - return std::ceil(frames * 10000000.0 / get_rate(stm)); -} - REFERENCE_TIME frames_to_hns(uint32_t rate, uint32_t frames) { @@ -819,17 +826,17 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count, } } - long out_frames = cubeb_resampler_fill(stm->resampler.get(), - input_buffer, - &input_frames_count, - dest, - output_frames_needed); + long out_frames = + cubeb_resampler_fill(stm->resampler.get(), input_buffer, + &input_frames_count, dest, output_frames_needed); /* TODO: Report out_frames < 0 as an error via the API. */ XASSERT(out_frames >= 0); + float volume = 1.0; { auto_lock lock(stm->stream_reset_lock); stm->frames_written += out_frames; + volume = stm->volume; } /* Go in draining mode if we got fewer frames than requested. If the stream @@ -843,22 +850,50 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count, /* If this is not true, there will be glitches. It is alright to have produced less frames if we are draining, though. */ - XASSERT(out_frames == output_frames_needed || stm->draining || !has_output(stm) || stm->has_dummy_output); + XASSERT(out_frames == output_frames_needed || stm->draining || + !has_output(stm) || stm->has_dummy_output); - // We don't bother mixing dummy output as it will be silenced, otherwise mix output if needed +#ifndef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME + if (has_output(stm) && !stm->has_dummy_output && volume != 1.0) { + // Adjust the output volume. + // Note: This could be integrated with the remixing below. + long out_samples = out_frames * stm->output_stream_params.channels; + if (volume == 0.0) { + memset(dest, 0, out_samples * stm->bytes_per_sample); + } else { + switch (stm->output_stream_params.format) { + case CUBEB_SAMPLE_FLOAT32NE: { + float * buf = static_cast(dest); + for (long i = 0; i < out_samples; ++i) { + buf[i] *= volume; + } + break; + } + case CUBEB_SAMPLE_S16NE: { + short * buf = static_cast(dest); + for (long i = 0; i < out_samples; ++i) { + buf[i] = static_cast(static_cast(buf[i]) * volume); + } + break; + } + default: + XASSERT(false); + } + } + } +#endif + + // We don't bother mixing dummy output as it will be silenced, otherwise mix + // output if needed if (!stm->has_dummy_output && has_output(stm) && stm->output_mixer) { XASSERT(dest == stm->mix_buffer.data()); size_t dest_size = - out_frames * stm->output_stream_params.channels * stm->bytes_per_sample; + out_frames * stm->output_stream_params.channels * stm->bytes_per_sample; XASSERT(dest_size <= stm->mix_buffer.size()); size_t output_buffer_size = - out_frames * stm->output_mix_params.channels * stm->bytes_per_sample; - int ret = cubeb_mixer_mix(stm->output_mixer.get(), - out_frames, - dest, - dest_size, - output_buffer, - output_buffer_size); + out_frames * stm->output_mix_params.channels * stm->bytes_per_sample; + int ret = cubeb_mixer_mix(stm->output_mixer.get(), out_frames, dest, + dest_size, output_buffer, output_buffer_size); if (ret < 0) { LOG("Error remixing content (%d)", ret); } @@ -867,12 +902,23 @@ refill(cubeb_stream * stm, void * input_buffer, long input_frames_count, return out_frames; } -int wasapi_stream_reset_default_device(cubeb_stream * stm); +int +trigger_async_reconfigure(cubeb_stream * stm) +{ + XASSERT(stm && stm->reconfigure_event); + BOOL ok = SetEvent(stm->reconfigure_event); + if (!ok) { + LOG("SetEvent on reconfigure_event failed: %lx", GetLastError()); + return CUBEB_ERROR; + } + return CUBEB_OK; +} /* This helper grabs all the frames available from a capture client, put them in - * linear_input_buffer. linear_input_buffer should be cleared before the - * callback exits. This helper does not work with exclusive mode streams. */ -bool get_input_buffer(cubeb_stream * stm) + * the linear_input_buffer. This helper does not work with exclusive mode + * streams. */ +bool +get_input_buffer(cubeb_stream * stm) { XASSERT(has_input(stm)); @@ -889,14 +935,13 @@ bool get_input_buffer(cubeb_stream * stm) // single packet each time. However, if we're pulling from the stream we may // need to grab multiple packets worth of frames that have accumulated (so // need a loop). - for (hr = stm->capture_client->GetNextPacketSize(&next); - next > 0; + for (hr = stm->capture_client->GetNextPacketSize(&next); next > 0; hr = stm->capture_client->GetNextPacketSize(&next)) { if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { // Application can recover from this error. More info // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx LOG("Device invalidated error, reset default device"); - wasapi_stream_reset_default_device(stm); + trigger_async_reconfigure(stm); return true; } @@ -906,11 +951,8 @@ bool get_input_buffer(cubeb_stream * stm) } UINT32 frames; - hr = stm->capture_client->GetBuffer(&input_packet, - &frames, - &flags, - &dev_pos, - &pc_position); + hr = stm->capture_client->GetBuffer(&input_packet, &frames, &flags, + &dev_pos, &pc_position); if (FAILED(hr)) { LOG("GetBuffer failed for capture: %lx", hr); @@ -921,14 +963,19 @@ bool get_input_buffer(cubeb_stream * stm) if (stm->context->performance_counter_frequency) { LARGE_INTEGER now; UINT64 now_hns; - // See https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudiocaptureclient-getbuffer, section "Remarks". + // See + // https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudiocaptureclient-getbuffer, + // section "Remarks". QueryPerformanceCounter(&now); - now_hns = 10000000 * now.QuadPart / stm->context->performance_counter_frequency; + now_hns = + 10000000 * now.QuadPart / stm->context->performance_counter_frequency; if (now_hns >= pc_position) { stm->input_latency_hns = now_hns - pc_position; } } + stm->total_input_frames += frames; + UINT32 input_stream_samples = frames * stm->input_stream_params.channels; // We do not explicitly handle the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY // flag. There a two primary (non exhaustive) scenarios we anticipate this @@ -940,10 +987,12 @@ bool get_input_buffer(cubeb_stream * stm) // stream to drive input in the case of input only loopback. Without // a dummy output, input only loopback would glitch on silence. However, // the dummy input should push silence to the loopback and prevent - // discontinuities. See https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/ + // discontinuities. See + // https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/ // As the first scenario can be ignored, and we anticipate the second // scenario is mitigated, we ignore the flag. - // For more info: https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx, + // For more info: + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd370859(v=vs.85).aspx, // https://msdn.microsoft.com/en-us/library/windows/desktop/dd371458(v=vs.85).aspx if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { LOG("insert silence: ps=%u", frames); @@ -951,25 +1000,21 @@ bool get_input_buffer(cubeb_stream * stm) } else { if (stm->input_mixer) { bool ok = stm->linear_input_buffer->reserve( - stm->linear_input_buffer->length() + input_stream_samples); + stm->linear_input_buffer->length() + input_stream_samples); XASSERT(ok); size_t input_packet_size = - frames * stm->input_mix_params.channels * - cubeb_sample_size(stm->input_mix_params.format); + frames * stm->input_mix_params.channels * + cubeb_sample_size(stm->input_mix_params.format); size_t linear_input_buffer_size = - input_stream_samples * - cubeb_sample_size(stm->input_stream_params.format); - cubeb_mixer_mix(stm->input_mixer.get(), - frames, - input_packet, - input_packet_size, - stm->linear_input_buffer->end(), + input_stream_samples * + cubeb_sample_size(stm->input_stream_params.format); + cubeb_mixer_mix(stm->input_mixer.get(), frames, input_packet, + input_packet_size, stm->linear_input_buffer->end(), linear_input_buffer_size); stm->linear_input_buffer->set_length( - stm->linear_input_buffer->length() + input_stream_samples); + stm->linear_input_buffer->length() + input_stream_samples); } else { - stm->linear_input_buffer->push( - input_packet, input_stream_samples); + stm->linear_input_buffer->push(input_packet, input_stream_samples); } } hr = stm->capture_client->ReleaseBuffer(frames); @@ -980,6 +1025,8 @@ bool get_input_buffer(cubeb_stream * stm) offset += input_stream_samples; } + ALOGV("get_input_buffer: got %d frames", offset); + XASSERT(stm->linear_input_buffer->length() >= offset); return true; @@ -987,7 +1034,8 @@ bool get_input_buffer(cubeb_stream * stm) /* Get an output buffer from the render_client. It has to be released before * exiting the callback. */ -bool get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count) +bool +get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count) { UINT32 padding_out; HRESULT hr; @@ -996,11 +1044,11 @@ bool get_output_buffer(cubeb_stream * stm, void *& buffer, size_t & frame_count) hr = stm->output_client->GetCurrentPadding(&padding_out); if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - // Application can recover from this error. More info - // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx - LOG("Device invalidated error, reset default device"); - wasapi_stream_reset_default_device(stm); - return true; + // Application can recover from this error. More info + // https://msdn.microsoft.com/en-us/library/windows/desktop/dd316605(v=vs.85).aspx + LOG("Device invalidated error, reset default device"); + trigger_async_reconfigure(stm); + return true; } if (FAILED(hr)) { @@ -1048,12 +1096,15 @@ refill_callback_duplex(cubeb_stream * stm) XASSERT(has_input(stm) && has_output(stm)); - rv = get_input_buffer(stm); - if (!rv) { - return rv; + if (stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) { + HRESULT rv = get_input_buffer(stm); + if (FAILED(rv)) { + return rv; + } } - input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels; + input_frames = + stm->linear_input_buffer->length() / stm->input_stream_params.channels; rv = get_output_buffer(stm, output_buffer, output_frames); if (!rv) { @@ -1072,25 +1123,26 @@ refill_callback_duplex(cubeb_stream * stm) return false; } + stm->total_output_frames += output_frames; + + ALOGV("in: %zu, out: %zu, missing: %ld, ratio: %f", stm->total_input_frames, + stm->total_output_frames, + static_cast(stm->total_output_frames) - stm->total_input_frames, + static_cast(stm->total_output_frames) / stm->total_input_frames); + if (stm->has_dummy_output) { - ALOGV("Duplex callback (dummy output): input frames: %Iu, output frames: %Iu", - input_frames, output_frames); + ALOGV( + "Duplex callback (dummy output): input frames: %Iu, output frames: %Iu", + input_frames, output_frames); // We don't want to expose the dummy output to the callback so don't pass // the output buffer (it will be released later with silence in it) - refill(stm, - stm->linear_input_buffer->data(), - input_frames, - nullptr, - 0); + refill(stm, stm->linear_input_buffer->data(), input_frames, nullptr, 0); } else { ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu", input_frames, output_frames); - refill(stm, - stm->linear_input_buffer->data(), - input_frames, - output_buffer, + refill(stm, stm->linear_input_buffer->data(), input_frames, output_buffer, output_frames); } @@ -1098,7 +1150,8 @@ refill_callback_duplex(cubeb_stream * stm) if (stm->has_dummy_output) { // If output is a dummy output, make sure it's silent - hr = stm->render_client->ReleaseBuffer(output_frames, AUDCLNT_BUFFERFLAGS_SILENT); + hr = stm->render_client->ReleaseBuffer(output_frames, + AUDCLNT_BUFFERFLAGS_SILENT); } else { hr = stm->render_client->ReleaseBuffer(output_frames, 0); } @@ -1122,18 +1175,16 @@ refill_callback_input(cubeb_stream * stm) return rv; } - input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels; + input_frames = + stm->linear_input_buffer->length() / stm->input_stream_params.channels; if (!input_frames) { return true; } ALOGV("Input callback: input frames: %Iu", input_frames); - long read = refill(stm, - stm->linear_input_buffer->data(), - input_frames, - nullptr, - 0); + long read = + refill(stm, stm->linear_input_buffer->data(), input_frames, nullptr, 0); XASSERT(read >= 0); @@ -1161,14 +1212,10 @@ refill_callback_output(cubeb_stream * stm) return true; } - long got = refill(stm, - nullptr, - 0, - output_buffer, - output_frames); + long got = refill(stm, nullptr, 0, output_buffer, 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); XASSERT(got >= 0); XASSERT(size_t(got) == output_frames || stm->draining); @@ -1182,8 +1229,7 @@ refill_callback_output(cubeb_stream * stm) return size_t(got) == output_frames || stm->draining; } -static unsigned int __stdcall -wasapi_stream_render_loop(LPVOID stream) +static unsigned int __stdcall wasapi_stream_render_loop(LPVOID stream) { cubeb_stream * stm = static_cast(stream); std::atomic * emergency_bailout = stm->emergency_bailout; @@ -1196,23 +1242,18 @@ wasapi_stream_render_loop(LPVOID stream) } bool is_playing = true; - HANDLE wait_array[4] = { - stm->shutdown_event, - stm->reconfigure_event, - stm->refill_event, - stm->input_available_event - }; + HANDLE wait_array[4] = {stm->shutdown_event, stm->reconfigure_event, + stm->refill_event, stm->input_available_event}; HANDLE mmcss_handle = NULL; HRESULT hr = 0; DWORD mmcss_task_index = 0; struct auto_com { - auto_com() { + auto_com() + { HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED); XASSERT(SUCCEEDED(hr)); } - ~auto_com() { - CoUninitialize(); - } + ~auto_com() { CoUninitialize(); } } com; /* We could consider using "Pro Audio" here for WebAudio and @@ -1220,7 +1261,8 @@ wasapi_stream_render_loop(LPVOID stream) mmcss_handle = AvSetMmThreadCharacteristicsA("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", GetLastError()); + LOG("Unable to use mmcss to bump the render thread priority: %lx", + GetLastError()); } /* WaitForMultipleObjects timeout can trigger in cases where we don't want to @@ -1231,16 +1273,15 @@ wasapi_stream_render_loop(LPVOID stream) const unsigned timeout_limit = 3; while (is_playing) { // We want to check the emergency bailout variable before a - // and after the WaitForMultipleObject, because the handles WaitForMultipleObjects - // is going to wait on might have been closed already. + // and after the WaitForMultipleObject, because the handles + // WaitForMultipleObjects is going to wait on might have been closed + // already. if (*emergency_bailout) { delete emergency_bailout; return 0; } DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array), - wait_array, - FALSE, - 1000); + wait_array, FALSE, 1000); if (*emergency_bailout) { delete emergency_bailout; return 0; @@ -1274,8 +1315,8 @@ wasapi_stream_render_loop(LPVOID stream) auto_lock lock(stm->stream_reset_lock); close_wasapi_stream(stm); LOG("Stream closed."); - /* Reopen a stream and start it immediately. This will automatically pick the - new default device for this role. */ + /* Reopen a stream and start it immediately. This will automatically + pick the new default device for this role. */ int r = setup_wasapi_stream(stm); if (r != CUBEB_OK) { LOG("Error setting up the stream during reconfigure."); @@ -1308,15 +1349,23 @@ wasapi_stream_render_loop(LPVOID stream) } break; } - case WAIT_OBJECT_0 + 2: /* refill */ + case WAIT_OBJECT_0 + 2: /* refill */ XASSERT((has_input(stm) && has_output(stm)) || (!has_input(stm) && has_output(stm))); is_playing = stm->refill_callback(stm); break; - case WAIT_OBJECT_0 + 3: /* input available */ - if (has_input(stm) && has_output(stm)) { continue; } - is_playing = stm->refill_callback(stm); + case WAIT_OBJECT_0 + 3: { /* input available */ + HRESULT rv = get_input_buffer(stm); + if (FAILED(rv)) { + return rv; + } + + if (!has_output(stm)) { + is_playing = stm->refill_callback(stm); + } + break; + } case WAIT_TIMEOUT: XASSERT(stm->shutdown_event == wait_array[0]); if (++timeout_count >= timeout_limit) { @@ -1342,38 +1391,37 @@ wasapi_stream_render_loop(LPVOID stream) return 0; } -void wasapi_destroy(cubeb * context); +void +wasapi_destroy(cubeb * context); -HRESULT register_notification_client(cubeb_stream * stm) +HRESULT +register_notification_client(cubeb_stream * stm) { - assert(stm->device_enumerator); + XASSERT(stm->device_enumerator); - stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event, stm->role)); + stm->notification_client.reset(new wasapi_endpoint_notification_client( + stm->reconfigure_event, stm->role)); - HRESULT hr = stm->device_enumerator->RegisterEndpointNotificationCallback(stm->notification_client.get()); + HRESULT hr = stm->device_enumerator->RegisterEndpointNotificationCallback( + stm->notification_client.get()); if (FAILED(hr)) { LOG("Could not register endpoint notification callback: %lx", hr); stm->notification_client = nullptr; - stm->device_enumerator = nullptr; } return hr; } -HRESULT unregister_notification_client(cubeb_stream * stm) +HRESULT +unregister_notification_client(cubeb_stream * stm) { - XASSERT(stm); - HRESULT hr; + XASSERT(stm->device_enumerator); - if (!stm->device_enumerator) { - return S_OK; - } - - hr = stm->device_enumerator->UnregisterEndpointNotificationCallback(stm->notification_client.get()); + HRESULT hr = stm->device_enumerator->UnregisterEndpointNotificationCallback( + stm->notification_client.get()); if (FAILED(hr)) { // We can't really do anything here, we'll probably leak the - // notification client, but we can at least release the enumerator. - stm->device_enumerator = nullptr; + // notification client. return S_OK; } @@ -1382,12 +1430,13 @@ HRESULT unregister_notification_client(cubeb_stream * stm) return S_OK; } -HRESULT get_endpoint(com_ptr & device, LPCWSTR devid) +HRESULT +get_endpoint(com_ptr & device, LPCWSTR devid) { com_ptr enumerator; - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), - NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(enumerator.receive())); + HRESULT hr = + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(enumerator.receive())); if (FAILED(hr)) { LOG("Could not get device enumerator: %lx", hr); return hr; @@ -1402,20 +1451,23 @@ HRESULT get_endpoint(com_ptr & device, LPCWSTR devid) return S_OK; } -HRESULT register_collection_notification_client(cubeb * context) +HRESULT +register_collection_notification_client(cubeb * context) { - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), - NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(context->device_collection_enumerator.receive())); + HRESULT hr = CoCreateInstance( + __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(context->device_collection_enumerator.receive())); if (FAILED(hr)) { LOG("Could not get device enumerator: %lx", hr); return hr; } - context->collection_notification_client.reset(new wasapi_collection_notification_client(context)); + context->collection_notification_client.reset( + new wasapi_collection_notification_client(context)); - hr = context->device_collection_enumerator->RegisterEndpointNotificationCallback( - context->collection_notification_client.get()); + hr = context->device_collection_enumerator + ->RegisterEndpointNotificationCallback( + context->collection_notification_client.get()); if (FAILED(hr)) { LOG("Could not register endpoint notification callback: %lx", hr); context->collection_notification_client.reset(); @@ -1425,10 +1477,12 @@ HRESULT register_collection_notification_client(cubeb * context) return hr; } -HRESULT unregister_collection_notification_client(cubeb * context) +HRESULT +unregister_collection_notification_client(cubeb * context) { - HRESULT hr = context->device_collection_enumerator-> - UnregisterEndpointNotificationCallback(context->collection_notification_client.get()); + HRESULT hr = context->device_collection_enumerator + ->UnregisterEndpointNotificationCallback( + context->collection_notification_client.get()); if (FAILED(hr)) { return hr; } @@ -1439,12 +1493,14 @@ HRESULT unregister_collection_notification_client(cubeb * context) return hr; } -HRESULT get_default_endpoint(com_ptr & device, EDataFlow direction, ERole role) +HRESULT +get_default_endpoint(com_ptr & device, EDataFlow direction, + ERole role) { com_ptr enumerator; - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), - NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(enumerator.receive())); + HRESULT hr = + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(enumerator.receive())); if (FAILED(hr)) { LOG("Could not get device enumerator: %lx", hr); return hr; @@ -1485,12 +1541,14 @@ current_stream_delay(cubeb_stream * stm) } double cur_pos = static_cast(pos) / freq; - double max_pos = static_cast(stm->frames_written) / stm->output_mix_params.rate; + double max_pos = + static_cast(stm->frames_written) / stm->output_mix_params.rate; double delay = std::max(max_pos - cur_pos, 0.0); return delay; } +#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME int stream_set_volume(cubeb_stream * stm, float volume) { @@ -1517,7 +1575,7 @@ stream_set_volume(cubeb_stream * stm, float volume) volumes[i] = volume; } - hr = stm->audio_stream_volume->SetAllVolumes(channels, volumes); + hr = stm->audio_stream_volume->SetAllVolumes(channels, volumes); if (FAILED(hr)) { LOG("could not set the channels volume: %lx", hr); return CUBEB_ERROR; @@ -1525,10 +1583,12 @@ stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } -} // namespace anonymous +#endif +} // namespace extern "C" { -int wasapi_init(cubeb ** context, char const * context_name) +int +wasapi_init(cubeb ** context, char const * context_name) { /* We don't use the device yet, but need to make sure we can initialize one so that this backend is not incorrectly enabled on platforms that don't @@ -1557,7 +1617,8 @@ int wasapi_init(cubeb ** context, char const * context_name) if (QueryPerformanceFrequency(&frequency)) { ctx->performance_counter_frequency = frequency.QuadPart; } else { - LOG("Failed getting performance counter frequency, latency reporting will be inacurate"); + LOG("Failed getting performance counter frequency, latency reporting will " + "be inacurate"); ctx->performance_counter_frequency = 0; } @@ -1568,7 +1629,8 @@ int wasapi_init(cubeb ** context, char const * context_name) } namespace { -bool stop_and_join_render_thread(cubeb_stream * stm) +bool +stop_and_join_render_thread(cubeb_stream * stm) { bool rv = true; LOG("Stop and join render thread."); @@ -1597,7 +1659,8 @@ bool stop_and_join_render_thread(cubeb_stream * stm) *(stm->emergency_bailout) = true; // We give the ownership to the rendering thread. stm->emergency_bailout = nullptr; - LOG("Destroy WaitForSingleObject on thread failed: %lx, %lx", r, GetLastError()); + LOG("Destroy WaitForSingleObject on thread failed: %lx, %lx", r, + GetLastError()); rv = false; } @@ -1616,7 +1679,8 @@ bool stop_and_join_render_thread(cubeb_stream * stm) return rv; } -void wasapi_destroy(cubeb * context) +void +wasapi_destroy(cubeb * context) { if (context->device_ids) { cubeb_strings_destroy(context->device_ids); @@ -1625,7 +1689,8 @@ void wasapi_destroy(cubeb * context) delete context; } -char const * wasapi_get_backend_id(cubeb * context) +char const * +wasapi_get_backend_id(cubeb * context) { return "wasapi"; } @@ -1642,9 +1707,8 @@ wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } com_ptr client; - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp()); + hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, + client.receive_vpp()); if (FAILED(hr)) { return CUBEB_ERROR; } @@ -1662,9 +1726,11 @@ wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } int -wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) +wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency_frames) { - if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) { + if (params.format != CUBEB_SAMPLE_FLOAT32NE && + params.format != CUBEB_SAMPLE_S16NE) { return CUBEB_ERROR_INVALID_FORMAT; } @@ -1678,9 +1744,8 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten } com_ptr client; - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp()); + hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, + client.receive_vpp()); if (FAILED(hr)) { LOG("Could not activate device for latency: %lx", hr); return CUBEB_ERROR; @@ -1694,18 +1759,21 @@ wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * laten return CUBEB_ERROR; } - LOG("default device period: %I64d, minimum device period: %I64d", default_period, minimum_period); + 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 */ + http://msdn.microsoft.com/en-us/library/windows/desktop/dd370871%28v=vs.85%29.aspx + */ - #ifdef _WIN32_WINNT_WIN10 - *latency_frames = hns_to_frames(params.rate, minimum_period); - #else - *latency_frames = hns_to_frames(params.rate, default_period); - #endif + // #ifdef _WIN32_WINNT_WIN10 +#if 0 + *latency_frames = hns_to_frames(params.rate, minimum_period); +#else + *latency_frames = hns_to_frames(params.rate, default_period); +#endif LOG("Minimum latency in frames: %u", *latency_frames); @@ -1722,9 +1790,8 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) } com_ptr client; - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp()); + hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, + client.receive_vpp()); if (FAILED(hr)) { return CUBEB_ERROR; } @@ -1743,7 +1810,8 @@ wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) return CUBEB_OK; } -void wasapi_stream_destroy(cubeb_stream * stm); +void +wasapi_stream_destroy(cubeb_stream * stm); static void waveformatex_update_derived_properties(WAVEFORMATEX * format) @@ -1751,7 +1819,8 @@ waveformatex_update_derived_properties(WAVEFORMATEX * format) format->nBlockAlign = format->wBitsPerSample * format->nChannels / 8; format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign; if (format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast(format); + WAVEFORMATEXTENSIBLE * format_pcm = + reinterpret_cast(format); format_pcm->Samples.wValidBitsPerSample = format->wBitsPerSample; } } @@ -1759,22 +1828,28 @@ waveformatex_update_derived_properties(WAVEFORMATEX * format) /* Based on the mix format and the stream format, try to find a way to play what the user requested. */ static void -handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptr & mix_format, const cubeb_stream_params * stream_params) +handle_channel_layout(cubeb_stream * stm, EDataFlow direction, + com_heap_ptr & mix_format, + const cubeb_stream_params * stream_params) { - com_ptr & audio_client = (direction == eRender) ? stm->output_client : stm->input_client; + com_ptr & audio_client = + (direction == eRender) ? stm->output_client : stm->input_client; XASSERT(audio_client); /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1], so the reinterpret_cast below should be safe. In practice, this is not true, and we just want to bail out and let the rest of the code find a good conversion path instead of trying to make WASAPI do it by itself. - [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/ + [1]: + http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/ if (mix_format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { return; } - WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast(mix_format.get()); + WAVEFORMATEXTENSIBLE * format_pcm = + reinterpret_cast(mix_format.get()); - /* Stash a copy of the original mix format in case we need to restore it later. */ + /* Stash a copy of the original mix format in case we need to restore it + * later. */ WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm; /* Get the channel mask by the channel layout. @@ -1786,8 +1861,7 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptrIsFormatSupported(AUDCLNT_SHAREMODE_SHARED, - mix_format.get(), - &tmp); + mix_format.get(), &tmp); com_heap_ptr closest(tmp); if (hr == S_FALSE) { /* Channel layout not supported, but WASAPI gives us a suggestion. Use it, @@ -1795,7 +1869,8 @@ handle_channel_layout(cubeb_stream * stm, EDataFlow direction, com_heap_ptrnChannels); XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE); - WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast(closest.get()); + WAVEFORMATEXTENSIBLE * closest_pcm = + reinterpret_cast(closest.get()); format_pcm->dwChannelMask = closest_pcm->dwChannelMask; mix_format->nChannels = closest->nChannels; waveformatex_update_derived_properties(mix_format.get()); @@ -1818,10 +1893,11 @@ initialize_iaudioclient2(com_ptr & audio_client) com_ptr audio_client2; audio_client->QueryInterface(audio_client2.receive()); if (!audio_client2) { - LOG("Could not get IAudioClient2 interface, not setting AUDCLNT_STREAMOPTIONS_RAW."); + LOG("Could not get IAudioClient2 interface, not setting " + "AUDCLNT_STREAMOPTIONS_RAW."); return CUBEB_OK; } - AudioClientProperties properties = { 0 }; + AudioClientProperties properties = {0}; properties.cbSize = sizeof(AudioClientProperties); #ifndef __MINGW32__ properties.Options |= AUDCLNT_STREAMOPTIONS_RAW; @@ -1834,12 +1910,12 @@ initialize_iaudioclient2(com_ptr & audio_client) return CUBEB_OK; } -static bool +// Not static to suppress a warning. +/* static */ bool initialize_iaudioclient3(com_ptr & audio_client, cubeb_stream * stm, const com_heap_ptr & mix_format, - DWORD flags, - EDataFlow direction) + DWORD flags, EDataFlow direction) { com_ptr audio_client3; audio_client->QueryInterface(audio_client3.receive()); @@ -1855,10 +1931,6 @@ initialize_iaudioclient3(com_ptr & audio_client, return false; } - // IAudioClient3 doesn't support AUDCLNT_STREAMFLAGS_NOPERSIST, and will return - // AUDCLNT_E_INVALID_STREAM_FLAG. This is undocumented. - flags = flags & ~AUDCLNT_STREAMFLAGS_NOPERSIST; - // Some people have reported glitches with capture streams: // http://blog.nirbheek.in/2018/03/low-latency-audio-on-windows-with.html if (direction == eCapture) { @@ -1867,9 +1939,10 @@ initialize_iaudioclient3(com_ptr & audio_client, } // 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 + // 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 @@ -1878,18 +1951,24 @@ initialize_iaudioclient3(com_ptr & audio_client, // would do this, then stop and use IAudioClient instead. HRESULT hr; - uint32_t default_period = 0, fundamental_period = 0, min_period = 0, max_period = 0; - hr = audio_client3->GetSharedModeEnginePeriod(mix_format.get(), &default_period, &fundamental_period, &min_period, &max_period); + uint32_t default_period = 0, fundamental_period = 0, min_period = 0, + max_period = 0; + hr = audio_client3->GetSharedModeEnginePeriod( + mix_format.get(), &default_period, &fundamental_period, &min_period, + &max_period); if (FAILED(hr)) { LOG("Could not get shared mode engine period: error: %lx", hr); return false; } uint32_t requested_latency = stm->latency; if (requested_latency >= default_period) { - LOG("Requested latency %i greater than default latency %i, not using IAudioClient3", requested_latency, default_period); + LOG("Requested latency %i greater than default latency %i, not using " + "IAudioClient3", + requested_latency, default_period); return false; } - LOG("Got shared mode engine period: default=%i fundamental=%i min=%i max=%i", default_period, fundamental_period, min_period, max_period); + 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; if (requested_latency < min_period) { @@ -1897,25 +1976,28 @@ initialize_iaudioclient3(com_ptr & audio_client, } requested_latency -= (requested_latency - min_period) % fundamental_period; if (requested_latency != old_requested_latency) { - LOG("Requested latency %i was adjusted to %i", old_requested_latency, requested_latency); + LOG("Requested latency %i was adjusted to %i", old_requested_latency, + requested_latency); } - hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency, mix_format.get(), NULL); + hr = audio_client3->InitializeSharedAudioStream(flags, requested_latency, + mix_format.get(), NULL); if (SUCCEEDED(hr)) { return true; - } - else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) { + } else if (hr == AUDCLNT_E_ENGINE_PERIODICITY_LOCKED) { LOG("Got AUDCLNT_E_ENGINE_PERIODICITY_LOCKED, adjusting latency request"); } else { - LOG("Could not initialize shared stream with IAudioClient3: error: %lx", hr); + LOG("Could not initialize shared stream with IAudioClient3: error: %lx", + hr); return false; } uint32_t current_period = 0; - WAVEFORMATEX* current_format = nullptr; + WAVEFORMATEX * current_format = nullptr; // We have to pass a valid WAVEFORMATEX** and not nullptr, otherwise // GetCurrentSharedModeEnginePeriod will return E_POINTER - hr = audio_client3->GetCurrentSharedModeEnginePeriod(¤t_format, ¤t_period); + hr = audio_client3->GetCurrentSharedModeEnginePeriod(¤t_format, + ¤t_period); CoTaskMemFree(current_format); if (FAILED(hr)) { LOG("Could not get current shared mode engine period: error: %lx", hr); @@ -1923,13 +2005,16 @@ initialize_iaudioclient3(com_ptr & audio_client, } if (current_period >= default_period) { - LOG("Current shared mode engine period %i too high, not using IAudioClient", current_period); + LOG("Current shared mode engine period %i too high, not using IAudioClient", + current_period); return false; } - hr = audio_client3->InitializeSharedAudioStream(flags, current_period, mix_format.get(), NULL); + hr = audio_client3->InitializeSharedAudioStream(flags, current_period, + mix_format.get(), NULL); if (SUCCEEDED(hr)) { - LOG("Current shared mode engine period is %i instead of requested %i", current_period, requested_latency); + LOG("Current shared mode engine period is %i instead of requested %i", + current_period, requested_latency); return true; } @@ -1939,18 +2024,16 @@ initialize_iaudioclient3(com_ptr & audio_client, #define DIRECTION_NAME (direction == eCapture ? "capture" : "render") -template -int setup_wasapi_stream_one_side(cubeb_stream * stm, - cubeb_stream_params * stream_params, - wchar_t const * devid, - EDataFlow direction, - REFIID riid, - com_ptr & audio_client, - uint32_t * buffer_frame_count, - HANDLE & event, - T & render_or_capture_client, - cubeb_stream_params * mix_params, - com_ptr& device) +template +int +setup_wasapi_stream_one_side(cubeb_stream * stm, + cubeb_stream_params * stream_params, + wchar_t const * devid, EDataFlow direction, + REFIID riid, com_ptr & audio_client, + uint32_t * buffer_frame_count, HANDLE & event, + T & render_or_capture_client, + cubeb_stream_params * mix_params, + com_ptr & device) { HRESULT hr; bool is_loopback = stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK; @@ -1974,12 +2057,16 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, // If caller has requested loopback but not specified a device, look for // the default render device. Otherwise look for the default device // appropriate to the direction. - hr = get_default_endpoint(device, is_loopback ? eRender : direction, pref_to_role(stream_params->prefs)); + hr = get_default_endpoint(device, is_loopback ? eRender : direction, + pref_to_role(stream_params->prefs)); if (FAILED(hr)) { if (is_loopback) { - LOG("Could not get default render endpoint for loopback, error: %lx\n", hr); + LOG("Could not get default render endpoint for loopback, error: " + "%lx\n", + hr); } else { - LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr); + LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, + hr); } return CUBEB_ERROR; } @@ -1993,16 +2080,16 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, NULL, audio_client.receive_vpp()); if (hr == E_NOINTERFACE) { #endif - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, audio_client.receive_vpp()); + hr = device->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, + audio_client.receive_vpp()); #if 0 } #endif if (FAILED(hr)) { LOG("Could not activate the device to get an audio" - " client for %s: error: %lx\n", DIRECTION_NAME, hr); + " client for %s: error: %lx\n", + DIRECTION_NAME, hr); // A particular device can't be activated because it has been // unplugged, try fall back to the default audio device. if (devid && hr == AUDCLNT_E_DEVICE_INVALIDATED) { @@ -2024,14 +2111,33 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, hr = audio_client->GetMixFormat(&tmp); if (FAILED(hr)) { LOG("Could not fetch current mix format from the audio" - " client for %s: error: %lx", DIRECTION_NAME, hr); + " client for %s: error: %lx", + DIRECTION_NAME, hr); return CUBEB_ERROR; } com_heap_ptr mix_format(tmp); mix_format->wBitsPerSample = stm->bytes_per_sample * 8; + if (mix_format->wFormatTag == WAVE_FORMAT_PCM || + mix_format->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { + switch (mix_format->wBitsPerSample) { + case 8: + case 16: + mix_format->wFormatTag = WAVE_FORMAT_PCM; + break; + case 32: + mix_format->wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + break; + default: + LOG("%u bits per sample is incompatible with PCM wave formats", + mix_format->wBitsPerSample); + return CUBEB_ERROR; + } + } + if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast(mix_format.get()); + WAVEFORMATEXTENSIBLE * format_pcm = + reinterpret_cast(mix_format.get()); format_pcm->SubFormat = stm->waveformatextensible_sub_format; } waveformatex_update_derived_properties(mix_format.get()); @@ -2050,18 +2156,11 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, LOG("Setup requested=[f=%d r=%u c=%u l=%u] mix=[f=%d r=%u c=%u l=%u]", stream_params->format, stream_params->rate, stream_params->channels, - stream_params->layout, - mix_params->format, mix_params->rate, mix_params->channels, - mix_params->layout); - + stream_params->layout, mix_params->format, mix_params->rate, + mix_params->channels, mix_params->layout); DWORD flags = 0; - bool is_persist = stream_params->prefs & CUBEB_STREAM_PREF_PERSIST; - if (!is_persist) { - flags |= AUDCLNT_STREAMFLAGS_NOPERSIST; - } - // Check if a loopback device should be requested. Note that event callbacks // do not work with loopback devices, so only request these if not looping. if (is_loopback) { @@ -2079,34 +2178,30 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, return CUBEB_ERROR; } - REFERENCE_TIME latency_hns; + REFERENCE_TIME latency_hns = frames_to_hns(stream_params->rate, stm->latency); + stm->input_bluetooth_handsfree = false; - uint32_t latency_frames = stm->latency; cubeb_device_info device_info; - int rv = wasapi_create_device(stm->context, device_info, stm->device_enumerator.get(), device.get()); - if (rv == CUBEB_OK) { - const char* HANDSFREE_TAG = "BTHHFEENUM"; + if (wasapi_create_device(stm->context, device_info, + stm->device_enumerator.get(), + device.get()) == CUBEB_OK) { + const char * HANDSFREE_TAG = "BTHHFENUM"; size_t len = sizeof(HANDSFREE_TAG); - if (direction == eCapture && strncmp(device_info.group_id, HANDSFREE_TAG, len) == 0) { - // Rather high-latency to prevent constant under-runs in this particular - // case of an input device using bluetooth handsfree. - uint32_t default_period_frames = hns_to_frames(device_info.default_rate, default_period); - latency_frames = default_period_frames * 4; - stm->input_bluetooth_handsfree = true; - LOG("Input is a bluetooth device in handsfree, latency increased to %u frames from a default of %u", latency_frames, default_period_frames); - } else { - uint32_t minimum_period_frames = hns_to_frames(device_info.default_rate, minimum_period); - latency_frames = std::max(latency_frames, minimum_period_frames); - stm->input_bluetooth_handsfree = false; - LOG("Input is a not bluetooth handsfree, latency %s to %u frames (minimum %u)", latency_frames < minimum_period_frames ? "increased" : "set", latency_frames, minimum_period_frames); + if (direction == eCapture) { + uint32_t default_period_frames = + hns_to_frames(device_info.default_rate, default_period); + if (strlen(device_info.group_id) >= len && + strncmp(device_info.group_id, HANDSFREE_TAG, len) == 0) { + stm->input_bluetooth_handsfree = true; + } + // This multiplicator has been found empirically. + uint32_t latency_frames = default_period_frames * 8; + LOG("Input: latency increased to %u frames from a default of %u", + latency_frames, default_period_frames); + latency_hns = frames_to_hns(device_info.default_rate, latency_frames); } - - latency_hns = frames_to_hns(device_info.default_rate, latency_frames); - wasapi_destroy_device(&device_info); } else { - stm->input_bluetooth_handsfree = false; - latency_hns = frames_to_hns(mix_params->rate, latency_frames); LOG("Could not get cubeb_device_info."); } @@ -2122,12 +2217,8 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, LOG("Initialized with IAudioClient3"); } else { #endif - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, - flags, - latency_hns, - 0, - mix_format.get(), - NULL); + hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, flags, latency_hns, 0, + mix_format.get(), NULL); #if 0 } #endif @@ -2139,16 +2230,19 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, hr = audio_client->GetBufferSize(buffer_frame_count); if (FAILED(hr)) { LOG("Could not get the buffer size from the client" - " for %s %lx.", DIRECTION_NAME, hr); + " for %s %lx.", + DIRECTION_NAME, hr); return CUBEB_ERROR; } + LOG("Buffer size is: %d for %s\n", *buffer_frame_count, DIRECTION_NAME); + // Events are used if not looping back if (!is_loopback) { hr = audio_client->SetEventHandle(event); if (FAILED(hr)) { - LOG("Could set the event handle for the %s client %lx.", - DIRECTION_NAME, hr); + LOG("Could set the event handle for the %s client %lx.", DIRECTION_NAME, + hr); return CUBEB_ERROR; } } @@ -2164,9 +2258,11 @@ int setup_wasapi_stream_one_side(cubeb_stream * stm, #undef DIRECTION_NAME -void wasapi_find_matching_output_device(cubeb_stream * stm) { +void +wasapi_find_matching_output_device(cubeb_stream * stm) +{ HRESULT hr; - cubeb_device_info * input_device; + cubeb_device_info * input_device = nullptr; cubeb_device_collection collection; // Only try to match to an output device if the input device is a bluetooth @@ -2187,62 +2283,66 @@ void wasapi_find_matching_output_device(cubeb_stream * stm) { return; } - int rv = wasapi_enumerate_devices(stm->context, (cubeb_device_type)(CUBEB_DEVICE_TYPE_INPUT|CUBEB_DEVICE_TYPE_OUTPUT), &collection); + int rv = wasapi_enumerate_devices( + stm->context, + (cubeb_device_type)(CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT), + &collection); + if (rv != CUBEB_OK) { + return; + } // Find the input device, and then find the output device with the same group // id and the same rate. for (uint32_t i = 0; i < collection.count; i++) { - cubeb_device_info dev = collection.device[i]; - if (dev.devid == input_device_id) { - input_device = &dev; + if (collection.device[i].devid == input_device_id) { + input_device = &collection.device[i]; break; } } for (uint32_t i = 0; i < collection.count; i++) { - cubeb_device_info dev = collection.device[i]; - if (dev.type == CUBEB_DEVICE_TYPE_OUTPUT && - dev.group_id && !strcmp(dev.group_id, input_device->group_id) && + cubeb_device_info & dev = collection.device[i]; + if (dev.type == CUBEB_DEVICE_TYPE_OUTPUT && dev.group_id && input_device && + !strcmp(dev.group_id, input_device->group_id) && dev.default_rate == input_device->default_rate) { - LOG("Found matching device for %s: %s", input_device->friendly_name, dev.friendly_name); - stm->output_device_id = utf8_to_wstr(reinterpret_cast(dev.devid)); + LOG("Found matching device for %s: %s", input_device->friendly_name, + dev.friendly_name); + stm->output_device_id = + utf8_to_wstr(reinterpret_cast(dev.devid)); } } wasapi_device_collection_destroy(stm->context, &collection); } -int setup_wasapi_stream(cubeb_stream * stm) +int +setup_wasapi_stream(cubeb_stream * stm) { int rv; stm->stream_reset_lock.assert_current_thread_owns(); - XASSERT((!stm->output_client || !stm->input_client) && "WASAPI stream already setup, close it first."); + XASSERT((!stm->output_client || !stm->input_client) && + "WASAPI stream already setup, close it first."); if (has_input(stm)) { LOG("(%p) Setup capture: device=%p", stm, stm->input_device_id.get()); - rv = setup_wasapi_stream_one_side(stm, - &stm->input_stream_params, - stm->input_device_id.get(), - eCapture, - __uuidof(IAudioCaptureClient), - stm->input_client, - &stm->input_buffer_frame_count, - stm->input_available_event, - stm->capture_client, - &stm->input_mix_params, - stm->input_device); + rv = setup_wasapi_stream_one_side( + stm, &stm->input_stream_params, stm->input_device_id.get(), eCapture, + __uuidof(IAudioCaptureClient), stm->input_client, + &stm->input_buffer_frame_count, stm->input_available_event, + stm->capture_client, &stm->input_mix_params, stm->input_device); if (rv != CUBEB_OK) { LOG("Failure to open the input side."); return rv; } - // We initializing an input stream, buffer ahead two buffers worth of silence. - // This delays the input side slightly, but allow to not glitch when no input - // is available when calling into the resampler to call the callback: the input - // refill event will be set shortly after to compensate for this lack of data. - // In debug, four buffers are used, to avoid tripping up assertions down the line. + // We initializing an input stream, buffer ahead two buffers worth of + // silence. This delays the input side slightly, but allow to not glitch + // when no input is available when calling into the resampler to call the + // callback: the input refill event will be set shortly after to compensate + // for this lack of data. In debug, four buffers are used, to avoid tripping + // up assertions down the line. #if !defined(DEBUG) const int silent_buffer_count = 2; #else @@ -2265,7 +2365,8 @@ int setup_wasapi_stream(cubeb_stream * stm) // we attempt to open that same device in output mode in order to drive the // loopback via the output events. stm->has_dummy_output = false; - if (!has_output(stm) && stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) { + if (!has_output(stm) && + stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) { stm->output_stream_params.rate = stm->input_stream_params.rate; stm->output_stream_params.channels = stm->input_stream_params.channels; stm->output_stream_params.layout = stm->input_stream_params.layout; @@ -2284,28 +2385,25 @@ int setup_wasapi_stream(cubeb_stream * stm) if (has_output(stm)) { LOG("(%p) Setup render: device=%p", stm, stm->output_device_id.get()); - rv = setup_wasapi_stream_one_side(stm, - &stm->output_stream_params, - stm->output_device_id.get(), - eRender, - __uuidof(IAudioRenderClient), - stm->output_client, - &stm->output_buffer_frame_count, - stm->refill_event, - stm->render_client, - &stm->output_mix_params, - stm->output_device); + rv = setup_wasapi_stream_one_side( + stm, &stm->output_stream_params, stm->output_device_id.get(), eRender, + __uuidof(IAudioRenderClient), stm->output_client, + &stm->output_buffer_frame_count, stm->refill_event, stm->render_client, + &stm->output_mix_params, stm->output_device); if (rv != CUBEB_OK) { LOG("Failure to open the output side."); return rv; } - HRESULT hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume), - stm->audio_stream_volume.receive_vpp()); + HRESULT hr = 0; +#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME + hr = stm->output_client->GetService(__uuidof(IAudioStreamVolume), + stm->audio_stream_volume.receive_vpp()); if (FAILED(hr)) { LOG("Could not get the IAudioStreamVolume: %lx", hr); return CUBEB_ERROR; } +#endif XASSERT(stm->frames_written == 0); hr = stm->output_client->GetService(__uuidof(IAudioClock), @@ -2315,11 +2413,13 @@ int setup_wasapi_stream(cubeb_stream * stm) return CUBEB_ERROR; } +#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME /* Restore the stream volume over a device change. */ if (stream_set_volume(stm, stm->volume) != CUBEB_OK) { LOG("Could not set the volume."); return CUBEB_ERROR; } +#endif } /* If we have both input and output, we resample to @@ -2346,14 +2446,12 @@ int setup_wasapi_stream(cubeb_stream * stm) cubeb_stream_params output_params = stm->output_mix_params; output_params.channels = stm->output_stream_params.channels; - stm->resampler.reset( - cubeb_resampler_create(stm, - has_input(stm) ? &input_params : nullptr, - has_output(stm) ? &output_params : nullptr, - target_sample_rate, - stm->data_callback, - stm->user_ptr, - stm->voice ? CUBEB_RESAMPLER_QUALITY_VOIP : CUBEB_RESAMPLER_QUALITY_DESKTOP)); + stm->resampler.reset(cubeb_resampler_create( + stm, has_input(stm) ? &input_params : nullptr, + has_output(stm) ? &output_params : nullptr, target_sample_rate, + stm->data_callback, stm->user_ptr, + stm->voice ? CUBEB_RESAMPLER_QUALITY_VOIP + : CUBEB_RESAMPLER_QUALITY_DESKTOP)); if (!stm->resampler) { LOG("Could not get a resampler"); return CUBEB_ERROR; @@ -2378,28 +2476,28 @@ int setup_wasapi_stream(cubeb_stream * stm) LOG("Input stream using undefined layout! Any mixing may be " "unpredictable!\n"); } - stm->input_mixer.reset(cubeb_mixer_create(stm->input_stream_params.format, - stm->input_mix_params.channels, - stm->input_mix_params.layout, - stm->input_stream_params.channels, - stm->input_stream_params.layout)); + stm->input_mixer.reset(cubeb_mixer_create( + stm->input_stream_params.format, stm->input_mix_params.channels, + stm->input_mix_params.layout, stm->input_stream_params.channels, + stm->input_stream_params.layout)); assert(stm->input_mixer); } // Create output mixer. - if (has_output(stm) && stm->output_mix_params.layout != stm->output_stream_params.layout) { + if (has_output(stm) && + stm->output_mix_params.layout != stm->output_stream_params.layout) { if (stm->output_mix_params.layout == CUBEB_LAYOUT_UNDEFINED) { - LOG("Output stream using undefined layout! Any mixing may be unpredictable!\n"); + LOG("Output stream using undefined layout! Any mixing may be " + "unpredictable!\n"); } - stm->output_mixer.reset(cubeb_mixer_create(stm->output_stream_params.format, - stm->output_stream_params.channels, - stm->output_stream_params.layout, - stm->output_mix_params.channels, - stm->output_mix_params.layout)); + stm->output_mixer.reset(cubeb_mixer_create( + stm->output_stream_params.format, stm->output_stream_params.channels, + stm->output_stream_params.layout, stm->output_mix_params.channels, + stm->output_mix_params.layout)); assert(stm->output_mixer); // Input is up/down mixed when depacketized in get_input_buffer. stm->mix_buffer.resize( - frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count)); + frames_to_bytes_before_mix(stm, stm->output_buffer_frame_count)); } return CUBEB_OK; @@ -2417,12 +2515,12 @@ pref_to_role(cubeb_stream_prefs prefs) int wasapi_stream_init(cubeb * context, cubeb_stream ** stream, - char const * stream_name, - cubeb_devid input_device, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, - unsigned int latency_frames, cubeb_data_callback data_callback, + unsigned int latency_frames, + cubeb_data_callback data_callback, cubeb_state_callback state_callback, void * user_ptr) { int rv; @@ -2434,7 +2532,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, return CUBEB_ERROR_INVALID_FORMAT; } - std::unique_ptr stm(new cubeb_stream(), wasapi_stream_destroy); + std::unique_ptr stm( + new cubeb_stream(), wasapi_stream_destroy); stm->context = context; stm->data_callback = data_callback; @@ -2443,9 +2542,9 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, stm->role = eConsole; stm->input_bluetooth_handsfree = false; - HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), - NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(stm->device_enumerator.receive())); + HRESULT hr = + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(stm->device_enumerator.receive())); if (FAILED(hr)) { LOG("Could not get device enumerator: %lx", hr); return hr; @@ -2453,11 +2552,13 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, if (input_stream_params) { stm->input_stream_params = *input_stream_params; - stm->input_device_id = utf8_to_wstr(reinterpret_cast(input_device)); + stm->input_device_id = + utf8_to_wstr(reinterpret_cast(input_device)); } if (output_stream_params) { stm->output_stream_params = *output_stream_params; - stm->output_device_id = utf8_to_wstr(reinterpret_cast(output_device)); + stm->output_device_id = + utf8_to_wstr(reinterpret_cast(output_device)); } if (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_VOICE || @@ -2467,19 +2568,20 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, stm->voice = false; } - switch (output_stream_params ? output_stream_params->format : input_stream_params->format) { - case CUBEB_SAMPLE_S16NE: - stm->bytes_per_sample = sizeof(short); - stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_PCM; - stm->linear_input_buffer.reset(new auto_array_wrapper_impl); - break; - case CUBEB_SAMPLE_FLOAT32NE: - stm->bytes_per_sample = sizeof(float); - stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - stm->linear_input_buffer.reset(new auto_array_wrapper_impl); - break; - default: - return CUBEB_ERROR_INVALID_FORMAT; + switch (output_stream_params ? output_stream_params->format + : input_stream_params->format) { + case CUBEB_SAMPLE_S16NE: + stm->bytes_per_sample = sizeof(short); + stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_PCM; + stm->linear_input_buffer.reset(new auto_array_wrapper_impl); + break; + case CUBEB_SAMPLE_FLOAT32NE: + stm->bytes_per_sample = sizeof(float); + stm->waveformatextensible_sub_format = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + stm->linear_input_buffer.reset(new auto_array_wrapper_impl); + break; + default: + return CUBEB_ERROR_INVALID_FORMAT; } stm->latency = latency_frames; @@ -2514,10 +2616,12 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, return rv; } - if (!((input_stream_params ? - (input_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0) || - (output_stream_params ? - (output_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0))) { + if (!((input_stream_params ? (input_stream_params->prefs & + CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) + : 0) || + (output_stream_params ? (output_stream_params->prefs & + CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) + : 0))) { HRESULT hr = register_notification_client(stm.get()); if (FAILED(hr)) { /* this is not fatal, we can still play audio, but we won't be able @@ -2532,7 +2636,8 @@ wasapi_stream_init(cubeb * context, cubeb_stream ** stream, return CUBEB_OK; } -void close_wasapi_stream(cubeb_stream * stm) +void +close_wasapi_stream(cubeb_stream * stm) { XASSERT(stm); @@ -2547,19 +2652,28 @@ void close_wasapi_stream(cubeb_stream * stm) stm->output_device = nullptr; stm->input_device = nullptr; +#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME stm->audio_stream_volume = nullptr; +#endif stm->audio_clock = nullptr; - stm->total_frames_written += static_cast(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params))); + stm->total_frames_written += static_cast( + round(stm->frames_written * + stream_to_mix_samplerate_ratio(stm->output_stream_params, + stm->output_mix_params))); stm->frames_written = 0; stm->resampler.reset(); stm->output_mixer.reset(); stm->input_mixer.reset(); stm->mix_buffer.clear(); + if (stm->linear_input_buffer) { + stm->linear_input_buffer->clear(); + } } -void wasapi_stream_destroy(cubeb_stream * stm) +void +wasapi_stream_destroy(cubeb_stream * stm) { XASSERT(stm); LOG("Stream destroy (%p)", stm); @@ -2594,17 +2708,16 @@ void wasapi_stream_destroy(cubeb_stream * stm) delete stm; } -enum StreamDirection { - OUTPUT, - INPUT -}; +enum StreamDirection { OUTPUT, INPUT }; -int stream_start_one_side(cubeb_stream * stm, StreamDirection dir) +int +stream_start_one_side(cubeb_stream * stm, StreamDirection dir) { XASSERT((dir == OUTPUT && stm->output_client) || (dir == INPUT && stm->input_client)); - HRESULT hr = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start(); + HRESULT hr = + dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start(); if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { LOG("audioclient invalidated for %s device, reconfiguring", dir == OUTPUT ? "output" : "input"); @@ -2622,7 +2735,8 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir) return r; } - HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() : stm->input_client->Start(); + HRESULT hr2 = dir == OUTPUT ? stm->output_client->Start() + : stm->input_client->Start(); if (FAILED(hr2)) { LOG("could not start the %s stream after reconfig: %lx", dir == OUTPUT ? "output" : "input", hr); @@ -2637,7 +2751,8 @@ int stream_start_one_side(cubeb_stream * stm, StreamDirection dir) return CUBEB_OK; } -int wasapi_stream_start(cubeb_stream * stm) +int +wasapi_stream_start(cubeb_stream * stm) { auto_lock lock(stm->stream_reset_lock); @@ -2673,7 +2788,9 @@ int wasapi_stream_start(cubeb_stream * stm) } cubeb_async_log_reset_threads(); - stm->thread = (HANDLE) _beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + stm->thread = + (HANDLE)_beginthreadex(NULL, 512 * 1024, wasapi_stream_render_loop, stm, + STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); if (stm->thread == NULL) { LOG("could not create WASAPI render thread."); return CUBEB_ERROR; @@ -2692,7 +2809,8 @@ int wasapi_stream_start(cubeb_stream * stm) return CUBEB_OK; } -int wasapi_stream_stop(cubeb_stream * stm) +int +wasapi_stream_stop(cubeb_stream * stm) { XASSERT(stm); HRESULT hr; @@ -2731,18 +2849,8 @@ int wasapi_stream_stop(cubeb_stream * stm) return CUBEB_OK; } -int wasapi_stream_reset_default_device(cubeb_stream * stm) -{ - XASSERT(stm && stm->reconfigure_event); - BOOL ok = SetEvent(stm->reconfigure_event); - if (!ok) { - LOG("SetEvent on reconfigure_event failed: %lx", GetLastError()); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position) +int +wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position) { XASSERT(stm && position); auto_lock lock(stm->stream_reset_lock); @@ -2752,11 +2860,16 @@ int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position) } /* Calculate how far behind the current stream head the playback cursor is. */ - uint64_t stream_delay = static_cast(current_stream_delay(stm) * stm->output_stream_params.rate); + uint64_t stream_delay = static_cast(current_stream_delay(stm) * + stm->output_stream_params.rate); /* Calculate the logical stream head in frames at the stream sample rate. */ - uint64_t max_pos = stm->total_frames_written + - static_cast(round(stm->frames_written * stream_to_mix_samplerate_ratio(stm->output_stream_params, stm->output_mix_params))); + uint64_t max_pos = + stm->total_frames_written + + static_cast( + round(stm->frames_written * + stream_to_mix_samplerate_ratio(stm->output_stream_params, + stm->output_mix_params))); *position = max_pos; if (stream_delay <= *position) { @@ -2771,7 +2884,8 @@ int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position) return CUBEB_OK; } -int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) +int +wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { XASSERT(stm && latency); @@ -2797,11 +2911,11 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) // This happens on windows 10: no error, but always 0 for latency. if (latency_hns == 0) { LOG("GetStreamLatency returned 0, using workaround."); - double delay_s = current_stream_delay(stm); - // convert to sample-frames - *latency = delay_s * stm->output_stream_params.rate; + double delay_s = current_stream_delay(stm); + // convert to sample-frames + *latency = delay_s * stm->output_stream_params.rate; } else { - *latency = hns_to_frames(stm, latency_hns); + *latency = hns_to_frames(stm, latency_hns); } LOG("Output latency %u frames.", *latency); @@ -2809,8 +2923,8 @@ int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) return CUBEB_OK; } - -int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency) +int +wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency) { XASSERT(stm && latency); @@ -2831,7 +2945,8 @@ int wasapi_stream_get_input_latency(cubeb_stream * stm, uint32_t * latency) return CUBEB_OK; } -int wasapi_stream_set_volume(cubeb_stream * stm, float volume) +int +wasapi_stream_set_volume(cubeb_stream * stm, float volume) { auto_lock lock(stm->stream_reset_lock); @@ -2839,9 +2954,11 @@ int wasapi_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_ERROR; } +#ifdef CUBEB_WASAPI_USE_IAUDIOSTREAMVOLUME if (stream_set_volume(stm, volume) != CUBEB_OK) { return CUBEB_ERROR; } +#endif stm->volume = volume; @@ -2862,26 +2979,26 @@ wstr_to_utf8(LPCWSTR str) } static std::unique_ptr -utf8_to_wstr(char const * str) -{ +utf8_to_wstr(char const * str) { int size = ::MultiByteToWideChar(CP_UTF8, 0, str, -1, nullptr, 0); if (size <= 0) { return nullptr; } - std::unique_ptr ret(new wchar_t[size]); + std::unique_ptr ret(new wchar_t[size]); ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret.get(), size); return ret; } -static com_ptr -wasapi_get_device_node(IMMDeviceEnumerator * enumerator, IMMDevice * dev) +static com_ptr wasapi_get_device_node( + IMMDeviceEnumerator * enumerator, IMMDevice * dev) { com_ptr ret; com_ptr devtopo; com_ptr connector; - if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, devtopo.receive_vpp())) && + if (SUCCEEDED(dev->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, + devtopo.receive_vpp())) && SUCCEEDED(devtopo->GetConnector(0, connector.receive()))) { wchar_t * tmp = nullptr; if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&tmp))) { @@ -2917,7 +3034,8 @@ wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id, /* `ret` must be deallocated with `wasapi_destroy_device`, iff the return value * of this function is `CUBEB_OK`. */ int -wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev) +wasapi_create_device(cubeb * ctx, cubeb_device_info & ret, + IMMDeviceEnumerator * enumerator, IMMDevice * dev) { com_ptr endpoint; com_ptr devnode; @@ -2986,7 +3104,7 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * } if (!ret.friendly_name) { // This is not fatal, but a valid string is expected in all cases. - char* empty = new char[1]; + char * empty = new char[1]; empty[0] = '\0'; ret.friendly_name = empty; } @@ -3009,20 +3127,24 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * if (!ret.group_id) { // This is not fatal, but a valid string is expected in all cases. - char* empty = new char[1]; + char * empty = new char[1]; empty[0] = '\0'; ret.group_id = empty; } ret.preferred = CUBEB_DEVICE_PREF_NONE; if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator)) { - ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA); + ret.preferred = + (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_MULTIMEDIA); } - if (wasapi_is_default_device(flow, eCommunications, device_id.get(), enumerator)) { - ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE); + if (wasapi_is_default_device(flow, eCommunications, device_id.get(), + enumerator)) { + ret.preferred = + (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_VOICE); } if (wasapi_is_default_device(flow, eConsole, device_id.get(), enumerator)) { - ret.preferred = (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_NOTIFICATION); + ret.preferred = + (cubeb_device_pref)(ret.preferred | CUBEB_DEVICE_PREF_NOTIFICATION); } if (flow == eRender) { @@ -3032,29 +3154,32 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * } switch (state) { - case DEVICE_STATE_ACTIVE: - ret.state = CUBEB_DEVICE_STATE_ENABLED; - break; - case DEVICE_STATE_UNPLUGGED: - ret.state = CUBEB_DEVICE_STATE_UNPLUGGED; - break; - default: - ret.state = CUBEB_DEVICE_STATE_DISABLED; - break; + case DEVICE_STATE_ACTIVE: + ret.state = CUBEB_DEVICE_STATE_ENABLED; + break; + case DEVICE_STATE_UNPLUGGED: + ret.state = CUBEB_DEVICE_STATE_UNPLUGGED; + break; + default: + ret.state = CUBEB_DEVICE_STATE_DISABLED; + break; }; - ret.format = static_cast(CUBEB_DEVICE_FMT_F32NE | CUBEB_DEVICE_FMT_S16NE); + ret.format = static_cast(CUBEB_DEVICE_FMT_F32NE | + CUBEB_DEVICE_FMT_S16NE); ret.default_format = CUBEB_DEVICE_FMT_F32NE; prop_variant fmtvar; hr = propstore->GetValue(PKEY_AudioEngine_DeviceFormat, &fmtvar); if (SUCCEEDED(hr) && fmtvar.vt == VT_BLOB) { if (fmtvar.blob.cbSize == sizeof(PCMWAVEFORMAT)) { - const PCMWAVEFORMAT * pcm = reinterpret_cast(fmtvar.blob.pBlobData); + const PCMWAVEFORMAT * pcm = + reinterpret_cast(fmtvar.blob.pBlobData); 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); + WAVEFORMATEX * wfx = + reinterpret_cast(fmtvar.blob.pBlobData); if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || wfx->wFormatTag == WAVE_FORMAT_PCM) { @@ -3064,7 +3189,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * } } - if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) && + 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); @@ -3081,8 +3207,8 @@ wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * void wasapi_destroy_device(cubeb_device_info * device) { - delete [] device->friendly_name; - delete [] device->group_id; + delete[] device->friendly_name; + delete[] device->group_id; } static int @@ -3095,19 +3221,25 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, UINT cc, i; EDataFlow flow; - hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, - CLSCTX_INPROC_SERVER, IID_PPV_ARGS(enumerator.receive())); + hr = + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(enumerator.receive())); if (FAILED(hr)) { LOG("Could not get device enumerator: %lx", hr); return CUBEB_ERROR; } - if (type == CUBEB_DEVICE_TYPE_OUTPUT) flow = eRender; - else if (type == CUBEB_DEVICE_TYPE_INPUT) flow = eCapture; - else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) flow = eAll; - else return CUBEB_ERROR; + if (type == CUBEB_DEVICE_TYPE_OUTPUT) + flow = eRender; + else if (type == CUBEB_DEVICE_TYPE_INPUT) + flow = eCapture; + else if (type & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) + flow = eAll; + else + return CUBEB_ERROR; - hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, collection.receive()); + hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, + collection.receive()); if (FAILED(hr)) { LOG("Could not enumerate audio endpoints: %lx", hr); return CUBEB_ERROR; @@ -3128,11 +3260,11 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, com_ptr dev; hr = collection->Item(i, dev.receive()); if (FAILED(hr)) { - LOG("IMMDeviceCollection::Item(%u) failed: %lx", i-1, hr); + LOG("IMMDeviceCollection::Item(%u) failed: %lx", i - 1, hr); continue; } - if (wasapi_create_device(context, devices[out->count], - enumerator.get(), dev.get()) == CUBEB_OK) { + if (wasapi_create_device(context, devices[out->count], enumerator.get(), + dev.get()) == CUBEB_OK) { out->count += 1; } } @@ -3142,24 +3274,25 @@ wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, } static int -wasapi_device_collection_destroy(cubeb * /*ctx*/, cubeb_device_collection * collection) +wasapi_device_collection_destroy(cubeb * /*ctx*/, + cubeb_device_collection * collection) { XASSERT(collection); for (size_t n = 0; n < collection->count; n++) { - cubeb_device_info& dev = collection->device[n]; + cubeb_device_info & dev = collection->device[n]; wasapi_destroy_device(&dev); } - delete [] collection->device; + delete[] collection->device; return CUBEB_OK; } static int -wasapi_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) +wasapi_register_device_collection_changed( + cubeb * context, cubeb_device_type devtype, + cubeb_device_collection_changed_callback collection_changed_callback, + void * user_ptr) { if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) { return CUBEB_ERROR_INVALID_PARAMETER; @@ -3229,27 +3362,27 @@ wasapi_register_device_collection_changed(cubeb * context, } cubeb_ops const wasapi_ops = { - /*.init =*/ wasapi_init, - /*.get_backend_id =*/ wasapi_get_backend_id, - /*.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, - /*.enumerate_devices =*/ wasapi_enumerate_devices, - /*.device_collection_destroy =*/ wasapi_device_collection_destroy, - /*.destroy =*/ wasapi_destroy, - /*.stream_init =*/ wasapi_stream_init, - /*.stream_destroy =*/ wasapi_stream_destroy, - /*.stream_start =*/ wasapi_stream_start, - /*.stream_stop =*/ wasapi_stream_stop, - /*.stream_reset_default_device =*/ wasapi_stream_reset_default_device, - /*.stream_get_position =*/ wasapi_stream_get_position, - /*.stream_get_latency =*/ wasapi_stream_get_latency, - /*.stream_get_input_latency =*/ wasapi_stream_get_input_latency, - /*.stream_set_volume =*/ wasapi_stream_set_volume, - /*.stream_set_name =*/ NULL, - /*.stream_get_current_device =*/ NULL, - /*.stream_device_destroy =*/ NULL, - /*.stream_register_device_changed_callback =*/ NULL, - /*.register_device_collection_changed =*/ wasapi_register_device_collection_changed, + /*.init =*/wasapi_init, + /*.get_backend_id =*/wasapi_get_backend_id, + /*.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, + /*.enumerate_devices =*/wasapi_enumerate_devices, + /*.device_collection_destroy =*/wasapi_device_collection_destroy, + /*.destroy =*/wasapi_destroy, + /*.stream_init =*/wasapi_stream_init, + /*.stream_destroy =*/wasapi_stream_destroy, + /*.stream_start =*/wasapi_stream_start, + /*.stream_stop =*/wasapi_stream_stop, + /*.stream_get_position =*/wasapi_stream_get_position, + /*.stream_get_latency =*/wasapi_stream_get_latency, + /*.stream_get_input_latency =*/wasapi_stream_get_input_latency, + /*.stream_set_volume =*/wasapi_stream_set_volume, + /*.stream_set_name =*/NULL, + /*.stream_get_current_device =*/NULL, + /*.stream_device_destroy =*/NULL, + /*.stream_register_device_changed_callback =*/NULL, + /*.register_device_collection_changed =*/ + wasapi_register_device_collection_changed, }; -} // namespace anonymous +} // namespace diff --git a/externals/cubeb/src/cubeb_winmm.c b/externals/cubeb/src/cubeb_winmm.c index a94c2a1c1..169b74385 100755 --- a/externals/cubeb/src/cubeb_winmm.c +++ b/externals/cubeb/src/cubeb_winmm.c @@ -8,70 +8,81 @@ #define WINVER 0x0501 #undef WIN32_LEAN_AND_MEAN +#include "cubeb-internal.h" +#include "cubeb/cubeb.h" #include -#include -#include -#include +#include #include #include #include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" +#include + +/* clang-format off */ +/* These need to be included after windows.h */ +#include +#include +/* clang-format on */ /* This is missing from the MinGW headers. Use a safe fallback. */ #if !defined(MEMORY_ALLOCATION_ALIGNMENT) #define MEMORY_ALLOCATION_ALIGNMENT 16 #endif -/**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/ +/**This is also missing from the MinGW headers. It also appears to be + * undocumented by Microsoft.*/ #ifndef WAVE_FORMAT_48M08 -#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */ #endif #ifndef WAVE_FORMAT_48M16 -#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */ #endif #ifndef WAVE_FORMAT_48S08 -#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */ #endif #ifndef WAVE_FORMAT_48S16 -#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ +#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */ #endif #ifndef WAVE_FORMAT_96M08 -#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ +#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */ #endif #ifndef WAVE_FORMAT_96M16 -#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */ +#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */ #endif #ifndef WAVE_FORMAT_96S08 -#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */ +#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */ #endif #ifndef WAVE_FORMAT_96S16 -#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ +#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */ #endif /**Taken from winbase.h, also not in MinGW.*/ #ifndef STACK_SIZE_PARAM_IS_A_RESERVATION -#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only +#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only #endif #ifndef DRVM_MAPPER -#define DRVM_MAPPER (0x2000) +#define DRVM_MAPPER (0x2000) #endif #ifndef DRVM_MAPPER_PREFERRED_GET -#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) +#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER + 21) #endif #ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET -#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23) +#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER + 23) #endif #define CUBEB_STREAM_MAX 32 #define NBUFS 4 -const GUID KSDATAFORMAT_SUBTYPE_PCM = -{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; -const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = -{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } }; +const GUID KSDATAFORMAT_SUBTYPE_PCM = { + 0x00000001, + 0x0000, + 0x0010, + {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; +const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = { + 0x00000003, + 0x0000, + 0x0010, + {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; struct cubeb_stream_item { SLIST_ENTRY head; @@ -110,6 +121,10 @@ struct cubeb_stream { CRITICAL_SECTION lock; uint64_t written; float soft_volume; + /* For position wrap-around handling: */ + size_t frame_size; + DWORD prev_pos_lo_dword; + DWORD pos_hi_dword; }; static size_t @@ -175,7 +190,7 @@ winmm_refill_stream(cubeb_stream * stm) hdr = winmm_get_next_buffer(stm); - wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params); + wanted = (DWORD)stm->buffer_size / bytes_per_frame(stm->params); /* It is assumed that the caller is holding this lock. It must be dropped during the callback to avoid deadlocks. */ @@ -199,16 +214,16 @@ winmm_refill_stream(cubeb_stream * stm) if (stm->soft_volume != -1.0) { if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) { - float * b = (float *) hdr->lpData; + float * b = (float *)hdr->lpData; uint32_t i; for (i = 0; i < got * stm->params.channels; i++) { b[i] *= stm->soft_volume; } } else { - short * b = (short *) hdr->lpData; + short * b = (short *)hdr->lpData; uint32_t i; for (i = 0; i < got * stm->params.channels; i++) { - b[i] = (short) (b[i] * stm->soft_volume); + b[i] = (short)(b[i] * stm->soft_volume); } } } @@ -223,10 +238,9 @@ winmm_refill_stream(cubeb_stream * stm) LeaveCriticalSection(&stm->lock); } -static unsigned __stdcall -winmm_buffer_thread(void * user_ptr) +static unsigned __stdcall winmm_buffer_thread(void * user_ptr) { - cubeb * ctx = (cubeb *) user_ptr; + cubeb * ctx = (cubeb *)user_ptr; XASSERT(ctx); for (;;) { @@ -242,7 +256,7 @@ winmm_buffer_thread(void * user_ptr) item = InterlockedFlushSList(ctx->work); while (item != NULL) { PSLIST_ENTRY tmp = item; - winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream); + winmm_refill_stream(((struct cubeb_stream_item *)tmp)->stream); item = item->Next; _aligned_free(tmp); } @@ -256,16 +270,18 @@ winmm_buffer_thread(void * user_ptr) } static void CALLBACK -winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2) +winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, + DWORD_PTR p1, DWORD_PTR p2) { - cubeb_stream * stm = (cubeb_stream *) user_ptr; + cubeb_stream * stm = (cubeb_stream *)user_ptr; struct cubeb_stream_item * item; if (msg != WOM_DONE) { return; } - item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT); + item = _aligned_malloc(sizeof(struct cubeb_stream_item), + MEMORY_ALLOCATION_ALIGNMENT); XASSERT(item); item->stream = stm; InterlockedPushEntrySList(stm->context->work, &item->head); @@ -284,7 +300,8 @@ calculate_minimum_latency(void) return 500; } - /* Vista's WinMM implementation underruns when less than 200ms of audio is buffered. */ + /* Vista's WinMM implementation underruns when less than 200ms of audio is + * buffered. */ memset(&osvi, 0, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvi.dwMajorVersion = 6; @@ -294,14 +311,16 @@ calculate_minimum_latency(void) VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL); VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL); - if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) { + if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != + 0) { return 200; } return 100; } -static void winmm_destroy(cubeb * ctx); +static void +winmm_destroy(cubeb * ctx); /*static*/ int winmm_init(cubeb ** context, char const * context_name) @@ -331,7 +350,9 @@ winmm_init(cubeb ** context, char const * context_name) return CUBEB_ERROR; } - ctx->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + ctx->thread = + (HANDLE)_beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, + STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); if (!ctx->thread) { winmm_destroy(ctx); return CUBEB_ERROR; @@ -382,18 +403,18 @@ winmm_destroy(cubeb * ctx) free(ctx); } -static void winmm_stream_destroy(cubeb_stream * stm); +static void +winmm_stream_destroy(cubeb_stream * stm); static int -winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, - cubeb_devid input_device, +winmm_stream_init(cubeb * context, cubeb_stream ** stream, + char const * stream_name, cubeb_devid input_device, cubeb_stream_params * input_stream_params, cubeb_devid output_device, cubeb_stream_params * output_stream_params, unsigned int latency_frames, cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) + cubeb_state_callback state_callback, void * user_ptr) { MMRESULT r; WAVEFORMATEXTENSIBLE wfx; @@ -452,8 +473,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n return CUBEB_ERROR_INVALID_FORMAT; } - wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; - wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Format.nBlockAlign = + (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; + wfx.Format.nAvgBytesPerSec = + wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; EnterCriticalSection(&context->lock); @@ -485,9 +508,11 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n latency_ms = context->minimum_latency_ms; } - bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS); + bufsz = (size_t)(stm->params.rate / 1000.0 * latency_ms * + bytes_per_frame(stm->params) / NBUFS); if (bufsz % bytes_per_frame(stm->params) != 0) { - bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params)); + bufsz += + bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params)); } XASSERT(bufsz % bytes_per_frame(stm->params) == 0); @@ -506,7 +531,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n /* winmm_buffer_callback will be called during waveOutOpen, so all other initialization must be complete before calling it. */ r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format, - (DWORD_PTR) winmm_buffer_callback, (DWORD_PTR) stm, + (DWORD_PTR)winmm_buffer_callback, (DWORD_PTR)stm, CALLBACK_FUNCTION); if (r != MMSYSERR_NOERROR) { winmm_stream_destroy(stm); @@ -536,6 +561,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n winmm_refill_stream(stm); } + stm->frame_size = bytes_per_frame(stm->params); + stm->prev_pos_lo_dword = 0; + stm->pos_hi_dword = 0; + *stream = stm; return CUBEB_OK; @@ -580,7 +609,8 @@ winmm_stream_destroy(cubeb_stream * stm) for (i = 0; i < NBUFS; ++i) { if (stm->buffers[i].dwFlags & WHDR_PREPARED) { - waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i])); + waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], + sizeof(stm->buffers[i])); } } @@ -619,7 +649,8 @@ winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) } static int -winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency) +winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, + uint32_t * latency) { // 100ms minimum, if we are not in a bizarre configuration. *latency = ctx->minimum_latency_ms * params.rate / 1000; @@ -686,6 +717,58 @@ winmm_stream_stop(cubeb_stream * stm) return CUBEB_OK; } +/* +Microsoft wave audio docs say "samples are the preferred time format in which +to represent the current position", but relying on this causes problems on +Windows XP, the only OS cubeb_winmm is used on. + +While the wdmaud.sys driver internally tracks a 64-bit position and ensures no +backward movement, the WinMM API limits the position returned from +waveOutGetPosition() to a 32-bit DWORD (this applies equally to XP x64). The +higher 32 bits are chopped off, and to an API consumer the position can appear +to move backward. + +In theory, even a 32-bit TIME_SAMPLES position should provide plenty of +playback time for typical use cases before this pseudo wrap-around, e.g: + (2^32 - 1)/48000 = ~24:51:18 for 48.0 kHz stereo; + (2^32 - 1)/44100 = ~27:03:12 for 44.1 kHz stereo. +In reality, wdmaud.sys doesn't provide a TIME_SAMPLES position at all, only a +32-bit TIME_BYTES position, from which wdmaud.drv derives TIME_SAMPLES: + SamplePos = (BytePos * 8) / BitsPerFrame, + where BitsPerFrame = Channels * BitsPerSample, +Per dom\media\AudioSampleFormat.h, desktop builds always use 32-bit FLOAT32 +samples, so the maximum for TIME_SAMPLES should be: + (2^29 - 1)/48000 = ~03:06:25; + (2^29 - 1)/44100 = ~03:22:54. +This might still be OK for typical browser usage, but there's also a bug in the +formula above: BytePos * 8 (BytePos << 3) is done on a 32-bit BytePos, without +first casting it to 64 bits, so the highest 3 bits, if set, would get shifted +out, and the maximum possible TIME_SAMPLES drops unacceptably low: + (2^26 - 1)/48000 = ~00:23:18; + (2^26 - 1)/44100 = ~00:25:22. + +To work around these limitations, we just get the position in TIME_BYTES, +recover the 64-bit value, and do our own conversion to samples. +*/ + +/* Convert chopped 32-bit waveOutGetPosition() into 64-bit true position. */ +static uint64_t +update_64bit_position(cubeb_stream * stm, DWORD pos_lo_dword) +{ + /* Caller should be holding stm->lock. */ + if (pos_lo_dword < stm->prev_pos_lo_dword) { + stm->pos_hi_dword++; + LOG("waveOutGetPosition() has wrapped around: %#lx -> %#lx", + stm->prev_pos_lo_dword, pos_lo_dword); + LOG("Wrap-around count = %#lx", stm->pos_hi_dword); + LOG("Current 64-bit position = %#llx", + (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword)); + } + stm->prev_pos_lo_dword = pos_lo_dword; + + return (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword); +} + static int winmm_stream_get_position(cubeb_stream * stm, uint64_t * position) { @@ -693,15 +776,17 @@ winmm_stream_get_position(cubeb_stream * stm, uint64_t * position) MMTIME time; EnterCriticalSection(&stm->lock); - time.wType = TIME_SAMPLES; + /* See the long comment above for why not just use TIME_SAMPLES here. */ + time.wType = TIME_BYTES; r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); - LeaveCriticalSection(&stm->lock); - if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) { + if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) { + LeaveCriticalSection(&stm->lock); return CUBEB_ERROR; } - *position = time.u.sample; + *position = update_64bit_position(stm, time.u.cb) / stm->frame_size; + LeaveCriticalSection(&stm->lock); return CUBEB_OK; } @@ -711,20 +796,24 @@ winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency) { MMRESULT r; MMTIME time; - uint64_t written; + uint64_t written, position; EnterCriticalSection(&stm->lock); - time.wType = TIME_SAMPLES; + /* See the long comment above for why not just use TIME_SAMPLES here. */ + time.wType = TIME_BYTES; r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); - written = stm->written; - LeaveCriticalSection(&stm->lock); - if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) { + if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) { + LeaveCriticalSection(&stm->lock); return CUBEB_ERROR; } - XASSERT(written - time.u.sample <= UINT32_MAX); - *latency = (uint32_t) (written - time.u.sample); + position = update_64bit_position(stm, time.u.cb); + written = stm->written; + LeaveCriticalSection(&stm->lock); + + XASSERT((written - (position / stm->frame_size)) <= UINT32_MAX); + *latency = (uint32_t)(written - (position / stm->frame_size)); return CUBEB_OK; } @@ -738,11 +827,18 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume) return CUBEB_OK; } -#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16) -#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16) -#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16) -#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16) -#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16) +#define MM_11025HZ_MASK \ + (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16) +#define MM_22050HZ_MASK \ + (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16) +#define MM_44100HZ_MASK \ + (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16) +#define MM_48000HZ_MASK \ + (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | \ + WAVE_FORMAT_48S16) +#define MM_96000HZ_MASK \ + (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | \ + WAVE_FORMAT_96S16) static void winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats) { @@ -752,17 +848,20 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats) info->max_rate = 11025; } if (formats & MM_22050HZ_MASK) { - if (info->min_rate == 0) info->min_rate = 22050; + if (info->min_rate == 0) + info->min_rate = 22050; info->max_rate = 22050; info->default_rate = 22050; } if (formats & MM_44100HZ_MASK) { - if (info->min_rate == 0) info->min_rate = 44100; + if (info->min_rate == 0) + info->min_rate = 44100; info->max_rate = 44100; info->default_rate = 44100; } if (formats & MM_48000HZ_MASK) { - if (info->min_rate == 0) info->min_rate = 48000; + if (info->min_rate == 0) + info->min_rate = 48000; info->max_rate = 48000; info->default_rate = 48000; } @@ -775,11 +874,14 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats) } } -#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \ - WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16) +#define MM_S16_MASK \ + (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | \ + WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | \ + WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16) static int winmm_query_supported_formats(UINT devid, DWORD formats, - cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt) + cubeb_device_fmt * supfmt, + cubeb_device_fmt * deffmt) { WAVEFORMATEXTENSIBLE wfx; @@ -793,13 +895,16 @@ winmm_query_supported_formats(UINT devid, DWORD formats, wfx.Format.nChannels = 2; wfx.Format.nSamplesPerSec = 44100; wfx.Format.wBitsPerSample = 32; - wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; - wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Format.nBlockAlign = + (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; + wfx.Format.nAvgBytesPerSec = + wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; wfx.Format.cbSize = 22; wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR) + if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == + MMSYSERR_NOERROR) *supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE); return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR; @@ -813,10 +918,10 @@ guid_to_cstr(LPGUID guid) return NULL; } _snprintf_s(ret, 40, _TRUNCATE, - "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", - guid->Data1, guid->Data2, guid->Data3, - guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], - guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]); + "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid->Data1, + guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], + guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], + guid->Data4[6], guid->Data4[7]); return ret; } @@ -826,13 +931,15 @@ winmm_query_preferred_out_device(UINT devid) DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status; cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE; - if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, - (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD_PTR)&mmpref, + (DWORD_PTR)&status) == MMSYSERR_NOERROR && devid == mmpref) ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION; - if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, - (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, + (DWORD_PTR)&compref, + (DWORD_PTR)&status) == MMSYSERR_NOERROR && devid == compref) ret |= CUBEB_DEVICE_PREF_VOICE; @@ -851,10 +958,11 @@ device_id_idx(UINT devid) } static void -winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, UINT devid) +winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, + UINT devid) { XASSERT(ret); - ret->devid = (cubeb_devid) devid; + ret->devid = (cubeb_devid)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); ret->group_id = guid_to_cstr(&caps->ProductGuid); @@ -866,8 +974,8 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, ret->max_channels = caps->wChannels; winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); + winmm_query_supported_formats(devid, caps->dwFormats, &ret->format, + &ret->default_format); /* Hardcoded latency estimates... */ ret->latency_lo = 100 * ret->default_rate / 1000; @@ -875,10 +983,11 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, } static void -winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, UINT devid) +winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, + UINT devid) { XASSERT(ret); - ret->devid = (cubeb_devid) devid; + ret->devid = (cubeb_devid)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); ret->group_id = NULL; @@ -890,8 +999,8 @@ winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, U ret->max_channels = caps->wChannels; winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); + winmm_query_supported_formats(devid, caps->dwFormats, &ret->format, + &ret->default_format); /* Hardcoded latency estimates... */ ret->latency_lo = 100 * ret->default_rate / 1000; @@ -904,13 +1013,15 @@ winmm_query_preferred_in_device(UINT devid) DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status; cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE; - if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, - (DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, + (DWORD_PTR)&mmpref, + (DWORD_PTR)&status) == MMSYSERR_NOERROR && devid == mmpref) ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION; - if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, - (DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR && + if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET, + (DWORD_PTR)&compref, + (DWORD_PTR)&status) == MMSYSERR_NOERROR && devid == compref) ret |= CUBEB_DEVICE_PREF_VOICE; @@ -918,10 +1029,11 @@ winmm_query_preferred_in_device(UINT devid) } static void -winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, UINT devid) +winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, + UINT devid) { XASSERT(ret); - ret->devid = (cubeb_devid) devid; + ret->devid = (cubeb_devid)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); ret->group_id = guid_to_cstr(&caps->ProductGuid); @@ -933,8 +1045,8 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U ret->max_channels = caps->wChannels; winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); + winmm_query_supported_formats(devid, caps->dwFormats, &ret->format, + &ret->default_format); /* Hardcoded latency estimates... */ ret->latency_lo = 100 * ret->default_rate / 1000; @@ -942,10 +1054,11 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U } static void -winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UINT devid) +winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, + UINT devid) { XASSERT(ret); - ret->devid = (cubeb_devid) devid; + ret->devid = (cubeb_devid)devid; ret->device_id = device_id_idx(devid); ret->friendly_name = _strdup(caps->szPname); ret->group_id = NULL; @@ -957,8 +1070,8 @@ winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UIN ret->max_channels = caps->wChannels; winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); + winmm_query_supported_formats(devid, caps->dwFormats, &ret->format, + &ret->default_format); /* Hardcoded latency estimates... */ ret->latency_lo = 100 * ret->default_rate / 1000; @@ -989,7 +1102,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type, for (i = 0; i < outcount; i++) { dev = &devices[collection->count]; - if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR) { + if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == + MMSYSERR_NOERROR) { winmm_create_device_from_outcaps2(dev, &woc2, i); collection->count += 1; } else if (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR) { @@ -1008,7 +1122,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type, for (i = 0; i < incount; i++) { dev = &devices[collection->count]; - if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR) { + if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == + MMSYSERR_NOERROR) { winmm_create_device_from_incaps2(dev, &wic2, i); collection->count += 1; } else if (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR) { @@ -1030,13 +1145,13 @@ winmm_device_collection_destroy(cubeb * ctx, uint32_t i; XASSERT(collection); - (void) ctx; + (void)ctx; for (i = 0; i < collection->count; i++) { - free((void *) collection->device[i].device_id); - free((void *) collection->device[i].friendly_name); - free((void *) collection->device[i].group_id); - free((void *) collection->device[i].vendor_name); + free((void *)collection->device[i].device_id); + free((void *)collection->device[i].friendly_name); + free((void *)collection->device[i].group_id); + free((void *)collection->device[i].vendor_name); } free(collection->device); @@ -1044,26 +1159,24 @@ winmm_device_collection_destroy(cubeb * ctx, } static struct cubeb_ops const winmm_ops = { - /*.init =*/ winmm_init, - /*.get_backend_id =*/ winmm_get_backend_id, - /*.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, - /*.enumerate_devices =*/ winmm_enumerate_devices, - /*.device_collection_destroy =*/ winmm_device_collection_destroy, - /*.destroy =*/ winmm_destroy, - /*.stream_init =*/ winmm_stream_init, - /*.stream_destroy =*/ winmm_stream_destroy, - /*.stream_start =*/ winmm_stream_start, - /*.stream_stop =*/ winmm_stream_stop, - /*.stream_reset_default_device =*/ NULL, - /*.stream_get_position =*/ winmm_stream_get_position, - /*.stream_get_latency = */ winmm_stream_get_latency, - /*.stream_get_input_latency = */ NULL, - /*.stream_set_volume =*/ winmm_stream_set_volume, - /*.stream_set_name =*/ NULL, - /*.stream_get_current_device =*/ NULL, - /*.stream_device_destroy =*/ NULL, - /*.stream_register_device_changed_callback=*/ NULL, - /*.register_device_collection_changed =*/ NULL -}; + /*.init =*/winmm_init, + /*.get_backend_id =*/winmm_get_backend_id, + /*.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, + /*.enumerate_devices =*/winmm_enumerate_devices, + /*.device_collection_destroy =*/winmm_device_collection_destroy, + /*.destroy =*/winmm_destroy, + /*.stream_init =*/winmm_stream_init, + /*.stream_destroy =*/winmm_stream_destroy, + /*.stream_start =*/winmm_stream_start, + /*.stream_stop =*/winmm_stream_stop, + /*.stream_get_position =*/winmm_stream_get_position, + /*.stream_get_latency = */ winmm_stream_get_latency, + /*.stream_get_input_latency = */ NULL, + /*.stream_set_volume =*/winmm_stream_set_volume, + /*.stream_set_name =*/NULL, + /*.stream_get_current_device =*/NULL, + /*.stream_device_destroy =*/NULL, + /*.stream_register_device_changed_callback=*/NULL, + /*.register_device_collection_changed =*/NULL}; diff --git a/externals/cubeb/subprojects/speex/arch.h b/externals/cubeb/subprojects/speex/arch.h new file mode 100755 index 000000000..73a45a069 --- /dev/null +++ b/externals/cubeb/subprojects/speex/arch.h @@ -0,0 +1,235 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** + @file arch.h + @brief Various architecture definitions Speex +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef ARCH_H +#define ARCH_H + +/* A couple test to catch stupid option combinations */ +#ifdef FIXED_POINT + +#ifdef FLOATING_POINT +#error You cannot compile as floating point and fixed point at the same time +#endif +#ifdef _USE_SSE +#error SSE is only for floating-point +#endif +#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM)) +#error Make up your mind. What CPU do you have? +#endif +#ifdef VORBIS_PSYCHO +#error Vorbis-psy model currently not implemented in fixed-point +#endif + +#else + +#ifndef FLOATING_POINT +#error You now need to define either FIXED_POINT or FLOATING_POINT +#endif +#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM) +#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions? +#endif +#ifdef FIXED_POINT_DEBUG +#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?" +#endif + + +#endif + +#ifndef OUTSIDE_SPEEX +#include "speex/speexdsp_types.h" +#endif + +#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */ +#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */ +#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */ +#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */ +#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */ +#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */ + +#ifdef FIXED_POINT + +typedef spx_int16_t spx_word16_t; +typedef spx_int32_t spx_word32_t; +typedef spx_word32_t spx_mem_t; +typedef spx_word16_t spx_coef_t; +typedef spx_word16_t spx_lsp_t; +typedef spx_word32_t spx_sig_t; + +#define Q15ONE 32767 + +#define LPC_SCALING 8192 +#define SIG_SCALING 16384 +#define LSP_SCALING 8192. +#define GAMMA_SCALING 32768. +#define GAIN_SCALING 64 +#define GAIN_SCALING_1 0.015625 + +#define LPC_SHIFT 13 +#define LSP_SHIFT 13 +#define SIG_SHIFT 14 +#define GAIN_SHIFT 6 + +#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x))) + +#define VERY_SMALL 0 +#define VERY_LARGE32 ((spx_word32_t)2147483647) +#define VERY_LARGE16 ((spx_word16_t)32767) +#define Q15_ONE ((spx_word16_t)32767) + + +#ifdef FIXED_DEBUG +#include "fixed_debug.h" +#else + +#include "fixed_generic.h" + +#ifdef ARM5E_ASM +#include "fixed_arm5e.h" +#elif defined (ARM4_ASM) +#include "fixed_arm4.h" +#elif defined (BFIN_ASM) +#include "fixed_bfin.h" +#endif + +#endif + + +#else + +typedef float spx_mem_t; +typedef float spx_coef_t; +typedef float spx_lsp_t; +typedef float spx_sig_t; +typedef float spx_word16_t; +typedef float spx_word32_t; + +#define Q15ONE 1.0f +#define LPC_SCALING 1.f +#define SIG_SCALING 1.f +#define LSP_SCALING 1.f +#define GAMMA_SCALING 1.f +#define GAIN_SCALING 1.f +#define GAIN_SCALING_1 1.f + + +#define VERY_SMALL 1e-15f +#define VERY_LARGE32 1e15f +#define VERY_LARGE16 1e15f +#define Q15_ONE ((spx_word16_t)1.f) + +#define QCONST16(x,bits) (x) +#define QCONST32(x,bits) (x) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) (x) +#define EXTEND32(x) (x) +#define SHR16(a,shift) (a) +#define SHL16(a,shift) (a) +#define SHR32(a,shift) (a) +#define SHL32(a,shift) (a) +#define PSHR16(a,shift) (a) +#define PSHR32(a,shift) (a) +#define VSHR32(a,shift) (a) +#define SATURATE16(x,a) (x) +#define SATURATE32(x,a) (x) +#define SATURATE32PSHR(x,shift,a) (x) + +#define PSHR(a,shift) (a) +#define SHR(a,shift) (a) +#define SHL(a,shift) (a) +#define SATURATE(x,a) (x) + +#define ADD16(a,b) ((a)+(b)) +#define SUB16(a,b) ((a)-(b)) +#define ADD32(a,b) ((a)+(b)) +#define SUB32(a,b) ((a)-(b)) +#define MULT16_16_16(a,b) ((a)*(b)) +#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b)) +#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b)) + +#define MULT16_32_Q11(a,b) ((a)*(b)) +#define MULT16_32_Q13(a,b) ((a)*(b)) +#define MULT16_32_Q14(a,b) ((a)*(b)) +#define MULT16_32_Q15(a,b) ((a)*(b)) +#define MULT16_32_P15(a,b) ((a)*(b)) + +#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b)) + +#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b)) +#define MAC16_16_P13(c,a,b) ((c)+(a)*(b)) +#define MULT16_16_Q11_32(a,b) ((a)*(b)) +#define MULT16_16_Q13(a,b) ((a)*(b)) +#define MULT16_16_Q14(a,b) ((a)*(b)) +#define MULT16_16_Q15(a,b) ((a)*(b)) +#define MULT16_16_P15(a,b) ((a)*(b)) +#define MULT16_16_P13(a,b) ((a)*(b)) +#define MULT16_16_P14(a,b) ((a)*(b)) + +#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b)) +#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) +#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b)) + +#define WORD2INT(x) ((x) < -32767.5f ? -32768 : \ + ((x) > 32766.5f ? 32767 : (spx_int16_t)floor(.5 + (x)))) +#endif + + +#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X) + +/* 2 on TI C5x DSP */ +#define BYTES_PER_CHAR 2 +#define BITS_PER_CHAR 16 +#define LOG2_BITS_PER_CHAR 4 + +#else + +#define BYTES_PER_CHAR 1 +#define BITS_PER_CHAR 8 +#define LOG2_BITS_PER_CHAR 3 + +#endif + + + +#ifdef FIXED_DEBUG +extern long long spx_mips; +#endif + + +#endif diff --git a/externals/cubeb/subprojects/speex/fixed_generic.h b/externals/cubeb/subprojects/speex/fixed_generic.h new file mode 100755 index 000000000..12d27aac5 --- /dev/null +++ b/externals/cubeb/subprojects/speex/fixed_generic.h @@ -0,0 +1,110 @@ +/* Copyright (C) 2003 Jean-Marc Valin */ +/** + @file fixed_generic.h + @brief Generic fixed-point operations +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef FIXED_GENERIC_H +#define FIXED_GENERIC_H + +#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) +#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits)))) + +#define NEG16(x) (-(x)) +#define NEG32(x) (-(x)) +#define EXTRACT16(x) ((spx_word16_t)(x)) +#define EXTEND32(x) ((spx_word32_t)(x)) +#define SHR16(a,shift) ((a) >> (shift)) +#define SHL16(a,shift) ((a) << (shift)) +#define SHR32(a,shift) ((a) >> (shift)) +#define SHL32(a,shift) ((a) << (shift)) +#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift)) +#define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift)) +#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift))) +#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) +#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + +#define SATURATE32PSHR(x,shift,a) (((x)>=(SHL32(a,shift))) ? (a) : \ + (x)<=-(SHL32(a,shift)) ? -(a) : \ + (PSHR32(x, shift))) + +#define SHR(a,shift) ((a) >> (shift)) +#define SHL(a,shift) ((spx_word32_t)(a) << (shift)) +#define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift)) +#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x))) + + +#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b))) +#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b)) +#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b)) +#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b)) + + +/* result fits in 16 bits */ +#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b)))) + +/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */ +#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b))) + +#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b)))) +#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12)) +#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13)) +#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14)) + +#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)) +#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))) + +#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15)) +#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)) +#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))) + + +#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11))) +#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13))) +#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13))) + +#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11)) +#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13)) +#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14)) +#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15)) + +#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13)) +#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14)) +#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15)) + +#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15)) + +#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b)))) +#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b)))) +#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b))) +#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b))) + +#endif diff --git a/externals/cubeb/subprojects/speex/resample.c b/externals/cubeb/subprojects/speex/resample.c new file mode 100755 index 000000000..10cb06593 --- /dev/null +++ b/externals/cubeb/subprojects/speex/resample.c @@ -0,0 +1,1240 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + Copyright (C) 2008 Thorvald Natvig + + File: resample.c + Arbitrary resampling code + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +/* + The design goals of this code are: + - Very fast algorithm + - SIMD-friendly algorithm + - Low memory requirement + - Good *perceptual* quality (and not best SNR) + + Warning: This resampler is relatively new. Although I think I got rid of + all the major bugs and I don't expect the API to change anymore, there + may be something I've missed. So use with caution. + + This algorithm is based on this original resampling algorithm: + Smith, Julius O. Digital Audio Resampling Home Page + Center for Computer Research in Music and Acoustics (CCRMA), + Stanford University, 2007. + Web published at http://ccrma.stanford.edu/~jos/resample/. + + There is one main difference, though. This resampler uses cubic + interpolation instead of linear interpolation in the above paper. This + makes the table much smaller and makes it possible to compute that table + on a per-stream basis. In turn, being able to tweak the table for each + stream makes it possible to both reduce complexity on simple ratios + (e.g. 2/3), and get rid of the rounding operations in the inner loop. + The latter both reduces CPU time and makes the algorithm more SIMD-friendly. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef OUTSIDE_SPEEX +#include +static void *speex_alloc (int size) {return calloc(size,1);} +static void *speex_realloc (void *ptr, int size) {return realloc(ptr, size);} +static void speex_free (void *ptr) {free(ptr);} +#include "speex_resampler.h" +#include "arch.h" +#else /* OUTSIDE_SPEEX */ + +#include "speex/speex_resampler.h" +#include "arch.h" +#include "os_support.h" +#endif /* OUTSIDE_SPEEX */ + +#include "stack_alloc.h" +#include +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#define IMAX(a,b) ((a) > (b) ? (a) : (b)) +#define IMIN(a,b) ((a) < (b) ? (a) : (b)) + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef UINT32_MAX +#define UINT32_MAX 4294967296U +#endif + +#ifdef _USE_SSE +#include "resample_sse.h" +#endif + +#ifdef _USE_NEON +#include "resample_neon.h" +#endif + +/* Numer of elements to allocate on the stack */ +#ifdef VAR_ARRAYS +#define FIXED_STACK_ALLOC 8192 +#else +#define FIXED_STACK_ALLOC 1024 +#endif + +typedef int (*resampler_basic_func)(SpeexResamplerState *, spx_uint32_t , const spx_word16_t *, spx_uint32_t *, spx_word16_t *, spx_uint32_t *); + +struct SpeexResamplerState_ { + spx_uint32_t in_rate; + spx_uint32_t out_rate; + spx_uint32_t num_rate; + spx_uint32_t den_rate; + + int quality; + spx_uint32_t nb_channels; + spx_uint32_t filt_len; + spx_uint32_t mem_alloc_size; + spx_uint32_t buffer_size; + int int_advance; + int frac_advance; + float cutoff; + spx_uint32_t oversample; + int initialised; + int started; + + /* These are per-channel */ + spx_int32_t *last_sample; + spx_uint32_t *samp_frac_num; + spx_uint32_t *magic_samples; + + spx_word16_t *mem; + spx_word16_t *sinc_table; + spx_uint32_t sinc_table_length; + resampler_basic_func resampler_ptr; + + int in_stride; + int out_stride; +} ; + +static const double kaiser12_table[68] = { + 0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076, + 0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014, + 0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601, + 0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014, + 0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490, + 0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546, + 0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178, + 0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947, + 0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058, + 0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438, + 0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734, + 0.00001000, 0.00000000}; +/* +static const double kaiser12_table[36] = { + 0.99440475, 1.00000000, 0.99440475, 0.97779076, 0.95066529, 0.91384741, + 0.86843014, 0.81573067, 0.75723148, 0.69451601, 0.62920216, 0.56287762, + 0.49704014, 0.43304576, 0.37206735, 0.31506490, 0.26276832, 0.21567274, + 0.17404546, 0.13794294, 0.10723616, 0.08164178, 0.06075685, 0.04409466, + 0.03111947, 0.02127838, 0.01402878, 0.00886058, 0.00531256, 0.00298291, + 0.00153438, 0.00069463, 0.00025272, 0.0000527734, 0.00000500, 0.00000000}; +*/ +static const double kaiser10_table[36] = { + 0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446, + 0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347, + 0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962, + 0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451, + 0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739, + 0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000}; + +static const double kaiser8_table[36] = { + 0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200, + 0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126, + 0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272, + 0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758, + 0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490, + 0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000}; + +static const double kaiser6_table[36] = { + 0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003, + 0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565, + 0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561, + 0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058, + 0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600, + 0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000}; + +struct FuncDef { + const double *table; + int oversample; +}; + +static const struct FuncDef _KAISER12 = {kaiser12_table, 64}; +#define KAISER12 (&_KAISER12) +/*static struct FuncDef _KAISER12 = {kaiser12_table, 32}; +#define KAISER12 (&_KAISER12)*/ +static const struct FuncDef _KAISER10 = {kaiser10_table, 32}; +#define KAISER10 (&_KAISER10) +static const struct FuncDef _KAISER8 = {kaiser8_table, 32}; +#define KAISER8 (&_KAISER8) +static const struct FuncDef _KAISER6 = {kaiser6_table, 32}; +#define KAISER6 (&_KAISER6) + +struct QualityMapping { + int base_length; + int oversample; + float downsample_bandwidth; + float upsample_bandwidth; + const struct FuncDef *window_func; +}; + + +/* This table maps conversion quality to internal parameters. There are two + reasons that explain why the up-sampling bandwidth is larger than the + down-sampling bandwidth: + 1) When up-sampling, we can assume that the spectrum is already attenuated + close to the Nyquist rate (from an A/D or a previous resampling filter) + 2) Any aliasing that occurs very close to the Nyquist rate will be masked + by the sinusoids/noise just below the Nyquist rate (guaranteed only for + up-sampling). +*/ +static const struct QualityMapping quality_map[11] = { + { 8, 4, 0.830f, 0.860f, KAISER6 }, /* Q0 */ + { 16, 4, 0.850f, 0.880f, KAISER6 }, /* Q1 */ + { 32, 4, 0.882f, 0.910f, KAISER6 }, /* Q2 */ /* 82.3% cutoff ( ~60 dB stop) 6 */ + { 48, 8, 0.895f, 0.917f, KAISER8 }, /* Q3 */ /* 84.9% cutoff ( ~80 dB stop) 8 */ + { 64, 8, 0.921f, 0.940f, KAISER8 }, /* Q4 */ /* 88.7% cutoff ( ~80 dB stop) 8 */ + { 80, 16, 0.922f, 0.940f, KAISER10}, /* Q5 */ /* 89.1% cutoff (~100 dB stop) 10 */ + { 96, 16, 0.940f, 0.945f, KAISER10}, /* Q6 */ /* 91.5% cutoff (~100 dB stop) 10 */ + {128, 16, 0.950f, 0.950f, KAISER10}, /* Q7 */ /* 93.1% cutoff (~100 dB stop) 10 */ + {160, 16, 0.960f, 0.960f, KAISER10}, /* Q8 */ /* 94.5% cutoff (~100 dB stop) 10 */ + {192, 32, 0.968f, 0.968f, KAISER12}, /* Q9 */ /* 95.5% cutoff (~100 dB stop) 10 */ + {256, 32, 0.975f, 0.975f, KAISER12}, /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */ +}; +/*8,24,40,56,80,104,128,160,200,256,320*/ +static double compute_func(float x, const struct FuncDef *func) +{ + float y, frac; + double interp[4]; + int ind; + y = x*func->oversample; + ind = (int)floor(y); + frac = (y-ind); + /* CSE with handle the repeated powers */ + interp[3] = -0.1666666667*frac + 0.1666666667*(frac*frac*frac); + interp[2] = frac + 0.5*(frac*frac) - 0.5*(frac*frac*frac); + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[0] = -0.3333333333*frac + 0.5*(frac*frac) - 0.1666666667*(frac*frac*frac); + /* Just to make sure we don't have rounding problems */ + interp[1] = 1.f-interp[3]-interp[2]-interp[0]; + + /*sum = frac*accum[1] + (1-frac)*accum[2];*/ + return interp[0]*func->table[ind] + interp[1]*func->table[ind+1] + interp[2]*func->table[ind+2] + interp[3]*func->table[ind+3]; +} + +#if 0 +#include +int main(int argc, char **argv) +{ + int i; + for (i=0;i<256;i++) + { + printf ("%f\n", compute_func(i/256., KAISER12)); + } + return 0; +} +#endif + +#ifdef FIXED_POINT +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6f) + return WORD2INT(32768.*cutoff); + else if (fabs(x) > .5f*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return WORD2INT(32768.*cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func)); +} +#else +/* The slow way of computing a sinc for the table. Should improve that some day */ +static spx_word16_t sinc(float cutoff, float x, int N, const struct FuncDef *window_func) +{ + /*fprintf (stderr, "%f ", x);*/ + float xx = x * cutoff; + if (fabs(x)<1e-6) + return cutoff; + else if (fabs(x) > .5*N) + return 0; + /*FIXME: Can it really be any slower than this? */ + return cutoff*sin(M_PI*xx)/(M_PI*xx) * compute_func(fabs(2.*x/N), window_func); +} +#endif + +#ifdef FIXED_POINT +static void cubic_coef(spx_word16_t x, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + spx_word16_t x2, x3; + x2 = MULT16_16_P15(x, x); + x3 = MULT16_16_P15(x, x2); + interp[0] = PSHR32(MULT16_16(QCONST16(-0.16667f, 15),x) + MULT16_16(QCONST16(0.16667f, 15),x3),15); + interp[1] = EXTRACT16(EXTEND32(x) + SHR32(SUB32(EXTEND32(x2),EXTEND32(x3)),1)); + interp[3] = PSHR32(MULT16_16(QCONST16(-0.33333f, 15),x) + MULT16_16(QCONST16(.5f,15),x2) - MULT16_16(QCONST16(0.16667f, 15),x3),15); + /* Just to make sure we don't have rounding problems */ + interp[2] = Q15_ONE-interp[0]-interp[1]-interp[3]; + if (interp[2]<32767) + interp[2]+=1; +} +#else +static void cubic_coef(spx_word16_t frac, spx_word16_t interp[4]) +{ + /* Compute interpolation coefficients. I'm not sure whether this corresponds to cubic interpolation + but I know it's MMSE-optimal on a sinc */ + interp[0] = -0.16667f*frac + 0.16667f*frac*frac*frac; + interp[1] = frac + 0.5f*frac*frac - 0.5f*frac*frac*frac; + /*interp[2] = 1.f - 0.5f*frac - frac*frac + 0.5f*frac*frac*frac;*/ + interp[3] = -0.33333f*frac + 0.5f*frac*frac - 0.16667f*frac*frac*frac; + /* Just to make sure we don't have rounding problems */ + interp[2] = 1.-interp[0]-interp[1]-interp[3]; +} +#endif + +static int resampler_basic_direct_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinct = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_SINGLE + int j; + sum = 0; + for(j=0;j= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_direct_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const spx_word16_t *sinc_table = st->sinc_table; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + double sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *sinct = & sinc_table[samp_frac_num*N]; + const spx_word16_t *iptr = & in[last_sample]; + +#ifndef OVERRIDE_INNER_PRODUCT_DOUBLE + int j; + double accum[4] = {0,0,0,0}; + + for(j=0;j= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +static int resampler_basic_interpolate_single(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_SINGLE + int j; + spx_word32_t accum[4] = {0,0,0,0}; + + for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],SHR32(accum[0], 1)) + MULT16_32_Q15(interp[1],SHR32(accum[1], 1)) + MULT16_32_Q15(interp[2],SHR32(accum[2], 1)) + MULT16_32_Q15(interp[3],SHR32(accum[3], 1)); + sum = SATURATE32PSHR(sum, 15, 32767); +#else + cubic_coef(frac, interp); + sum = interpolate_product_single(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = sum; + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +#ifdef FIXED_POINT +#else +/* This is the same as the previous function, except with a double-precision accumulator */ +static int resampler_basic_interpolate_double(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + const int N = st->filt_len; + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + spx_word32_t sum; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + const spx_word16_t *iptr = & in[last_sample]; + + const int offset = samp_frac_num*st->oversample/st->den_rate; +#ifdef FIXED_POINT + const spx_word16_t frac = PDIV32(SHL32((samp_frac_num*st->oversample) % st->den_rate,15),st->den_rate); +#else + const spx_word16_t frac = ((float)((samp_frac_num*st->oversample) % st->den_rate))/st->den_rate; +#endif + spx_word16_t interp[4]; + + +#ifndef OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE + int j; + double accum[4] = {0,0,0,0}; + + for(j=0;jsinc_table[4+(j+1)*st->oversample-offset-2]); + accum[1] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset-1]); + accum[2] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset]); + accum[3] += MULT16_16(curr_in,st->sinc_table[4+(j+1)*st->oversample-offset+1]); + } + + cubic_coef(frac, interp); + sum = MULT16_32_Q15(interp[0],accum[0]) + MULT16_32_Q15(interp[1],accum[1]) + MULT16_32_Q15(interp[2],accum[2]) + MULT16_32_Q15(interp[3],accum[3]); +#else + cubic_coef(frac, interp); + sum = interpolate_product_double(iptr, st->sinc_table + st->oversample + 4 - offset - 2, N, st->oversample, interp); +#endif + + out[out_stride * out_sample++] = PSHR32(sum,15); + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} +#endif + +/* This resampler is used to produce zero output in situations where memory + for the filter could not be allocated. The expected numbers of input and + output samples are still processed so that callers failing to check error + codes are not surprised, possibly getting into infinite loops. */ +static int resampler_basic_zero(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_word16_t *in, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + int out_sample = 0; + int last_sample = st->last_sample[channel_index]; + spx_uint32_t samp_frac_num = st->samp_frac_num[channel_index]; + const int out_stride = st->out_stride; + const int int_advance = st->int_advance; + const int frac_advance = st->frac_advance; + const spx_uint32_t den_rate = st->den_rate; + + while (!(last_sample >= (spx_int32_t)*in_len || out_sample >= (spx_int32_t)*out_len)) + { + out[out_stride * out_sample++] = 0; + last_sample += int_advance; + samp_frac_num += frac_advance; + if (samp_frac_num >= den_rate) + { + samp_frac_num -= den_rate; + last_sample++; + } + } + + st->last_sample[channel_index] = last_sample; + st->samp_frac_num[channel_index] = samp_frac_num; + return out_sample; +} + +static int _muldiv(spx_uint32_t *result, spx_uint32_t value, spx_uint32_t mul, spx_uint32_t div) +{ + speex_assert(result); + spx_uint32_t major = value / div; + spx_uint32_t remainder = value % div; + /* TODO: Could use 64 bits operation to check for overflow. But only guaranteed in C99+ */ + if (remainder > UINT32_MAX / mul || major > UINT32_MAX / mul + || major * mul > UINT32_MAX - remainder * mul / div) + return RESAMPLER_ERR_OVERFLOW; + *result = remainder * mul / div + major * mul; + return RESAMPLER_ERR_SUCCESS; +} + +static int update_filter(SpeexResamplerState *st) +{ + spx_uint32_t old_length = st->filt_len; + spx_uint32_t old_alloc_size = st->mem_alloc_size; + int use_direct; + spx_uint32_t min_sinc_table_length; + spx_uint32_t min_alloc_size; + + st->int_advance = st->num_rate/st->den_rate; + st->frac_advance = st->num_rate%st->den_rate; + st->oversample = quality_map[st->quality].oversample; + st->filt_len = quality_map[st->quality].base_length; + + if (st->num_rate > st->den_rate) + { + /* down-sampling */ + st->cutoff = quality_map[st->quality].downsample_bandwidth * st->den_rate / st->num_rate; + if (_muldiv(&st->filt_len,st->filt_len,st->num_rate,st->den_rate) != RESAMPLER_ERR_SUCCESS) + goto fail; + /* Round up to make sure we have a multiple of 8 for SSE */ + st->filt_len = ((st->filt_len-1)&(~0x7))+8; + if (2*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (4*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (8*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (16*st->den_rate < st->num_rate) + st->oversample >>= 1; + if (st->oversample < 1) + st->oversample = 1; + } else { + /* up-sampling */ + st->cutoff = quality_map[st->quality].upsample_bandwidth; + } + + /* Choose the resampling type that requires the least amount of memory */ +#ifdef RESAMPLE_FULL_SINC_TABLE + use_direct = 1; + if (INT_MAX/sizeof(spx_word16_t)/st->den_rate < st->filt_len) + goto fail; +#else + use_direct = st->filt_len*st->den_rate <= st->filt_len*st->oversample+8 + && INT_MAX/sizeof(spx_word16_t)/st->den_rate >= st->filt_len; +#endif + if (use_direct) + { + min_sinc_table_length = st->filt_len*st->den_rate; + } else { + if ((INT_MAX/sizeof(spx_word16_t)-8)/st->oversample < st->filt_len) + goto fail; + + min_sinc_table_length = st->filt_len*st->oversample+8; + } + if (st->sinc_table_length < min_sinc_table_length) + { + spx_word16_t *sinc_table = (spx_word16_t *)speex_realloc(st->sinc_table,min_sinc_table_length*sizeof(spx_word16_t)); + if (!sinc_table) + goto fail; + + st->sinc_table = sinc_table; + st->sinc_table_length = min_sinc_table_length; + } + if (use_direct) + { + spx_uint32_t i; + for (i=0;iden_rate;i++) + { + spx_int32_t j; + for (j=0;jfilt_len;j++) + { + st->sinc_table[i*st->filt_len+j] = sinc(st->cutoff,((j-(spx_int32_t)st->filt_len/2+1)-((float)i)/st->den_rate), st->filt_len, quality_map[st->quality].window_func); + } + } +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_direct_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_direct_double; + else + st->resampler_ptr = resampler_basic_direct_single; +#endif + /*fprintf (stderr, "resampler uses direct sinc table and normalised cutoff %f\n", cutoff);*/ + } else { + spx_int32_t i; + for (i=-4;i<(spx_int32_t)(st->oversample*st->filt_len+4);i++) + st->sinc_table[i+4] = sinc(st->cutoff,(i/(float)st->oversample - st->filt_len/2), st->filt_len, quality_map[st->quality].window_func); +#ifdef FIXED_POINT + st->resampler_ptr = resampler_basic_interpolate_single; +#else + if (st->quality>8) + st->resampler_ptr = resampler_basic_interpolate_double; + else + st->resampler_ptr = resampler_basic_interpolate_single; +#endif + /*fprintf (stderr, "resampler uses interpolated sinc table and normalised cutoff %f\n", cutoff);*/ + } + + /* Here's the place where we update the filter memory to take into account + the change in filter length. It's probably the messiest part of the code + due to handling of lots of corner cases. */ + + /* Adding buffer_size to filt_len won't overflow here because filt_len + could be multiplied by sizeof(spx_word16_t) above. */ + min_alloc_size = st->filt_len-1 + st->buffer_size; + if (min_alloc_size > st->mem_alloc_size) + { + spx_word16_t *mem; + if (INT_MAX/sizeof(spx_word16_t)/st->nb_channels < min_alloc_size) + goto fail; + else if (!(mem = (spx_word16_t*)speex_realloc(st->mem, st->nb_channels*min_alloc_size * sizeof(*mem)))) + goto fail; + + st->mem = mem; + st->mem_alloc_size = min_alloc_size; + } + if (!st->started) + { + spx_uint32_t i; + for (i=0;inb_channels*st->mem_alloc_size;i++) + st->mem[i] = 0; + /*speex_warning("reinit filter");*/ + } else if (st->filt_len > old_length) + { + spx_uint32_t i; + /* Increase the filter length */ + /*speex_warning("increase filter size");*/ + for (i=st->nb_channels;i--;) + { + spx_uint32_t j; + spx_uint32_t olen = old_length; + /*if (st->magic_samples[i])*/ + { + /* Try and remove the magic samples as if nothing had happened */ + + /* FIXME: This is wrong but for now we need it to avoid going over the array bounds */ + olen = old_length + 2*st->magic_samples[i]; + for (j=old_length-1+st->magic_samples[i];j--;) + st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]] = st->mem[i*old_alloc_size+j]; + for (j=0;jmagic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = 0; + st->magic_samples[i] = 0; + } + if (st->filt_len > olen) + { + /* If the new filter length is still bigger than the "augmented" length */ + /* Copy data going backward */ + for (j=0;jmem[i*st->mem_alloc_size+(st->filt_len-2-j)] = st->mem[i*st->mem_alloc_size+(olen-2-j)]; + /* Then put zeros for lack of anything better */ + for (;jfilt_len-1;j++) + st->mem[i*st->mem_alloc_size+(st->filt_len-2-j)] = 0; + /* Adjust last_sample */ + st->last_sample[i] += (st->filt_len - olen)/2; + } else { + /* Put back some of the magic! */ + st->magic_samples[i] = (olen - st->filt_len)/2; + for (j=0;jfilt_len-1+st->magic_samples[i];j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + } + } + } else if (st->filt_len < old_length) + { + spx_uint32_t i; + /* Reduce filter length, this a bit tricky. We need to store some of the memory as "magic" + samples so they can be used directly as input the next time(s) */ + for (i=0;inb_channels;i++) + { + spx_uint32_t j; + spx_uint32_t old_magic = st->magic_samples[i]; + st->magic_samples[i] = (old_length - st->filt_len)/2; + /* We must copy some of the memory that's no longer used */ + /* Copy data going backward */ + for (j=0;jfilt_len-1+st->magic_samples[i]+old_magic;j++) + st->mem[i*st->mem_alloc_size+j] = st->mem[i*st->mem_alloc_size+j+st->magic_samples[i]]; + st->magic_samples[i] += old_magic; + } + } + return RESAMPLER_ERR_SUCCESS; + +fail: + st->resampler_ptr = resampler_basic_zero; + /* st->mem may still contain consumed input samples for the filter. + Restore filt_len so that filt_len - 1 still points to the position after + the last of these samples. */ + st->filt_len = old_length; + return RESAMPLER_ERR_ALLOC_FAILED; +} + +EXPORT SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + return speex_resampler_init_frac(nb_channels, in_rate, out_rate, in_rate, out_rate, quality, err); +} + +EXPORT SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate, int quality, int *err) +{ + SpeexResamplerState *st; + int filter_err; + + if (nb_channels == 0 || ratio_num == 0 || ratio_den == 0 || quality > 10 || quality < 0) + { + if (err) + *err = RESAMPLER_ERR_INVALID_ARG; + return NULL; + } + st = (SpeexResamplerState *)speex_alloc(sizeof(SpeexResamplerState)); + if (!st) + { + if (err) + *err = RESAMPLER_ERR_ALLOC_FAILED; + return NULL; + } + st->initialised = 0; + st->started = 0; + st->in_rate = 0; + st->out_rate = 0; + st->num_rate = 0; + st->den_rate = 0; + st->quality = -1; + st->sinc_table_length = 0; + st->mem_alloc_size = 0; + st->filt_len = 0; + st->mem = 0; + st->resampler_ptr = 0; + + st->cutoff = 1.f; + st->nb_channels = nb_channels; + st->in_stride = 1; + st->out_stride = 1; + + st->buffer_size = 160; + + /* Per channel data */ + if (!(st->last_sample = (spx_int32_t*)speex_alloc(nb_channels*sizeof(spx_int32_t)))) + goto fail; + if (!(st->magic_samples = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)))) + goto fail; + if (!(st->samp_frac_num = (spx_uint32_t*)speex_alloc(nb_channels*sizeof(spx_uint32_t)))) + goto fail; + + speex_resampler_set_quality(st, quality); + speex_resampler_set_rate_frac(st, ratio_num, ratio_den, in_rate, out_rate); + + filter_err = update_filter(st); + if (filter_err == RESAMPLER_ERR_SUCCESS) + { + st->initialised = 1; + } else { + speex_resampler_destroy(st); + st = NULL; + } + if (err) + *err = filter_err; + + return st; + +fail: + if (err) + *err = RESAMPLER_ERR_ALLOC_FAILED; + speex_resampler_destroy(st); + return NULL; +} + +EXPORT void speex_resampler_destroy(SpeexResamplerState *st) +{ + speex_free(st->mem); + speex_free(st->sinc_table); + speex_free(st->last_sample); + speex_free(st->magic_samples); + speex_free(st->samp_frac_num); + speex_free(st); +} + +static int speex_resampler_process_native(SpeexResamplerState *st, spx_uint32_t channel_index, spx_uint32_t *in_len, spx_word16_t *out, spx_uint32_t *out_len) +{ + int j=0; + const int N = st->filt_len; + int out_sample = 0; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + spx_uint32_t ilen; + + st->started = 1; + + /* Call the right resampler through the function ptr */ + out_sample = st->resampler_ptr(st, channel_index, mem, in_len, out, out_len); + + if (st->last_sample[channel_index] < (spx_int32_t)*in_len) + *in_len = st->last_sample[channel_index]; + *out_len = out_sample; + st->last_sample[channel_index] -= *in_len; + + ilen = *in_len; + + for(j=0;jmagic_samples[channel_index]; + spx_word16_t *mem = st->mem + channel_index * st->mem_alloc_size; + const int N = st->filt_len; + + speex_resampler_process_native(st, channel_index, &tmp_in_len, *out, &out_len); + + st->magic_samples[channel_index] -= tmp_in_len; + + /* If we couldn't process all "magic" input samples, save the rest for next time */ + if (st->magic_samples[channel_index]) + { + spx_uint32_t i; + for (i=0;imagic_samples[channel_index];i++) + mem[N-1+i]=mem[N-1+i+tmp_in_len]; + } + *out += out_len*st->out_stride; + return out_len; +} + +#ifdef FIXED_POINT +EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +#else +EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +#endif +{ + int j; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const int filt_offs = st->filt_len - 1; + const spx_uint32_t xlen = st->mem_alloc_size - filt_offs; + const int istride = st->in_stride; + + if (st->magic_samples[channel_index]) + olen -= speex_resampler_magic(st, channel_index, &out, olen); + if (! st->magic_samples[channel_index]) { + while (ilen && olen) { + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = olen; + + if (in) { + for(j=0;jout_stride; + if (in) + in += ichunk * istride; + } + } + *in_len -= ilen; + *out_len -= olen; + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +#ifdef FIXED_POINT +EXPORT int speex_resampler_process_float(SpeexResamplerState *st, spx_uint32_t channel_index, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +#else +EXPORT int speex_resampler_process_int(SpeexResamplerState *st, spx_uint32_t channel_index, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +#endif +{ + int j; + const int istride_save = st->in_stride; + const int ostride_save = st->out_stride; + spx_uint32_t ilen = *in_len; + spx_uint32_t olen = *out_len; + spx_word16_t *x = st->mem + channel_index * st->mem_alloc_size; + const spx_uint32_t xlen = st->mem_alloc_size - (st->filt_len - 1); +#ifdef VAR_ARRAYS + const unsigned int ylen = (olen < FIXED_STACK_ALLOC) ? olen : FIXED_STACK_ALLOC; + VARDECL(spx_word16_t *ystack); + ALLOC(ystack, ylen, spx_word16_t); +#else + const unsigned int ylen = FIXED_STACK_ALLOC; + spx_word16_t ystack[FIXED_STACK_ALLOC]; +#endif + + st->out_stride = 1; + + while (ilen && olen) { + spx_word16_t *y = ystack; + spx_uint32_t ichunk = (ilen > xlen) ? xlen : ilen; + spx_uint32_t ochunk = (olen > ylen) ? ylen : olen; + spx_uint32_t omagic = 0; + + if (st->magic_samples[channel_index]) { + omagic = speex_resampler_magic(st, channel_index, &y, ochunk); + ochunk -= omagic; + olen -= omagic; + } + if (! st->magic_samples[channel_index]) { + if (in) { + for(j=0;jfilt_len-1]=WORD2INT(in[j*istride_save]); +#else + x[j+st->filt_len-1]=in[j*istride_save]; +#endif + } else { + for(j=0;jfilt_len-1]=0; + } + + speex_resampler_process_native(st, channel_index, &ichunk, y, &ochunk); + } else { + ichunk = 0; + ochunk = 0; + } + + for (j=0;jout_stride = ostride_save; + *in_len -= ilen; + *out_len -= olen; + + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_process_interleaved_float(SpeexResamplerState *st, const float *in, spx_uint32_t *in_len, float *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_out_len = *out_len; + spx_uint32_t bak_in_len = *in_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;inb_channels;i++) + { + *out_len = bak_out_len; + *in_len = bak_in_len; + if (in != NULL) + speex_resampler_process_float(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_float(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_process_interleaved_int(SpeexResamplerState *st, const spx_int16_t *in, spx_uint32_t *in_len, spx_int16_t *out, spx_uint32_t *out_len) +{ + spx_uint32_t i; + int istride_save, ostride_save; + spx_uint32_t bak_out_len = *out_len; + spx_uint32_t bak_in_len = *in_len; + istride_save = st->in_stride; + ostride_save = st->out_stride; + st->in_stride = st->out_stride = st->nb_channels; + for (i=0;inb_channels;i++) + { + *out_len = bak_out_len; + *in_len = bak_in_len; + if (in != NULL) + speex_resampler_process_int(st, i, in+i, in_len, out+i, out_len); + else + speex_resampler_process_int(st, i, NULL, in_len, out+i, out_len); + } + st->in_stride = istride_save; + st->out_stride = ostride_save; + return st->resampler_ptr == resampler_basic_zero ? RESAMPLER_ERR_ALLOC_FAILED : RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_set_rate(SpeexResamplerState *st, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + return speex_resampler_set_rate_frac(st, in_rate, out_rate, in_rate, out_rate); +} + +EXPORT void speex_resampler_get_rate(SpeexResamplerState *st, spx_uint32_t *in_rate, spx_uint32_t *out_rate) +{ + *in_rate = st->in_rate; + *out_rate = st->out_rate; +} + +static inline spx_uint32_t _gcd(spx_uint32_t a, spx_uint32_t b) +{ + while (b != 0) + { + spx_uint32_t temp = a; + + a = b; + b = temp % b; + } + return a; +} + +EXPORT int speex_resampler_set_rate_frac(SpeexResamplerState *st, spx_uint32_t ratio_num, spx_uint32_t ratio_den, spx_uint32_t in_rate, spx_uint32_t out_rate) +{ + spx_uint32_t fact; + spx_uint32_t old_den; + spx_uint32_t i; + + if (ratio_num == 0 || ratio_den == 0) + return RESAMPLER_ERR_INVALID_ARG; + + if (st->in_rate == in_rate && st->out_rate == out_rate && st->num_rate == ratio_num && st->den_rate == ratio_den) + return RESAMPLER_ERR_SUCCESS; + + old_den = st->den_rate; + st->in_rate = in_rate; + st->out_rate = out_rate; + st->num_rate = ratio_num; + st->den_rate = ratio_den; + + fact = _gcd (st->num_rate, st->den_rate); + + st->num_rate /= fact; + st->den_rate /= fact; + + if (old_den > 0) + { + for (i=0;inb_channels;i++) + { + if (_muldiv(&st->samp_frac_num[i],st->samp_frac_num[i],st->den_rate,old_den) != RESAMPLER_ERR_SUCCESS) + return RESAMPLER_ERR_OVERFLOW; + /* Safety net */ + if (st->samp_frac_num[i] >= st->den_rate) + st->samp_frac_num[i] = st->den_rate-1; + } + } + + if (st->initialised) + return update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT void speex_resampler_get_ratio(SpeexResamplerState *st, spx_uint32_t *ratio_num, spx_uint32_t *ratio_den) +{ + *ratio_num = st->num_rate; + *ratio_den = st->den_rate; +} + +EXPORT int speex_resampler_set_quality(SpeexResamplerState *st, int quality) +{ + if (quality > 10 || quality < 0) + return RESAMPLER_ERR_INVALID_ARG; + if (st->quality == quality) + return RESAMPLER_ERR_SUCCESS; + st->quality = quality; + if (st->initialised) + return update_filter(st); + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT void speex_resampler_get_quality(SpeexResamplerState *st, int *quality) +{ + *quality = st->quality; +} + +EXPORT void speex_resampler_set_input_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->in_stride = stride; +} + +EXPORT void speex_resampler_get_input_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->in_stride; +} + +EXPORT void speex_resampler_set_output_stride(SpeexResamplerState *st, spx_uint32_t stride) +{ + st->out_stride = stride; +} + +EXPORT void speex_resampler_get_output_stride(SpeexResamplerState *st, spx_uint32_t *stride) +{ + *stride = st->out_stride; +} + +EXPORT int speex_resampler_get_input_latency(SpeexResamplerState *st) +{ + return st->filt_len / 2; +} + +EXPORT int speex_resampler_get_output_latency(SpeexResamplerState *st) +{ + return ((st->filt_len / 2) * st->den_rate + (st->num_rate >> 1)) / st->num_rate; +} + +EXPORT int speex_resampler_skip_zeros(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;inb_channels;i++) + st->last_sample[i] = st->filt_len/2; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT int speex_resampler_reset_mem(SpeexResamplerState *st) +{ + spx_uint32_t i; + for (i=0;inb_channels;i++) + { + st->last_sample[i] = 0; + st->magic_samples[i] = 0; + st->samp_frac_num[i] = 0; + } + for (i=0;inb_channels*(st->filt_len-1);i++) + st->mem[i] = 0; + return RESAMPLER_ERR_SUCCESS; +} + +EXPORT const char *speex_resampler_strerror(int err) +{ + switch (err) + { + case RESAMPLER_ERR_SUCCESS: + return "Success."; + case RESAMPLER_ERR_ALLOC_FAILED: + return "Memory allocation failed."; + case RESAMPLER_ERR_BAD_STATE: + return "Bad resampler state."; + case RESAMPLER_ERR_INVALID_ARG: + return "Invalid argument."; + case RESAMPLER_ERR_PTR_OVERLAP: + return "Input and output buffers overlap."; + default: + return "Unknown error. Bad error code or strange version mismatch."; + } +} diff --git a/externals/cubeb/subprojects/speex/resample_neon.h b/externals/cubeb/subprojects/speex/resample_neon.h new file mode 100755 index 000000000..0acbd27b9 --- /dev/null +++ b/externals/cubeb/subprojects/speex/resample_neon.h @@ -0,0 +1,201 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + * Copyright (C) 2008 Thorvald Natvig + * Copyright (C) 2011 Texas Instruments + * author Jyri Sarha + */ +/** + @file resample_neon.h + @brief Resampler functions (NEON version) +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#ifdef FIXED_POINT +#ifdef __thumb2__ +static inline int32_t saturate_32bit_to_16bit(int32_t a) { + int32_t ret; + asm ("ssat %[ret], #16, %[a]" + : [ret] "=&r" (ret) + : [a] "r" (a) + : ); + return ret; +} +#else +static inline int32_t saturate_32bit_to_16bit(int32_t a) { + int32_t ret; + asm ("vmov.s32 d0[0], %[a]\n" + "vqmovn.s32 d0, q0\n" + "vmov.s16 %[ret], d0[0]\n" + : [ret] "=&r" (ret) + : [a] "r" (a) + : "q0"); + return ret; +} +#endif +#undef WORD2INT +#define WORD2INT(x) (saturate_32bit_to_16bit(x)) + +#define OVERRIDE_INNER_PRODUCT_SINGLE +/* Only works when len % 4 == 0 */ +static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len) +{ + int32_t ret; + uint32_t remainder = len % 16; + len = len - remainder; + + asm volatile (" cmp %[len], #0\n" + " bne 1f\n" + " vld1.16 {d16}, [%[b]]!\n" + " vld1.16 {d20}, [%[a]]!\n" + " subs %[remainder], %[remainder], #4\n" + " vmull.s16 q0, d16, d20\n" + " beq 5f\n" + " b 4f\n" + "1:" + " vld1.16 {d16, d17, d18, d19}, [%[b]]!\n" + " vld1.16 {d20, d21, d22, d23}, [%[a]]!\n" + " subs %[len], %[len], #16\n" + " vmull.s16 q0, d16, d20\n" + " vmlal.s16 q0, d17, d21\n" + " vmlal.s16 q0, d18, d22\n" + " vmlal.s16 q0, d19, d23\n" + " beq 3f\n" + "2:" + " vld1.16 {d16, d17, d18, d19}, [%[b]]!\n" + " vld1.16 {d20, d21, d22, d23}, [%[a]]!\n" + " subs %[len], %[len], #16\n" + " vmlal.s16 q0, d16, d20\n" + " vmlal.s16 q0, d17, d21\n" + " vmlal.s16 q0, d18, d22\n" + " vmlal.s16 q0, d19, d23\n" + " bne 2b\n" + "3:" + " cmp %[remainder], #0\n" + " beq 5f\n" + "4:" + " vld1.16 {d16}, [%[b]]!\n" + " vld1.16 {d20}, [%[a]]!\n" + " subs %[remainder], %[remainder], #4\n" + " vmlal.s16 q0, d16, d20\n" + " bne 4b\n" + "5:" + " vaddl.s32 q0, d0, d1\n" + " vadd.s64 d0, d0, d1\n" + " vqmovn.s64 d0, q0\n" + " vqrshrn.s32 d0, q0, #15\n" + " vmov.s16 %[ret], d0[0]\n" + : [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b), + [len] "+r" (len), [remainder] "+r" (remainder) + : + : "cc", "q0", + "d16", "d17", "d18", "d19", + "d20", "d21", "d22", "d23"); + + return ret; +} +#elif defined(FLOATING_POINT) + +static inline int32_t saturate_float_to_16bit(float a) { + int32_t ret; + asm ("vmov.f32 d0[0], %[a]\n" + "vcvt.s32.f32 d0, d0, #15\n" + "vqrshrn.s32 d0, q0, #15\n" + "vmov.s16 %[ret], d0[0]\n" + : [ret] "=&r" (ret) + : [a] "r" (a) + : "q0"); + return ret; +} +#undef WORD2INT +#define WORD2INT(x) (saturate_float_to_16bit(x)) + +#define OVERRIDE_INNER_PRODUCT_SINGLE +/* Only works when len % 4 == 0 */ +static inline float inner_product_single(const float *a, const float *b, unsigned int len) +{ + float ret; + uint32_t remainder = len % 16; + len = len - remainder; + + asm volatile (" cmp %[len], #0\n" + " bne 1f\n" + " vld1.32 {q4}, [%[b]]!\n" + " vld1.32 {q8}, [%[a]]!\n" + " subs %[remainder], %[remainder], #4\n" + " vmul.f32 q0, q4, q8\n" + " bne 4f\n" + " b 5f\n" + "1:" + " vld1.32 {q4, q5}, [%[b]]!\n" + " vld1.32 {q8, q9}, [%[a]]!\n" + " vld1.32 {q6, q7}, [%[b]]!\n" + " vld1.32 {q10, q11}, [%[a]]!\n" + " subs %[len], %[len], #16\n" + " vmul.f32 q0, q4, q8\n" + " vmul.f32 q1, q5, q9\n" + " vmul.f32 q2, q6, q10\n" + " vmul.f32 q3, q7, q11\n" + " beq 3f\n" + "2:" + " vld1.32 {q4, q5}, [%[b]]!\n" + " vld1.32 {q8, q9}, [%[a]]!\n" + " vld1.32 {q6, q7}, [%[b]]!\n" + " vld1.32 {q10, q11}, [%[a]]!\n" + " subs %[len], %[len], #16\n" + " vmla.f32 q0, q4, q8\n" + " vmla.f32 q1, q5, q9\n" + " vmla.f32 q2, q6, q10\n" + " vmla.f32 q3, q7, q11\n" + " bne 2b\n" + "3:" + " vadd.f32 q4, q0, q1\n" + " vadd.f32 q5, q2, q3\n" + " cmp %[remainder], #0\n" + " vadd.f32 q0, q4, q5\n" + " beq 5f\n" + "4:" + " vld1.32 {q6}, [%[b]]!\n" + " vld1.32 {q10}, [%[a]]!\n" + " subs %[remainder], %[remainder], #4\n" + " vmla.f32 q0, q6, q10\n" + " bne 4b\n" + "5:" + " vadd.f32 d0, d0, d1\n" + " vpadd.f32 d0, d0, d0\n" + " vmov.f32 %[ret], d0[0]\n" + : [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b), + [len] "+l" (len), [remainder] "+l" (remainder) + : + : "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8", + "q9", "q10", "q11"); + return ret; +} +#endif diff --git a/externals/cubeb/subprojects/speex/resample_sse.h b/externals/cubeb/subprojects/speex/resample_sse.h new file mode 100755 index 000000000..fed5b8276 --- /dev/null +++ b/externals/cubeb/subprojects/speex/resample_sse.h @@ -0,0 +1,128 @@ +/* Copyright (C) 2007-2008 Jean-Marc Valin + * Copyright (C) 2008 Thorvald Natvig + */ +/** + @file resample_sse.h + @brief Resampler functions (SSE version) +*/ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Xiph.org Foundation nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include + +#define OVERRIDE_INNER_PRODUCT_SINGLE +static inline float inner_product_single(const float *a, const float *b, unsigned int len) +{ + int i; + float ret; + __m128 sum = _mm_setzero_ps(); + for (i=0;i +#define OVERRIDE_INNER_PRODUCT_DOUBLE + +static inline double inner_product_double(const float *a, const float *b, unsigned int len) +{ + int i; + double ret; + __m128d sum = _mm_setzero_pd(); + __m128 t; + for (i=0;i +# else +# ifdef HAVE_ALLOCA_H +# include +# else +# include +# endif +# endif +#endif + +/** + * @def ALIGN(stack, size) + * + * Aligns the stack to a 'size' boundary + * + * @param stack Stack + * @param size New size boundary + */ + +/** + * @def PUSH(stack, size, type) + * + * Allocates 'size' elements of type 'type' on the stack + * + * @param stack Stack + * @param size Number of elements + * @param type Type of element + */ + +/** + * @def VARDECL(var) + * + * Declare variable on stack + * + * @param var Variable to declare + */ + +/** + * @def ALLOC(var, size, type) + * + * Allocate 'size' elements of 'type' on stack + * + * @param var Name of variable to allocate + * @param size Number of elements + * @param type Type of element + */ + +#ifdef ENABLE_VALGRIND + +#include + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#else + +#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1)) + +#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type)))) + +#endif + +#if defined(VAR_ARRAYS) +#define VARDECL(var) +#define ALLOC(var, size, type) type var[size] +#elif defined(USE_ALLOCA) +#define VARDECL(var) var +#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size)) +#else +#define VARDECL(var) var +#define ALLOC(var, size, type) var = PUSH(stack, size, type) +#endif + + +#endif diff --git a/externals/cubeb/test/test_duplex.cpp b/externals/cubeb/test/test_duplex.cpp index bc71431b9..3620bf066 100755 --- a/externals/cubeb/test/test_duplex.cpp +++ b/externals/cubeb/test/test_duplex.cpp @@ -179,3 +179,137 @@ TEST(cubeb, duplex_collection_change) ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream"; cubeb_stream_destroy(stream); } + +long data_cb_input(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes) +{ + if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) { + return CUBEB_ERROR; + } + + return nframes; +} + +void state_cb_input(cubeb_stream * stream, void * /*user*/, cubeb_state state) +{ + if (stream == NULL) + return; + + switch (state) { + case CUBEB_STATE_STARTED: + fprintf(stderr, "stream started\n"); break; + case CUBEB_STATE_STOPPED: + fprintf(stderr, "stream stopped\n"); break; + case CUBEB_STATE_DRAINED: + fprintf(stderr, "stream drained\n"); break; + case CUBEB_STATE_ERROR: + fprintf(stderr, "stream runs into error state\n"); break; + default: + fprintf(stderr, "unknown stream state %d\n", state); + } + + return; +} + +std::vector get_devices(cubeb * ctx, cubeb_device_type type) { + std::vector devices; + + cubeb_device_collection collection; + int r = cubeb_enumerate_devices(ctx, type, &collection); + + if (r != CUBEB_OK) { + fprintf(stderr, "Failed to enumerate devices\n"); + return devices; + } + + for (uint32_t i = 0; i < collection.count; i++) { + if (collection.device[i].state == CUBEB_DEVICE_STATE_ENABLED) { + devices.emplace_back(collection.device[i].devid); + } + } + + cubeb_device_collection_destroy(ctx, &collection); + + return devices; +} + +TEST(cubeb, one_duplex_one_input) +{ + cubeb *ctx; + cubeb_stream *duplex_stream; + cubeb_stream_params input_params; + cubeb_stream_params output_params; + int r; + user_state_duplex duplex_stream_state; + uint32_t latency_frames = 0; + + r = common_init(&ctx, "Cubeb duplex example"); + ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library"; + + std::unique_ptr + cleanup_cubeb_at_exit(ctx, cubeb_destroy); + + /* This test needs at least two available input devices. */ + std::vector input_devices = get_devices(ctx, CUBEB_DEVICE_TYPE_INPUT); + if (input_devices.size() < 2) { + return; + } + + /* This test needs at least one available output device. */ + std::vector output_devices = get_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT); + if (output_devices.size() < 1) { + return; + } + + cubeb_devid duplex_input = input_devices.front(); + cubeb_devid duplex_output = nullptr; // default device + cubeb_devid input_only = input_devices.back(); + + /* typical use-case: mono voice input, stereo output, low latency. */ + input_params.format = STREAM_FORMAT; + input_params.rate = SAMPLE_FREQUENCY; + input_params.channels = INPUT_CHANNELS; + input_params.layout = CUBEB_LAYOUT_UNDEFINED; + input_params.prefs = CUBEB_STREAM_PREF_VOICE; + + output_params.format = STREAM_FORMAT; + output_params.rate = SAMPLE_FREQUENCY; + output_params.channels = OUTPUT_CHANNELS; + output_params.layout = OUTPUT_LAYOUT; + output_params.prefs = CUBEB_STREAM_PREF_NONE; + + r = cubeb_get_min_latency(ctx, &output_params, &latency_frames); + ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency"; + + r = cubeb_stream_init(ctx, &duplex_stream, "Cubeb duplex", + duplex_input, &input_params, duplex_output, &output_params, + latency_frames, data_cb_duplex, state_cb_duplex, &duplex_stream_state); + ASSERT_EQ(r, CUBEB_OK) << "Error initializing duplex cubeb stream"; + + std::unique_ptr + cleanup_stream_at_exit(duplex_stream, cubeb_stream_destroy); + + r = cubeb_stream_start(duplex_stream); + ASSERT_EQ(r, CUBEB_OK) << "Could not start duplex stream"; + delay(500); + + cubeb_stream *input_stream; + r = cubeb_stream_init(ctx, &input_stream, "Cubeb input", + input_only, &input_params, NULL, NULL, + latency_frames, data_cb_input, state_cb_input, nullptr); + ASSERT_EQ(r, CUBEB_OK) << "Error initializing input-only cubeb stream"; + + std::unique_ptr + cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy); + + r = cubeb_stream_start(input_stream); + ASSERT_EQ(r, CUBEB_OK) << "Could not start input stream"; + delay(500); + + r = cubeb_stream_stop(duplex_stream); + ASSERT_EQ(r, CUBEB_OK) << "Could not stop duplex stream"; + + r = cubeb_stream_stop(input_stream); + ASSERT_EQ(r, CUBEB_OK) << "Could not stop input stream"; + + ASSERT_FALSE(duplex_stream_state.invalid_audio_value.load()); +} diff --git a/externals/cubeb/test/test_sanity.cpp b/externals/cubeb/test/test_sanity.cpp index e1a60d0a4..5fc72f535 100755 --- a/externals/cubeb/test/test_sanity.cpp +++ b/externals/cubeb/test/test_sanity.cpp @@ -612,7 +612,7 @@ TEST(cubeb, drain) r = cubeb_stream_start(stream); ASSERT_EQ(r, CUBEB_OK); - delay(500); + delay(5000); do_drain = 1; @@ -642,65 +642,6 @@ TEST(cubeb, drain) do_drain = 0; } -TEST(cubeb, device_reset) -{ - int r; - cubeb * ctx; - cubeb_stream * stream; - cubeb_stream_params params; - uint64_t position; - - r = common_init(&ctx, "test_sanity"); - ASSERT_EQ(r, CUBEB_OK); - ASSERT_NE(ctx, nullptr); - - if (strcmp(cubeb_get_backend_id(ctx), "wasapi")) { - // cubeb_stream_reset_default_device is only useful and implemented in the - // WASAPI backend. - return; - } - - params.format = STREAM_FORMAT; - params.rate = STREAM_RATE; - params.channels = STREAM_CHANNELS; - params.layout = STREAM_LAYOUT; - params.prefs = CUBEB_STREAM_PREF_NONE; - - r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY, - test_data_callback, test_state_callback, &dummy); - ASSERT_EQ(r, CUBEB_OK); - ASSERT_NE(stream, nullptr); - - r = cubeb_stream_start(stream); - ASSERT_EQ(r, CUBEB_OK); - - uint32_t iterations = 5; - uint64_t previous_position = 0; - while (iterations--) { - r = cubeb_stream_get_position(stream, &position); - ASSERT_EQ(r, CUBEB_OK); - ASSERT_GE(position, previous_position); - previous_position = position; - delay(100); - } - - r = cubeb_stream_reset_default_device(stream); - ASSERT_EQ(r, CUBEB_OK); - - iterations = 5; - while (iterations--) { - r = cubeb_stream_get_position(stream, &position); - ASSERT_EQ(r, CUBEB_OK); - ASSERT_GE(position, previous_position); - previous_position = position; - delay(100); - } - - cubeb_stream_stop(stream); - cubeb_stream_destroy(stream); - cubeb_destroy(ctx); -} - TEST(cubeb, DISABLED_eos_during_prefill) { // This test needs to be implemented. diff --git a/externals/cubeb/test/test_tone.cpp b/externals/cubeb/test/test_tone.cpp index de4c5269f..70a71885e 100755 --- a/externals/cubeb/test/test_tone.cpp +++ b/externals/cubeb/test/test_tone.cpp @@ -114,7 +114,7 @@ TEST(cubeb, tone) cleanup_stream_at_exit(stream, cubeb_stream_destroy); cubeb_stream_start(stream); - delay(500); + delay(5000); cubeb_stream_stop(stream); ASSERT_TRUE(user_data->position.load());