diff --git a/.gitmodules b/.gitmodules index 3c341c5177..94d60fb94e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -40,3 +40,8 @@ [submodule "Externals/VulkanMemoryAllocator"] path = Externals/VulkanMemoryAllocator url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator.git +[submodule "Externals/cubeb/cubeb"] + path = Externals/cubeb/cubeb + url = https://github.com/mozilla/cubeb.git + branch = master + shallow = true diff --git a/Externals/cubeb/AUTHORS b/Externals/cubeb/AUTHORS deleted file mode 100644 index f0f9595227..0000000000 --- a/Externals/cubeb/AUTHORS +++ /dev/null @@ -1,16 +0,0 @@ -Matthew Gregan -Alexandre Ratchov -Michael Wu -Paul Adenot -David Richards -Sebastien Alaiwan -KO Myung-Hun -Haakon Sporsheim -Alex Chronopoulos -Jan Beich -Vito Caputo -Landry Breuil -Jacek Caban -Paul Hancock -Ted Mielczarek -Chun-Min Chang diff --git a/Externals/cubeb/CMakeLists.txt b/Externals/cubeb/CMakeLists.txt index 7cd84dee8b..b3eccb548e 100644 --- a/Externals/cubeb/CMakeLists.txt +++ b/Externals/cubeb/CMakeLists.txt @@ -1,62 +1,81 @@ # 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) option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(BUILD_RUST_LIBS "Build rust backends" OFF) +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() - if (NOT MSVC) set(CMAKE_C_STANDARD 99) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) endif() -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}/cubeb/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_RUST_LIBS) + if(EXISTS "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-pulse-rs") + set(USE_PULSE_RUST 1) + endif() + if(EXISTS "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-coreaudio-rs") + set(USE_AUDIOUNIT_RUST 1) endif() endif() -set(CMAKE_C_VISIBILITY_PRESET hidden) -set(CMAKE_CXX_VISIBILITY_PRESET hidden) -set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +# On OS/2, visibility attribute is not supported. +if(NOT OS2) + set(CMAKE_C_VISIBILITY_PRESET hidden) + set(CMAKE_CXX_VISIBILITY_PRESET hidden) + set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) +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 - src/cubeb.c - src/cubeb_mixer.cpp - src/cubeb_resampler.cpp - src/cubeb_panner.cpp - src/cubeb_log.cpp - src/cubeb_strings.c - $) + cubeb/src/cubeb.c + cubeb/src/cubeb_mixer.cpp + cubeb/src/cubeb_resampler.cpp + cubeb/src/cubeb_log.cpp + cubeb/src/cubeb_strings.c + cubeb/src/cubeb_utils.cpp +) dolphin_disable_warnings_msvc(cubeb) target_include_directories(cubeb - PUBLIC $ $ + PUBLIC $ $ +) +set_target_properties(cubeb PROPERTIES + VERSION ${cubeb_VERSION} + SOVERSION ${cubeb_VERSION_MAJOR} ) -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) add_sanitizers(cubeb) @@ -66,8 +85,10 @@ target_include_directories(cubeb PUBLIC $ ) -install(DIRECTORY ${CMAKE_SOURCE_DIR}/include DESTINATION ${CMAKE_INSTALL_PREFIX}) -install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_PREFIX}/include/cubeb) +include(GNUInstallDirs) + +install(DIRECTORY ${CMAKE_SOURCE_DIR}/cubeb/include/${PROJECT_NAME} TYPE INCLUDE) +install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}) include(CMakePackageConfigHelpers) write_basic_package_version_file( @@ -76,94 +97,164 @@ write_basic_package_version_file( ) configure_package_config_file( - "Config.cmake.in" + "cubeb/Config.cmake.in" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" - INSTALL_DESTINATION "lib/cmake/${PROJECT_NAME}" + INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) -install(TARGETS cubeb - EXPORT "${PROJECT_NAME}Targets" - DESTINATION ${CMAKE_INSTALL_PREFIX} - LIBRARY DESTINATION "lib" - ARCHIVE DESTINATION "lib" - RUNTIME DESTINATION "bin" - INCLUDES DESTINATION "include" -) install( FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" - DESTINATION "lib/cmake/${PROJECT_NAME}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) + +install(TARGETS cubeb EXPORT "${PROJECT_NAME}Targets") install( EXPORT "${PROJECT_NAME}Targets" NAMESPACE "${PROJECT_NAME}::" - DESTINATION "lib/cmake/${PROJECT_NAME}" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}" ) -add_library(speex OBJECT - src/speex/resample.c) -dolphin_disable_warnings_msvc(speex) -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 cubeb/subprojects/speex/resample.c) + dolphin_disable_warnings_msvc(speex) + set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE) + target_include_directories(speex INTERFACE cubeb/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) + set(USE_AAUDIO OFF) # too new for the Android versions we're targetting + + 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) + set(USE_AAUDIO OFF) # too new for the Android versions we're targetting + 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 cubeb/src/cubeb_pulse.c) + target_compile_definitions(cubeb PRIVATE USE_PULSE) +endif() + +if(USE_ALSA) + target_sources(cubeb PRIVATE cubeb/src/cubeb_alsa.c) + target_compile_definitions(cubeb PRIVATE USE_ALSA) +endif() + +if(USE_JACK) + target_sources(cubeb PRIVATE cubeb/src/cubeb_jack.cpp) + target_compile_definitions(cubeb PRIVATE USE_JACK) +endif() + +if(USE_SNDIO) + target_sources(cubeb PRIVATE cubeb/src/cubeb_sndio.c) + target_compile_definitions(cubeb PRIVATE USE_SNDIO) +endif() + +if(USE_AAUDIO) + target_sources(cubeb PRIVATE cubeb/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 - src/cubeb_audiounit.cpp - src/cubeb_osx_run_loop.cpp) + cubeb/src/cubeb_audiounit.cpp + cubeb/src/cubeb_osx_run_loop.cpp) target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT) target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices") endif() -check_include_files(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 pulse) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(cubeb PRIVATE dl) - endif() -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 asound pthread) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(cubeb PRIVATE dl) - endif() -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 jack pthread) - if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(cubeb PRIVATE dl) - endif() -endif() - check_include_files(audioclient.h USE_WASAPI) if(USE_WASAPI) target_sources(cubeb PRIVATE - src/cubeb_wasapi.cpp) + cubeb/src/cubeb_wasapi.cpp) target_compile_definitions(cubeb PRIVATE USE_WASAPI) - target_link_libraries(cubeb PRIVATE avrt) + target_link_libraries(cubeb PRIVATE avrt ole32 ksuser) endif() check_include_files("windows.h;mmsystem.h" USE_WINMM) if(USE_WINMM) target_sources(cubeb PRIVATE - src/cubeb_winmm.c) + cubeb/src/cubeb_winmm.c) target_compile_definitions(cubeb PRIVATE USE_WINMM) target_link_libraries(cubeb PRIVATE winmm) endif() @@ -171,31 +262,90 @@ endif() check_include_files(SLES/OpenSLES.h USE_OPENSL) if(USE_OPENSL) target_sources(cubeb PRIVATE - src/cubeb_opensl.c) + cubeb/src/cubeb_opensl.c + cubeb/src/cubeb-jni.cpp) target_compile_definitions(cubeb PRIVATE USE_OPENSL) target_link_libraries(cubeb PRIVATE OpenSLES) endif() +check_include_files(sys/soundcard.h HAVE_SYS_SOUNDCARD_H) +if(HAVE_SYS_SOUNDCARD_H) + try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests" + ${PROJECT_SOURCE_DIR}/cubeb/cmake/compile_tests/oss_is_v4.c) + if(USE_OSS) + target_sources(cubeb PRIVATE + cubeb/src/cubeb_oss.c) + target_compile_definitions(cubeb PRIVATE USE_OSS) + endif() +endif() + check_include_files(android/log.h USE_AUDIOTRACK) if(USE_AUDIOTRACK) target_sources(cubeb PRIVATE - src/cubeb_audiotrack.c) + cubeb/src/cubeb_audiotrack.c) target_compile_definitions(cubeb PRIVATE USE_AUDIOTRACK) target_link_libraries(cubeb PRIVATE log) endif() -check_include_files(sndio.h USE_SNDIO) -if(USE_SNDIO) +check_include_files(sys/audioio.h USE_SUN) +if(USE_SUN) target_sources(cubeb PRIVATE - src/cubeb_sndio.c) - target_compile_definitions(cubeb PRIVATE USE_SNDIO) - target_link_libraries(cubeb PRIVATE sndio) + cubeb/src/cubeb_sun.c) + target_compile_definitions(cubeb PRIVATE USE_SUN) endif() check_include_files(kai.h USE_KAI) if(USE_KAI) target_sources(cubeb PRIVATE - src/cubeb_kai.c) + cubeb/src/cubeb_kai.c) target_compile_definitions(cubeb PRIVATE USE_KAI) target_link_libraries(cubeb PRIVATE kai) endif() + +if(USE_PULSE AND USE_PULSE_RUST) + include(ExternalProject) + set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust) + ExternalProject_Add( + cubeb_pulse_rs + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND cargo build COMMAND cargo build --release + BUILD_ALWAYS ON + BINARY_DIR "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-pulse-rs" + INSTALL_COMMAND "" + LOG_BUILD ON) + add_dependencies(cubeb cubeb_pulse_rs) + target_compile_definitions(cubeb PRIVATE USE_PULSE_RUST) + target_link_libraries(cubeb PRIVATE + debug "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-pulse-rs/target/debug/libcubeb_pulse.a" + optimized "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-pulse-rs/target/release/libcubeb_pulse.a" pulse) +endif() + +if(USE_AUDIOUNIT AND USE_AUDIOUNIT_RUST) + include(ExternalProject) + set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust) + ExternalProject_Add( + cubeb_coreaudio_rs + DOWNLOAD_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_COMMAND cargo build COMMAND cargo build --release + BUILD_ALWAYS ON + BINARY_DIR "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-coreaudio-rs" + INSTALL_COMMAND "" + LOG_BUILD ON) + add_dependencies(cubeb cubeb_coreaudio_rs) + target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT_RUST) + target_link_libraries(cubeb PRIVATE + debug "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-coreaudio-rs/target/debug/libcubeb_coreaudio.a" + optimized "${PROJECT_SOURCE_DIR}/cubeb/src/cubeb-coreaudio-rs/target/release/libcubeb_coreaudio.a") +endif() + +find_package(Doxygen) +if(DOXYGEN_FOUND) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY) + add_custom_target(doc ALL + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs + COMMENT "Generating API documentation with Doxygen" VERBATIM) +endif() + diff --git a/Externals/cubeb/Config.cmake.in b/Externals/cubeb/Config.cmake.in deleted file mode 100644 index c5326ef33a..0000000000 --- a/Externals/cubeb/Config.cmake.in +++ /dev/null @@ -1,4 +0,0 @@ -@PACKAGE_INIT@ - -include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake") -check_required_components(cubeb) \ No newline at end of file diff --git a/Externals/cubeb/INSTALL.md b/Externals/cubeb/INSTALL.md deleted file mode 100644 index 81df5ea025..0000000000 --- a/Externals/cubeb/INSTALL.md +++ /dev/null @@ -1,24 +0,0 @@ -# Build instructions for libcubeb - -You must have CMake v3.1 or later installed. - -1. `git clone --recursive https://github.com/kinetiknz/cubeb.git` -2. `mkdir cubeb-build` -3. `cd cubeb-build` -3. `cmake ../cubeb` -4. `cmake --build .` -5. `ctest` - -# Windows build notes - -Windows builds can use Microsoft Visual Studio 2015 (the default) or MinGW-w64 -with Win32 threads (by passing `cmake -G` to generate the appropriate build -configuration). To build with MinGW-w64, install the following items: - -- Download and install MinGW-w64 with Win32 threads. -- Download and install CMake. -- Run MinGW-w64 Terminal from the Start Menu. -- Follow the build steps above, but at step 3 run: - `cmake -G "MinGW Makefiles" ..` -- Continue the build steps above. - diff --git a/Externals/cubeb/LICENSE b/Externals/cubeb/LICENSE deleted file mode 100644 index fffc9dc405..0000000000 --- a/Externals/cubeb/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright © 2011 Mozilla Foundation - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Externals/cubeb/README.md b/Externals/cubeb/README.md deleted file mode 100644 index d26b3b645b..0000000000 --- a/Externals/cubeb/README.md +++ /dev/null @@ -1,6 +0,0 @@ -[![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) - -See INSTALL.md for build instructions. - -Licensed under an ISC-style license. See LICENSE for details. diff --git a/Externals/cubeb/cmake/sanitizers-cmake/LICENSE b/Externals/cubeb/cmake/sanitizers-cmake/LICENSE deleted file mode 100644 index 2520efdc03..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) - 2013 Matthew Arsenault - 2015-2016 RWTH Aachen University, Federal Republic of Germany - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Externals/cubeb/cmake/sanitizers-cmake/README.md b/Externals/cubeb/cmake/sanitizers-cmake/README.md deleted file mode 100644 index 54a82556be..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/README.md +++ /dev/null @@ -1,73 +0,0 @@ -# sanitizers-cmake - - [![](https://img.shields.io/github/issues-raw/arsenm/sanitizers-cmake.svg?style=flat-square)](https://github.com/arsenm/sanitizers-cmake/issues) -[![MIT](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE) - -CMake module to enable sanitizers for binary targets. - - -## Include into your project - -To use [FindSanitizers.cmake](cmake/FindSanitizers.cmake), simply add this repository as git submodule into your own repository -```Shell -mkdir externals -git submodule add git://github.com/arsenm/sanitizers-cmake.git externals/sanitizers-cmake -``` -and adding ```externals/sanitizers-cmake/cmake``` to your ```CMAKE_MODULE_PATH``` -```CMake -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/externals/sanitizers-cmake/cmake" ${CMAKE_MODULE_PATH}) -``` - -If you don't use git or dislike submodules you can copy the files in [cmake directory](cmake) into your repository. *Be careful and keep updates in mind!* - -Now you can simply run ```find_package``` in your CMake files: -```CMake -find_package(Sanitizers) -``` - - -## Usage - -You can enable the sanitizers with ``SANITIZE_ADDRESS``, ``SANITIZE_MEMORY``, ``SANITIZE_THREAD`` or ``SANITIZE_UNDEFINED`` options in your CMake configuration. You can do this by passing e.g. ``-DSANITIZE_ADDRESS=On`` on your command line or with your graphical interface. - -If sanitizers are supported by your compiler, the specified targets will be build with sanitizer support. If your compiler has no sanitizing capabilities (I asume intel compiler doesn't) you'll get a warning but CMake will continue processing and sanitizing will simply just be ignored. - -#### Compiler issues - -Different compilers may be using different implementations for sanitizers. If you'll try to sanitize targets with C and Fortran code but don't use gcc & gfortran but clang & gfortran, this will cause linking problems. To avoid this, such problems will be detected and sanitizing will be disabled for these targets. - -Even C only targets may cause problems in certain situations. Some problems have been seen with AddressSanitizer for preloading or dynamic linking. In such cases you may try the ``SANITIZE_LINK_STATIC`` to link sanitizers for gcc static. - - - -## Build targets with sanitizer support - -To enable sanitizer support you simply have to add ``add_sanitizers()`` after defining your target. To provide a sanitizer blacklist file you can use the ``add_sanitizer_blacklist()`` function: -```CMake -find_package(Sanitizers) - -add_sanitizer_blacklist("blacklist.txt") - -add_executable(some_exe foo.c bar.c) -add_sanitizers(some_exe) - -add_library(some_lib foo.c bar.c) -add_sanitizers(some_lib) -``` - -## Run your application - -The sanitizers check your program, while it's running. In some situations (e.g. LD_PRELOAD your target) it might be required to preload the used AddressSanitizer library first. In this case you may use the ``asan-wrapper`` script defined in ``ASan_WRAPPER`` variable to execute your application with ``${ASan_WRAPPER} myexe arg1 ...``. - - -## Contribute - -Anyone is welcome to contribute. Simply fork this repository, make your changes **in an own branch** and create a pull-request for your change. Please do only one change per pull-request. - -You found a bug? Please fill out an [issue](https://github.com/arsenm/sanitizers-cmake/issues) and include any data to reproduce the bug. - - -#### Contributors - -* [Matt Arsenault](https://github.com/arsenm) -* [Alexander Haase](https://github.com/alehaa) diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindASan.cmake b/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindASan.cmake deleted file mode 100644 index fcebb43757..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindASan.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -option(SANITIZE_ADDRESS "Enable AddressSanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - # Clang 3.2+ use this version. The no-omit-frame-pointer option is optional. - "-g -fsanitize=address -fno-omit-frame-pointer" - "-g -fsanitize=address" - - # Older deprecated flag for ASan - "-g -faddress-sanitizer" -) - - -if (SANITIZE_ADDRESS AND (SANITIZE_THREAD OR SANITIZE_MEMORY)) - message(FATAL_ERROR "AddressSanitizer is not compatible with " - "ThreadSanitizer or MemorySanitizer.") -endif () - - -include(sanitize-helpers) - -if (SANITIZE_ADDRESS) - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "AddressSanitizer" - "ASan") - - find_program(ASan_WRAPPER "asan-wrapper" PATHS ${CMAKE_MODULE_PATH}) - mark_as_advanced(ASan_WRAPPER) -endif () - -function (add_sanitize_address TARGET) - if (NOT SANITIZE_ADDRESS) - return() - endif () - - saitizer_add_flags(${TARGET} "AddressSanitizer" "ASan") -endfunction () diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindMSan.cmake b/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindMSan.cmake deleted file mode 100644 index 3b0a4addcb..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindMSan.cmake +++ /dev/null @@ -1,57 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -option(SANITIZE_MEMORY "Enable MemorySanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - "-g -fsanitize=memory" -) - - -include(sanitize-helpers) - -if (SANITIZE_MEMORY) - if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - message(WARNING "MemorySanitizer disabled for target ${TARGET} because " - "MemorySanitizer is supported for Linux systems only.") - set(SANITIZE_MEMORY Off CACHE BOOL - "Enable MemorySanitizer for sanitized targets." FORCE) - elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) - message(WARNING "MemorySanitizer disabled for target ${TARGET} because " - "MemorySanitizer is supported for 64bit systems only.") - set(SANITIZE_MEMORY Off CACHE BOOL - "Enable MemorySanitizer for sanitized targets." FORCE) - else () - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "MemorySanitizer" - "MSan") - endif () -endif () - -function (add_sanitize_memory TARGET) - if (NOT SANITIZE_MEMORY) - return() - endif () - - saitizer_add_flags(${TARGET} "MemorySanitizer" "MSan") -endfunction () diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindSanitizers.cmake b/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindSanitizers.cmake deleted file mode 100644 index 1c4622fc53..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindSanitizers.cmake +++ /dev/null @@ -1,87 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# If any of the used compiler is a GNU compiler, add a second option to static -# link against the sanitizers. -option(SANITIZE_LINK_STATIC "Try to link static against sanitizers." Off) - - - - -set(FIND_QUIETLY_FLAG "") -if (DEFINED Sanitizers_FIND_QUIETLY) - set(FIND_QUIETLY_FLAG "QUIET") -endif () - -find_package(ASan ${FIND_QUIETLY_FLAG}) -find_package(TSan ${FIND_QUIETLY_FLAG}) -find_package(MSan ${FIND_QUIETLY_FLAG}) -find_package(UBSan ${FIND_QUIETLY_FLAG}) - - - - -function(sanitizer_add_blacklist_file FILE) - if(NOT IS_ABSOLUTE ${FILE}) - set(FILE "${CMAKE_CURRENT_SOURCE_DIR}/${FILE}") - endif() - get_filename_component(FILE "${FILE}" REALPATH) - - sanitizer_check_compiler_flags("-fsanitize-blacklist=${FILE}" - "SanitizerBlacklist" "SanBlist") -endfunction() - -function(add_sanitizers ...) - # If no sanitizer is enabled, return immediately. - if (NOT (SANITIZE_ADDRESS OR SANITIZE_MEMORY OR SANITIZE_THREAD OR - SANITIZE_UNDEFINED)) - return() - endif () - - foreach (TARGET ${ARGV}) - # Check if this target will be compiled by exactly one compiler. Other- - # wise sanitizers can't be used and a warning should be printed once. - sanitizer_target_compilers(${TARGET} TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - if (NUM_COMPILERS GREATER 1) - message(WARNING "Can't use any sanitizers for target ${TARGET}, " - "because it will be compiled by incompatible compilers. " - "Target will be compiled without sanitzers.") - return() - - # If the target is compiled by no known compiler, ignore it. - elseif (NUM_COMPILERS EQUAL 0) - message(WARNING "Can't use any sanitizers for target ${TARGET}, " - "because it uses an unknown compiler. Target will be " - "compiled without sanitzers.") - return() - endif () - - # Add sanitizers for target. - add_sanitize_address(${TARGET}) - add_sanitize_thread(${TARGET}) - add_sanitize_memory(${TARGET}) - add_sanitize_undefined(${TARGET}) - endforeach () -endfunction(add_sanitizers) diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindTSan.cmake b/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindTSan.cmake deleted file mode 100644 index 0e80f29b64..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindTSan.cmake +++ /dev/null @@ -1,64 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -option(SANITIZE_THREAD "Enable ThreadSanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - "-g -fsanitize=thread" -) - - -# ThreadSanitizer is not compatible with MemorySanitizer. -if (SANITIZE_THREAD AND SANITIZE_MEMORY) - message(FATAL_ERROR "ThreadSanitizer is not compatible with " - "MemorySanitizer.") -endif () - - -include(sanitize-helpers) - -if (SANITIZE_THREAD) - if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " - "ThreadSanitizer is supported for Linux systems only.") - set(SANITIZE_THREAD Off CACHE BOOL - "Enable ThreadSanitizer for sanitized targets." FORCE) - elseif (NOT ${CMAKE_SIZEOF_VOID_P} EQUAL 8) - message(WARNING "ThreadSanitizer disabled for target ${TARGET} because " - "ThreadSanitizer is supported for 64bit systems only.") - set(SANITIZE_THREAD Off CACHE BOOL - "Enable ThreadSanitizer for sanitized targets." FORCE) - else () - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" "ThreadSanitizer" - "TSan") - endif () -endif () - -function (add_sanitize_thread TARGET) - if (NOT SANITIZE_THREAD) - return() - endif () - - saitizer_add_flags(${TARGET} "ThreadSanitizer" "TSan") -endfunction () diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindUBSan.cmake b/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindUBSan.cmake deleted file mode 100644 index 69486742a4..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/FindUBSan.cmake +++ /dev/null @@ -1,46 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -option(SANITIZE_UNDEFINED - "Enable UndefinedBehaviorSanitizer for sanitized targets." Off) - -set(FLAG_CANDIDATES - "-g -fsanitize=undefined" -) - - -include(sanitize-helpers) - -if (SANITIZE_UNDEFINED) - sanitizer_check_compiler_flags("${FLAG_CANDIDATES}" - "UndefinedBehaviorSanitizer" "UBSan") -endif () - -function (add_sanitize_undefined TARGET) - if (NOT SANITIZE_UNDEFINED) - return() - endif () - - saitizer_add_flags(${TARGET} "UndefinedBehaviorSanitizer" "UBSan") -endfunction () diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/asan-wrapper b/Externals/cubeb/cmake/sanitizers-cmake/cmake/asan-wrapper deleted file mode 100755 index 5d54103372..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/asan-wrapper +++ /dev/null @@ -1,55 +0,0 @@ -#!/bin/sh - -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# This script is a wrapper for AddressSanitizer. In some special cases you need -# to preload AddressSanitizer to avoid error messages - e.g. if you're -# preloading another library to your application. At the moment this script will -# only do something, if we're running on a Linux platform. OSX might not be -# affected. - - -# Exit immediately, if platform is not Linux. -if [ "$(uname)" != "Linux" ] -then - exec $@ -fi - - -# Get the used libasan of the application ($1). If a libasan was found, it will -# be prepended to LD_PRELOAD. -libasan=$(ldd $1 | grep libasan | sed "s/^[[:space:]]//" | cut -d' ' -f1) -if [ -n "$libasan" ] -then - if [ -n "$LD_PRELOAD" ] - then - export LD_PRELOAD="$libasan:$LD_PRELOAD" - else - export LD_PRELOAD="$libasan" - fi -fi - -# Execute the application. -exec $@ diff --git a/Externals/cubeb/cmake/sanitizers-cmake/cmake/sanitize-helpers.cmake b/Externals/cubeb/cmake/sanitizers-cmake/cmake/sanitize-helpers.cmake deleted file mode 100644 index 6f3decdfac..0000000000 --- a/Externals/cubeb/cmake/sanitizers-cmake/cmake/sanitize-helpers.cmake +++ /dev/null @@ -1,170 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) -# 2013 Matthew Arsenault -# 2015-2016 RWTH Aachen University, Federal Republic of Germany -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Helper function to get the language of a source file. -function (sanitizer_lang_of_source FILE RETURN_VAR) - get_filename_component(FILE_EXT "${FILE}" EXT) - string(TOLOWER "${FILE_EXT}" FILE_EXT) - string(SUBSTRING "${FILE_EXT}" 1 -1 FILE_EXT) - - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - list(FIND CMAKE_${LANG}_SOURCE_FILE_EXTENSIONS "${FILE_EXT}" TEMP) - if (NOT ${TEMP} EQUAL -1) - set(${RETURN_VAR} "${LANG}" PARENT_SCOPE) - return() - endif () - endforeach() - - set(${RETURN_VAR} "" PARENT_SCOPE) -endfunction () - - -# Helper function to get compilers used by a target. -function (sanitizer_target_compilers TARGET RETURN_VAR) - # Check if all sources for target use the same compiler. If a target uses - # e.g. C and Fortran mixed and uses different compilers (e.g. clang and - # gfortran) this can trigger huge problems, because different compilers may - # use different implementations for sanitizers. - set(BUFFER "") - get_target_property(TSOURCES ${TARGET} SOURCES) - foreach (FILE ${TSOURCES}) - # If expression was found, FILE is a generator-expression for an object - # library. Object libraries will be ignored. - string(REGEX MATCH "TARGET_OBJECTS:([^ >]+)" _file ${FILE}) - if ("${_file}" STREQUAL "") - sanitizer_lang_of_source(${FILE} LANG) - if (LANG) - list(APPEND BUFFER ${CMAKE_${LANG}_COMPILER_ID}) - endif () - endif () - endforeach () - - list(REMOVE_DUPLICATES BUFFER) - set(${RETURN_VAR} "${BUFFER}" PARENT_SCOPE) -endfunction () - - -# Helper function to check compiler flags for language compiler. -function (sanitizer_check_compiler_flag FLAG LANG VARIABLE) - if (${LANG} STREQUAL "C") - include(CheckCCompilerFlag) - check_c_compiler_flag("${FLAG}" ${VARIABLE}) - - elseif (${LANG} STREQUAL "CXX") - include(CheckCXXCompilerFlag) - check_cxx_compiler_flag("${FLAG}" ${VARIABLE}) - - elseif (${LANG} STREQUAL "Fortran") - # CheckFortranCompilerFlag was introduced in CMake 3.x. To be compatible - # with older Cmake versions, we will check if this module is present - # before we use it. Otherwise we will define Fortran coverage support as - # not available. - include(CheckFortranCompilerFlag OPTIONAL RESULT_VARIABLE INCLUDED) - if (INCLUDED) - check_fortran_compiler_flag("${FLAG}" ${VARIABLE}) - elseif (NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Performing Test ${VARIABLE}") - message(STATUS "Performing Test ${VARIABLE}" - " - Failed (Check not supported)") - endif () - endif() -endfunction () - - -# Helper function to test compiler flags. -function (sanitizer_check_compiler_flags FLAG_CANDIDATES NAME PREFIX) - set(CMAKE_REQUIRED_QUIET ${${PREFIX}_FIND_QUIETLY}) - - get_property(ENABLED_LANGUAGES GLOBAL PROPERTY ENABLED_LANGUAGES) - foreach (LANG ${ENABLED_LANGUAGES}) - # Sanitizer flags are not dependend on language, but the used compiler. - # So instead of searching flags foreach language, search flags foreach - # compiler used. - set(COMPILER ${CMAKE_${LANG}_COMPILER_ID}) - if (NOT DEFINED ${PREFIX}_${COMPILER}_FLAGS) - foreach (FLAG ${FLAG_CANDIDATES}) - if(NOT CMAKE_REQUIRED_QUIET) - message(STATUS "Try ${COMPILER} ${NAME} flag = [${FLAG}]") - endif() - - set(CMAKE_REQUIRED_FLAGS "${FLAG}") - unset(${PREFIX}_FLAG_DETECTED CACHE) - sanitizer_check_compiler_flag("${FLAG}" ${LANG} - ${PREFIX}_FLAG_DETECTED) - - if (${PREFIX}_FLAG_DETECTED) - # If compiler is a GNU compiler, search for static flag, if - # SANITIZE_LINK_STATIC is enabled. - if (SANITIZE_LINK_STATIC AND (${COMPILER} STREQUAL "GNU")) - string(TOLOWER ${PREFIX} PREFIX_lower) - sanitizer_check_compiler_flag( - "-static-lib${PREFIX_lower}" ${LANG} - ${PREFIX}_STATIC_FLAG_DETECTED) - - if (${PREFIX}_STATIC_FLAG_DETECTED) - set(FLAG "-static-lib${PREFIX_lower} ${FLAG}") - endif () - endif () - - set(${PREFIX}_${COMPILER}_FLAGS "${FLAG}" CACHE STRING - "${NAME} flags for ${COMPILER} compiler.") - mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) - break() - endif () - endforeach () - - if (NOT ${PREFIX}_FLAG_DETECTED) - set(${PREFIX}_${COMPILER}_FLAGS "" CACHE STRING - "${NAME} flags for ${COMPILER} compiler.") - mark_as_advanced(${PREFIX}_${COMPILER}_FLAGS) - - message(WARNING "${NAME} is not available for ${COMPILER} " - "compiler. Targets using this compiler will be " - "compiled without ${NAME}.") - endif () - endif () - endforeach () -endfunction () - - -# Helper to assign sanitizer flags for TARGET. -function (saitizer_add_flags TARGET NAME PREFIX) - # Get list of compilers used by target and check, if sanitizer is available - # for this target. Other compiler checks like check for conflicting - # compilers will be done in add_sanitizers function. - sanitizer_target_compilers(${TARGET} TARGET_COMPILER) - list(LENGTH TARGET_COMPILER NUM_COMPILERS) - if ("${${PREFIX}_${TARGET_COMPILER}_FLAGS}" STREQUAL "") - return() - endif() - - # Set compile- and link-flags for target. - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY COMPILE_FLAGS " ${SanBlist_${TARGET_COMPILER}_FLAGS}") - set_property(TARGET ${TARGET} APPEND_STRING - PROPERTY LINK_FLAGS " ${${PREFIX}_${TARGET_COMPILER}_FLAGS}") -endfunction () diff --git a/Externals/cubeb/cubeb b/Externals/cubeb/cubeb new file mode 160000 index 0000000000..773f16b7ea --- /dev/null +++ b/Externals/cubeb/cubeb @@ -0,0 +1 @@ +Subproject commit 773f16b7ea308392c05be3e290163d1f636e6024 diff --git a/Externals/cubeb/cubeb.supp b/Externals/cubeb/cubeb.supp deleted file mode 100644 index 0012ea51e6..0000000000 --- a/Externals/cubeb/cubeb.supp +++ /dev/null @@ -1,36 +0,0 @@ -{ - snd_config_update-malloc - Memcheck:Leak - fun:malloc - ... - fun:snd_config_update_r -} -{ - snd1_dlobj_cache_get-malloc - Memcheck:Leak - fun:malloc - ... - fun:snd1_dlobj_cache_get -} -{ - parse_defs-malloc - Memcheck:Leak - fun:malloc - ... - fun:parse_defs -} -{ - parse_defs-calloc - Memcheck:Leak - fun:calloc - ... - fun:parse_defs -} -{ - pa_client_conf_from_x11-malloc - Memcheck:Leak - fun:malloc - ... - fun:pa_client_conf_from_x11 -} - diff --git a/Externals/cubeb/exports.props b/Externals/cubeb/exports.props index 306e3d4921..bfea3d3abe 100644 --- a/Externals/cubeb/exports.props +++ b/Externals/cubeb/exports.props @@ -2,7 +2,7 @@ - $(ExternalsDir)cubeb\include;$(ExternalsDir)cubeb\msvc;%(AdditionalIncludeDirectories) + $(ExternalsDir)cubeb\cubeb\include;$(ExternalsDir)cubeb\msvc;%(AdditionalIncludeDirectories) diff --git a/Externals/cubeb/include/cubeb/cubeb.h b/Externals/cubeb/include/cubeb/cubeb.h deleted file mode 100644 index 9b091e8fad..0000000000 --- a/Externals/cubeb/include/cubeb/cubeb.h +++ /dev/null @@ -1,638 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382) -#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 - -#include -#include -#include "cubeb_export.h" - -#if defined(__cplusplus) -extern "C" { -#endif - -/** @mainpage - - @section intro Introduction - - This is the documentation for the libcubeb C API. - libcubeb is a callback-based audio API library allowing the - authoring of portable multiplatform audio playback and recording. - - @section example Example code - - This example shows how to create a duplex stream that pipes the microphone - to the speakers, with minimal latency and the proper sample-rate for the - platform. - - @code - cubeb * app_ctx; - cubeb_init(&app_ctx, "Example Application"); - int rv; - uint32_t rate; - uint32_t latency_frames; - uint64_t ts; - - rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames); - if (rv != CUBEB_OK) { - fprintf(stderr, "Could not get minimum latency"); - return rv; - } - - rv = cubeb_get_preferred_sample_rate(app_ctx, output_params, &rate); - if (rv != CUBEB_OK) { - fprintf(stderr, "Could not get preferred sample-rate"); - return rv; - } - - cubeb_stream_params output_params; - output_params.format = CUBEB_SAMPLE_FLOAT32NE; - output_params.rate = rate; - output_params.channels = 2; - - cubeb_stream_params input_params; - output_params.format = CUBEB_SAMPLE_FLOAT32NE; - output_params.rate = rate; - output_params.channels = 1; - - cubeb_stream * stm; - rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1", - NULL, input_params, - NULL, output_params, - latency_frames, - data_cb, state_cb, - NULL); - if (rv != CUBEB_OK) { - fprintf(stderr, "Could not open the stream"); - return rv; - } - - rv = cubeb_stream_start(stm); - if (rv != CUBEB_OK) { - fprintf(stderr, "Could not start the stream"); - return rv; - } - for (;;) { - cubeb_stream_get_position(stm, &ts); - printf("time=%llu\n", ts); - sleep(1); - } - rv = cubeb_stream_stop(stm); - if (rv != CUBEB_OK) { - fprintf(stderr, "Could not stop the stream"); - return rv; - } - - cubeb_stream_destroy(stm); - cubeb_destroy(app_ctx); - @endcode - - @code - long data_cb(cubeb_stream * stm, void * user, - void * input_buffer, void * output_buffer, long nframes) - { - float * in = input_buffer; - float * out = output_buffer; - - for (i = 0; i < nframes; ++i) { - for (c = 0; c < 2; ++c) { - buf[i][c] = in[i]; - } - } - return nframes; - } - @endcode - - @code - void state_cb(cubeb_stream * stm, void * user, cubeb_state state) - { - printf("state=%d\n", state); - } - @endcode -*/ - -/** @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. */ - -/** Sample format enumeration. */ -typedef enum { - /**< Little endian 16-bit signed PCM. */ - CUBEB_SAMPLE_S16LE, - /**< Big endian 16-bit signed PCM. */ - CUBEB_SAMPLE_S16BE, - /**< Little endian 32-bit IEEE floating point PCM. */ - CUBEB_SAMPLE_FLOAT32LE, - /**< Big endian 32-bit IEEE floating point PCM. */ - CUBEB_SAMPLE_FLOAT32BE, -#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__) - /**< Native endian 16-bit signed PCM. */ - CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16BE, - /**< Native endian 32-bit IEEE floating point PCM. */ - CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32BE -#else - /**< Native endian 16-bit signed PCM. */ - CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16LE, - /**< Native endian 32-bit IEEE floating point PCM. */ - CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32LE -#endif -} cubeb_sample_format; - -/** An opaque handle used to refer a particular input or output device - * across calls. */ -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_level; - -/** SMPTE channel layout (also known as wave order) - * DUAL-MONO L R - * DUAL-MONO-LFE L R LFE - * MONO M - * MONO-LFE M LFE - * STEREO L R - * STEREO-LFE L R LFE - * 3F L R C - * 3F-LFE L R C LFE - * 2F1 L R S - * 2F1-LFE L R LFE S - * 3F1 L R C S - * 3F1-LFE L R C LFE S - * 2F2 L R LS RS - * 2F2-LFE L R LFE LS RS - * 3F2 L R C LS RS - * 3F2-LFE L R C LFE LS RS - * 3F3R-LFE L R C LFE RC LS RS - * 3F4-LFE L R C LFE RLS RRS LS RS - * - * The abbreviation of channel name is defined in following table: - * Abbr Channel name - * --------------------------- - * M Mono - * L Left - * R Right - * C Center - * LS Left Surround - * RS Right Surround - * RLS Rear Left Surround - * RC Rear Center - * RRS Rear Right Surround - * LFE Low Frequency Effects - */ - -typedef enum { - CUBEB_LAYOUT_UNDEFINED, // Indicate the speaker's layout is undefined. - CUBEB_LAYOUT_DUAL_MONO, - CUBEB_LAYOUT_DUAL_MONO_LFE, - CUBEB_LAYOUT_MONO, - CUBEB_LAYOUT_MONO_LFE, - CUBEB_LAYOUT_STEREO, - CUBEB_LAYOUT_STEREO_LFE, - CUBEB_LAYOUT_3F, - CUBEB_LAYOUT_3F_LFE, - CUBEB_LAYOUT_2F1, - CUBEB_LAYOUT_2F1_LFE, - CUBEB_LAYOUT_3F1, - CUBEB_LAYOUT_3F1_LFE, - CUBEB_LAYOUT_2F2, - CUBEB_LAYOUT_2F2_LFE, - CUBEB_LAYOUT_3F2, - CUBEB_LAYOUT_3F2_LFE, - CUBEB_LAYOUT_3F3R_LFE, - CUBEB_LAYOUT_3F4_LFE, - CUBEB_LAYOUT_MAX -} cubeb_channel_layout; - -/** 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_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 */ -} cubeb_device; - -/** Stream states signaled via state_callback. */ -typedef enum { - CUBEB_STATE_STARTED, /**< Stream started. */ - CUBEB_STATE_STOPPED, /**< Stream stopped. */ - CUBEB_STATE_DRAINED, /**< Stream drained. */ - CUBEB_STATE_ERROR /**< Stream disabled due to error. */ -} cubeb_state; - -/** Result code enumeration. */ -enum { - 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. */ -}; - -/** - * Whether a particular device is an input device (e.g. a microphone), or an - * output device (e.g. headphones). */ -typedef enum { - CUBEB_DEVICE_TYPE_UNKNOWN, - CUBEB_DEVICE_TYPE_INPUT, - CUBEB_DEVICE_TYPE_OUTPUT -} cubeb_device_type; - -/** - * 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; - -/** - * 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; - -#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 -#else -/** 16-bit integers, native endianess, when on a Little Endian environment. */ -#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 -#endif -/** All the 16-bit integers types. */ -#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) -/** All the device formats types. */ -#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; - -/** This structure holds the characteristics - * of an input or output audio device. It is obtained using - * `cubeb_enumerate_devices`, which returns these structures via - * `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_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. */ - - uint32_t latency_lo; /**< Lowest possible latency in frames. */ - uint32_t latency_hi; /**< Higest possible latency in frames. */ -} cubeb_device_info; - -/** Device collection. - * Returned by `cubeb_enumerate_devices` and destroyed by - * `cubeb_device_collection_destroy`. */ -typedef struct { - cubeb_device_info * device; /**< Array of pointers to device info. */ - size_t count; /**< Device count in collection. */ -} cubeb_device_collection; - -/** User supplied data callback. - - Calling other cubeb functions from this callback is unsafe. - - The code in the callback should be non-blocking. - - Returning less than the number of frames this callback asks for or - provides puts the stream in drain mode. This callback will not be called - again, and the state callback will be called with CUBEB_STATE_DRAINED when - all the frames have been output. - @param stream The stream for which this callback fired. - @param user_ptr The pointer passed to cubeb_stream_init. - @param input_buffer A pointer containing the input data, or nullptr - if this is an output-only stream. - @param output_buffer A pointer to a buffer to be filled with audio samples, - or nullptr if this is an input-only stream. - @param nframes The number of frames of the two buffer. - @retval Number of frames written to the output buffer. If this number is - less than nframes, then the stream will start to drain. - @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); - -/** 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); - -/** - * 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); - -/** - * 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); - -/** User supplied callback called when a message needs logging. */ -typedef void (* cubeb_log_callback)(char const * fmt, ...); - -/** Initialize an application context. This will perform any library or - application scoped initialization. - @param context A out param where an opaque pointer to the application - context will be returned. - @param context_name A name for the context. Depending on the platform this - can appear in different locations. - @param backend_name The name of the cubeb backend user desires to select. - Accepted values self-documented in cubeb.c: init_oneshot - If NULL, a default ordering is used for backend choice. - A valid choice overrides all other possible backends, - so long as the backend was included at compile time. - @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); - -/** 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); - -/** Get the maximum possible number of channels. - @param context A pointer to the cubeb context. - @param max_channels The maximum number of channels. - @retval CUBEB_OK - @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); - -/** Get the minimal latency value, in frames, that is guaranteed to work - when creating a stream for the specified sample rate. This is platform, - hardware and backend dependent. - @param context A pointer to the cubeb context. - @param params On some backends, the minimum achievable latency depends on - the characteristics of the stream. - @param latency_frames The latency value, in frames, to pass to - cubeb_stream_init. - @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); - -/** Get the preferred sample rate for this backend: this is hardware and - platform dependent, and can avoid resampling, and/or trigger fastpaths. - @param context A pointer to the cubeb context. - @param rate The samplerate (in Hz) the current configuration prefers. - @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); - -/** Get the preferred layout for this backend: this is hardware and - platform dependent. - @param context A pointer to the cubeb context. - @param layout The layout of the current speaker configuration. - @retval CUBEB_OK - @retval CUBEB_ERROR_INVALID_PARAMETER - @retval CUBEB_ERROR_NOT_SUPPORTED */ -CUBEB_EXPORT int cubeb_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layout); - -/** 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); - -/** Initialize a stream associated with the supplied application context. - @param context A pointer to the cubeb context. - @param stream An out parameter to be filled with the an opaque pointer to a - 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. - @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. - @param output_stream_params Parameters for the output side of the stream, or - NULL if this stream is input only. - @param latency_frames Stream latency in frames. Valid range - is [1, 96000]. - @param data_callback Will be called to preroll data before playback is - started by cubeb_stream_start. - @param state_callback A pointer to a state callback. - @param user_ptr A pointer that will be passed to the callbacks. This pointer - must outlive the life time of the stream. - @retval CUBEB_OK - @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); - -/** 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); - -/** Start playback. - @param stream - @retval CUBEB_OK - @retval CUBEB_ERROR */ -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); - -/** 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); - -/** 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 - can hear the sound. - @param stream - @param latency Current approximate stream latency in frames. - @retval CUBEB_OK - @retval CUBEB_ERROR_NOT_SUPPORTED - @retval CUBEB_ERROR */ -CUBEB_EXPORT int cubeb_stream_get_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) - @retval CUBEB_OK - @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); - -/** If the stream is stereo, set the left/right panning. If the stream is mono, - this has no effect. - @param stream the stream for which to change the panning - @param panning a number from -1.0 to 1.0. -1.0 means that the stream is - fully mixed in the left channel, 1.0 means the stream is fully - mixed in the right channel. 0.0 is equal power in the right and - left channel (default). - @retval CUBEB_OK - @retval CUBEB_ERROR_INVALID_PARAMETER if stream is null or if panning is - outside the [-1.0, 1.0] range. - @retval CUBEB_ERROR_NOT_SUPPORTED - @retval CUBEB_ERROR stream is not mono nor stereo */ -CUBEB_EXPORT int cubeb_stream_set_panning(cubeb_stream * stream, float panning); - -/** Get the current output device for this stream. - @param stm the stream for which to query the current output device - @param device a pointer in which the current output device will be stored. - @retval CUBEB_OK in case of success - @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); - -/** Destroy a cubeb_device structure. - @param stream the stream passed in cubeb_stream_get_current_device - @param devices the devices to destroy - @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); - -/** Set a callback to be notified when the output device changes. - @param stream the stream for which to set the callback. - @param device_changed_callback a function called whenever the device has - changed. Passing NULL allow to unregister a function - @retval CUBEB_OK - @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); - -/** Returns enumerated devices. - @param context - @param devtype device type to include - @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); - -/** 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); - -/** Registers a callback which is called when the system detects - a new device or a device is removed. - @param context - @param devtype device type to include - @param callback a function called whenever the system device list changes. - Passing NULL allow to unregister a function - @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); - -/** Set a callback to be called with a message. - @param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL. - @param log_callback A function called with a message when there is - something to log. Pass NULL to unregister. - @retval CUBEB_OK in case of success. - @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); - -#if defined(__cplusplus) -} -#endif - -#endif /* CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 */ diff --git a/Externals/cubeb/msvc/cubeb.vcxproj b/Externals/cubeb/msvc/cubeb.vcxproj index d492b4ac9c..7d5f3c85b2 100644 --- a/Externals/cubeb/msvc/cubeb.vcxproj +++ b/Externals/cubeb/msvc/cubeb.vcxproj @@ -17,7 +17,7 @@ - ..\include;..\src;..\msvc;%(AdditionalIncludeDirectories) + ..\cubeb\include;..\cubeb\src;..\msvc;..\cubeb\subprojects;%(AdditionalIncludeDirectories) WIN32;USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;%(PreprocessorDefinitions) @@ -27,41 +27,45 @@ - + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + diff --git a/Externals/cubeb/msvc/cubeb.vcxproj.filters b/Externals/cubeb/msvc/cubeb.vcxproj.filters index 959f152aaf..8611fe88dc 100644 --- a/Externals/cubeb/msvc/cubeb.vcxproj.filters +++ b/Externals/cubeb/msvc/cubeb.vcxproj.filters @@ -1,55 +1,55 @@  - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + speex - + speex - + speex - + speex - + speex - + speex - + speex - - - - - - - - - + + + + + + + + + speex diff --git a/Externals/cubeb/src/android/audiotrack_definitions.h b/Externals/cubeb/src/android/audiotrack_definitions.h deleted file mode 100644 index cd501533d9..0000000000 --- a/Externals/cubeb/src/android/audiotrack_definitions.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -/* - * The following definitions are copied from the android sources. Only the - * relevant enum member and values needed are copied. - */ - -/* - * 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 - */ -struct Buffer { - uint32_t flags; - int channelCount; - int format; - size_t frameCount; - size_t size; - union { - void* raw; - short* i16; - int8_t* i8; - }; -}; - -enum event_type { - EVENT_MORE_DATA = 0, - EVENT_UNDERRUN = 1, - EVENT_LOOP_END = 2, - EVENT_MARKER = 3, - EVENT_NEW_POS = 4, - EVENT_BUFFER_END = 5 -}; - -/** - * 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_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) -} 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) -} AudioTrack_ChannelMapping_Legacy; - -typedef enum { - AUDIO_FORMAT_PCM = 0x00000000, - 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/sles_definitions.h b/Externals/cubeb/src/android/sles_definitions.h deleted file mode 100644 index 1b1ace567e..0000000000 --- a/Externals/cubeb/src/android/sles_definitions.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * This file is similar to the file "OpenSLES_AndroidConfiguration.h" found in - * the Android NDK, but removes the #ifdef __cplusplus defines, so we can keep - * using a C compiler in cubeb. - */ - -#ifndef OPENSL_ES_ANDROIDCONFIGURATION_H_ -#define OPENSL_ES_ANDROIDCONFIGURATION_H_ - -/*---------------------------------------------------------------------------*/ -/* Android AudioRecorder configuration */ -/*---------------------------------------------------------------------------*/ - -/** Audio recording preset */ -/** Audio recording preset key */ -#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) -/** generic recording configuration on the platform */ -#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) -/** uses the main microphone tuned for voice recognition */ -#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) - -/** Audio recording get session ID (read only) */ -/** Audio recording get session ID key */ -#define SL_ANDROID_KEY_RECORDING_SESSION_ID ((const SLchar*) "androidRecordingSessionId") - -/*---------------------------------------------------------------------------*/ -/* Android AudioPlayer configuration */ -/*---------------------------------------------------------------------------*/ - -/** Audio playback stream type */ -/** Audio playback stream type key */ -#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) -/* same as android.media.AudioManager.STREAM_SYSTEM */ -#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001) -/* same as android.media.AudioManager.STREAM_RING */ -#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002) -/* same as android.media.AudioManager.STREAM_MUSIC */ -#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003) -/* same as android.media.AudioManager.STREAM_ALARM */ -#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004) -/* same as android.media.AudioManager.STREAM_NOTIFICATION */ -#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005) -/* same as android.media.AudioManager.STREAM_BLUETOOTH_SCO */ -#define SL_ANDROID_STREAM_BLUETOOTH_SCO ((SLint32) 0x00000006) -/* same as android.media.AudioManager.STREAM_SYSTEM_ENFORCED */ -#define SL_ANDROID_STREAM_SYSTEM_ENFORCED ((SLint32) 0x00000007) - -#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */ diff --git a/Externals/cubeb/src/cubeb-internal.h b/Externals/cubeb/src/cubeb-internal.h deleted file mode 100644 index 25bad3a1d0..0000000000 --- a/Externals/cubeb/src/cubeb-internal.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright © 2013 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5) -#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 - -#include "cubeb/cubeb.h" -#include "cubeb_log.h" -#include "cubeb_assert.h" -#include -#include - -#ifdef __clang__ -#ifndef CLANG_ANALYZER_NORETURN -#if __has_feature(attribute_analyzer_noreturn) -#define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn)) -#else -#define CLANG_ANALYZER_NORETURN -#endif // ifndef CLANG_ANALYZER_NORETURN -#endif // __has_feature(attribute_analyzer_noreturn) -#else // __clang__ -#define CLANG_ANALYZER_NORETURN -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - -#if defined(__cplusplus) -} -#endif - -typedef struct { - char const * name; - unsigned int const channels; - cubeb_channel_layout const layout; -} cubeb_layout_map; - -extern cubeb_layout_map const CUBEB_CHANNEL_LAYOUT_MAPS[CUBEB_LAYOUT_MAX]; - -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 (* get_preferred_channel_layout)(cubeb * context, cubeb_channel_layout * layout); - 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_set_volume)(cubeb_stream * stream, float volumes); - int (* stream_set_panning)(cubeb_stream * stream, float panning); - 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-sles.h b/Externals/cubeb/src/cubeb-sles.h deleted file mode 100644 index ac22150e1f..0000000000 --- a/Externals/cubeb/src/cubeb-sles.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef _CUBEB_SLES_H_ -#define _CUBEB_SLES_H_ -#include - -static SLresult -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); -} - -static void -cubeb_destroy_sles_engine(SLObjectItf * self) -{ - if (*self != NULL) { - (**self)->Destroy(*self); - *self = NULL; - } -} - -static SLresult -cubeb_realize_sles_engine(SLObjectItf self) -{ - return (*self)->Realize(self, SL_BOOLEAN_FALSE); -} - -#endif diff --git a/Externals/cubeb/src/cubeb-speex-resampler.h b/Externals/cubeb/src/cubeb-speex-resampler.h deleted file mode 100644 index 9ecf747cb0..0000000000 --- a/Externals/cubeb/src/cubeb-speex-resampler.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/Externals/cubeb/src/cubeb.c b/Externals/cubeb/src/cubeb.c deleted file mode 100644 index f78f4aff14..0000000000 --- a/Externals/cubeb/src/cubeb.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - * Copyright © 2013 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#undef NDEBUG -#include -#include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" - -#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0]))) - -struct cubeb { - struct cubeb_ops * ops; -}; - -struct cubeb_stream { - struct cubeb * context; -}; - -#if defined(USE_PULSE) -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); -#endif -#if defined(USE_JACK) -int jack_init (cubeb ** context, char const * context_name); -#endif -#if defined(USE_ALSA) -int alsa_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_AUDIOUNIT) -int audiounit_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_WINMM) -int winmm_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_WASAPI) -int wasapi_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_SNDIO) -int sndio_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_OPENSL) -int opensl_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_AUDIOTRACK) -int audiotrack_init(cubeb ** context, char const * context_name); -#endif -#if defined(USE_KAI) -int kai_init(cubeb ** context, char const * context_name); -#endif - -static int -validate_stream_params(cubeb_stream_params * input_stream_params, - cubeb_stream_params * output_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 > 8) { - 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 > 8) { - 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 || - 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; - - switch (params->format) { - case CUBEB_SAMPLE_S16LE: - case CUBEB_SAMPLE_S16BE: - case CUBEB_SAMPLE_FLOAT32LE: - case CUBEB_SAMPLE_FLOAT32BE: - return CUBEB_OK; - } - - return CUBEB_ERROR_INVALID_FORMAT; -} - -static int -validate_latency(int latency) -{ - if (latency < 1 || latency > 96000) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - return CUBEB_OK; -} - -int -cubeb_init(cubeb ** context, char const * context_name, char const * backend_name) -{ - int (* init_oneshot)(cubeb **, char const *) = NULL; - - if (backend_name != NULL) { - if (!strcmp(backend_name, "pulse")) { -#if defined(USE_PULSE) - init_oneshot = pulse_init; -#endif - } else if (!strcmp(backend_name, "pulse-rust")) { -#if defined(USE_PULSE_RUST) - init_oneshot = pulse_rust_init; -#endif - } else if (!strcmp(backend_name, "jack")) { -#if defined(USE_JACK) - init_oneshot = jack_init; -#endif - } else if (!strcmp(backend_name, "alsa")) { -#if defined(USE_ALSA) - init_oneshot = alsa_init; -#endif - } else if (!strcmp(backend_name, "audiounit")) { -#if defined(USE_AUDIOUNIT) - init_oneshot = audiounit_init; -#endif - } else if (!strcmp(backend_name, "wasapi")) { -#if defined(USE_WASAPI) - init_oneshot = wasapi_init; -#endif - } else if (!strcmp(backend_name, "winmm")) { -#if defined(USE_WINMM) - init_oneshot = winmm_init; -#endif - } else if (!strcmp(backend_name, "sndio")) { -#if defined(USE_SNDIO) - init_oneshot = sndio_init; -#endif - } else if (!strcmp(backend_name, "opensl")) { -#if defined(USE_OPENSL) - init_oneshot = opensl_init; -#endif - } else if (!strcmp(backend_name, "audiotrack")) { -#if defined(USE_AUDIOTRACK) - init_oneshot = audiotrack_init; -#endif - } else if (!strcmp(backend_name, "kai")) { -#if defined(USE_KAI) - init_oneshot = kai_init; -#endif - } else { - /* Already set */ - } - } - - int (* default_init[])(cubeb **, char const *) = { - /* - * init_oneshot must be at the top to allow user - * to override all other choices - */ - init_oneshot, -#if defined(USE_PULSE) - pulse_init, -#endif -#if defined(USE_JACK) - jack_init, -#endif -#if defined(USE_ALSA) - alsa_init, -#endif -#if defined(USE_AUDIOUNIT) - audiounit_init, -#endif -#if defined(USE_WASAPI) - wasapi_init, -#endif -#if defined(USE_WINMM) - winmm_init, -#endif -#if defined(USE_SNDIO) - sndio_init, -#endif -#if defined(USE_OPENSL) - opensl_init, -#endif -#if defined(USE_AUDIOTRACK) - audiotrack_init, -#endif -#if defined(USE_KAI) - kai_init, -#endif - }; - int i; - - if (!context) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - -#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. */ - OK(get_backend_id); - OK(destroy); - OK(stream_init); - OK(stream_destroy); - OK(stream_start); - OK(stream_stop); - OK(stream_get_position); - return CUBEB_OK; - } - } - return CUBEB_ERROR; -} - -char const * -cubeb_get_backend_id(cubeb * context) -{ - if (!context) { - return NULL; - } - - return context->ops->get_backend_id(context); -} - -int -cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels) -{ - if (!context || !max_channels) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!context->ops->get_max_channel_count) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return context->ops->get_max_channel_count(context, max_channels); -} - -int -cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms) -{ - if (!context || !params || !latency_ms) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!context->ops->get_min_latency) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return context->ops->get_min_latency(context, *params, latency_ms); -} - -int -cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate) -{ - if (!context || !rate) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!context->ops->get_preferred_sample_rate) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return context->ops->get_preferred_sample_rate(context, rate); -} - -int -cubeb_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layout) -{ - if (!context || !layout) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!context->ops->get_preferred_channel_layout) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return context->ops->get_preferred_channel_layout(context, layout); -} - -void -cubeb_destroy(cubeb * context) -{ - if (!context) { - return; - } - - context->ops->destroy(context); -} - -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, - unsigned int latency, - cubeb_data_callback data_callback, - cubeb_state_callback state_callback, - void * user_ptr) -{ - int r; - - if (!context || !stream) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - 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); - - if (r == CUBEB_ERROR_INVALID_FORMAT) { - 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); - } - - return r; -} - -void -cubeb_stream_destroy(cubeb_stream * stream) -{ - if (!stream) { - return; - } - - stream->context->ops->stream_destroy(stream); -} - -int -cubeb_stream_start(cubeb_stream * stream) -{ - if (!stream) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - return stream->context->ops->stream_start(stream); -} - -int -cubeb_stream_stop(cubeb_stream * stream) -{ - if (!stream) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - 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) -{ - if (!stream || !position) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - return stream->context->ops->stream_get_position(stream, position); -} - -int -cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency) -{ - if (!stream || !latency) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_get_latency) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return stream->context->ops->stream_get_latency(stream, latency); -} - -int -cubeb_stream_set_volume(cubeb_stream * stream, float volume) -{ - if (!stream || volume > 1.0 || volume < 0.0) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_set_volume) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return stream->context->ops->stream_set_volume(stream, volume); -} - -int cubeb_stream_set_panning(cubeb_stream * stream, float panning) -{ - if (!stream || panning < -1.0 || panning > 1.0) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_set_panning) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return stream->context->ops->stream_set_panning(stream, panning); -} - -int cubeb_stream_get_current_device(cubeb_stream * stream, - cubeb_device ** const device) -{ - if (!stream || !device) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_get_current_device) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return stream->context->ops->stream_get_current_device(stream, device); -} - -int cubeb_stream_device_destroy(cubeb_stream * stream, - cubeb_device * device) -{ - if (!stream || !device) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_device_destroy) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - 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) -{ - if (!stream) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (!stream->context->ops->stream_register_device_changed_callback) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback); -} - -static -void log_device(cubeb_device_info * device_info) -{ - char devfmts[128] = ""; - 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; - }; - - 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; - }; - - 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; - }; - - if (device_info->format & CUBEB_DEVICE_FMT_S16LE) { - strcat(devfmts, " S16LE"); - } - if (device_info->format & CUBEB_DEVICE_FMT_S16BE) { - strcat(devfmts, " S16BE"); - } - if (device_info->format & CUBEB_DEVICE_FMT_F32LE) { - strcat(devfmts, " F32LE"); - } - if (device_info->format & CUBEB_DEVICE_FMT_F32BE) { - strcat(devfmts, " F32BE"); - } - - LOG("DeviceID: \"%s\"%s\n" - "\tName:\t\"%s\"\n" - "\tGroup:\t\"%s\"\n" - "\tVendor:\t\"%s\"\n" - "\tType:\t%s\n" - "\tState:\t%s\n" - "\tMaximum channels:\t%u\n" - "\tFormat:\t%s (0x%x) (default: %s)\n" - "\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); -} - -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) - return CUBEB_ERROR_INVALID_PARAMETER; - if (collection == NULL) - return CUBEB_ERROR_INVALID_PARAMETER; - if (!context->ops->enumerate_devices) - return CUBEB_ERROR_NOT_SUPPORTED; - - rv = context->ops->enumerate_devices(context, devtype, collection); - - if (g_cubeb_log_callback) { - for (size_t i = 0; i < collection->count; i++) { - log_device(&collection->device[i]); - } - } - - return rv; -} - -int cubeb_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection) -{ - int r; - - if (context == NULL || collection == NULL) - return CUBEB_ERROR_INVALID_PARAMETER; - - if (!context->ops->device_collection_destroy) - return CUBEB_ERROR_NOT_SUPPORTED; - - if (!collection->device) - return CUBEB_OK; - - r = context->ops->device_collection_destroy(context, collection); - if (r == CUBEB_OK) { - collection->device = NULL; - collection->count = 0; - } - - return r; -} - -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) - 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); -} - -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; - } - - if (!log_callback && log_level != CUBEB_LOG_DISABLED) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - if (g_cubeb_log_callback && log_callback) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - g_cubeb_log_callback = log_callback; - g_cubeb_log_level = log_level; - - // Logging a message here allows to initialize the asynchronous logger from a - // thread that is not the audio rendering thread, and especially to not - // initialize it the first time we find a verbose log, which is often in the - // audio rendering callback, that runs from the audio rendering thread, and - // that is high priority, and that we don't want to block. - if (log_level >= CUBEB_LOG_VERBOSE) { - ALOGV("Starting cubeb log"); - } - - return CUBEB_OK; -} - diff --git a/Externals/cubeb/src/cubeb_alsa.c b/Externals/cubeb/src/cubeb_alsa.c deleted file mode 100644 index 8cb37ee96c..0000000000 --- a/Externals/cubeb/src/cubeb_alsa.c +++ /dev/null @@ -1,1372 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#undef NDEBUG -#define _DEFAULT_SOURCE -#define _BSD_SOURCE -#define _XOPEN_SOURCE 500 -#include -#include -#include -#include -#include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" - -#define CUBEB_STREAM_MAX 16 -#define CUBEB_WATCHDOG_MS 10000 - -#define CUBEB_ALSA_PCM_NAME "default" - -#define ALSA_PA_PLUGIN "ALSA <-> PulseAudio PCM I/O Plugin" - -/* ALSA is not thread-safe. snd_pcm_t instances are individually protected - by the owning cubeb_stream's mutex. snd_pcm_t creation and destruction - is not thread-safe until ALSA 1.0.24 (see alsa-lib.git commit 91c9c8f1), - so those calls must be wrapped in the following mutex. */ -static pthread_mutex_t cubeb_alsa_mutex = PTHREAD_MUTEX_INITIALIZER; -static int cubeb_alsa_error_handler_set = 0; - -static struct cubeb_ops const alsa_ops; - -struct cubeb { - struct cubeb_ops const * ops; - - pthread_t thread; - - /* Mutex for streams array, must not be held while blocked in poll(2). */ - pthread_mutex_t mutex; - - /* Sparse array of streams managed by this context. */ - cubeb_stream * streams[CUBEB_STREAM_MAX]; - - /* fds and nfds are only updated by alsa_run when rebuild is set. */ - struct pollfd * fds; - nfds_t nfds; - int rebuild; - - int shutdown; - - /* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */ - int control_fd_read; - int control_fd_write; - - /* Track number of active streams. This is limited to CUBEB_STREAM_MAX - due to resource contraints. */ - unsigned int active_streams; - - /* Local configuration with handle_underrun workaround set for PulseAudio - ALSA plugin. Will be NULL if the PA ALSA plugin is not in use or the - workaround is not required. */ - snd_config_t * local_config; - int is_pa; -}; - -enum stream_state { - INACTIVE, - RUNNING, - DRAINING, - PROCESSING, - ERROR -}; - -struct cubeb_stream { - cubeb * context; - pthread_mutex_t mutex; - snd_pcm_t * pcm; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * user_ptr; - snd_pcm_uframes_t stream_position; - snd_pcm_uframes_t last_position; - snd_pcm_uframes_t buffer_size; - cubeb_stream_params params; - - /* Every member after this comment is protected by the owning context's - mutex rather than the stream's mutex, or is only used on the context's - run thread. */ - pthread_cond_t cond; /* Signaled when the stream's state is changed. */ - - 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. */ - nfds_t nfds; - - struct timeval drain_timeout; - - /* XXX: Horrible hack -- if an active stream has been idle for - CUBEB_WATCHDOG_MS it will be disabled and the error callback will be - called. This works around a bug seen with older versions of ALSA and - PulseAudio where streams would stop requesting new data despite still - being logically active and playing. */ - struct timeval last_activity; - float volume; - - char * buffer; - snd_pcm_uframes_t bufframes; - snd_pcm_stream_t stream_type; - - struct cubeb_stream * other_stream; -}; - -static int -any_revents(struct pollfd * fds, nfds_t nfds) -{ - nfds_t i; - - for (i = 0; i < nfds; ++i) { - if (fds[i].revents) { - return 1; - } - } - - return 0; -} - -static int -cmp_timeval(struct timeval * a, struct timeval * b) -{ - if (a->tv_sec == b->tv_sec) { - if (a->tv_usec == b->tv_usec) { - return 0; - } - return a->tv_usec > b->tv_usec ? 1 : -1; - } - return a->tv_sec > b->tv_sec ? 1 : -1; -} - -static int -timeval_to_relative_ms(struct timeval * tv) -{ - struct timeval now; - struct timeval dt; - long long t; - int r; - - gettimeofday(&now, NULL); - r = cmp_timeval(tv, &now); - if (r >= 0) { - timersub(tv, &now, &dt); - } else { - timersub(&now, tv, &dt); - } - t = dt.tv_sec; - t *= 1000; - t += (dt.tv_usec + 500) / 1000; - - if (t > INT_MAX) { - t = INT_MAX; - } else if (t < INT_MIN) { - t = INT_MIN; - } - - return r >= 0 ? t : -t; -} - -static int -ms_until(struct timeval * tv) -{ - return timeval_to_relative_ms(tv); -} - -static int -ms_since(struct timeval * tv) -{ - return -timeval_to_relative_ms(tv); -} - -static void -rebuild(cubeb * ctx) -{ - nfds_t nfds; - int i; - nfds_t j; - cubeb_stream * stm; - - assert(ctx->rebuild); - - /* Always count context's control pipe fd. */ - nfds = 1; - for (i = 0; i < CUBEB_STREAM_MAX; ++i) { - stm = ctx->streams[i]; - if (stm) { - stm->fds = NULL; - if (stm->state == RUNNING) { - nfds += stm->nfds; - } - } - } - - free(ctx->fds); - ctx->fds = calloc(nfds, sizeof(struct pollfd)); - assert(ctx->fds); - ctx->nfds = nfds; - - /* Include context's control pipe fd. */ - ctx->fds[0].fd = ctx->control_fd_read; - ctx->fds[0].events = POLLIN | POLLERR; - - for (i = 0, j = 1; i < CUBEB_STREAM_MAX; ++i) { - stm = ctx->streams[i]; - if (stm && stm->state == RUNNING) { - memcpy(&ctx->fds[j], stm->saved_fds, stm->nfds * sizeof(struct pollfd)); - stm->fds = &ctx->fds[j]; - j += stm->nfds; - } - } - - ctx->rebuild = 0; -} - -static void -poll_wake(cubeb * ctx) -{ - if (write(ctx->control_fd_write, "x", 1) < 0) { - /* ignore write error */ - } -} - -static void -set_timeout(struct timeval * timeout, unsigned int ms) -{ - gettimeofday(timeout, NULL); - timeout->tv_sec += ms / 1000; - timeout->tv_usec += (ms % 1000) * 1000; -} - -static void -stream_buffer_decrement(cubeb_stream * stm, long count) -{ - char * bufremains = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, count); - memmove(stm->buffer, bufremains, snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes - count)); - stm->bufframes -= count; -} - -static void -alsa_set_stream_state(cubeb_stream * stm, enum stream_state state) -{ - cubeb * ctx; - int r; - - ctx = stm->context; - stm->state = state; - r = pthread_cond_broadcast(&stm->cond); - assert(r == 0); - ctx->rebuild = 1; - poll_wake(ctx); -} - -static enum stream_state -alsa_process_stream(cubeb_stream * stm) -{ - unsigned short revents; - snd_pcm_sframes_t avail; - int draining; - - draining = 0; - - pthread_mutex_lock(&stm->mutex); - - /* 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. */ - snd_pcm_poll_descriptors_revents(stm->pcm, stm->fds, stm->nfds, &revents); - - avail = snd_pcm_avail_update(stm->pcm); - - /* Got null event? Bail and wait for another wakeup. */ - if (avail == 0) { - pthread_mutex_unlock(&stm->mutex); - return RUNNING; - } - - /* 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; - } - - /* Capture: Read available frames */ - if (stm->stream_type == SND_PCM_STREAM_CAPTURE && avail > 0) { - snd_pcm_sframes_t got; - - if (avail + stm->bufframes > stm->buffer_size) { - /* Buffer overflow. Skip and overwrite with new data. */ - stm->bufframes = 0; - // TODO: should it be marked as DRAINING? - } - - got = snd_pcm_readi(stm->pcm, stm->buffer+stm->bufframes, avail); - - if (got < 0) { - avail = got; // the error handler below will recover us - } else { - stm->bufframes += got; - stm->stream_position += got; - - gettimeofday(&stm->last_activity, NULL); - } - } - - /* 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)) { - 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; - - /* 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)) { - 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); - pthread_mutex_lock(&stm->mutex); - - if (wrote < 0) { - avail = wrote; // the error handler below will recover us - } else { - stream_buffer_decrement(stm, wrote); - - if (stm->other_stream) { - stm->other_stream->bufframes += wrote; - } - } - } - - /* 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 && - (!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 + 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) { - got = stm->other_stream->bufframes; - } - - pthread_mutex_unlock(&stm->mutex); - got = stm->data_callback(stm, stm->user_ptr, other_buffer, buftail, got); - pthread_mutex_lock(&stm->mutex); - - if (got < 0) { - avail = got; // the error handler below will recover us - } else { - stm->bufframes += got; - - if (stm->other_stream) { - stream_buffer_decrement(stm->other_stream, got); - } - } - } - - /* 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) { - long drain_frames = avail - stm->bufframes; - double drain_time = (double) drain_frames / stm->params.rate; - - char * buftail = stm->buffer + snd_pcm_frames_to_bytes(stm->pcm, stm->bufframes); - memset(buftail, 0, snd_pcm_frames_to_bytes(stm->pcm, drain_frames)); - stm->bufframes = avail; - - /* Mark as draining, unless we're waiting for capture */ - if (!stm->other_stream || stm->other_stream->bufframes > 0) { - set_timeout(&stm->drain_timeout, drain_time * 1000); - - draining = 1; - } - } - - /* Playback: Have enough data and no errors. Let's write it out. */ - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > 0) { - snd_pcm_sframes_t wrote; - - if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) { - 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; - for (uint32_t i = 0; i < avail * stm->params.channels; i++) { - b[i] *= stm->volume; - } - } - - wrote = snd_pcm_writei(stm->pcm, stm->buffer, avail); - if (wrote < 0) { - avail = wrote; // the error handler below will recover us - } else { - stream_buffer_decrement(stm, wrote); - - stm->stream_position += wrote; - gettimeofday(&stm->last_activity, NULL); - } - } - - /* Got some error? Let's try to recover the stream. */ - if (avail < 0) { - avail = 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 && - snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) { - avail = snd_pcm_start(stm->pcm); - } - } - - /* Failed to recover, this stream must be broken. */ - if (avail < 0) { - pthread_mutex_unlock(&stm->mutex); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - return ERROR; - } - - pthread_mutex_unlock(&stm->mutex); - return draining ? DRAINING : RUNNING; -} - -static int -alsa_run(cubeb * ctx) -{ - int r; - int timeout; - int i; - char dummy; - cubeb_stream * stm; - enum stream_state state; - - pthread_mutex_lock(&ctx->mutex); - - if (ctx->rebuild) { - rebuild(ctx); - } - - /* Wake up at least once per second for the watchdog. */ - timeout = 1000; - for (i = 0; i < CUBEB_STREAM_MAX; ++i) { - stm = ctx->streams[i]; - if (stm && stm->state == DRAINING) { - r = ms_until(&stm->drain_timeout); - if (r >= 0 && timeout > r) { - timeout = r; - } - } - } - - pthread_mutex_unlock(&ctx->mutex); - r = poll(ctx->fds, ctx->nfds, timeout); - pthread_mutex_lock(&ctx->mutex); - - if (r > 0) { - if (ctx->fds[0].revents & POLLIN) { - if (read(ctx->control_fd_read, &dummy, 1) < 0) { - /* ignore read error */ - } - - if (ctx->shutdown) { - pthread_mutex_unlock(&ctx->mutex); - return -1; - } - } - - for (i = 0; i < CUBEB_STREAM_MAX; ++i) { - 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)) { - alsa_set_stream_state(stm, PROCESSING); - pthread_mutex_unlock(&ctx->mutex); - state = alsa_process_stream(stm); - pthread_mutex_lock(&ctx->mutex); - alsa_set_stream_state(stm, state); - } - } - } else if (r == 0) { - for (i = 0; i < CUBEB_STREAM_MAX; ++i) { - stm = ctx->streams[i]; - if (stm) { - 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) { - alsa_set_stream_state(stm, ERROR); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - } - } - } - } - - pthread_mutex_unlock(&ctx->mutex); - - return 0; -} - -static void * -alsa_run_thread(void * context) -{ - cubeb * ctx = context; - int r; - - do { - r = alsa_run(ctx); - } while (r >= 0); - - return NULL; -} - -static snd_config_t * -get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm) -{ - int r; - snd_config_t * slave_pcm; - snd_config_t * slave_def; - snd_config_t * pcm; - char const * string; - char node_name[64]; - - slave_def = NULL; - - r = snd_config_search(root_pcm, "slave", &slave_pcm); - if (r < 0) { - return NULL; - } - - r = snd_config_get_string(slave_pcm, &string); - if (r >= 0) { - r = snd_config_search_definition(lconf, "pcm_slave", string, &slave_def); - if (r < 0) { - return NULL; - } - } - - do { - r = snd_config_search(slave_def ? slave_def : slave_pcm, "pcm", &pcm); - if (r < 0) { - break; - } - - r = snd_config_get_string(slave_def ? slave_def : slave_pcm, &string); - if (r < 0) { - break; - } - - r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); - if (r < 0 || r > (int) sizeof(node_name)) { - break; - } - r = snd_config_search(lconf, node_name, &pcm); - if (r < 0) { - break; - } - - return pcm; - } while (0); - - if (slave_def) { - snd_config_delete(slave_def); - } - - return NULL; -} - -/* Work around PulseAudio ALSA plugin bug where the PA server forces a - 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 */ -static snd_config_t * -init_local_config_with_workaround(char const * pcm_name) -{ - int r; - snd_config_t * lconf; - snd_config_t * pcm_node; - snd_config_t * node; - char const * string; - char node_name[64]; - - lconf = NULL; - - if (snd_config == NULL) { - return NULL; - } - - r = snd_config_copy(&lconf, snd_config); - if (r < 0) { - return NULL; - } - - do { - r = snd_config_search_definition(lconf, "pcm", pcm_name, &pcm_node); - if (r < 0) { - break; - } - - r = snd_config_get_id(pcm_node, &string); - if (r < 0) { - break; - } - - r = snprintf(node_name, sizeof(node_name), "pcm.%s", string); - if (r < 0 || r > (int) sizeof(node_name)) { - break; - } - r = snd_config_search(lconf, node_name, &pcm_node); - if (r < 0) { - break; - } - - /* 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. */ - r = snd_config_search(pcm_node, "type", &node); - if (r < 0) { - break; - } - - r = snd_config_get_string(node, &string); - if (r < 0) { - break; - } - - if (strcmp(string, "pulse") != 0) { - break; - } - - /* Don't clobber an explicit existing handle_underrun value, set it only - if it doesn't already exist. */ - r = snd_config_search(pcm_node, "handle_underrun", &node); - if (r != -ENOENT) { - break; - } - - /* Disable pcm_pulse's asynchronous underrun handling. */ - r = snd_config_imake_integer(&node, "handle_underrun", 0); - if (r < 0) { - break; - } - - r = snd_config_add(pcm_node, node); - if (r < 0) { - break; - } - - return lconf; - } while (0); - - snd_config_delete(lconf); - - return NULL; -} - -static int -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 = snd_pcm_open_lconf(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config); - } else { - r = snd_pcm_open(pcm, pcm_name, stream, SND_PCM_NONBLOCK); - } - pthread_mutex_unlock(&cubeb_alsa_mutex); - - return r; -} - -static int -alsa_locked_pcm_close(snd_pcm_t * pcm) -{ - int r; - - pthread_mutex_lock(&cubeb_alsa_mutex); - r = snd_pcm_close(pcm); - pthread_mutex_unlock(&cubeb_alsa_mutex); - - return r; -} - -static int -alsa_register_stream(cubeb * ctx, cubeb_stream * stm) -{ - int i; - - pthread_mutex_lock(&ctx->mutex); - for (i = 0; i < CUBEB_STREAM_MAX; ++i) { - if (!ctx->streams[i]) { - ctx->streams[i] = stm; - break; - } - } - pthread_mutex_unlock(&ctx->mutex); - - return i == CUBEB_STREAM_MAX; -} - -static void -alsa_unregister_stream(cubeb_stream * stm) -{ - cubeb * ctx; - int i; - - ctx = stm->context; - - pthread_mutex_lock(&ctx->mutex); - for (i = 0; i < CUBEB_STREAM_MAX; ++i) { - if (ctx->streams[i] == stm) { - ctx->streams[i] = NULL; - break; - } - } - pthread_mutex_unlock(&ctx->mutex); -} - -static void -silent_error_handler(char const * file, int line, char const * function, - int err, char const * fmt, ...) -{ - (void)file; - (void)line; - (void)function; - (void)err; - (void)fmt; -} - -/*static*/ int -alsa_init(cubeb ** context, char const * context_name) -{ - (void)context_name; - cubeb * ctx; - int r; - int i; - int fd[2]; - pthread_attr_t attr; - snd_pcm_t * dummy; - - assert(context); - *context = NULL; - - pthread_mutex_lock(&cubeb_alsa_mutex); - if (!cubeb_alsa_error_handler_set) { - snd_lib_error_set_handler(silent_error_handler); - cubeb_alsa_error_handler_set = 1; - } - pthread_mutex_unlock(&cubeb_alsa_mutex); - - ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - - ctx->ops = &alsa_ops; - - r = pthread_mutex_init(&ctx->mutex, NULL); - assert(r == 0); - - r = pipe(fd); - assert(r == 0); - - for (i = 0; i < 2; ++i) { - fcntl(fd[i], F_SETFD, fcntl(fd[i], F_GETFD) | FD_CLOEXEC); - fcntl(fd[i], F_SETFL, fcntl(fd[i], F_GETFL) | O_NONBLOCK); - } - - ctx->control_fd_read = fd[0]; - ctx->control_fd_write = fd[1]; - - /* Force an early rebuild when alsa_run is first called to ensure fds and - nfds have been initialized. */ - ctx->rebuild = 1; - - r = pthread_attr_init(&attr); - assert(r == 0); - - r = pthread_attr_setstacksize(&attr, 256 * 1024); - assert(r == 0); - - r = pthread_create(&ctx->thread, &attr, alsa_run_thread, ctx); - assert(r == 0); - - r = pthread_attr_destroy(&attr); - assert(r == 0); - - /* 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); - if (r >= 0) { - alsa_locked_pcm_close(dummy); - } - ctx->is_pa = 0; - pthread_mutex_lock(&cubeb_alsa_mutex); - ctx->local_config = init_local_config_with_workaround(CUBEB_ALSA_PCM_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); - /* 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) { - pthread_mutex_lock(&cubeb_alsa_mutex); - snd_config_delete(ctx->local_config); - pthread_mutex_unlock(&cubeb_alsa_mutex); - ctx->local_config = NULL; - } else if (r >= 0) { - alsa_locked_pcm_close(dummy); - } - } - - *context = ctx; - - return CUBEB_OK; -} - -static char const * -alsa_get_backend_id(cubeb * ctx) -{ - (void)ctx; - return "alsa"; -} - -static void -alsa_destroy(cubeb * ctx) -{ - int r; - - assert(ctx); - - pthread_mutex_lock(&ctx->mutex); - ctx->shutdown = 1; - poll_wake(ctx); - pthread_mutex_unlock(&ctx->mutex); - - r = pthread_join(ctx->thread, NULL); - assert(r == 0); - - close(ctx->control_fd_read); - close(ctx->control_fd_write); - pthread_mutex_destroy(&ctx->mutex); - free(ctx->fds); - - if (ctx->local_config) { - pthread_mutex_lock(&cubeb_alsa_mutex); - snd_config_delete(ctx->local_config); - pthread_mutex_unlock(&cubeb_alsa_mutex); - } - - free(ctx); -} - -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, - 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) -{ - (void)stream_name; - cubeb_stream * stm; - int r; - 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; - - assert(ctx && stream); - - *stream = NULL; - - switch (stream_params->format) { - case CUBEB_SAMPLE_S16LE: - format = SND_PCM_FORMAT_S16_LE; - break; - case CUBEB_SAMPLE_S16BE: - format = SND_PCM_FORMAT_S16_BE; - break; - case CUBEB_SAMPLE_FLOAT32LE: - format = SND_PCM_FORMAT_FLOAT_LE; - break; - case CUBEB_SAMPLE_FLOAT32BE: - format = SND_PCM_FORMAT_FLOAT_BE; - break; - default: - return CUBEB_ERROR_INVALID_FORMAT; - } - - pthread_mutex_lock(&ctx->mutex); - if (ctx->active_streams >= CUBEB_STREAM_MAX) { - pthread_mutex_unlock(&ctx->mutex); - return CUBEB_ERROR; - } - ctx->active_streams += 1; - pthread_mutex_unlock(&ctx->mutex); - - stm = calloc(1, sizeof(*stm)); - assert(stm); - - stm->context = ctx; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->params = *stream_params; - stm->state = INACTIVE; - stm->volume = 1.0; - stm->buffer = NULL; - stm->bufframes = 0; - stm->stream_type = stream_type; - stm->other_stream = NULL; - - r = pthread_mutex_init(&stm->mutex, NULL); - assert(r == 0); - - 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); - if (r < 0) { - alsa_stream_destroy(stm); - return CUBEB_ERROR; - } - - r = snd_pcm_nonblock(stm->pcm, 1); - assert(r == 0); - - latency_us = latency_frames * 1e6 / stm->params.rate; - - /* Ugly hack: the PA ALSA plugin allows buffer configurations that can't - possibly work. See https://bugzilla.mozilla.org/show_bug.cgi?id=761274. - 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; - } - - r = snd_pcm_set_params(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED, - stm->params.channels, stm->params.rate, 1, - latency_us); - if (r < 0) { - alsa_stream_destroy(stm); - return CUBEB_ERROR_INVALID_FORMAT; - } - - r = 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 */ - stm->buffer_size *= 2; - stm->buffer = calloc(1, snd_pcm_frames_to_bytes(stm->pcm, stm->buffer_size)); - assert(stm->buffer); - - stm->nfds = snd_pcm_poll_descriptors_count(stm->pcm); - assert(stm->nfds > 0); - - stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd)); - assert(stm->saved_fds); - r = snd_pcm_poll_descriptors(stm->pcm, stm->saved_fds, stm->nfds); - assert((nfds_t) r == stm->nfds); - - if (alsa_register_stream(ctx, stm) != 0) { - alsa_stream_destroy(stm); - return CUBEB_ERROR; - } - - *stream = stm; - - return CUBEB_OK; -} - -static int -alsa_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) -{ - int result = CUBEB_OK; - 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, - 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, - data_callback, state_callback, user_ptr); - } - - if (result == CUBEB_OK && input_stream_params && output_stream_params) { - instm->other_stream = outstm; - outstm->other_stream = instm; - } - - if (result != CUBEB_OK && instm) { - alsa_stream_destroy(instm); - } - - *stream = outstm ? outstm : instm; - - return result; -} - -static void -alsa_stream_destroy(cubeb_stream * stm) -{ - int r; - cubeb * ctx; - - assert(stm && (stm->state == INACTIVE || - stm->state == ERROR || - stm->state == DRAINING)); - - ctx = stm->context; - - if (stm->other_stream) { - stm->other_stream->other_stream = NULL; // to stop infinite recursion - alsa_stream_destroy(stm->other_stream); - } - - pthread_mutex_lock(&stm->mutex); - if (stm->pcm) { - if (stm->state == DRAINING) { - snd_pcm_drain(stm->pcm); - } - alsa_locked_pcm_close(stm->pcm); - stm->pcm = NULL; - } - free(stm->saved_fds); - pthread_mutex_unlock(&stm->mutex); - pthread_mutex_destroy(&stm->mutex); - - r = pthread_cond_destroy(&stm->cond); - assert(r == 0); - - alsa_unregister_stream(stm); - - pthread_mutex_lock(&ctx->mutex); - assert(ctx->active_streams >= 1); - ctx->active_streams -= 1; - pthread_mutex_unlock(&ctx->mutex); - - free(stm->buffer); - - free(stm); -} - -static int -alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - int r; - cubeb_stream * stm; - snd_pcm_hw_params_t* hw_params; - cubeb_stream_params params; - params.rate = 44100; - params.format = CUBEB_SAMPLE_FLOAT32NE; - params.channels = 2; - - snd_pcm_hw_params_alloca(&hw_params); - - assert(ctx); - - r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL, NULL, NULL); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - assert(stm); - - r = snd_pcm_hw_params_any(stm->pcm, hw_params); - if (r < 0) { - return CUBEB_ERROR; - } - - r = snd_pcm_hw_params_get_channels_max(hw_params, max_channels); - if (r < 0) { - return CUBEB_ERROR; - } - - alsa_stream_destroy(stm); - - return CUBEB_OK; -} - -static int -alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) { - (void)ctx; - int r, dir; - snd_pcm_t * pcm; - snd_pcm_hw_params_t * hw_params; - - snd_pcm_hw_params_alloca(&hw_params); - - /* get a pcm, disabling resampling, so we get a rate the - * hardware/dmix/pulse/etc. supports. */ - r = snd_pcm_open(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE); - if (r < 0) { - return CUBEB_ERROR; - } - - r = snd_pcm_hw_params_any(pcm, hw_params); - if (r < 0) { - snd_pcm_close(pcm); - return CUBEB_ERROR; - } - - r = snd_pcm_hw_params_get_rate(hw_params, rate, &dir); - if (r >= 0) { - /* There is a default rate: use it. */ - snd_pcm_close(pcm); - return CUBEB_OK; - } - - /* Use a common rate, alsa may adjust it based on hw/etc. capabilities. */ - *rate = 44100; - - r = snd_pcm_hw_params_set_rate_near(pcm, hw_params, rate, NULL); - if (r < 0) { - snd_pcm_close(pcm); - return CUBEB_ERROR; - } - - snd_pcm_close(pcm); - - return CUBEB_OK; -} - -static int -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 - * machine. */ - *latency_frames = 40 * params.rate / 1000; - - return CUBEB_OK; -} - -static int -alsa_stream_start(cubeb_stream * stm) -{ - cubeb * ctx; - - assert(stm); - ctx = stm->context; - - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) { - int r = alsa_stream_start(stm->other_stream); - if (r != CUBEB_OK) - return r; - } - - pthread_mutex_lock(&stm->mutex); - /* Capture pcm must be started after initial setup/recover */ - if (stm->stream_type == SND_PCM_STREAM_CAPTURE && - snd_pcm_state(stm->pcm) == SND_PCM_STATE_PREPARED) { - snd_pcm_start(stm->pcm); - } - snd_pcm_pause(stm->pcm, 0); - gettimeofday(&stm->last_activity, NULL); - pthread_mutex_unlock(&stm->mutex); - - pthread_mutex_lock(&ctx->mutex); - if (stm->state != INACTIVE) { - pthread_mutex_unlock(&ctx->mutex); - return CUBEB_ERROR; - } - alsa_set_stream_state(stm, RUNNING); - pthread_mutex_unlock(&ctx->mutex); - - return CUBEB_OK; -} - -static int -alsa_stream_stop(cubeb_stream * stm) -{ - cubeb * ctx; - int r; - - assert(stm); - ctx = stm->context; - - if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && stm->other_stream) { - int r = alsa_stream_stop(stm->other_stream); - if (r != CUBEB_OK) - return r; - } - - pthread_mutex_lock(&ctx->mutex); - while (stm->state == PROCESSING) { - r = pthread_cond_wait(&stm->cond, &ctx->mutex); - assert(r == 0); - } - - alsa_set_stream_state(stm, INACTIVE); - pthread_mutex_unlock(&ctx->mutex); - - pthread_mutex_lock(&stm->mutex); - snd_pcm_pause(stm->pcm, 1); - pthread_mutex_unlock(&stm->mutex); - - return CUBEB_OK; -} - -static int -alsa_stream_get_position(cubeb_stream * stm, uint64_t * position) -{ - snd_pcm_sframes_t delay; - - assert(stm && position); - - pthread_mutex_lock(&stm->mutex); - - delay = -1; - if (snd_pcm_state(stm->pcm) != SND_PCM_STATE_RUNNING || - snd_pcm_delay(stm->pcm, &delay) != 0) { - *position = stm->last_position; - pthread_mutex_unlock(&stm->mutex); - return CUBEB_OK; - } - - assert(delay >= 0); - - *position = 0; - if (stm->stream_position >= (snd_pcm_uframes_t) delay) { - *position = stm->stream_position - delay; - } - - stm->last_position = *position; - - pthread_mutex_unlock(&stm->mutex); - return CUBEB_OK; -} - -static int -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. */ - if (snd_pcm_delay(stm->pcm, &delay)) { - return CUBEB_ERROR; - } - - *latency = delay; - - return CUBEB_OK; -} - -static int -alsa_stream_set_volume(cubeb_stream * stm, float volume) -{ - /* setting the volume using an API call does not seem very stable/supported */ - pthread_mutex_lock(&stm->mutex); - stm->volume = volume; - pthread_mutex_unlock(&stm->mutex); - - return CUBEB_OK; -} - -static int -alsa_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection) -{ - cubeb_device_info* device = NULL; - - if (!context) - return CUBEB_ERROR; - - uint32_t rate, max_channels; - int r; - - r = alsa_get_preferred_sample_rate(context, &rate); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - r = alsa_get_max_channel_count(context, &max_channels); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - char const * a_name = "default"; - 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->friendly_name = a_name; - device->group_id = a_name; - device->vendor_name = a_name; - device->type = type; - 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 = max_channels; - device->min_rate = rate; - device->max_rate = rate; - device->default_rate = rate; - device->latency_lo = 0; - device->latency_hi = 0; - - collection->device = device; - collection->count = 1; - - return CUBEB_OK; -} - -static int -alsa_device_collection_destroy(cubeb * context, - cubeb_device_collection * collection) -{ - assert(collection->count == 1); - (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, - .get_preferred_channel_layout = NULL, - .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_set_volume = alsa_stream_set_volume, - .stream_set_panning = 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_array_queue.h b/Externals/cubeb/src/cubeb_array_queue.h deleted file mode 100644 index a8ea4cd177..0000000000 --- a/Externals/cubeb/src/cubeb_array_queue.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_ARRAY_QUEUE_H -#define CUBEB_ARRAY_QUEUE_H - -#include -#include -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -typedef struct -{ - void ** buf; - size_t num; - size_t writePos; - size_t readPos; - pthread_mutex_t mutex; -} array_queue; - -array_queue * array_queue_create(size_t num) -{ - assert(num != 0); - 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; - new_queue->num = num; - - pthread_mutex_init(&new_queue->mutex, NULL); - - return new_queue; -} - -void array_queue_destroy(array_queue * aq) -{ - assert(aq); - - free(aq->buf); - pthread_mutex_destroy(&aq->mutex); - free(aq); -} - -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) - { - aq->buf[aq->writePos % aq->num] = item; - aq->writePos = (aq->writePos + 1) % aq->num; - ret = 0; - } - // else queue is full - pthread_mutex_unlock(&aq->mutex); - return ret; -} - -void* array_queue_pop(array_queue * aq) -{ - pthread_mutex_lock(&aq->mutex); - void * value = aq->buf[aq->readPos % aq->num]; - if(value) - { - aq->buf[aq->readPos % aq->num] = NULL; - aq->readPos = (aq->readPos + 1) % aq->num; - } - pthread_mutex_unlock(&aq->mutex); - return value; -} - -size_t array_queue_get_size(array_queue * aq) -{ - pthread_mutex_lock(&aq->mutex); - ssize_t r = aq->writePos - aq->readPos; - if (r < 0) { - r = aq->num + r; - assert(r >= 0); - } - pthread_mutex_unlock(&aq->mutex); - return (size_t)r; -} - -#if defined(__cplusplus) -} -#endif - -#endif //CUBE_ARRAY_QUEUE_H diff --git a/Externals/cubeb/src/cubeb_assert.h b/Externals/cubeb/src/cubeb_assert.h deleted file mode 100644 index 9257a2c865..0000000000 --- a/Externals/cubeb/src/cubeb_assert.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright © 2017 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_ASSERT -#define CUBEB_ASSERT - -#include -#include - -/** - * This allow using an external release assert method. This file should only - * export a function or macro called XASSERT that aborts the program. - */ - -#define XASSERT(expr) do { \ - if (!(expr)) { \ - fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \ - abort(); \ - } \ - } while (0) - -#endif diff --git a/Externals/cubeb/src/cubeb_audiotrack.c b/Externals/cubeb/src/cubeb_audiotrack.c deleted file mode 100644 index 6b7f375cd4..0000000000 --- a/Externals/cubeb/src/cubeb_audiotrack.c +++ /dev/null @@ -1,441 +0,0 @@ -/* - * Copyright © 2013 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(NDEBUG) -#define NDEBUG -#endif -#include -#include -#include -#include -#include -#include - -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "android/audiotrack_definitions.h" - -#ifndef ALOG -#if defined(DEBUG) || defined(FORCE_ALOG) -#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args) -#else -#define ALOG(args...) -#endif -#endif - -/** - * A lot of bytes for safety. It should be possible to bring this down a bit. */ -#define SIZE_AUDIOTRACK_INSTANCE 256 - -/** - * 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); - -static struct cubeb_ops const audiotrack_ops; -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); - /* 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); -}; - -struct cubeb { - struct cubeb_ops const * ops; - void * library; - struct AudioTrack klass; -}; - -struct cubeb_stream { - cubeb * context; - cubeb_stream_params params; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * instance; - void * user_ptr; - /* Number of frames that have been passed to the AudioTrack callback */ - long unsigned written; - int draining; -}; - -static void -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; - - if (stream->draining) { - return; - } - - got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount); - - stream->written += got; - - if (got != (long)b->frameCount) { - 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); - } - - break; - } - case EVENT_UNDERRUN: - ALOG("underrun in cubeb backend."); - break; - case EVENT_LOOP_END: - assert(0 && "We don't support the loop feature of audiotrack."); - break; - case EVENT_MARKER: - assert(stream->draining); - 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."); - break; - case EVENT_BUFFER_END: - assert(0 && "Should not happen."); - break; - } -} - -/* We are running on gingerbread if we found the gingerbread signature for - * getMinFrameCount */ -static int -audiotrack_version_is_gingerbread(cubeb * ctx) -{ - return ctx->klass.get_min_frame_count_gingerbread != NULL; -} - -int -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); - } else { - 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"); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -int -audiotrack_init(cubeb ** context, char const * context_name) -{ - cubeb * ctx; - struct AudioTrack* c; - - assert(context); - *context = NULL; - - ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - - /* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android - * 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on - * the first call to a dlsym'ed function. Somehow this does not happen when - * using only the name of the library. */ - ctx->library = dlopen("libmedia.so", RTLD_LAZY); - if (!ctx->library) { - ALOG("dlopen error: %s.", dlerror()); - free(ctx); - return CUBEB_ERROR; - } - - /* Recent Android first, then Gingerbread. */ - 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("_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); - if (!ctx->klass.get_min_frame_count) { - 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); - - /* 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)) { - ALOG("Could not find all the symbols we need."); - audiotrack_destroy(ctx); - return CUBEB_ERROR; - } - - ctx->ops = &audiotrack_ops; - - *context = ctx; - - return CUBEB_OK; -} - -char const * -audiotrack_get_backend_id(cubeb * context) -{ - return "audiotrack"; -} - -static int -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 */ - *max_channels = 2; - - return CUBEB_OK; -} - -static int -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. */ - int r; - - r = audiotrack_get_min_frame_count(ctx, ¶ms, (int *)latency_ms); - if (r != CUBEB_OK) { - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - status_t r; - - r = ctx->klass.get_output_samplingrate((int32_t *)rate, 3 /* MUSIC */); - - return r == 0 ? CUBEB_OK : CUBEB_ERROR; -} - -void -audiotrack_destroy(cubeb * context) -{ - assert(context); - - dlclose(context->library); - - free(context); -} - -int -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) -{ - cubeb_stream * stm; - int32_t channels; - uint32_t min_frame_count; - - assert(ctx && stream); - - assert(!input_stream_params && "not supported"); - if (input_device || output_device) { - /* Device selection not yet implemented. */ - return CUBEB_ERROR_DEVICE_UNAVAILABLE; - } - - if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE || - output_stream_params->format == CUBEB_SAMPLE_FLOAT32BE) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) { - return CUBEB_ERROR; - } - - stm = calloc(1, sizeof(*stm)); - assert(stm); - - stm->context = ctx; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->params = *output_stream_params; - - stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1); - (*(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; - } else { - 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); - - if (ctx->klass.check(stm->instance)) { - ALOG("stream not initialized properly."); - audiotrack_stream_destroy(stm); - return CUBEB_ERROR; - } - - *stream = stm; - - return CUBEB_OK; -} - -void -audiotrack_stream_destroy(cubeb_stream * stream) -{ - assert(stream->context); - - stream->context->klass.dtor(stream->instance); - - free(stream->instance); - stream->instance = NULL; - free(stream); -} - -int -audiotrack_stream_start(cubeb_stream * stream) -{ - assert(stream->instance); - - stream->context->klass.start(stream->instance); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); - - return CUBEB_OK; -} - -int -audiotrack_stream_stop(cubeb_stream * stream) -{ - assert(stream->instance); - - stream->context->klass.pause(stream->instance); - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); - - return CUBEB_OK; -} - -int -audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position) -{ - uint32_t p; - - assert(stream->instance && position); - stream->context->klass.get_position(stream->instance, &p); - *position = p; - - return CUBEB_OK; -} - -int -audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency) -{ - assert(stream->instance && latency); - - /* Android returns the latency in ms, we want it in frames. */ - *latency = stream->context->klass.latency(stream->instance); - /* with rate <= 96000, we won't overflow until 44.739 seconds of latency */ - *latency = (*latency * stream->params.rate) / 1000; - - return 0; -} - -int -audiotrack_stream_set_volume(cubeb_stream * stream, float volume) -{ - status_t status; - - status = stream->context->klass.set_volume(stream->instance, volume, volume); - - if (status) { - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -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, - .get_preferred_channel_layout = NULL, - .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_set_volume = audiotrack_stream_set_volume, - .stream_set_panning = 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 deleted file mode 100644 index cfde47dd74..0000000000 --- a/Externals/cubeb/src/cubeb_audiounit.cpp +++ /dev/null @@ -1,3367 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#undef NDEBUG - -#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_mixer.h" -#include "cubeb_panner.h" -#if !TARGET_OS_IPHONE -#include "cubeb_osx_run_loop.h" -#endif -#include "cubeb_resampler.h" -#include "cubeb_ring_array.h" -#include -#include -#include -#include - -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 -typedef UInt32 AudioFormatFlags; -#endif - -#define AU_OUT_BUS 0 -#define AU_IN_BUS 1 - -const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb"; - -#ifdef ALOGV -#undef ALOGV -#endif -#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__);}) - -/* 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 - * frames. */ -const uint32_t SAFE_MIN_LATENCY_FRAMES = 256; -const uint32_t SAFE_MAX_LATENCY_FRAMES = 512; - -void audiounit_stream_stop_internal(cubeb_stream * stm); -void audiounit_stream_start_internal(cubeb_stream * stm); -static void audiounit_close_stream(cubeb_stream *stm); -static int audiounit_setup_stream(cubeb_stream *stm); -static std::vector -audiounit_get_devices_of_type(cubeb_device_type devtype); - -extern cubeb_ops const audiounit_ops; - -struct cubeb { - cubeb_ops const * ops = &audiounit_ops; - owned_critical_section mutex; - std::atomic active_streams{ 0 }; - uint32_t global_latency_frames = 0; - cubeb_device_collection_changed_callback collection_changed_callback = nullptr; - void * collection_changed_user_ptr = nullptr; - /* Differentiate input from output devices. */ - cubeb_device_type collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN; - std::vector devtype_device_array; - // The queue is asynchronously deallocated once all references to it are released - dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL); - // Current used channel layout - std::atomic layout{ CUBEB_LAYOUT_UNDEFINED }; -}; - -static std::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 std::unique_ptr(acl, free); -} - -enum io_side { - INPUT, - OUTPUT, -}; - -static char const * -to_string(io_side side) -{ - switch (side) { - case INPUT: - return "input"; - case OUTPUT: - return "output"; - } -} - -typedef uint32_t device_flags_value; - -enum device_flags { - DEV_UKNOWN = 0x00, /* Unkown */ - 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 */ -}; - -struct device_info { - AudioDeviceID id = kAudioObjectUnknown; - device_flags_value flags = DEV_UKNOWN; -}; - -struct cubeb_stream { - explicit cubeb_stream(cubeb * context); - - cubeb * context; - cubeb_data_callback data_callback = nullptr; - cubeb_state_callback state_callback = nullptr; - 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_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED }; - device_info input_device; - device_info output_device; - /* User pointer of data_callback */ - void * user_ptr = nullptr; - /* Format descriptions */ - AudioStreamBasicDescription input_desc; - AudioStreamBasicDescription output_desc; - /* I/O AudioUnits */ - AudioUnit input_unit = nullptr; - AudioUnit output_unit = nullptr; - /* I/O device sample rate */ - Float64 input_hw_rate = 0; - Float64 output_hw_rate = 0; - /* Expected I/O thread interleave, - * calculated from I/O hw rate. */ - int expected_output_callbacks_in_a_row = 0; - owned_critical_section mutex; - /* Hold the input samples in every - * input callback iteration */ - std::unique_ptr input_linear_buffer; - owned_critical_section input_linear_buffer_lock; - // After the resampling some input data remains stored inside - // the resampler. This number is used in order to calculate - // the number of extra silence frames in input. - std::atomic available_input_frames{ 0 }; - /* Frames on input buffer */ - std::atomic input_buffer_frames{ 0 }; - /* Frame counters */ - std::atomic frames_played{ 0 }; - uint64_t frames_queued = 0; - std::atomic frames_read{ 0 }; - std::atomic shutdown{ true }; - std::atomic draining{ false }; - /* Latency requested by the user. */ - uint32_t latency_frames = 0; - std::atomic current_latency_frames{ 0 }; - uint64_t hw_latency_frames = UINT64_MAX; - std::atomic panning{ 0 }; - std::unique_ptr resampler; - /* This is true if a device change callback is currently running. */ - std::atomic switching_device{ false }; - std::atomic buffer_size_change_state{ false }; - AudioDeviceID aggregate_device_id = 0; // the aggregate device id - AudioObjectID plugin_id = 0; // used to create aggregate device - /* Mixer interface */ - std::unique_ptr mixer; -}; - -bool has_input(cubeb_stream * stm) -{ - return stm->input_stream_params.rate != 0; -} - -bool has_output(cubeb_stream * stm) -{ - return stm->output_stream_params.rate != 0; -} - -cubeb_channel -channel_label_to_cubeb_channel(UInt32 label) -{ - switch (label) { - case kAudioChannelLabel_Mono: return CHANNEL_MONO; - case kAudioChannelLabel_Left: return CHANNEL_LEFT; - case kAudioChannelLabel_Right: return CHANNEL_RIGHT; - case kAudioChannelLabel_Center: return CHANNEL_CENTER; - case kAudioChannelLabel_LFEScreen: return CHANNEL_LFE; - case kAudioChannelLabel_LeftSurround: return CHANNEL_LS; - case kAudioChannelLabel_RightSurround: return CHANNEL_RS; - case kAudioChannelLabel_RearSurroundLeft: return CHANNEL_RLS; - case kAudioChannelLabel_RearSurroundRight: return CHANNEL_RRS; - case kAudioChannelLabel_CenterSurround: return CHANNEL_RCENTER; - case kAudioChannelLabel_Unknown: return CHANNEL_UNMAPPED; - default: return CHANNEL_INVALID; - } -} - -AudioChannelLabel -cubeb_channel_to_channel_label(cubeb_channel channel) -{ - switch (channel) { - case CHANNEL_MONO: return kAudioChannelLabel_Mono; - case CHANNEL_LEFT: return kAudioChannelLabel_Left; - case CHANNEL_RIGHT: return kAudioChannelLabel_Right; - case CHANNEL_CENTER: return kAudioChannelLabel_Center; - case CHANNEL_LFE: return kAudioChannelLabel_LFEScreen; - case CHANNEL_LS: return kAudioChannelLabel_LeftSurround; - case CHANNEL_RS: return kAudioChannelLabel_RightSurround; - case CHANNEL_RLS: return kAudioChannelLabel_RearSurroundLeft; - case CHANNEL_RRS: return kAudioChannelLabel_RearSurroundRight; - case CHANNEL_RCENTER: return kAudioChannelLabel_CenterSurround; - case CHANNEL_UNMAPPED: return kAudioChannelLabel_Unknown; - default: return kAudioChannelLabel_Unknown; - } -} - -#if TARGET_OS_IPHONE -typedef UInt32 AudioDeviceID; -typedef UInt32 AudioObjectID; - -#define AudioGetCurrentHostTime mach_absolute_time - -uint64_t -AudioConvertHostTimeToNanos(uint64_t host_time) -{ - static struct mach_timebase_info timebase_info; - static bool initialized = false; - if (!initialized) { - mach_timebase_info(&timebase_info); - initialized = true; - } - - long double answer = host_time; - if (timebase_info.numer != timebase_info.denom) { - answer *= timebase_info.numer; - answer /= timebase_info.denom; - } - return (uint64_t)answer; -} -#endif - -static int64_t -audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream) -{ - if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) { - return 0; - } - - uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime); - uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); - - return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL; -} - -static void -audiounit_set_global_latency(cubeb_stream * stm, uint32_t latency_frames) -{ - stm->mutex.assert_current_thread_owns(); - assert(stm->context->active_streams == 1); - stm->context->global_latency_frames = latency_frames; -} - -static void -audiounit_make_silent(AudioBuffer * ioData) -{ - assert(ioData); - assert(ioData->mData); - memset(ioData->mData, 0, ioData->mDataByteSize); -} - -static OSStatus -audiounit_render_input(cubeb_stream * stm, - AudioUnitRenderActionFlags * flags, - AudioTimeStamp const * tstamp, - UInt32 bus, - UInt32 input_frames) -{ - /* Create the AudioBufferList to store input. */ - AudioBufferList input_buffer_list; - 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.mNumberBuffers = 1; - - /* Render input samples */ - OSStatus r = AudioUnitRender(stm->input_unit, - flags, - tstamp, - bus, - input_frames, - &input_buffer_list); - - if (r != noErr) { - LOG("AudioUnitRender rv=%d", r); - return r; - } - - /* Copy input data in linear buffer. */ - { - auto_lock l(stm->input_linear_buffer_lock); - stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData, - input_frames * stm->input_desc.mChannelsPerFrame); - } - - /* Advance input frame counter. */ - assert(input_frames > 0); - stm->frames_read += input_frames; - stm->available_input_frames += input_frames; - - ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %d.", - 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->available_input_frames.load()); - - return noErr; -} - -static OSStatus -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); - - assert(stm->input_unit != NULL); - assert(AU_IN_BUS == bus); - - if (stm->shutdown) { - ALOG("(%p) input shutdown", stm); - return noErr; - } - - OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames); - if (r != noErr) { - return r; - } - - // Full Duplex. We'll call data_callback in the AudioUnit output callback. - if (stm->output_unit != NULL) { - return noErr; - } - - /* Input only. Call the user callback through resampler. - Resampler will deliver input buffer in the correct rate. */ - { - auto_lock l(stm->input_linear_buffer_lock); - 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); - assert(outframes >= 0); - - // Reset input buffer - stm->input_linear_buffer->clear(); - } - - return noErr; -} - -static uint32_t -minimum_resampling_input_frames(cubeb_stream *stm) -{ - return ceilf(stm->input_hw_rate / stm->output_hw_rate * stm->input_buffer_frames); -} - -static bool -is_extra_input_needed(cubeb_stream * stm) -{ - /* If the output callback came first and this is a duplex stream, we need to - * fill in some additional silence in the resampler. - * Otherwise, if we had more than expected callbacks in a row, or we're currently - * switching, we add some silence as well to compensate for the fact that - * we're lacking some input data. */ - return stm->frames_read == 0 || - stm->available_input_frames.load() < minimum_resampling_input_frames(stm); -} - -static void -audiounit_mix_output_buffer(cubeb_stream * stm, - long output_frames, - void * output_buffer, - unsigned long output_buffer_length) -{ - cubeb_stream_params output_mixer_params = { - stm->output_stream_params.format, - stm->output_stream_params.rate, - CUBEB_CHANNEL_LAYOUT_MAPS[stm->context->layout].channels, - stm->context->layout - }; - - // The downmixing(from 5.1) supports in-place conversion, so we can use - // the same buffer for both input and output of the mixer. - cubeb_mixer_mix(stm->mixer.get(), output_frames, - output_buffer, output_buffer_length, - output_buffer, output_buffer_length, - &stm->output_stream_params, &output_mixer_params); -} - -static OSStatus -audiounit_output_callback(void * user_ptr, - AudioUnitRenderActionFlags * /* flags */, - AudioTimeStamp const * tstamp, - UInt32 bus, - UInt32 output_frames, - AudioBufferList * outBufferList) -{ - assert(AU_OUT_BUS == bus); - assert(outBufferList->mNumberBuffers == 1); - - cubeb_stream * stm = static_cast(user_ptr); - - ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %d.", - stm, - (unsigned int) outBufferList->mNumberBuffers, - (unsigned int) outBufferList->mBuffers[0].mDataByteSize, - (unsigned int) outBufferList->mBuffers[0].mNumberChannels, - (unsigned int) output_frames, - stm->available_input_frames.load()); - - long input_frames = 0, input_frames_before_fill = 0; - void * output_buffer = NULL, * input_buffer = NULL; - - if (stm->shutdown) { - ALOG("(%p) output shutdown.", stm); - audiounit_make_silent(&outBufferList->mBuffers[0]); - return noErr; - } - - stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm); - if (stm->draining) { - OSStatus r = AudioOutputUnitStop(stm->output_unit); - assert(r == 0); - if (stm->input_unit) { - r = AudioOutputUnitStop(stm->input_unit); - assert(r == 0); - } - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - audiounit_make_silent(&outBufferList->mBuffers[0]); - return noErr; - } - /* Get output buffer. */ - output_buffer = outBufferList->mBuffers[0].mData; - /* If Full duplex get also input buffer */ - if (stm->input_unit != NULL) { - if (is_extra_input_needed(stm)) { - uint32_t min_input_frames = minimum_resampling_input_frames(stm); - { - auto_lock l(stm->input_linear_buffer_lock); - stm->input_linear_buffer->push_silence(min_input_frames * stm->input_desc.mChannelsPerFrame); - } - stm->available_input_frames += min_input_frames; - - ALOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," : - stm->switching_device ? "Device switching," : "Drop out,", min_input_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 pushed inside resampler. - input_frames_before_fill = input_frames; - } - - /* Call user callback through resampler. */ - long outframes = cubeb_resampler_fill(stm->resampler.get(), - input_buffer, - input_buffer ? &input_frames : NULL, - output_buffer, - output_frames); - - if (input_buffer) { - // Decrease counter by the number of frames used by resampler - stm->available_input_frames -= input_frames; - assert(stm->available_input_frames.load() >= 0); - // Pop from the buffer the frames pushed to the resampler. - auto_lock l(stm->input_linear_buffer_lock); - stm->input_linear_buffer->pop(input_frames_before_fill * stm->input_desc.mChannelsPerFrame); - } - - if (outframes < 0 || outframes > output_frames) { - stm->shutdown = true; - OSStatus r = AudioOutputUnitStop(stm->output_unit); - assert(r == 0); - if (stm->input_unit) { - r = AudioOutputUnitStop(stm->input_unit); - assert(r == 0); - } - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - audiounit_make_silent(&outBufferList->mBuffers[0]); - return noErr; - } - - size_t outbpf = stm->output_desc.mBytesPerFrame; - stm->draining = (UInt32) outframes < output_frames; - stm->frames_played = stm->frames_queued; - stm->frames_queued += outframes; - - AudioFormatFlags outaff = stm->output_desc.mFormatFlags; - float panning = (stm->output_desc.mChannelsPerFrame == 2) ? - stm->panning.load(std::memory_order_relaxed) : 0.0f; - - /* Post process output samples. */ - if (stm->draining) { - /* Clear missing frames (silence) */ - memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf); - } - /* Pan stereo. */ - if (panning != 0.0f) { - if (outaff & kAudioFormatFlagIsFloat) { - cubeb_pan_stereo_buffer_float((float*)output_buffer, outframes, panning); - } else if (outaff & kAudioFormatFlagIsSignedInteger) { - cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning); - } - } - - /* Mixing */ - if (stm->output_stream_params.layout != CUBEB_LAYOUT_UNDEFINED) { - unsigned long output_buffer_length = outBufferList->mBuffers[0].mDataByteSize; - audiounit_mix_output_buffer(stm, output_frames, output_buffer, output_buffer_length); - } - - return noErr; -} - -extern "C" { -int -audiounit_init(cubeb ** context, char const * /* context_name */) -{ -#if !TARGET_OS_IPHONE - cubeb_set_coreaudio_notification_runloop(); -#endif - - *context = new cubeb; - - return CUBEB_OK; -} -} - -static char const * -audiounit_get_backend_id(cubeb * /* ctx */) -{ - return "audiounit"; -} - -#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_uninstall_device_changed_callback(cubeb_stream * stm); -static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type); - -static int -audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side) -{ - assert(stm); - - device_info * info = nullptr; - cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN; - - if (side == INPUT) { - info = &stm->input_device; - type = CUBEB_DEVICE_TYPE_INPUT; - } else if (side == OUTPUT) { - info = &stm->output_device; - type = CUBEB_DEVICE_TYPE_OUTPUT; - } - memset(info, 0, sizeof(device_info)); - info->id = id; - - if (side == INPUT) { - info->flags |= DEV_INPUT; - } else if (side == OUTPUT) { - info->flags |= DEV_OUTPUT; - } - - AudioDeviceID default_device_id = audiounit_get_default_device_id(type); - if (default_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; - } - if (id == kAudioObjectUnknown) { - info->id = default_device_id; - info->flags |= DEV_SELECTED_DEFAULT; - } - - if (info->id == default_device_id) { - info->flags |= DEV_SYSTEM_DEFAULT; - } - - assert(info->id); - assert(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) -{ - auto_lock context_lock(stm->context->mutex); - assert((flags & DEV_INPUT && stm->input_unit) || - (flags & DEV_OUTPUT && stm->output_unit)); - if (!stm->shutdown) { - audiounit_stream_stop_internal(stm); - } - - int r = audiounit_uninstall_device_changed_callback(stm); - if (r != CUBEB_OK) { - LOG("(%p) Could not uninstall all device change listeners.", stm); - } - - { - auto_lock lock(stm->mutex); - float volume = 0.0; - int vol_rv = CUBEB_ERROR; - if (stm->output_unit) { - vol_rv = audiounit_stream_get_volume(stm, &volume); - } - - audiounit_close_stream(stm); - - /* Reinit occurs in 2 cases. When the device is not alive any more and when the - * default system device change. In both cases cubeb switch on the new default - * device. This is considered the most expected behavior for the user. */ - if (flags & DEV_INPUT) { - r = audiounit_set_device_info(stm, 0, INPUT); - assert(r == CUBEB_OK); - } - /* Always use the default output on reinit. This is not correct in every case - * but it is sufficient for Firefox and prevent reinit from reporting failures. - * It will change soon when reinit mechanism will be updated. */ - r = audiounit_set_device_info(stm, 0, OUTPUT); - assert(r == CUBEB_OK); - - if (audiounit_setup_stream(stm) != CUBEB_OK) { - LOG("(%p) Stream reinit failed.", stm); - return CUBEB_ERROR; - } - - if (vol_rv == CUBEB_OK) { - audiounit_stream_set_volume(stm, volume); - } - - // Reset input frames to force new stream pre-buffer - // silence if needed, check `is_extra_input_needed()` - stm->frames_read = 0; - - // If the stream was running, start it again. - if (!stm->shutdown) { - audiounit_stream_start_internal(stm); - } - } - return CUBEB_OK; -} - -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"; - } -} - -static OSStatus -audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count, - const AudioObjectPropertyAddress * addresses, - void * 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); - return noErr; - } - stm->switching_device = true; - device_flags_value switch_side = DEV_UKNOWN; - - 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); - // Allow restart to choose the new default - switch_side |= DEV_OUTPUT; - } - break; - case kAudioHardwarePropertyDefaultInputDevice: { - LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id=%d", (unsigned int) i, id); - // Allow restart to choose the new default - switch_side |= DEV_INPUT; - } - 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; - } - // Allow restart to choose the new default. Event register only for input. - switch_side |= DEV_INPUT; - } - break; - case kAudioDevicePropertyDataSource: { - LOG("Event[%u] - mSelector == kAudioHardwarePropertyDataSource for id=%d", (unsigned int) i, id); - if (stm->input_unit) { - switch_side |= DEV_INPUT; - } - if (stm->output_unit) { - switch_side |= DEV_OUTPUT; - } - } - break; - default: - LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector); - stm->switching_device = false; - return noErr; - } - } - - for (UInt32 i = 0; i < address_count; i++) { - 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; - } - } - } - - // Use a new thread, through the queue, to avoid deadlock when calling - // Get/SetProperties method from inside notify callback - dispatch_async(stm->context->serial_queue, ^() { - if (audiounit_reinit_stream(stm, switch_side) != CUBEB_OK) { - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); - LOG("(%p) Could not reopen the stream after switching.", stm); - } - stm->switching_device = false; - }); - - return noErr; -} - -OSStatus -audiounit_add_listener(cubeb_stream * stm, AudioDeviceID id, AudioObjectPropertySelector selector, - AudioObjectPropertyScope scope, AudioObjectPropertyListenerProc listener) -{ - AudioObjectPropertyAddress address = { - selector, - scope, - kAudioObjectPropertyElementMaster - }; - - return AudioObjectAddPropertyListener(id, &address, listener, stm); -} - -OSStatus -audiounit_remove_listener(cubeb_stream * stm, AudioDeviceID id, - AudioObjectPropertySelector selector, - AudioObjectPropertyScope scope, - AudioObjectPropertyListenerProc listener) -{ - AudioObjectPropertyAddress address = { - selector, - scope, - kAudioObjectPropertyElementMaster - }; - - return AudioObjectRemovePropertyListener(id, &address, listener, stm); -} - -static int -audiounit_install_device_changed_callback(cubeb_stream * stm) -{ - OSStatus rv; - 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. */ - rv = audiounit_add_listener(stm, stm->output_device.id, kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback); - if (rv != noErr) { - 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. */ - rv = audiounit_add_listener(stm, stm->input_device.id, kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback); - if (rv != noErr) { - 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. */ - rv = audiounit_add_listener(stm, stm->input_device.id, kAudioDevicePropertyDeviceIsAlive, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (rv != noErr) { - LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", rv, stm->input_device.id); - r = CUBEB_ERROR; - } - } - - return r; -} - -static int -audiounit_install_system_changed_callback(cubeb_stream * stm) -{ - OSStatus r; - - 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 - * dropdown list. */ - r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r); - return CUBEB_ERROR; - } - } - - if (stm->input_unit) { - /* This event will notify us when the default input device changes. */ - r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r); - return CUBEB_ERROR; - } - } - - return CUBEB_OK; -} - -static int -audiounit_uninstall_device_changed_callback(cubeb_stream * stm) -{ - OSStatus rv; - // Failing to uninstall listeners is not a fatal error. - int r = CUBEB_OK; - - if (stm->output_unit) { - rv = audiounit_remove_listener(stm, stm->output_device.id, kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback); - if (rv != noErr) { - LOG("AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id); - r = CUBEB_ERROR; - } - } - - if (stm->input_unit) { - rv = audiounit_remove_listener(stm, stm->input_device.id, kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback); - if (rv != noErr) { - LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id); - r = CUBEB_ERROR; - } - - rv = audiounit_remove_listener(stm, stm->input_device.id, kAudioDevicePropertyDeviceIsAlive, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (rv != noErr) { - LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", rv, stm->input_device.id); - r = CUBEB_ERROR; - } - } - return r; -} - -static int -audiounit_uninstall_system_changed_callback(cubeb_stream * stm) -{ - OSStatus r; - - if (stm->output_unit) { - r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - return CUBEB_ERROR; - } - } - - if (stm->input_unit) { - r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice, - kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback); - if (r != noErr) { - return CUBEB_ERROR; - } - } - return CUBEB_OK; -} - -/* Get the acceptable buffer size (in frames) that this device can work with. */ -static int -audiounit_get_acceptable_latency_range(AudioValueRange * latency_range) -{ - UInt32 size; - OSStatus r; - AudioDeviceID output_device_id; - AudioObjectPropertyAddress output_device_buffer_size_range = { - kAudioDevicePropertyBufferFrameSizeRange, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (output_device_id == kAudioObjectUnknown) { - LOG("Could not get default output device id."); - return CUBEB_ERROR; - } - - /* Get the buffer size range this device supports */ - size = sizeof(*latency_range); - - r = AudioObjectGetPropertyData(output_device_id, - &output_device_buffer_size_range, - 0, - NULL, - &size, - latency_range); - if (r != noErr) { - LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} -#endif /* !TARGET_OS_IPHONE */ - -static AudioObjectID -audiounit_get_default_device_id(cubeb_device_type type) -{ - AudioObjectPropertyAddress adr = { 0, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster }; - AudioDeviceID devid; - UInt32 size; - - if (type == CUBEB_DEVICE_TYPE_OUTPUT) { - adr.mSelector = kAudioHardwarePropertyDefaultOutputDevice; - } else if (type == CUBEB_DEVICE_TYPE_INPUT) { - adr.mSelector = kAudioHardwarePropertyDefaultInputDevice; - } else { - return kAudioObjectUnknown; - } - - size = sizeof(AudioDeviceID); - if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devid) != noErr) { - return kAudioObjectUnknown; - } - - return devid; -} - -int -audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ -#if TARGET_OS_IPHONE - //TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels] - *max_channels = 2; -#else - UInt32 size; - OSStatus r; - AudioDeviceID output_device_id; - AudioStreamBasicDescription stream_format; - AudioObjectPropertyAddress stream_format_address = { - kAudioDevicePropertyStreamFormat, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - assert(ctx && max_channels); - - output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (output_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; - } - - size = sizeof(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; - } - - *max_channels = stream_format.mChannelsPerFrame; -#endif - return CUBEB_OK; -} - -static int -audiounit_get_min_latency(cubeb * /* ctx */, - cubeb_stream_params /* params */, - uint32_t * latency_frames) -{ -#if TARGET_OS_IPHONE - //TODO: [[AVAudioSession sharedInstance] inputLatency] - return CUBEB_ERROR_NOT_SUPPORTED; -#else - AudioValueRange latency_range; - if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) { - LOG("Could not get acceptable latency range."); - return CUBEB_ERROR; - } - - *latency_frames = std::max(latency_range.mMinimum, - SAFE_MIN_LATENCY_FRAMES); -#endif - - return CUBEB_OK; -} - -static int -audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate) -{ -#if TARGET_OS_IPHONE - //TODO - return CUBEB_ERROR_NOT_SUPPORTED; -#else - UInt32 size; - OSStatus r; - Float64 fsamplerate; - AudioDeviceID output_device_id; - AudioObjectPropertyAddress samplerate_address = { - kAudioDevicePropertyNominalSampleRate, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster - }; - - output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (output_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; - } - - size = sizeof(fsamplerate); - r = AudioObjectGetPropertyData(output_device_id, - &samplerate_address, - 0, - NULL, - &size, - &fsamplerate); - - if (r != noErr) { - return CUBEB_ERROR; - } - - *rate = static_cast(fsamplerate); -#endif - return CUBEB_OK; -} - -static cubeb_channel_layout -audiounit_convert_channel_layout(AudioChannelLayout * layout) -{ - if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) { - // kAudioChannelLayoutTag_UseChannelBitmap - // kAudioChannelLayoutTag_Mono - // kAudioChannelLayoutTag_Stereo - // .... - LOG("Only handle UseChannelDescriptions for now.\n"); - return CUBEB_LAYOUT_UNDEFINED; - } - - // This devices has more channels that we can support, bail out. - if (layout->mNumberChannelDescriptions >= CHANNEL_MAX) { - LOG("Audio device has more than %d channels, bailing out.", CHANNEL_MAX); - return CUBEB_LAYOUT_UNDEFINED; - } - - cubeb_channel_map cm; - cm.channels = layout->mNumberChannelDescriptions; - for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) { - cm.map[i] = channel_label_to_cubeb_channel(layout->mChannelDescriptions[i].mChannelLabel); - } - - return cubeb_channel_map_to_layout(&cm); -} - -static cubeb_channel_layout -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); - if (rv != noErr) { - LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", rv); - return CUBEB_LAYOUT_UNDEFINED; - } - assert(size > 0); - - auto layout = make_sized_audio_channel_layout(size); - rv = AudioUnitGetProperty(output_unit, - kAudioUnitProperty_AudioChannelLayout, - kAudioUnitScope_Output, - AU_OUT_BUS, - layout.get(), - &size); - if (rv != noErr) { - LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv); - return CUBEB_LAYOUT_UNDEFINED; - } - - return audiounit_convert_channel_layout(layout.get()); -} - -static cubeb_channel_layout -audiounit_get_preferred_channel_layout() -{ - OSStatus rv = noErr; - UInt32 size = 0; - AudioDeviceID id; - - id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (id == kAudioObjectUnknown) { - return CUBEB_LAYOUT_UNDEFINED; - } - - AudioObjectPropertyAddress adr = { kAudioDevicePropertyPreferredChannelLayout, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster }; - rv = AudioObjectGetPropertyDataSize(id, &adr, 0, NULL, &size); - if (rv != noErr) { - return CUBEB_LAYOUT_UNDEFINED; - } - assert(size > 0); - - auto layout = make_sized_audio_channel_layout(size); - rv = AudioObjectGetPropertyData(id, &adr, 0, NULL, &size, layout.get()); - if (rv != noErr) { - return CUBEB_LAYOUT_UNDEFINED; - } - - return audiounit_convert_channel_layout(layout.get()); -} - -static int audiounit_create_unit(AudioUnit * unit, device_info * device); - -static int -audiounit_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout) -{ - // The preferred layout is only returned when the connected sound device - // (e.g. ASUS Xonar U7), has preferred layout setting. - // For default output on Mac, there is no preferred channel layout, - // so it might return UNDEFINED. - *layout = audiounit_get_preferred_channel_layout(); - - // If the preferred channel layout is UNDEFINED, then we try to access the - // current applied channel layout. - if (*layout == CUBEB_LAYOUT_UNDEFINED) { - // If we already have at least one cubeb stream, then the current channel - // layout must be updated. We can return it directly. - if (ctx->active_streams) { - *layout = ctx->layout; - return CUBEB_OK; - } - - // If there is no existed stream, then we create a default ouput unit and - // use it to get the current used channel layout. - AudioUnit output_unit = nullptr; - device_info default_out_device; - default_out_device.id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - default_out_device.flags = (DEV_OUTPUT | DEV_SYSTEM_DEFAULT); - if (default_out_device.id != kAudioObjectUnknown) { - audiounit_create_unit(&output_unit, &default_out_device); - *layout = audiounit_get_current_channel_layout(output_unit); - } - } - - if (*layout == CUBEB_LAYOUT_UNDEFINED) { - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static OSStatus audiounit_remove_device_listener(cubeb * context); - -static void -audiounit_destroy(cubeb * ctx) -{ - // Disabling this assert for bug 1083664 -- we seem to leak a stream - // assert(ctx->active_streams == 0); - if (ctx->active_streams > 0) { - LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, ctx->active_streams.load()); - } - - { - auto_lock lock(ctx->mutex); - /* Unregister the callback if necessary. */ - if (ctx->collection_changed_callback) { - audiounit_remove_device_listener(ctx); - } - } - - delete ctx; -} - -static void audiounit_stream_destroy(cubeb_stream * stm); - -static int -audio_stream_desc_init(AudioStreamBasicDescription * ss, - const cubeb_stream_params * stream_params) -{ - switch (stream_params->format) { - case CUBEB_SAMPLE_S16LE: - ss->mBitsPerChannel = 16; - ss->mFormatFlags = kAudioFormatFlagIsSignedInteger; - break; - case CUBEB_SAMPLE_S16BE: - ss->mBitsPerChannel = 16; - ss->mFormatFlags = kAudioFormatFlagIsSignedInteger | - kAudioFormatFlagIsBigEndian; - break; - case CUBEB_SAMPLE_FLOAT32LE: - ss->mBitsPerChannel = 32; - ss->mFormatFlags = kAudioFormatFlagIsFloat; - break; - case CUBEB_SAMPLE_FLOAT32BE: - ss->mBitsPerChannel = 32; - ss->mFormatFlags = kAudioFormatFlagIsFloat | - kAudioFormatFlagIsBigEndian; - break; - default: - return CUBEB_ERROR_INVALID_FORMAT; - } - - ss->mFormatID = kAudioFormatLinearPCM; - ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked; - ss->mSampleRate = stream_params->rate; - ss->mChannelsPerFrame = stream_params->channels; - - ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame; - ss->mFramesPerPacket = 1; - ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket; - - ss->mReserved = 0; - - return CUBEB_OK; -} - -void -audiounit_init_mixer(cubeb_stream * stm) -{ - // We only handle downmixing for now. - // The audio rendering mechanism on OS X will drop the extra channels beyond - // the channels that audio device can provide, so we need to downmix the - // audio data by ourselves to keep all the information. - stm->mixer.reset(cubeb_mixer_create(stm->output_stream_params.format, - CUBEB_MIXER_DIRECTION_DOWNMIX)); -} - -static int -audiounit_set_channel_layout(AudioUnit unit, - io_side side, - const cubeb_stream_params * stream_params) -{ - if (side != OUTPUT) { - return CUBEB_ERROR; - } - - assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED); - assert(stream_params->channels == CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].channels); - - OSStatus r; - size_t size = sizeof(AudioChannelLayout); - auto layout = make_sized_audio_channel_layout(size); - - switch (stream_params->layout) { - case CUBEB_LAYOUT_DUAL_MONO: - case CUBEB_LAYOUT_STEREO: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; - break; - case CUBEB_LAYOUT_MONO: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_Mono; - break; - case CUBEB_LAYOUT_3F: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_0; - break; - case CUBEB_LAYOUT_2F1: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_1; - break; - case CUBEB_LAYOUT_3F1: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_1; - break; - case CUBEB_LAYOUT_2F2: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_2_2; - break; - case CUBEB_LAYOUT_3F2: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_ITU_3_2; - break; - case CUBEB_LAYOUT_3F2_LFE: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_AudioUnit_5_1; - break; - default: - layout->mChannelLayoutTag = kAudioChannelLayoutTag_Unknown; - break; - } - - // For those layouts that can't be matched to coreaudio's predefined layout, - // we use customized layout. - if (layout->mChannelLayoutTag == kAudioChannelLayoutTag_Unknown) { - size = offsetof(AudioChannelLayout, mChannelDescriptions[stream_params->channels]); - layout = make_sized_audio_channel_layout(size); - layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions; - layout->mNumberChannelDescriptions = stream_params->channels; - for (UInt32 i = 0 ; i < stream_params->channels ; ++i) { - layout->mChannelDescriptions[i].mChannelLabel = - cubeb_channel_to_channel_label(CHANNEL_INDEX_TO_ORDER[stream_params->layout][i]); - layout->mChannelDescriptions[i].mChannelFlags = kAudioChannelFlags_AllOff; - } - } - - r = AudioUnitSetProperty(unit, - kAudioUnitProperty_AudioChannelLayout, - kAudioUnitScope_Input, - AU_OUT_BUS, - layout.get(), - size); - if (r != noErr) { - LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", to_string(side), r); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -void -audiounit_layout_init(cubeb_stream * stm, io_side side) -{ - // We currently don't support the input layout setting. - if (side == INPUT) { - return; - } - - audiounit_set_channel_layout(stm->output_unit, OUTPUT, &stm->output_stream_params); - - // Update the current used channel layout for the cubeb context. - // Notice that this channel layout may be different from the layout we set above, - // because OSX doesn't return error when the output device can NOT provide - // our desired layout. Thus, we update the layout evertime when the cubeb_stream - // is created and use it when we need to mix audio data. - stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit); -} - -static std::vector -audiounit_get_sub_devices(AudioDeviceID device_id) -{ - std::vector sub_devices; - AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - UInt32 size = 0; - OSStatus rv = AudioObjectGetPropertyDataSize(device_id, - &property_address, - 0, - nullptr, - &size); - - if (rv != noErr) { - sub_devices.push_back(device_id); - return sub_devices; - } - - uint32_t count = static_cast(size / sizeof(AudioObjectID)); - sub_devices.resize(count); - rv = AudioObjectGetPropertyData(device_id, - &property_address, - 0, - nullptr, - &size, - sub_devices.data()); - if (rv != noErr) { - sub_devices.clear(); - sub_devices.push_back(device_id); - } else { - LOG("Found %u sub-devices", count); - } - return sub_devices; -} - -static int -audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID * aggregate_device_id) -{ - AudioObjectPropertyAddress address_plugin_bundle_id = { kAudioHardwarePropertyPlugInForBundleID, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - UInt32 size = 0; - OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, - &address_plugin_bundle_id, - 0, NULL, - &size); - if (r != noErr) { - LOG("AudioHardwareGetPropertyInfo/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r); - return CUBEB_ERROR; - } - - AudioValueTranslation translation_value; - CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio"); - translation_value.mInputData = &in_bundle_ref; - translation_value.mInputDataSize = sizeof(in_bundle_ref); - translation_value.mOutputData = plugin_id; - translation_value.mOutputDataSize = sizeof(*plugin_id); - - r = AudioObjectGetPropertyData(kAudioObjectSystemObject, - &address_plugin_bundle_id, - 0, - nullptr, - &size, - &translation_value); - if (r != noErr) { - LOG("AudioHardwareGetProperty/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); - if (r != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, rv=%d", r); - return CUBEB_ERROR; - } - - 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("CubebAggregateDevice_%llx"), 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.CubebAggregateDevice_%llx"), 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); - 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); - CFRelease(aggregate_device_stacked_key); - - r = AudioObjectGetPropertyData(*plugin_id, - &create_aggregate_device_address, - sizeof(aggregate_device_dict), - &aggregate_device_dict, - &size, - aggregate_device_id); - CFRelease(aggregate_device_dict); - if (r != noErr) { - LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", r); - return CUBEB_ERROR; - } - LOG("New aggregate device %u", *aggregate_device_id); - - return CUBEB_OK; -} - -static CFStringRef -get_device_name(AudioDeviceID id) -{ - UInt32 size = sizeof(CFStringRef); - CFStringRef UIname; - AudioObjectPropertyAddress address_uuid = { kAudioDevicePropertyDeviceUID, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - OSStatus err = AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname); - return (err == noErr) ? UIname : NULL; -} - -static int -audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id, - AudioDeviceID input_device_id, - AudioDeviceID output_device_id) -{ - LOG("Add devices input %u and output %u into aggregate device %u", - input_device_id, output_device_id, aggregate_device_id); - const std::vector output_sub_devices = audiounit_get_sub_devices(output_device_id); - const std::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. */ - for (UInt32 i = 0; i < output_sub_devices.size(); i++) { - CFStringRef ref = get_device_name(output_sub_devices[i]); - if (ref == NULL) { - CFRelease(aggregate_sub_devices_array); - return CUBEB_ERROR; - } - CFArrayAppendValue(aggregate_sub_devices_array, ref); - } - for (UInt32 i = 0; i < input_sub_devices.size(); i++) { - CFStringRef ref = get_device_name(input_sub_devices[i]); - if (ref == NULL) { - CFRelease(aggregate_sub_devices_array); - return CUBEB_ERROR; - } - CFArrayAppendValue(aggregate_sub_devices_array, ref); - } - - 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); - CFRelease(aggregate_sub_devices_array); - if (rv != noErr) { - LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", rv); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id) -{ - assert(aggregate_device_id); - 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 std::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); - if (rv != noErr) { - LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", rv); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id) -{ - assert(aggregate_device_id); - 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); - if (rv != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, rv=%d", rv); - return CUBEB_ERROR; - } - - UInt32 subdevices_num = 0; - subdevices_num = size / sizeof(AudioObjectID); - AudioObjectID sub_devices[subdevices_num]; - size = sizeof(sub_devices); - - rv = AudioObjectGetPropertyData(aggregate_device_id, - &address_owned, - qualifier_data_size, - qualifier_data, - &size, - sub_devices); - if (rv != noErr) { - LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", rv); - return CUBEB_ERROR; - } - - 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); - if (rv != noErr) { - 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); - -/* - * 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") - * 2. Create a dictionary that describes the aggregate device - * (don't add sub-devices in that step, prone to fail [0]) - * 3. Ask the base plug-in to create the aggregate device (blank) - * 4. Add the array of sub-devices. - * 5. Set the master device (1st output device in our case) - * 6. Enable drift compensation for the non-master devices - * - * [0] https://lists.apple.com/archives/coreaudio-api/2006/Apr/msg00092.html - * [1] https://lists.apple.com/archives/coreaudio-api/2005/Jul/msg00150.html - * [2] CoreAudio.framework/Headers/AudioHardware.h - * */ -static int -audiounit_create_aggregate_device(cubeb_stream * stm) -{ - 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); - audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id); - return CUBEB_ERROR; - } - - 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); - 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; - } - - 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; - } - - return CUBEB_OK; -} - -static int -audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id) -{ - assert(aggregate_device_id && - *aggregate_device_id != kAudioDeviceUnknown && - plugin_id != kAudioObjectUnknown); - AudioObjectPropertyAddress destroy_aggregate_device_addr = { kAudioPlugInDestroyAggregateDevice, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster}; - UInt32 size; - OSStatus rv = AudioObjectGetPropertyDataSize(plugin_id, - &destroy_aggregate_device_addr, - 0, - NULL, - &size); - if (rv != noErr) { - LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, rv=%d", rv); - return CUBEB_ERROR; - } - - rv = AudioObjectGetPropertyData(plugin_id, - &destroy_aggregate_device_addr, - 0, - NULL, - &size, - aggregate_device_id); - if (rv != noErr) { - LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d", rv); - return CUBEB_ERROR; - } - - LOG("Destroyed aggregate device %d", *aggregate_device_id); - *aggregate_device_id = 0; - return CUBEB_OK; -} - -static int -audiounit_new_unit_instance(AudioUnit * unit, device_info * device) -{ - AudioComponentDescription desc; - AudioComponent comp; - OSStatus rv; - - desc.componentType = kAudioUnitType_Output; -#if TARGET_OS_IPHONE - desc.componentSubType = kAudioUnitSubType_RemoteIO; -#else - // Use the DefaultOutputUnit for output when no device is specified - // 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)) { - desc.componentSubType = kAudioUnitSubType_DefaultOutput; - } else { - desc.componentSubType = kAudioUnitSubType_HALOutput; - } -#endif - desc.componentManufacturer = kAudioUnitManufacturer_Apple; - desc.componentFlags = 0; - desc.componentFlagsMask = 0; - comp = AudioComponentFindNext(NULL, &desc); - if (comp == NULL) { - LOG("Could not find matching audio hardware."); - return CUBEB_ERROR; - } - - rv = AudioComponentInstanceNew(comp, unit); - if (rv != noErr) { - LOG("AudioComponentInstanceNew rv=%d", rv); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -enum enable_state { - DISABLE, - ENABLE, -}; - -static int -audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state) -{ - OSStatus rv; - UInt32 enable = state; - rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO, - (side == INPUT) ? kAudioUnitScope_Input : kAudioUnitScope_Output, - (side == INPUT) ? AU_IN_BUS : AU_OUT_BUS, - &enable, - sizeof(UInt32)); - if (rv != noErr) { - LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -static int -audiounit_create_unit(AudioUnit * unit, device_info * device) -{ - assert(*unit == nullptr); - assert(device); - - OSStatus rv; - int r; - - r = audiounit_new_unit_instance(unit, device); - if (r != CUBEB_OK) { - return r; - } - assert(*unit); - - if ((device->flags & DEV_SYSTEM_DEFAULT) - && (device->flags & DEV_OUTPUT)) { - return CUBEB_OK; - } - - - if (device->flags & DEV_INPUT) { - r = audiounit_enable_unit_scope(unit, INPUT, ENABLE); - if (r != CUBEB_OK) { - LOG("Failed to enable audiounit input scope "); - return r; - } - r = audiounit_enable_unit_scope(unit, OUTPUT, DISABLE); - if (r != CUBEB_OK) { - LOG("Failed to disable audiounit output scope "); - return r; - } - } else if (device->flags & DEV_OUTPUT) { - r = audiounit_enable_unit_scope(unit, OUTPUT, ENABLE); - if (r != CUBEB_OK) { - LOG("Failed to enable audiounit output scope "); - return r; - } - r = audiounit_enable_unit_scope(unit, INPUT, DISABLE); - if (r != CUBEB_OK) { - LOG("Failed to disable audiounit input scope "); - return r; - } - } else { - assert(false); - } - - rv = AudioUnitSetProperty(*unit, - kAudioOutputUnitProperty_CurrentDevice, - kAudioUnitScope_Global, - 0, - &device->id, sizeof(AudioDeviceID)); - if (rv != noErr) { - LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d", rv); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity) -{ - uint32_t size = capacity * stream->input_buffer_frames * stream->input_desc.mChannelsPerFrame; - if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) { - stream->input_linear_buffer.reset(new auto_array_wrapper_impl(size)); - } else { - stream->input_linear_buffer.reset(new auto_array_wrapper_impl(size)); - } - assert(stream->input_linear_buffer->length() == 0); - - return CUBEB_OK; -} - -static uint32_t -audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames) -{ - // For the 1st stream set anything within safe min-max - assert(stm->context->active_streams > 0); - if (stm->context->active_streams == 1) { - return std::max(std::min(latency_frames, SAFE_MAX_LATENCY_FRAMES), - SAFE_MIN_LATENCY_FRAMES); - } - assert(stm->output_unit); - - // If more than one stream operates in parallel - // allow only lower values of latency - int r; - 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); - if (r != noErr) { - LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize rv=%d", r); - return 0; - } - - output_buffer_size = std::max(std::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); - if (r != noErr) { - LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize rv=%d", r); - return 0; - } - - input_buffer_size = std::max(std::min(input_buffer_size, SAFE_MAX_LATENCY_FRAMES), - SAFE_MIN_LATENCY_FRAMES); - } - - // Every following active streams can only set smaller latency - UInt32 upper_latency_limit = 0; - if (input_buffer_size != 0 && output_buffer_size != 0) { - upper_latency_limit = std::min(input_buffer_size, output_buffer_size); - } else if (input_buffer_size != 0) { - upper_latency_limit = input_buffer_size; - } else if (output_buffer_size != 0) { - upper_latency_limit = output_buffer_size; - } else { - upper_latency_limit = SAFE_MAX_LATENCY_FRAMES; - } - - return std::max(std::min(latency_frames, upper_latency_limit), - SAFE_MIN_LATENCY_FRAMES); -} - -/* - * Change buffer size is prone to deadlock thus we change it - * following the steps: - * - register a listener for the buffer size property - * - change the property - * - wait until the listener is executed - * - property has changed, remove the listener - * */ -static void -buffer_size_changed_callback(void * inClientData, - AudioUnit inUnit, - AudioUnitPropertyID inPropertyID, - AudioUnitScope inScope, - AudioUnitElement inElement) -{ - cubeb_stream * stm = (cubeb_stream *)inClientData; - - AudioUnit au = inUnit; - AudioUnitScope au_scope = kAudioUnitScope_Input; - AudioUnitElement au_element = inElement; - char const * au_type = "output"; - - if (AU_IN_BUS == inElement) { - au_scope = kAudioUnitScope_Output; - au_type = "input"; - } - - 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; - break; - } - } -} - -static int -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; - AudioUnitElement au_element = AU_OUT_BUS; - - if (side == INPUT) { - au = stm->input_unit; - au_scope = kAudioUnitScope_Output; - au_element = AU_IN_BUS; - } - - uint32_t buffer_frames = 0; - UInt32 size = sizeof(buffer_frames); - 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); - 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); - return CUBEB_OK; - } - - r = AudioUnitAddPropertyListener(au, - kAudioDevicePropertyBufferFrameSize, - buffer_size_changed_callback, - stm); - if (r != noErr) { - 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, - sizeof(new_size_frames)); - if (r != noErr) { - LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); - - r = AudioUnitRemovePropertyListenerWithUserData(au, - kAudioDevicePropertyBufferFrameSize, - buffer_size_changed_callback, - stm); - if (r != noErr) { - LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); - } - - return CUBEB_ERROR; - } - - int count = 0; - while (!stm->buffer_size_change_state && count++ < 30) { - 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); - } - LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count); - } - - r = AudioUnitRemovePropertyListenerWithUserData(au, - kAudioDevicePropertyBufferFrameSize, - buffer_size_changed_callback, - stm); - if (r != noErr) { - LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize rv=%d", to_string(side), r); - return CUBEB_ERROR; - } - - if (!stm->buffer_size_change_state && count >= 30) { - LOG("(%p) Error, did not get buffer size change callback ...", stm); - return CUBEB_ERROR; - } - - LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side), new_size_frames); - return CUBEB_OK; -} - -static int -audiounit_configure_input(cubeb_stream * stm) -{ - assert(stm && stm->input_unit); - - int r = 0; - UInt32 size; - AURenderCallbackStruct aurcbs_in; - - 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, - &size); - if (r != noErr) { - LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r); - return CUBEB_ERROR; - } - stm->input_hw_rate = input_hw_desc.mSampleRate; - LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate); - - /* Set format description according to the input params. */ - r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params); - if (r != CUBEB_OK) { - LOG("(%p) Setting format description for input failed.", stm); - return r; - } - - // Use latency to set buffer size - stm->input_buffer_frames = stm->latency_frames; - r = audiounit_set_buffer_size(stm, stm->input_buffer_frames, INPUT); - if (r != CUBEB_OK) { - LOG("(%p) Error in change input buffer size.", stm); - return CUBEB_ERROR; - } - - AudioStreamBasicDescription src_desc = stm->input_desc; - /* Input AudioUnit must be configured with device's sample rate. - 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, - sizeof(AudioStreamBasicDescription)); - if (r != noErr) { - LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r); - return CUBEB_ERROR; - } - - /* Frames per buffer in the input callback. */ - r = AudioUnitSetProperty(stm->input_unit, - kAudioUnitProperty_MaximumFramesPerSlice, - kAudioUnitScope_Global, - AU_IN_BUS, - &stm->input_buffer_frames, - sizeof(UInt32)); - if (r != noErr) { - LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice rv=%d", r); - return CUBEB_ERROR; - } - - // Input only capacity - unsigned int array_capacity = 1; - if (has_output(stm)) { - // Full-duplex increase capacity - array_capacity = 8; - } - if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) { - return CUBEB_ERROR; - } - - 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)); - if (r != noErr) { - LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback rv=%d", r); - return CUBEB_ERROR; - } - - LOG("(%p) Input audiounit init successfully.", stm); - - return CUBEB_OK; -} - -static int -audiounit_configure_output(cubeb_stream * stm) -{ - assert(stm && stm->output_unit); - - int r; - AURenderCallbackStruct aurcbs_out; - UInt32 size; - - - 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); - - r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params); - if (r != CUBEB_OK) { - LOG("(%p) Could not initialize the audio stream description.", stm); - return r; - } - - /* Get output device sample rate. */ - 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, - &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); - - 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); - return CUBEB_ERROR; - } - - r = audiounit_set_buffer_size(stm, stm->latency_frames, OUTPUT); - if (r != CUBEB_OK) { - LOG("(%p) Error in change output buffer size.", stm); - return CUBEB_ERROR; - } - - /* Frames per buffer in the input callback. */ - 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); - 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)); - if (r != noErr) { - LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback rv=%d", r); - return CUBEB_ERROR; - } - - if (stm->output_stream_params.layout != CUBEB_LAYOUT_UNDEFINED) { - audiounit_layout_init(stm, OUTPUT); - audiounit_init_mixer(stm); - } - LOG("(%p) Output audiounit init successfully.", stm); - return CUBEB_OK; -} - -static int -audiounit_setup_stream(cubeb_stream * stm) -{ - stm->mutex.assert_current_thread_owns(); - - int r = 0; - - device_info in_dev_info = stm->input_device; - device_info out_dev_info = stm->output_device; - - if (has_input(stm) && has_output(stm) && - stm->input_device.id != stm->output_device.id) { - r = audiounit_create_aggregate_device(stm); - if (r != CUBEB_OK) { - stm->aggregate_device_id = 0; - LOG("(%p) Create aggregate devices failed.", stm); - // !!!NOTE: It is not necessary to return here. If it does not - // return it will fallback to the old implementation. The intention - // is to investigate how often it fails. I plan to remove - // it after a couple of weeks. - return r; - } else { - in_dev_info.id = out_dev_info.id = stm->aggregate_device_id; - in_dev_info.flags = DEV_INPUT; - out_dev_info.flags = DEV_OUTPUT; - } - } - - if (has_input(stm)) { - r = audiounit_create_unit(&stm->input_unit, &in_dev_info); - if (r != CUBEB_OK) { - LOG("(%p) AudioUnit creation for input failed.", stm); - return r; - } - } - - if (has_output(stm)) { - r = audiounit_create_unit(&stm->output_unit, &out_dev_info); - if (r != CUBEB_OK) { - LOG("(%p) AudioUnit creation for output failed.", stm); - return r; - } - } - - /* Latency cannot change if another stream is operating in parallel. In this case - * latecy is set to the other stream value. */ - if (stm->context->active_streams > 1) { - LOG("(%p) More than one active stream, use global latency.", stm); - stm->latency_frames = stm->context->global_latency_frames; - } else { - /* Silently clamp the latency down to the platform default, because we - * synthetize the clock from the callbacks, and we want the clock to update - * often. */ - stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames); - assert(stm->latency_frames); // Ungly error check - audiounit_set_global_latency(stm, stm->latency_frames); - } - - /* Configure I/O stream */ - if (has_input(stm)) { - r = audiounit_configure_input(stm); - if (r != CUBEB_OK) { - LOG("(%p) Configure audiounit input failed.", stm); - return r; - } - } - - if (has_output(stm)) { - r = audiounit_configure_output(stm); - if (r != CUBEB_OK) { - LOG("(%p) Configure audiounit output failed.", stm); - return r; - } - } - - // Setting the latency doesn't work well for USB headsets (eg. plantronics). - // Keep the default latency for now. -#if 0 - buffer_size = latency; - - /* Get the range of latency this particular device can work with, and clamp - * the requested latency to this acceptable range. */ -#if !TARGET_OS_IPHONE - if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) { - return CUBEB_ERROR; - } - - if (buffer_size < (unsigned int) latency_range.mMinimum) { - buffer_size = (unsigned int) latency_range.mMinimum; - } else if (buffer_size > (unsigned int) latency_range.mMaximum) { - buffer_size = (unsigned int) latency_range.mMaximum; - } - - /** - * Get the default buffer size. If our latency request is below the default, - * set it. Otherwise, use the default latency. - **/ - size = sizeof(default_buffer_size); - if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) { - return CUBEB_ERROR; - } - - if (buffer_size < default_buffer_size) { - /* Set the maximum number of frame that the render callback will ask for, - * effectively setting the latency of the stream. This is process-wide. */ - if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize, - kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) { - return CUBEB_ERROR; - } - } -#else // TARGET_OS_IPHONE - //TODO: [[AVAudioSession sharedInstance] inputLatency] - // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios -#endif -#endif - - /* We use a resampler because input AudioUnit operates - * reliable only in the capture device sample rate. - * Resampler will convert it to the user sample rate - * and deliver it to the callback. */ - uint32_t target_sample_rate; - if (has_input(stm)) { - target_sample_rate = stm->input_stream_params.rate; - } else { - assert(has_output(stm)); - target_sample_rate = stm->output_stream_params.rate; - } - - cubeb_stream_params input_unconverted_params; - if (has_input(stm)) { - input_unconverted_params = stm->input_stream_params; - /* Use the rate of the input device. */ - input_unconverted_params.rate = stm->input_hw_rate; - } - - /* 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)); - if (!stm->resampler) { - LOG("(%p) Could not create resampler.", stm); - return CUBEB_ERROR; - } - - if (stm->input_unit != NULL) { - r = AudioUnitInitialize(stm->input_unit); - if (r != noErr) { - LOG("AudioUnitInitialize/input rv=%d", r); - return CUBEB_ERROR; - } - } - - if (stm->output_unit != NULL) { - r = AudioUnitInitialize(stm->output_unit); - if (r != noErr) { - LOG("AudioUnitInitialize/output rv=%d", r); - return CUBEB_ERROR; - } - } - - 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); - } - - r = audiounit_install_device_changed_callback(stm); - if (r != CUBEB_OK) { - 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) -{ - PodZero(&input_desc, 1); - PodZero(&output_desc, 1); -} - -static int -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) -{ - std::unique_ptr stm(new cubeb_stream(context), - audiounit_stream_destroy); - context->active_streams += 1; - int r; - - assert(context); - *stream = NULL; - assert(latency_frames > 0); - if ((input_device && !input_stream_params) || - (output_device && !output_stream_params)) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - /* These could be different in the future if we have both - * full-duplex stream and different devices for input vs output. */ - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->latency_frames = latency_frames; - if (input_stream_params) { - stm->input_stream_params = *input_stream_params; - r = audiounit_set_device_info(stm.get(), reinterpret_cast(input_device), INPUT); - if (r != CUBEB_OK) { - LOG("(%p) Fail to set device info for input.", stm.get()); - return r; - } - } - if (output_stream_params) { - stm->output_stream_params = *output_stream_params; - r = audiounit_set_device_info(stm.get(), reinterpret_cast(output_device), OUTPUT); - if (r != CUBEB_OK) { - LOG("(%p) Fail to set device info for output.", stm.get()); - return r; - } - } - - auto_lock context_lock(context->mutex); - { - // It's not critical to lock here, because no other thread has been started - // yet, but it allows to assert that the lock has been taken in - // `audiounit_setup_stream`. - auto_lock lock(stm->mutex); - r = audiounit_setup_stream(stm.get()); - } - - if (r != CUBEB_OK) { - LOG("(%p) Could not setup the audiounit stream.", stm.get()); - return r; - } - - r = audiounit_install_system_changed_callback(stm.get()); - if (r != CUBEB_OK) { - LOG("(%p) Could not install the device change callback.", stm.get()); - return r; - } - - *stream = stm.release(); - LOG("(%p) Cubeb stream init successful.", *stream); - return CUBEB_OK; -} - -static void -audiounit_close_stream(cubeb_stream *stm) -{ - stm->mutex.assert_current_thread_owns(); - - if (stm->input_unit) { - AudioUnitUninitialize(stm->input_unit); - AudioComponentInstanceDispose(stm->input_unit); - stm->input_unit = nullptr; - } - - stm->input_linear_buffer.reset(); - - if (stm->output_unit) { - AudioUnitUninitialize(stm->output_unit); - AudioComponentInstanceDispose(stm->output_unit); - stm->output_unit = nullptr; - } - - stm->resampler.reset(); - stm->mixer.reset(); - - if (stm->aggregate_device_id) { - audiounit_destroy_aggregate_device(stm->plugin_id, &stm->aggregate_device_id); - stm->aggregate_device_id = 0; - } -} - -static void -audiounit_stream_destroy(cubeb_stream * stm) -{ - stm->shutdown = true; - - int r = audiounit_uninstall_system_changed_callback(stm); - if (r != CUBEB_OK) { - LOG("(%p) Could not uninstall the device changed callback", stm); - } - - r = audiounit_uninstall_device_changed_callback(stm); - if (r != CUBEB_OK) { - LOG("(%p) Could not uninstall all device change listeners", stm); - } - - auto_lock context_lock(stm->context->mutex); - audiounit_stream_stop_internal(stm); - - // Execute close in serial queue to avoid collision - // with reinit when un/plug devices - dispatch_sync(stm->context->serial_queue, ^() { - auto_lock lock(stm->mutex); - audiounit_close_stream(stm); - }); - - assert(stm->context->active_streams >= 1); - stm->context->active_streams -= 1; - - LOG("Cubeb stream (%p) destroyed successful.", stm); - delete stm; -} - -void -audiounit_stream_start_internal(cubeb_stream * stm) -{ - OSStatus r; - if (stm->input_unit != NULL) { - r = AudioOutputUnitStart(stm->input_unit); - assert(r == 0); - } - if (stm->output_unit != NULL) { - r = AudioOutputUnitStart(stm->output_unit); - assert(r == 0); - } -} - -static int -audiounit_stream_start(cubeb_stream * stm) -{ - auto_lock context_lock(stm->context->mutex); - stm->shutdown = false; - stm->draining = false; - - audiounit_stream_start_internal(stm); - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); - - LOG("Cubeb stream (%p) started successfully.", stm); - return CUBEB_OK; -} - -void -audiounit_stream_stop_internal(cubeb_stream * stm) -{ - OSStatus r; - if (stm->input_unit != NULL) { - r = AudioOutputUnitStop(stm->input_unit); - assert(r == 0); - } - if (stm->output_unit != NULL) { - r = AudioOutputUnitStop(stm->output_unit); - assert(r == 0); - } -} - -static int -audiounit_stream_stop(cubeb_stream * stm) -{ - auto_lock context_lock(stm->context->mutex); - stm->shutdown = true; - - audiounit_stream_stop_internal(stm); - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); - - LOG("Cubeb stream (%p) stopped successfully.", stm); - return CUBEB_OK; -} - -static int -audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position) -{ - assert(stm); - *position = stm->frames_played; - return CUBEB_OK; -} - -int -audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency) -{ -#if TARGET_OS_IPHONE - //TODO - return CUBEB_ERROR_NOT_SUPPORTED; -#else - auto_lock lock(stm->mutex); - if (stm->hw_latency_frames == UINT64_MAX) { - UInt32 size; - uint32_t device_latency_frames, device_safety_offset; - double unit_latency_sec; - AudioDeviceID output_device_id; - OSStatus r; - AudioObjectPropertyAddress latency_address = { - kAudioDevicePropertyLatency, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - AudioObjectPropertyAddress safety_offset_address = { - kAudioDevicePropertySafetyOffset, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (output_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; - } - - size = sizeof(unit_latency_sec); - r = AudioUnitGetProperty(stm->output_unit, - kAudioUnitProperty_Latency, - kAudioUnitScope_Global, - 0, - &unit_latency_sec, - &size); - if (r != noErr) { - LOG("AudioUnitGetProperty/kAudioUnitProperty_Latency rv=%d", r); - return CUBEB_ERROR; - } - - size = sizeof(device_latency_frames); - r = AudioObjectGetPropertyData(output_device_id, - &latency_address, - 0, - NULL, - &size, - &device_latency_frames); - if (r != noErr) { - LOG("AudioUnitGetPropertyData/latency_frames rv=%d", r); - return CUBEB_ERROR; - } - - size = sizeof(device_safety_offset); - r = AudioObjectGetPropertyData(output_device_id, - &safety_offset_address, - 0, - NULL, - &size, - &device_safety_offset); - if (r != noErr) { - LOG("AudioUnitGetPropertyData/safety_offset rv=%d", r); - return CUBEB_ERROR; - } - - /* This part is fixed and depend on the stream parameter and the hardware. */ - stm->hw_latency_frames = - static_cast(unit_latency_sec * stm->output_desc.mSampleRate) - + device_latency_frames - + device_safety_offset; - } - - *latency = stm->hw_latency_frames + stm->current_latency_frames; - - return CUBEB_OK; -#endif -} - -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); - if (r != noErr) { - LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -static int -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); - - if (r != noErr) { - LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r); - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -int audiounit_stream_set_panning(cubeb_stream * stm, float panning) -{ - if (stm->output_desc.mChannelsPerFrame > 2) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - - stm->panning.store(panning, std::memory_order_relaxed); - return CUBEB_OK; -} - -int audiounit_stream_get_current_device(cubeb_stream * stm, - cubeb_device ** const device) -{ -#if TARGET_OS_IPHONE - //TODO - return CUBEB_ERROR_NOT_SUPPORTED; -#else - OSStatus r; - UInt32 size; - UInt32 data; - char strdata[4]; - AudioDeviceID output_device_id; - AudioDeviceID input_device_id; - - AudioObjectPropertyAddress datasource_address = { - kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeOutput, - kAudioObjectPropertyElementMaster - }; - - AudioObjectPropertyAddress datasource_address_input = { - kAudioDevicePropertyDataSource, - kAudioDevicePropertyScopeInput, - kAudioObjectPropertyElementMaster - }; - - *device = NULL; - - output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT); - if (output_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; - } - - *device = new cubeb_device; - if (!*device) { - return CUBEB_ERROR; - } - PodZero(*device, 1); - - size = sizeof(UInt32); - /* This fails with some USB headset, so simply return an empty string. */ - r = AudioObjectGetPropertyData(output_device_id, - &datasource_address, - 0, NULL, &size, &data); - if (r != noErr) { - size = 0; - data = 0; - } - - (*device)->output_name = new char[size + 1]; - if (!(*device)->output_name) { - return CUBEB_ERROR; - } - - // Turn the four chars packed into a uint32 into a string - strdata[0] = (char)(data >> 24); - strdata[1] = (char)(data >> 16); - strdata[2] = (char)(data >> 8); - strdata[3] = (char)(data); - - memcpy((*device)->output_name, strdata, size); - (*device)->output_name[size] = '\0'; - - input_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT); - if (input_device_id == kAudioObjectUnknown) { - return CUBEB_ERROR; - } - - size = sizeof(UInt32); - r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data); - if (r != noErr) { - LOG("(%p) Error when getting device !", stm); - size = 0; - data = 0; - } - - (*device)->input_name = new char[size + 1]; - if (!(*device)->input_name) { - return CUBEB_ERROR; - } - - // Turn the four chars packed into a uint32 into a string - strdata[0] = (char)(data >> 24); - strdata[1] = (char)(data >> 16); - strdata[2] = (char)(data >> 8); - strdata[3] = (char)(data); - - memcpy((*device)->input_name, strdata, size); - (*device)->input_name[size] = '\0'; - - return CUBEB_OK; -#endif -} - -int audiounit_stream_device_destroy(cubeb_stream * /* stream */, - cubeb_device * device) -{ - 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) -{ - auto_lock dev_cb_lock(stream->device_changed_callback_lock); - /* Note: second register without unregister first causes 'nope' error. - * Current implementation requires unregister before register a new cb. */ - assert(!stream->device_changed_callback); - stream->device_changed_callback = device_changed_callback; - return CUBEB_OK; -} - -static char * -audiounit_strref_to_cstr_utf8(CFStringRef strref) -{ - CFIndex len, size; - char * ret; - if (strref == NULL) { - return NULL; - } - - len = CFStringGetLength(strref); - // Add 1 to size to allow for '\0' termination character. - size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1; - ret = new char[size]; - - if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) { - delete [] ret; - ret = NULL; - } - - return ret; -} - -static uint32_t -audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope) -{ - 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) { - AudioBufferList * list = static_cast(alloca(size)); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) { - for (i = 0; i < list->mNumberBuffers; i++) - ret += list->mBuffers[i].mNumberChannels; - } - } - - return ret; -} - -static void -audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope, - uint32_t * min, uint32_t * max, uint32_t * def) -{ - 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) { - *def = fvalue; - } - } - - adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates; - UInt32 size = 0; - AudioValueRange range; - if (AudioObjectHasProperty(devid, &adr) && - AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) { - uint32_t count = size / sizeof(AudioValueRange); - std::vector ranges(count); - range.mMinimum = 9999999999.0; - range.mMaximum = 0.0; - 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; - if (ranges[i].mMinimum < range.mMinimum) - range.mMinimum = ranges[i].mMinimum; - } - } - *max = static_cast(range.mMaximum); - *min = static_cast(range.mMinimum); - } else { - *min = *max = 0; - } - -} - -static UInt32 -audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope) -{ - AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster }; - UInt32 size, dev, stream = 0, offset; - AudioStreamID sid[1]; - - adr.mSelector = kAudioDevicePropertyLatency; - size = sizeof(UInt32); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) { - dev = 0; - } - - adr.mSelector = kAudioDevicePropertyStreams; - size = sizeof(sid); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) { - adr.mSelector = kAudioStreamPropertyLatency; - size = sizeof(UInt32); - AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream); - } - - adr.mSelector = kAudioDevicePropertySafetyOffset; - size = sizeof(UInt32); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) { - offset = 0; - } - - return dev + stream + offset; -} - -static int -audiounit_create_device_from_hwdev(cubeb_device_info * ret, AudioObjectID devid, cubeb_device_type type) -{ - AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster }; - UInt32 size, ch, latency; - CFStringRef str = NULL; - AudioValueRange range; - - if (type == CUBEB_DEVICE_TYPE_OUTPUT) { - adr.mScope = kAudioDevicePropertyScopeOutput; - } else if (type == CUBEB_DEVICE_TYPE_INPUT) { - adr.mScope = kAudioDevicePropertyScopeInput; - } else { - return CUBEB_ERROR; - } - - ch = audiounit_get_channel_count(devid, adr.mScope); - if (ch == 0) { - return CUBEB_ERROR; - } - - PodZero(ret, 1); - - size = sizeof(CFStringRef); - adr.mSelector = kAudioDevicePropertyDeviceUID; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { - ret->device_id = audiounit_strref_to_cstr_utf8(str); - static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)), "cubeb_devid can't represent devid"); - ret->devid = reinterpret_cast(devid); - ret->group_id = ret->device_id; - CFRelease(str); - } - - size = sizeof(CFStringRef); - adr.mSelector = kAudioObjectPropertyName; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { - UInt32 ds; - size = sizeof(UInt32); - adr.mSelector = kAudioDevicePropertyDataSource; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds) == noErr) { - CFStringRef dsname; - AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) }; - adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString; - size = sizeof(AudioValueTranslation); - // If there is a datasource for this device, use it instead of the device - // name. - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl) == noErr) { - CFRelease(str); - str = dsname; - } - } - - ret->friendly_name = audiounit_strref_to_cstr_utf8(str); - CFRelease(str); - } - - size = sizeof(CFStringRef); - adr.mSelector = kAudioObjectPropertyManufacturer; - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) { - ret->vendor_name = audiounit_strref_to_cstr_utf8(str); - CFRelease(str); - } - - ret->type = type; - ret->state = CUBEB_DEVICE_STATE_ENABLED; - ret->preferred = (devid == audiounit_get_default_device_id(type)) ? - CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE; - - ret->max_channels = ch; - ret->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */ - /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */ - ret->default_format = CUBEB_DEVICE_FMT_F32NE; - audiounit_get_available_samplerate(devid, adr.mScope, - &ret->min_rate, &ret->max_rate, &ret->default_rate); - - latency = audiounit_get_device_presentation_latency(devid, adr.mScope); - - adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange; - size = sizeof(AudioValueRange); - if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) { - ret->latency_lo = latency + range.mMinimum; - ret->latency_hi = latency + range.mMaximum; - } else { - ret->latency_lo = 10 * ret->default_rate / 1000; /* Default to 10ms */ - ret->latency_hi = 100 * ret->default_rate / 1000; /* Default to 100ms */ - } - - return CUBEB_OK; -} - -static int -audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type, - cubeb_device_collection * collection) -{ - std::vector input_devs; - std::vector output_devs; - - // Count number of input and output devices. This is not - // necessarily the same as the count of raw devices supported by the - // system since, for example, with Soundflower installed, some - // devices may report as being both input *and* output and cubeb - // separates those into two different devices. - - if (type & CUBEB_DEVICE_TYPE_OUTPUT) { - output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT); - } - - if (type & CUBEB_DEVICE_TYPE_INPUT) { - input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT); - } - - auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()]; - collection->count = 0; - - if (type & CUBEB_DEVICE_TYPE_OUTPUT) { - for (auto dev: output_devs) { - auto device = &devices[collection->count]; - auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_OUTPUT); - if (err != CUBEB_OK) { - continue; - } - collection->count += 1; - } - } - - if (type & CUBEB_DEVICE_TYPE_INPUT) { - for (auto dev: input_devs) { - auto device = &devices[collection->count]; - auto err = audiounit_create_device_from_hwdev(device, dev, CUBEB_DEVICE_TYPE_INPUT); - if (err != CUBEB_OK) { - continue; - } - collection->count += 1; - } - } - - if (collection->count > 0) { - collection->device = devices; - } else { - delete [] devices; - collection->device = NULL; - } - - return CUBEB_OK; -} - -static int -audiounit_device_collection_destroy(cubeb * /* context */, - cubeb_device_collection * collection) -{ - for (size_t i = 0; i < collection->count; i++) { - delete [] collection->device[i].device_id; - delete [] collection->device[i].friendly_name; - delete [] collection->device[i].vendor_name; - } - delete [] collection->device; - - return CUBEB_OK; -} - -static std::vector -audiounit_get_devices_of_type(cubeb_device_type devtype) -{ - AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices, - kAudioObjectPropertyScopeGlobal, - kAudioObjectPropertyElementMaster }; - UInt32 size = 0; - OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size); - if (ret != noErr) { - return std::vector(); - } - /* Total number of input and output devices. */ - uint32_t count = (uint32_t)(size / sizeof(AudioObjectID)); - - std::vector devices(count); - ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, devices.data()); - if (ret != noErr) { - return std::vector(); - } - /* Expected sorted but did not find anything in the docs. */ - std::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; - - std::vector devices_in_scope; - for (uint32_t i = 0; i < count; ++i) { - /* For device in the given scope channel must be > 0. */ - if (audiounit_get_channel_count(devices[i], scope) > 0) { - devices_in_scope.push_back(devices[i]); - } - } - - return devices_in_scope; -} - -static OSStatus -audiounit_collection_changed_callback(AudioObjectID /* inObjectID */, - UInt32 /* inNumberAddresses */, - const AudioObjectPropertyAddress * /* inAddresses */, - void * inClientData) -{ - cubeb * context = static_cast(inClientData); - auto_lock lock(context->mutex); - - if (context->collection_changed_callback == NULL) { - /* Listener removed while waiting in mutex, abort. */ - return noErr; - } - - /* Differentiate input from output changes. */ - if (context->collection_changed_devtype == CUBEB_DEVICE_TYPE_INPUT || - context->collection_changed_devtype == CUBEB_DEVICE_TYPE_OUTPUT) { - std::vector devices = audiounit_get_devices_of_type(context->collection_changed_devtype); - /* When count is the same examine the devid for the case of coalescing. */ - if (context->devtype_device_array == devices) { - /* Device changed for the other scope, ignore. */ - return noErr; - } - /* Device on desired scope changed. */ - context->devtype_device_array = devices; - } - - context->collection_changed_callback(context, context->collection_changed_user_ptr); - return noErr; -} - -static OSStatus -audiounit_add_device_listener(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) -{ - /* Note: second register without unregister first causes 'nope' error. - * Current implementation requires unregister before register a new cb. */ - assert(context->collection_changed_callback == NULL); - - AudioObjectPropertyAddress devAddr; - devAddr.mSelector = kAudioHardwarePropertyDevices; - devAddr.mScope = kAudioObjectPropertyScopeGlobal; - devAddr.mElement = kAudioObjectPropertyElementMaster; - - OSStatus ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject, - &devAddr, - audiounit_collection_changed_callback, - context); - if (ret == noErr) { - /* Expected empty after unregister. */ - assert(context->devtype_device_array.empty()); - /* Listener works for input and output. - * When requested one of them we need to differentiate. */ - if (devtype == CUBEB_DEVICE_TYPE_INPUT || - devtype == CUBEB_DEVICE_TYPE_OUTPUT) { - /* Used to differentiate input from output device changes. */ - context->devtype_device_array = audiounit_get_devices_of_type(devtype); - } - context->collection_changed_devtype = devtype; - context->collection_changed_callback = collection_changed_callback; - context->collection_changed_user_ptr = user_ptr; - } - return ret; -} - -static OSStatus -audiounit_remove_device_listener(cubeb * context) -{ - AudioObjectPropertyAddress devAddr; - devAddr.mSelector = kAudioHardwarePropertyDevices; - devAddr.mScope = kAudioObjectPropertyScopeGlobal; - devAddr.mElement = kAudioObjectPropertyElementMaster; - - /* Note: unregister a non registered cb is not a problem, not checking. */ - OSStatus ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, - &devAddr, - audiounit_collection_changed_callback, - context); - if (ret == noErr) { - /* Reset all values. */ - context->collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN; - context->collection_changed_callback = NULL; - context->collection_changed_user_ptr = NULL; - context->devtype_device_array.clear(); - } - return ret; -} - -int audiounit_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) -{ - OSStatus ret; - auto_lock lock(context->mutex); - if (collection_changed_callback) { - ret = audiounit_add_device_listener(context, devtype, - collection_changed_callback, - user_ptr); - } else { - ret = audiounit_remove_device_listener(context); - } - return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR; -} - -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, - /*.get_preferred_channel_layout =*/ audiounit_get_preferred_channel_layout, - /*.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_set_volume =*/ audiounit_stream_set_volume, - /*.stream_set_panning =*/ audiounit_stream_set_panning, - /*.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 deleted file mode 100644 index c49f6bcb0b..0000000000 --- a/Externals/cubeb/src/cubeb_jack.cpp +++ /dev/null @@ -1,1052 +0,0 @@ -/* - * Copyright © 2012 David Richards - * Copyright © 2013 Sebastien Alaiwan - * Copyright © 2016 Damien Zammit - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#define _DEFAULT_SOURCE -#define _BSD_SOURCE -#ifndef __FreeBSD__ -#define _POSIX_SOURCE -#endif -#include -#include -#include -#include -#include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "cubeb_resampler.h" -#include "cubeb_utils.h" - -#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) \ - X(jack_set_info_function) - -#define IMPORT_FUNC(x) static decltype(x) * api_##x; -JACK_API_VISIT(IMPORT_FUNC); - -static const int MAX_STREAMS = 16; -static const int MAX_CHANNELS = 8; -static const int FIFO_SIZE = 4096 * sizeof(float); - -enum devstream { - NONE = 0, - IN_ONLY, - OUT_ONLY, - DUPLEX, -}; - -static void -s16ne_to_float(float * dst, const int16_t * src, size_t n) -{ - for (size_t i = 0; i < n; i++) - *(dst++) = (float)((float)*(src++) / 32767.0f); -} - -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; - *(dst++) = (int16_t)((int16_t)(*(src++) * 32767)); - } -} - -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 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, - .get_preferred_channel_layout = NULL, - .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_set_volume = cbjack_stream_set_volume, - .stream_set_panning = 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 { - cubeb * context; - - /**< Mutex for each stream */ - pthread_mutex_t mutex; - - 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; - cubeb_state_callback state_callback; - void * user_ptr; - cubeb_stream_params in_params; - cubeb_stream_params out_params; - - cubeb_resampler * resampler; - - uint64_t position; - bool pause; - float ratio; - enum devstream devs; - char stream_name[256]; - jack_port_t * output_ports[MAX_CHANNELS]; - jack_port_t * input_ports[MAX_CHANNELS]; - float volume; -}; - -struct cubeb { - struct cubeb_ops const * ops; - void * libjack; - - /**< Mutex for whole context */ - pthread_mutex_t mutex; - - /**< Audio buffers, converted to float */ - float in_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS]; - float out_float_interleaved_buffer[FIFO_SIZE * MAX_CHANNELS]; - - /**< Audio buffer, at the sampling rate of the output */ - float in_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3]; - int16_t in_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3]; - float out_resampled_interleaved_buffer_float[FIFO_SIZE * MAX_CHANNELS * 3]; - int16_t out_resampled_interleaved_buffer_s16ne[FIFO_SIZE * MAX_CHANNELS * 3]; - - cubeb_stream streams[MAX_STREAMS]; - unsigned int active_streams; - - cubeb_device_collection_changed_callback collection_changed_callback; - - bool active; - unsigned int jack_sample_rate; - unsigned int jack_latency; - unsigned int jack_xruns; - unsigned int jack_buffer_size; - unsigned int fragment_size; - unsigned int output_bytes_per_frame; - jack_client_t * jack_client; -}; - -static int -load_jack_lib(cubeb * context) -{ -#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 -#else - context->libjack = dlopen("libjack.so.0", RTLD_LAZY); -#endif - if (!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 - - return CUBEB_OK; -} - -static void -cbjack_connect_ports (cubeb_stream * stream) -{ - 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); - - if (*phys_in_ports == NULL) { - goto skipplayback; - } - - // Connect outputs to playback - for (unsigned int c = 0; c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) { - const char *src_port = api_jack_port_name (stream->output_ports[c]); - - api_jack_connect (stream->context->jack_client, src_port, phys_in_ports[c]); - } - -skipplayback: - if (*phys_out_ports == NULL) { - goto end; - } - // Connect inputs to capture - for (unsigned int c = 0; c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) { - const char *src_port = api_jack_port_name (stream->input_ports[c]); - - api_jack_connect (stream->context->jack_client, phys_out_ports[c], src_port); - } -end: - api_jack_free(phys_out_ports); - api_jack_free(phys_in_ports); -} - -static int -cbjack_xrun_callback(void * arg) -{ - cubeb * ctx = (cubeb *)arg; - - float delay = api_jack_get_xrun_delayed_usecs(ctx->jack_client); - int fragments = (int)ceilf( ((delay / 1000000.0) * ctx->jack_sample_rate ) - / (float)(ctx->jack_buffer_size) ); - ctx->jack_xruns += fragments; - return 0; -} - -static int -cbjack_graph_order_callback(void * arg) -{ - cubeb * ctx = (cubeb *)arg; - int i; - jack_latency_range_t latency_range; - jack_nframes_t port_latency, max_latency = 0; - - for (int j = 0; j < MAX_STREAMS; j++) { - cubeb_stream *stm = &ctx->streams[j]; - - if (!stm->in_use) - continue; - if (!stm->ports_ready) - continue; - - for (i = 0; i < (int)stm->out_params.channels; ++i) { - api_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; - } - /* Cap minimum latency to 128 frames */ - if (max_latency < 128) - max_latency = 128; - } - - ctx->jack_latency = max_latency; - - return 0; -} - -static int -cbjack_process(jack_nframes_t nframes, void * arg) -{ - cubeb * ctx = (cubeb *)arg; - int t_jack_xruns = ctx->jack_xruns; - int i; - - 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]; - - if (!stm->in_use) - continue; - - // handle xruns by skipping audio that should have been played - for (i = 0; i < t_jack_xruns; i++) { - stm->position += ctx->fragment_size * stm->ratio; - } - ctx->jack_xruns -= t_jack_xruns; - - if (!stm->ports_ready) - continue; - - 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); - } - 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); - } - 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]; - for (long f = 0; f < nframes; f++) { - buffer_out[f] = 0.f; - } - } - } - 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]; - for (long f = 0; f < nframes; f++) { - buffer_in[f] = 0.f; - } - } - } - } else { - - // 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; - - // 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); - } 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); - } - } 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); - } 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); - } - } else if (stm->devs == OUT_ONLY) { - if (stm->out_params.format == CUBEB_SAMPLE_S16NE) { - 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); - } - } - // unlock stream mutex - pthread_mutex_unlock(&stm->mutex); - - } else { - // could not lock mutex - // output silence - if (stm->devs & OUT_ONLY) { - for (unsigned int c = 0; c < stm->out_params.channels; c++) { - float* buffer_out = bufs_out[c]; - for (long f = 0; f < nframes; f++) { - buffer_out[f] = 0.f; - } - } - } - if (stm->devs & IN_ONLY) { - // capture silence - for (unsigned int c = 0; c < stm->in_params.channels; c++) { - float* buffer_in = bufs_in[c]; - for (long f = 0; f < nframes; f++) { - buffer_in[f] = 0.f; - } - } - } - } - } - } - return 0; -} - -static void -cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes) -{ - float * out_interleaved_buffer = nullptr; - - float * inptr = (in != NULL) ? *in : nullptr; - float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr; - - long needed_frames = (bufs_out != NULL) ? nframes : 0; - 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); - - 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]; - for (long f = 0; f < done_frames; f++) { - buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume; - } - if (done_frames < needed_frames) { - // draining - for (long f = done_frames; f < needed_frames; f++) { - buffer[f] = 0.f; - } - } - if (done_frames == 0) { - // stop, but first zero out the existing buffer - for (long f = 0; f < needed_frames; f++) { - buffer[f] = 0.f; - } - } - } - } - - if (done_frames >= 0 && done_frames < needed_frames) { - // set drained - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); - // stop stream - cbjack_stream_stop(stream); - } - if (done_frames > 0 && done_frames <= needed_frames) { - // advance stream position - stream->position += done_frames * stream->ratio; - } - if (done_frames < 0 || done_frames > needed_frames) { - // stream error - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR); - } -} - -static void -cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes) -{ - float * out_interleaved_buffer = nullptr; - - short * inptr = (in != NULL) ? *in : nullptr; - float * outptr = (bufs_out != NULL) ? *bufs_out : nullptr; - - long needed_frames = (bufs_out != NULL) ? nframes : 0; - 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); - - 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; - - 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]; - for (long f = 0; f < done_frames; f++) { - buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume; - } - if (done_frames < needed_frames) { - // draining - for (long f = done_frames; f < needed_frames; f++) { - buffer[f] = 0.f; - } - } - if (done_frames == 0) { - // stop, but first zero out the existing buffer - for (long f = 0; f < needed_frames; f++) { - buffer[f] = 0.f; - } - } - } - } - - if (done_frames >= 0 && done_frames < needed_frames) { - // set drained - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED); - // stop stream - cbjack_stream_stop(stream); - } - if (done_frames > 0 && done_frames <= needed_frames) { - // advance stream position - stream->position += done_frames * stream->ratio; - } - if (done_frames < 0 || done_frames > needed_frames) { - // stream error - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_ERROR); - } -} - -static void -cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch) -{ - 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; - } - } - if (format_mismatch) { - 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)); - } -} - -static void -silent_jack_error_callback(char const * /*msg*/) -{ -} - -/*static*/ int -jack_init (cubeb ** context, char const * context_name) -{ - int r; - - *context = NULL; - - cubeb * ctx = (cubeb *)calloc(1, sizeof(*ctx)); - if (ctx == NULL) { - return CUBEB_ERROR; - } - - r = load_jack_lib(ctx); - if (r != 0) { - cbjack_destroy(ctx); - return CUBEB_ERROR; - } - - api_jack_set_error_function(silent_jack_error_callback); - api_jack_set_info_function(silent_jack_error_callback); - - ctx->ops = &cbjack_ops; - - ctx->mutex = PTHREAD_MUTEX_INITIALIZER; - for (r = 0; r < MAX_STREAMS; r++) { - ctx->streams[r].mutex = PTHREAD_MUTEX_INITIALIZER; - } - - const char * jack_client_name = "cubeb"; - if (context_name) - jack_client_name = context_name; - - ctx->jack_client = api_jack_client_open(jack_client_name, - JackNoStartServer, - NULL); - - if (ctx->jack_client == NULL) { - cbjack_destroy(ctx); - return CUBEB_ERROR; - } - - 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); - - if (api_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_latency = 128 * 1000 / ctx->jack_sample_rate; - - ctx->active = true; - *context = ctx; - - return CUBEB_OK; -} - -static char const * -cbjack_get_backend_id(cubeb * /*context*/) -{ - return "jack"; -} - -static int -cbjack_get_max_channel_count(cubeb * /*ctx*/, uint32_t * max_channels) -{ - *max_channels = MAX_CHANNELS; - return CUBEB_OK; -} - -static int -cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms) -{ - *latency_ms = stm->context->jack_latency; - return CUBEB_OK; -} - -static int -cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, uint32_t * latency_ms) -{ - *latency_ms = ctx->jack_latency; - return CUBEB_OK; -} - -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); - if (!testclient) { - return CUBEB_ERROR; - } - - *rate = api_jack_get_sample_rate(testclient); - api_jack_client_close(testclient); - - } else { - *rate = api_jack_get_sample_rate(ctx->jack_client); - } - return CUBEB_OK; -} - -static void -cbjack_destroy(cubeb * context) -{ - context->active = false; - - if (context->jack_client != NULL) - api_jack_client_close (context->jack_client); - - if (context->libjack) - dlclose(context->libjack); - - free(context); -} - -static cubeb_stream * -context_alloc_stream(cubeb * context, char const * stream_name) -{ - for (int i = 0; i < MAX_STREAMS; i++) { - if (!context->streams[i].in_use) { - cubeb_stream * stm = &context->streams[i]; - stm->in_use = true; - snprintf(stm->stream_name, 255, "%s_%u", stream_name, i); - return stm; - } - } - return NULL; -} - -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) -{ - int stream_actual_rate = 0; - int jack_rate = api_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) - ) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - if (input_stream_params - && (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE && - input_stream_params->format != CUBEB_SAMPLE_S16NE) - ) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - if (input_device || output_device) - return CUBEB_ERROR_NOT_SUPPORTED; - - *stream = NULL; - - // Find a free stream. - pthread_mutex_lock(&context->mutex); - cubeb_stream * stm = context_alloc_stream(context, stream_name); - - // No free stream? - if (stm == NULL) { - pthread_mutex_unlock(&context->mutex); - return CUBEB_ERROR; - } - - // unlock context mutex - pthread_mutex_unlock(&context->mutex); - - // Lock active stream - pthread_mutex_lock(&stm->mutex); - - stm->ports_ready = false; - stm->user_ptr = user_ptr; - stm->context = context; - stm->devs = NONE; - if (output_stream_params && !input_stream_params) { - stm->out_params = *output_stream_params; - stream_actual_rate = stm->out_params.rate; - stm->out_params.rate = jack_rate; - stm->devs = OUT_ONLY; - if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { - context->output_bytes_per_frame = sizeof(float); - } else { - context->output_bytes_per_frame = sizeof(short); - } - } - if (input_stream_params && output_stream_params) { - stm->in_params = *input_stream_params; - stm->out_params = *output_stream_params; - stream_actual_rate = stm->out_params.rate; - stm->in_params.rate = jack_rate; - stm->out_params.rate = jack_rate; - stm->devs = DUPLEX; - if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) { - context->output_bytes_per_frame = sizeof(float); - stm->in_params.format = CUBEB_SAMPLE_FLOAT32NE; - } else { - context->output_bytes_per_frame = sizeof(short); - stm->in_params.format = CUBEB_SAMPLE_S16NE; - } - } else if (input_stream_params && !output_stream_params) { - stm->in_params = *input_stream_params; - stream_actual_rate = stm->in_params.rate; - stm->in_params.rate = jack_rate; - stm->devs = IN_ONLY; - if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) { - context->output_bytes_per_frame = sizeof(float); - } else { - context->output_bytes_per_frame = sizeof(short); - } - } - - stm->ratio = (float)stream_actual_rate / (float)jack_rate; - - stm->data_callback = data_callback; - 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->fragment_size = context->jack_buffer_size; - - if (stm->devs == NONE) { - pthread_mutex_unlock(&stm->mutex); - return CUBEB_ERROR; - } - - 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); - } 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); - } 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); - } - - if (!stm->resampler) { - stm->in_use = false; - pthread_mutex_unlock(&stm->mutex); - return CUBEB_ERROR; - } - - if (stm->devs == DUPLEX || stm->devs == OUT_ONLY) { - 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); - } - } - - if (stm->devs == DUPLEX || stm->devs == IN_ONLY) { - 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); - } - } - - cbjack_connect_ports(stm); - - *stream = stm; - - stm->ports_ready = true; - stm->pause = true; - pthread_mutex_unlock(&stm->mutex); - - return CUBEB_OK; -} - -static void -cbjack_stream_destroy(cubeb_stream * stream) -{ - pthread_mutex_lock(&stream->mutex); - stream->ports_ready = false; - - 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]); - stream->output_ports[c] = NULL; - } - } - } - - 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]); - stream->input_ports[c] = NULL; - } - } - } - - if (stream->resampler) { - cubeb_resampler_destroy(stream->resampler); - stream->resampler = NULL; - } - stream->in_use = false; - pthread_mutex_unlock(&stream->mutex); -} - -static int -cbjack_stream_start(cubeb_stream * stream) -{ - stream->pause = false; - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED); - return CUBEB_OK; -} - -static int -cbjack_stream_stop(cubeb_stream * stream) -{ - stream->pause = true; - stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED); - return CUBEB_OK; -} - -static int -cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position) -{ - *position = stream->position; - return CUBEB_OK; -} - -static int -cbjack_stream_set_volume(cubeb_stream * stm, float volume) -{ - stm->volume = volume; - return CUBEB_OK; -} - -static int -cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device) -{ - *device = (cubeb_device *)calloc(1, sizeof(cubeb_device)); - if (*device == NULL) - return CUBEB_ERROR; - - const char * j_in = "JACK capture"; - const char * j_out = "JACK playback"; - const char * empty = ""; - - if (stm->devs == DUPLEX) { - (*device)->input_name = strdup(j_in); - (*device)->output_name = strdup(j_out); - } else if (stm->devs == IN_ONLY) { - (*device)->input_name = strdup(j_in); - (*device)->output_name = strdup(empty); - } else if (stm->devs == OUT_ONLY) { - (*device)->input_name = strdup(empty); - (*device)->output_name = strdup(j_out); - } - - return CUBEB_OK; -} - -static int -cbjack_stream_device_destroy(cubeb_stream * /*stream*/, - cubeb_device * device) -{ - if (device->input_name) - free(device->input_name); - if (device->output_name) - free(device->output_name); - free(device); - return CUBEB_OK; -} - -#define JACK_DEFAULT_IN "JACK capture" -#define JACK_DEFAULT_OUT "JACK playback" - -static int -cbjack_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection) -{ - if (!context) - return CUBEB_ERROR; - - uint32_t rate; - cbjack_get_preferred_sample_rate(context, &rate); - - cubeb_device_info * devices = new cubeb_device_info[2]; - if (!devices) - return CUBEB_ERROR; - PodZero(devices, 2); - collection->count = 0; - - 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->friendly_name = JACK_DEFAULT_OUT; - cur->group_id = JACK_DEFAULT_OUT; - cur->vendor_name = JACK_DEFAULT_OUT; - cur->type = CUBEB_DEVICE_TYPE_OUTPUT; - cur->state = CUBEB_DEVICE_STATE_ENABLED; - cur->preferred = CUBEB_DEVICE_PREF_ALL; - cur->format = CUBEB_DEVICE_FMT_F32NE; - cur->default_format = CUBEB_DEVICE_FMT_F32NE; - cur->max_channels = MAX_CHANNELS; - cur->min_rate = rate; - cur->max_rate = rate; - cur->default_rate = rate; - cur->latency_lo = 0; - cur->latency_hi = 0; - 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->friendly_name = JACK_DEFAULT_IN; - cur->group_id = JACK_DEFAULT_IN; - cur->vendor_name = JACK_DEFAULT_IN; - cur->type = CUBEB_DEVICE_TYPE_INPUT; - cur->state = CUBEB_DEVICE_STATE_ENABLED; - cur->preferred = CUBEB_DEVICE_PREF_ALL; - cur->format = CUBEB_DEVICE_FMT_F32NE; - cur->default_format = CUBEB_DEVICE_FMT_F32NE; - cur->max_channels = MAX_CHANNELS; - cur->min_rate = rate; - cur->max_rate = rate; - cur->default_rate = rate; - cur->latency_lo = 0; - cur->latency_hi = 0; - collection->count += 1; - } - - collection->device = devices; - - return CUBEB_OK; -} - -static int -cbjack_device_collection_destroy(cubeb * /*ctx*/, - cubeb_device_collection * collection) -{ - XASSERT(collection); - delete [] collection->device; - return CUBEB_OK; -} diff --git a/Externals/cubeb/src/cubeb_kai.c b/Externals/cubeb/src/cubeb_kai.c deleted file mode 100644 index 4ce0ae72e5..0000000000 --- a/Externals/cubeb/src/cubeb_kai.c +++ /dev/null @@ -1,363 +0,0 @@ -/* - * Copyright © 2015 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#include -#include -#include -#include - -#include - -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" - -/* We don't support more than 2 channels in KAI */ -#define MAX_CHANNELS 2 - -#define NBUFS 2 -#define FRAME_SIZE 2048 - -struct cubeb_stream_item { - cubeb_stream * stream; -}; - -static struct cubeb_ops const kai_ops; - -struct cubeb { - struct cubeb_ops const * ops; -}; - -struct cubeb_stream { - cubeb * context; - cubeb_stream_params params; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * user_ptr; - - HKAI hkai; - KAISPEC spec; - uint64_t total_frames; - float soft_volume; - _fmutex mutex; - float float_buffer[FRAME_SIZE * MAX_CHANNELS]; -}; - -static inline long -frames_to_bytes(long frames, cubeb_stream_params params) -{ - return frames * 2 * params.channels; /* 2 bytes per frame */ -} - -static inline long -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*/ int -kai_init(cubeb ** context, char const * context_name) -{ - cubeb * ctx; - - XASSERT(context); - *context = NULL; - - if (kaiInit(KAIM_AUTO)) - return CUBEB_ERROR; - - ctx = calloc(1, sizeof(*ctx)); - XASSERT(ctx); - - ctx->ops = &kai_ops; - - *context = ctx; - - return CUBEB_OK; -} - -static char const * -kai_get_backend_id(cubeb * ctx) -{ - return "kai"; -} - -static void -kai_destroy(cubeb * ctx) -{ - kaiDone(); - - free(ctx); -} - -static void -float_to_s16ne(int16_t *dst, float *src, size_t n) -{ - long l; - - while (n--) { - l = lrintf(*src++ * 0x8000); - if (l > 32767) - l = 32767; - if (l < -32768) - l = -32768; - *dst++ = (int16_t)l; - } -} - -static ULONG APIENTRY -kai_callback(PVOID cbdata, PVOID buffer, ULONG len) -{ - cubeb_stream * stm = cbdata; - 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; - - wanted_frames = bytes_to_frames(len, stm->params); - frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames); - - _fmutex_request(&stm->mutex, 0); - stm->total_frames += frames; - soft_volume = stm->soft_volume; - _fmutex_release(&stm->mutex); - - if (frames < wanted_frames) - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - - if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) - float_to_s16ne(buffer, p, elements); - - if (soft_volume != -1.0f) { - int16_t *b = buffer; - int i; - - for (i = 0; i < elements; i++) - *b++ *= soft_volume; - } - - return frames_to_bytes(frames, stm->params); -} - -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, - 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) -{ - cubeb_stream * stm; - KAISPEC wanted_spec; - - XASSERT(!input_stream_params && "not supported."); - if (input_device || output_device) { - /* Device selection not yet implemented. */ - return CUBEB_ERROR_DEVICE_UNAVAILABLE; - } - - if (!output_stream_params) - return CUBEB_ERROR_INVALID_PARAMETER; - - if (output_stream_params->channels < 1 || - output_stream_params->channels > MAX_CHANNELS) - return CUBEB_ERROR_INVALID_FORMAT; - - XASSERT(context); - XASSERT(stream); - - *stream = NULL; - - stm = calloc(1, sizeof(*stm)); - XASSERT(stm); - - stm->context = context; - stm->params = *output_stream_params; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->soft_volume = -1.0f; - - if (_fmutex_create(&stm->mutex, 0)) { - free(stm); - return CUBEB_ERROR; - } - - 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; - - if (kaiOpen(&wanted_spec, &stm->spec, &stm->hkai)) { - _fmutex_close(&stm->mutex); - free(stm); - return CUBEB_ERROR; - } - - *stream = stm; - - return CUBEB_OK; -} - -static void -kai_stream_destroy(cubeb_stream * stm) -{ - kaiClose(stm->hkai); - _fmutex_close(&stm->mutex); - free(stm); -} - -static int -kai_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - XASSERT(ctx && max_channels); - - *max_channels = MAX_CHANNELS; - - return CUBEB_OK; -} - -static int -kai_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency) -{ - /* We have at least two buffers. One is being played, the other one is being - filled. So there is as much latency as one buffer. */ - *latency = FRAME_SIZE; - - return CUBEB_OK; -} - -static int -kai_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - cubeb_stream_params params; - KAISPEC wanted_spec; - KAISPEC spec; - HKAI hkai; - - params.format = CUBEB_SAMPLE_S16NE; - params.rate = 48000; - params.channels = 2; - - 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; - - /* Test 48KHz */ - if (kaiOpen(&wanted_spec, &spec, &hkai)) { - /* Not supported. Fall back to 44.1KHz */ - params.rate = 44100; - } else { - /* Supported. Use 48KHz */ - kaiClose(hkai); - } - - *rate = params.rate; - - return CUBEB_OK; -} - -static int -kai_stream_start(cubeb_stream * stm) -{ - if (kaiPlay(stm->hkai)) - return CUBEB_ERROR; - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); - - return CUBEB_OK; -} - -static int -kai_stream_stop(cubeb_stream * stm) -{ - if (kaiStop(stm->hkai)) - return CUBEB_ERROR; - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); - - return CUBEB_OK; -} - -static int -kai_stream_get_position(cubeb_stream * stm, uint64_t * position) -{ - _fmutex_request(&stm->mutex, 0); - *position = stm->total_frames; - _fmutex_release(&stm->mutex); - - return CUBEB_OK; -} - -static int -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); - - return CUBEB_OK; -} - -static int -kai_stream_set_volume(cubeb_stream * stm, float volume) -{ - _fmutex_request(&stm->mutex, 0); - stm->soft_volume = volume; - _fmutex_release(&stm->mutex); - - return CUBEB_OK; -} - -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_set_volume =*/ kai_stream_set_volume, - /*.stream_set_panning =*/ 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 deleted file mode 100644 index 54c7f4a154..0000000000 --- a/Externals/cubeb/src/cubeb_log.cpp +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#define NOMINMAX - -#include "cubeb_log.h" -#include "cubeb_ringbuffer.h" -#include -#ifdef _WIN32 -#include -#else -#include -#endif - -cubeb_log_level g_cubeb_log_level; -cubeb_log_callback g_cubeb_log_callback; - -/** The maximum size of a log message, after having been formatted. */ -const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256; -/** The maximum number of log messages that can be queued before dropping - * messages. */ -const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40; -/** Number of milliseconds to wait before dequeuing log messages. */ -#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 -{ -public: - cubeb_log_message() - { - *storage = '\0'; - } - cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE]) - { - size_t length = strlen(str); - /* paranoia against malformed message */ - assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE); - if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) { - return; - } - PodCopy(storage, str, length); - storage[length] = '\0'; - } - 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 -{ -public: - /* This is thread-safe since C++11 */ - static cubeb_async_logger & get() { - static cubeb_async_logger instance; - return instance; - } - void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE]) - { - cubeb_log_message msg(str); - msg_queue.enqueue(msg); - } - void run() - { - std::thread([this]() { - while (true) { - cubeb_log_message msg; - while (msg_queue.dequeue(&msg, 1)) { - LOGV("%s", msg.get()); - } -#ifdef _WIN32 - Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS); -#else - timespec sleep_duration = sleep_for; - timespec remainder; - do { - if (nanosleep(&sleep_duration, &remainder) == 0 || - errno != EINTR) { - break; - } - sleep_duration = remainder; - } while (remainder.tv_sec || remainder.tv_nsec); -#endif - } - }).detach(); - } - // 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(); - } -private: -#ifndef _WIN32 - const struct timespec sleep_for = { - 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(); - } - /** 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, ...) -{ - if (!g_cubeb_log_callback) { - return; - } - // This is going to copy a 256 bytes array around, which is fine. - // We don't want to allocate memory here, because this is made to - // be called from a real-time callback. - va_list args; - va_start(args, fmt); - char msg[CUBEB_LOG_MESSAGE_MAX_SIZE]; - vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args); - cubeb_async_logger::get().push(msg); - va_end(args); -} - -void cubeb_async_log_reset_threads() -{ - if (!g_cubeb_log_callback) { - return; - } - cubeb_async_logger::get().reset_producer_thread(); -} diff --git a/Externals/cubeb/src/cubeb_log.h b/Externals/cubeb/src/cubeb_log.h deleted file mode 100644 index a79976bb3f..0000000000 --- a/Externals/cubeb/src/cubeb_log.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_LOG -#define CUBEB_LOG - -#include "cubeb/cubeb.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(__GNUC__) || defined(__clang__) -#define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args))) -#else -#define PRINTF_FORMAT(fmt, args) -#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(); - -#ifdef __cplusplus -} -#endif - -#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", __FILE__, __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) - -#endif // CUBEB_LOG diff --git a/Externals/cubeb/src/cubeb_mixer.cpp b/Externals/cubeb/src/cubeb_mixer.cpp deleted file mode 100644 index 08787f1583..0000000000 --- a/Externals/cubeb/src/cubeb_mixer.cpp +++ /dev/null @@ -1,571 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#include -#include "cubeb-internal.h" -#include "cubeb_mixer.h" - -// DUAL_MONO(_LFE) is same as STEREO(_LFE). -#define MASK_MONO (1 << CHANNEL_MONO) -#define MASK_MONO_LFE (MASK_MONO | (1 << CHANNEL_LFE)) -#define MASK_STEREO ((1 << CHANNEL_LEFT) | (1 << CHANNEL_RIGHT)) -#define MASK_STEREO_LFE (MASK_STEREO | (1 << CHANNEL_LFE)) -#define MASK_3F (MASK_STEREO | (1 << CHANNEL_CENTER)) -#define MASK_3F_LFE (MASK_3F | (1 << CHANNEL_LFE)) -#define MASK_2F1 (MASK_STEREO | (1 << CHANNEL_RCENTER)) -#define MASK_2F1_LFE (MASK_2F1 | (1 << CHANNEL_LFE)) -#define MASK_3F1 (MASK_3F | (1 << CHANNEL_RCENTER)) -#define MASK_3F1_LFE (MASK_3F1 | (1 << CHANNEL_LFE)) -#define MASK_2F2 (MASK_STEREO | (1 << CHANNEL_LS) | (1 << CHANNEL_RS)) -#define MASK_2F2_LFE (MASK_2F2 | (1 << CHANNEL_LFE)) -#define MASK_3F2 (MASK_2F2 | (1 << CHANNEL_CENTER)) -#define MASK_3F2_LFE (MASK_3F2 | (1 << CHANNEL_LFE)) -#define MASK_3F3R_LFE (MASK_3F2_LFE | (1 << CHANNEL_RCENTER)) -#define MASK_3F4_LFE (MASK_3F2_LFE | (1 << CHANNEL_RLS) | (1 << CHANNEL_RRS)) - -cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map) -{ - uint32_t channel_mask = 0; - for (uint8_t i = 0 ; i < channel_map->channels ; ++i) { - if (channel_map->map[i] == CHANNEL_INVALID || - channel_map->map[i] == CHANNEL_UNMAPPED) { - return CUBEB_LAYOUT_UNDEFINED; - } - channel_mask |= 1 << channel_map->map[i]; - } - - switch(channel_mask) { - case MASK_MONO: return CUBEB_LAYOUT_MONO; - case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE; - case MASK_STEREO: return CUBEB_LAYOUT_STEREO; - case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE; - case MASK_3F: return CUBEB_LAYOUT_3F; - case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE; - case MASK_2F1: return CUBEB_LAYOUT_2F1; - case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE; - case MASK_3F1: return CUBEB_LAYOUT_3F1; - case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE; - case MASK_2F2: return CUBEB_LAYOUT_2F2; - case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE; - case MASK_3F2: return CUBEB_LAYOUT_3F2; - case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE; - case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE; - case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE; - default: return CUBEB_LAYOUT_UNDEFINED; - } -} - -cubeb_layout_map const CUBEB_CHANNEL_LAYOUT_MAPS[CUBEB_LAYOUT_MAX] = { - { "undefined", 0, CUBEB_LAYOUT_UNDEFINED }, - { "dual mono", 2, CUBEB_LAYOUT_DUAL_MONO }, - { "dual mono lfe", 3, CUBEB_LAYOUT_DUAL_MONO_LFE }, - { "mono", 1, CUBEB_LAYOUT_MONO }, - { "mono lfe", 2, CUBEB_LAYOUT_MONO_LFE }, - { "stereo", 2, CUBEB_LAYOUT_STEREO }, - { "stereo lfe", 3, CUBEB_LAYOUT_STEREO_LFE }, - { "3f", 3, CUBEB_LAYOUT_3F }, - { "3f lfe", 4, CUBEB_LAYOUT_3F_LFE }, - { "2f1", 3, CUBEB_LAYOUT_2F1 }, - { "2f1 lfe", 4, CUBEB_LAYOUT_2F1_LFE }, - { "3f1", 4, CUBEB_LAYOUT_3F1 }, - { "3f1 lfe", 5, CUBEB_LAYOUT_3F1_LFE }, - { "2f2", 4, CUBEB_LAYOUT_2F2 }, - { "2f2 lfe", 5, CUBEB_LAYOUT_2F2_LFE }, - { "3f2", 5, CUBEB_LAYOUT_3F2 }, - { "3f2 lfe", 6, CUBEB_LAYOUT_3F2_LFE }, - { "3f3r lfe", 7, CUBEB_LAYOUT_3F3R_LFE }, - { "3f4 lfe", 8, CUBEB_LAYOUT_3F4_LFE } -}; - -static int const CHANNEL_ORDER_TO_INDEX[CUBEB_LAYOUT_MAX][CHANNEL_MAX] = { -// M | L | R | C | LS | RS | RLS | RC | RRS | LFE - { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // UNDEFINED - { -1, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, // DUAL_MONO - { -1, 0, 1, -1, -1, -1, -1, -1, -1, 2 }, // DUAL_MONO_LFE - { 0, -1, -1, -1, -1, -1, -1, -1, -1, -1 }, // MONO - { 0, -1, -1, -1, -1, -1, -1, -1, -1, 1 }, // MONO_LFE - { -1, 0, 1, -1, -1, -1, -1, -1, -1, -1 }, // STEREO - { -1, 0, 1, -1, -1, -1, -1, -1, -1, 2 }, // STEREO_LFE - { -1, 0, 1, 2, -1, -1, -1, -1, -1, -1 }, // 3F - { -1, 0, 1, 2, -1, -1, -1, -1, -1, 3 }, // 3F_LFE - { -1, 0, 1, -1, -1, -1, -1, 2, -1, -1 }, // 2F1 - { -1, 0, 1, -1, -1, -1, -1, 3, -1, 2 }, // 2F1_LFE - { -1, 0, 1, 2, -1, -1, -1, 3, -1, -1 }, // 3F1 - { -1, 0, 1, 2, -1, -1, -1, 4, -1, 3 }, // 3F1_LFE - { -1, 0, 1, -1, 2, 3, -1, -1, -1, -1 }, // 2F2 - { -1, 0, 1, -1, 3, 4, -1, -1, -1, 2 }, // 2F2_LFE - { -1, 0, 1, 2, 3, 4, -1, -1, -1, -1 }, // 3F2 - { -1, 0, 1, 2, 4, 5, -1, -1, -1, 3 }, // 3F2_LFE - { -1, 0, 1, 2, 5, 6, -1, 4, -1, 3 }, // 3F3R_LFE - { -1, 0, 1, 2, 6, 7, 4, -1, 5, 3 }, // 3F4_LFE -}; - -// The downmix matrix from TABLE 2 in the ITU-R BS.775-3[1] defines a way to -// convert 3F2 input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 output data. We extend it -// to convert 3F2-LFE input data to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs -// output data. -// [1] https://www.itu.int/dms_pubrec/itu-r/rec/bs/R-REC-BS.775-3-201208-I!!PDF-E.pdf - -// Number of converted layouts: 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs. -unsigned int const SUPPORTED_LAYOUT_NUM = 12; -// Number of input channel for downmix conversion. -unsigned int const INPUT_CHANNEL_NUM = 6; // 3F2-LFE -// Max number of possible output channels. -unsigned int const MAX_OUTPUT_CHANNEL_NUM = 5; // 2F2-LFE or 3F1-LFE -float const INV_SQRT_2 = 0.707106f; // 1/sqrt(2) -// Each array contains coefficients that will be multiplied with -// { L, R, C, LFE, LS, RS } channels respectively. -static float const DOWNMIX_MATRIX_3F2_LFE[SUPPORTED_LAYOUT_NUM][MAX_OUTPUT_CHANNEL_NUM][INPUT_CHANNEL_NUM] = -{ -// 1F Mono - { - { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M - }, -// 1F Mono-LFE - { - { INV_SQRT_2, INV_SQRT_2, 1, 0, 0.5, 0.5 }, // M - { 0, 0, 0, 1, 0, 0 } // LFE - }, -// 2F Stereo - { - { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 }, // L - { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 } // R - }, -// 2F Stereo-LFE - { - { 1, 0, INV_SQRT_2, 0, INV_SQRT_2, 0 }, // L - { 0, 1, INV_SQRT_2, 0, 0, INV_SQRT_2 }, // R - { 0, 0, 0, 1, 0, 0 } // LFE - }, -// 3F - { - { 1, 0, 0, 0, INV_SQRT_2, 0 }, // L - { 0, 1, 0, 0, 0, INV_SQRT_2 }, // R - { 0, 0, 1, 0, 0, 0 } // C - }, -// 3F-LFE - { - { 1, 0, 0, 0, INV_SQRT_2, 0 }, // L - { 0, 1, 0, 0, 0, INV_SQRT_2 }, // R - { 0, 0, 1, 0, 0, 0 }, // C - { 0, 0, 0, 1, 0, 0 } // LFE - }, -// 2F1 - { - { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L - { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R - { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S - }, -// 2F1-LFE - { - { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L - { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R - { 0, 0, 0, 1, 0, 0 }, // LFE - { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S - }, -// 3F1 - { - { 1, 0, 0, 0, 0, 0 }, // L - { 0, 1, 0, 0, 0, 0 }, // R - { 0, 0, 1, 0, 0, 0 }, // C - { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S - }, -// 3F1-LFE - { - { 1, 0, 0, 0, 0, 0 }, // L - { 0, 1, 0, 0, 0, 0 }, // R - { 0, 0, 1, 0, 0, 0 }, // C - { 0, 0, 0, 1, 0, 0 }, // LFE - { 0, 0, 0, 0, INV_SQRT_2, INV_SQRT_2 } // S - }, -// 2F2 - { - { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L - { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R - { 0, 0, 0, 0, 1, 0 }, // LS - { 0, 0, 0, 0, 0, 1 } // RS - }, -// 2F2-LFE - { - { 1, 0, INV_SQRT_2, 0, 0, 0 }, // L - { 0, 1, INV_SQRT_2, 0, 0, 0 }, // R - { 0, 0, 0, 1, 0, 0 }, // LFE - { 0, 0, 0, 0, 1, 0 }, // LS - { 0, 0, 0, 0, 0, 1 } // RS - } -}; - -// Convert audio data from 3F2(-LFE) to 1F, 2F, 3F, 2F1, 3F1, 2F2 and their LFEs. -// -// ITU-R BS.775-3[1] provides spec for downmixing 3F2 data to 1F, 2F, 3F, 2F1, -// 3F1, 2F2 data. We simply add LFE to its defined matrix. If both the input -// and output have LFE channel, then we pass it's data. If only input or output -// has LFE, then we either drop it or append 0 to the LFE channel. -// -// Fig. 1: -// |<-------------- 1 -------------->|<-------------- 2 -------------->| -// +----+----+----+------+-----+-----+----+----+----+------+-----+-----+ -// | L0 | R0 | C0 | LFE0 | LS0 | RS0 | L1 | R1 | C1 | LFE1 | LS1 | RS1 | ... -// +----+----+----+------+-----+-----+----+----+----+------+-----+-----+ -// -// Fig. 2: -// |<-- 1 -->|<-- 2 -->| -// +----+----+----+----+ -// | L0 | R0 | L1 | R1 | ... -// +----+----+----+----+ -// -// The figures above shows an example for downmixing from 3F2-LFE(Fig. 1) to -// to stereo(Fig. 2), where L0 = L0 + 0.707 * (C0 + LS0), -// R0 = R0 + 0.707 * (C0 + RS0), L1 = L1 + 0.707 * (C1 + LS1), -// R1 = R1 + 0.707 * (C1 + RS1), ... -// -// Nevertheless, the downmixing method is a little bit different on OSX. -// The audio rendering mechanism on OS X will drop the extra channels beyond -// the channels that audio device can provide. The trick here is that OSX allows -// us to set the layout containing other channels that the output device can -// NOT provide. For example, setting 3F2-LFE layout to a stereo device is fine. -// Therefore, OSX expects we fill the buffer for playing sound by the defined -// layout, so there are some will-be-dropped data in the buffer: -// -// +---+---+---+-----+----+----+ -// | L | R | C | LFE | LS | RS | ... -// +---+---+---+-----+----+----+ -// ^ ^ ^ ^ -// The data for these four channels will be dropped! -// -// To keep all the information, we need to downmix the data before it's dropped. -// The figure below shows an example for downmixing from 3F2-LFE(Fig. 1) -// to stereo(Fig. 3) on OSX, where the LO, R0, L1, R0 are same as above. -// -// Fig. 3: -// |<---------- 1 ---------->|<---------- 2 ---------->| -// +----+----+---+---+---+---+----+----+---+---+---+---+ -// | L0 | R0 | x | x | x | x | L1 | R1 | x | x | x | x | ... -// +----+----+---+---+---+---+----+----+---+---+---+---+ -// |<-- dummy -->| |<-- dummy -->| -template -bool -downmix_3f2(unsigned long inframes, - T const * const in, unsigned long in_len, - T * out, unsigned long out_len, - cubeb_channel_layout in_layout, cubeb_channel_layout out_layout) -{ - if ((in_layout != CUBEB_LAYOUT_3F2 && in_layout != CUBEB_LAYOUT_3F2_LFE) || - out_layout < CUBEB_LAYOUT_MONO || out_layout > CUBEB_LAYOUT_2F2_LFE) { - return false; - } - - unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels; - unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels; - - // Conversion from 3F2 to 2F2-LFE or 3F1-LFE is allowed, so we use '<=' instead of '<'. - assert(out_channels <= in_channels); - - auto & downmix_matrix = DOWNMIX_MATRIX_3F2_LFE[out_layout - CUBEB_LAYOUT_MONO]; // The matrix is started from mono. - unsigned long out_index = 0; - for (unsigned long i = 0 ; i < inframes * in_channels; i += in_channels) { - for (unsigned int j = 0; j < out_channels; ++j) { - T sample = 0; - for (unsigned int k = 0 ; k < INPUT_CHANNEL_NUM ; ++k) { - // 3F2-LFE has 6 channels: L, R, C, LFE, LS, RS, while 3F2 has only 5 - // channels: L, R, C, LS, RS. Thus, we need to append 0 to LFE(index 3) - // to simulate a 3F2-LFE data when input layout is 3F2. - assert((in_layout == CUBEB_LAYOUT_3F2_LFE || k < 3) ? (i + k < in_len) : (k == 3) ? true : (i + k - 1 < in_len)); - T data = (in_layout == CUBEB_LAYOUT_3F2_LFE) ? in[i + k] : (k == 3) ? 0 : in[i + ((k < 3) ? k : k - 1)]; - sample += downmix_matrix[j][k] * data; - } - assert(out_index + j < out_len); - out[out_index + j] = sample; - } -#if defined(USE_AUDIOUNIT) - out_index += in_channels; -#else - out_index += out_channels; -#endif - } - - return true; -} - -/* Map the audio data by channel name. */ -template -bool -mix_remap(long inframes, - T const * const in, unsigned long in_len, - T * out, unsigned long out_len, - cubeb_channel_layout in_layout, cubeb_channel_layout out_layout) -{ - assert(in_layout != out_layout && inframes >= 0); - - // We might overwrite the data before we copied them to the mapped index - // (e.g. upmixing from stereo to 2F1 or mapping [L, R] to [R, L]) - if (in == out) { - return false; - } - - unsigned int in_channels = CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels; - unsigned int out_channels = CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels; - - uint32_t in_layout_mask = 0; - for (unsigned int i = 0 ; i < in_channels ; ++i) { - in_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[in_layout][i]; - } - - uint32_t out_layout_mask = 0; - for (unsigned int i = 0 ; i < out_channels ; ++i) { - out_layout_mask |= 1 << CHANNEL_INDEX_TO_ORDER[out_layout][i]; - } - - // If there is no matched channel, then do nothing. - if (!(out_layout_mask & in_layout_mask)) { - return false; - } - - for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) { - for (unsigned int j = 0; j < out_channels; ++j) { - cubeb_channel channel = CHANNEL_INDEX_TO_ORDER[out_layout][j]; - uint32_t channel_mask = 1 << channel; - int channel_index = CHANNEL_ORDER_TO_INDEX[in_layout][channel]; - assert(channel_index >= -1); - assert(out_index + j < out_len); - if (in_layout_mask & channel_mask) { - assert(channel_index != -1); - assert(i + channel_index < in_len); - out[out_index + j] = in[i + channel_index]; - } else { - assert(channel_index == -1); - out[out_index + j] = 0; - } - } - } - - return true; -} - -/* Drop the extra channels beyond the provided output channels. */ -template -void -downmix_fallback(long inframes, - T const * const in, unsigned long in_len, - T * out, unsigned long out_len, - unsigned int in_channels, unsigned int out_channels) -{ - assert(in_channels >= out_channels && inframes >= 0); - - if (in_channels == out_channels && in == out) { - return; - } - - for (unsigned long i = 0, out_index = 0; i < (unsigned long)inframes * in_channels; i += in_channels, out_index += out_channels) { - for (unsigned int j = 0; j < out_channels; ++j) { - assert(i + j < in_len && out_index + j < out_len); - out[out_index + j] = in[i + j]; - } - } -} - - -template -void -cubeb_downmix(long inframes, - T const * const in, unsigned long in_len, - T * out, unsigned long out_len, - cubeb_stream_params const * stream_params, - cubeb_stream_params const * mixer_params) -{ - assert(in && out); - assert(inframes); - assert(stream_params->channels >= mixer_params->channels && - mixer_params->channels > 0); - assert(stream_params->layout != CUBEB_LAYOUT_UNDEFINED); - - unsigned int in_channels = stream_params->channels; - cubeb_channel_layout in_layout = stream_params->layout; - - unsigned int out_channels = mixer_params->channels; - cubeb_channel_layout out_layout = mixer_params->layout; - - // If the channel number is different from the layout's setting, - // then we use fallback downmix mechanism. - if (out_channels == CUBEB_CHANNEL_LAYOUT_MAPS[out_layout].channels && - in_channels == CUBEB_CHANNEL_LAYOUT_MAPS[in_layout].channels) { - if (downmix_3f2(inframes, in, in_len, out, out_len, in_layout, out_layout)) { - return; - } - -#if defined(USE_AUDIOUNIT) - // We only support downmix for audio 5.1 on OS X currently. - return; -#endif - - if (mix_remap(inframes, in, in_len, out, out_len, in_layout, out_layout)) { - return; - } - } - - downmix_fallback(inframes, in, in_len, out, out_len, in_channels, out_channels); -} - -/* Upmix function, copies a mono channel into L and R. */ -template -void -mono_to_stereo(long insamples, T const * in, unsigned long in_len, - T * out, unsigned long out_len, unsigned int out_channels) -{ - for (long i = 0, j = 0; i < insamples; ++i, j += out_channels) { - assert((unsigned long)i < in_len && (unsigned long)j + 1 < out_len); - out[j] = out[j + 1] = in[i]; - } -} - -template -void -cubeb_upmix(long inframes, - T const * const in, unsigned long in_len, - T * out, unsigned long out_len, - cubeb_stream_params const * stream_params, - cubeb_stream_params const * mixer_params) -{ - assert(in && out); - assert(inframes); - assert(mixer_params->channels >= stream_params->channels && - stream_params->channels > 0); - - unsigned int in_channels = stream_params->channels; - unsigned int out_channels = mixer_params->channels; - - /* Either way, if we have 2 or more channels, the first two are L and R. */ - /* If we are playing a mono stream over stereo speakers, copy the data over. */ - if (in_channels == 1 && out_channels >= 2) { - mono_to_stereo(inframes, in, in_len, out, out_len, out_channels); - } else { - /* Copy through. */ - for (unsigned int i = 0, o = 0; i < inframes * in_channels; - i += in_channels, o += out_channels) { - for (unsigned int j = 0; j < in_channels; ++j) { - assert(i + j < in_len && o + j < out_len); - out[o + j] = in[i + j]; - } - } - } - - /* Check if more channels. */ - if (out_channels <= 2) { - return; - } - - /* Put silence in remaining channels. */ - for (long i = 0, o = 0; i < inframes; ++i, o += out_channels) { - for (unsigned int j = 2; j < out_channels; ++j) { - assert((unsigned long)o + j < out_len); - out[o + j] = 0.0; - } - } -} - -bool -cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer) -{ - return mixer->channels > stream->channels; -} - -bool -cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer) -{ - if (mixer->channels > stream->channels || mixer->layout == stream->layout) { - return false; - } - - return mixer->channels < stream->channels || - // When mixer.channels == stream.channels - mixer->layout == CUBEB_LAYOUT_UNDEFINED || // fallback downmix - (stream->layout == CUBEB_LAYOUT_3F2 && // 3f2 downmix - (mixer->layout == CUBEB_LAYOUT_2F2_LFE || - mixer->layout == CUBEB_LAYOUT_3F1_LFE)); -} - -bool -cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer) -{ - return stream->layout != CUBEB_LAYOUT_UNDEFINED && - (cubeb_should_upmix(stream, mixer) || cubeb_should_downmix(stream, mixer)); -} - -struct cubeb_mixer { - virtual void mix(long frames, - void * input_buffer, unsigned long input_buffer_length, - void * output_buffer, unsigned long output_buffer_length, - cubeb_stream_params const * stream_params, - cubeb_stream_params const * mixer_params) = 0; - virtual ~cubeb_mixer() {}; -}; - -template -struct cubeb_mixer_impl : public cubeb_mixer { - explicit cubeb_mixer_impl(unsigned int d) - : direction(d) - { - } - - void mix(long frames, - void * input_buffer, unsigned long input_buffer_length, - void * output_buffer, unsigned long output_buffer_length, - cubeb_stream_params const * stream_params, - cubeb_stream_params const * mixer_params) - { - if (frames <= 0) { - return; - } - - T * in = static_cast(input_buffer); - T * out = static_cast(output_buffer); - - if ((direction & CUBEB_MIXER_DIRECTION_DOWNMIX) && - cubeb_should_downmix(stream_params, mixer_params)) { - cubeb_downmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params); - } else if ((direction & CUBEB_MIXER_DIRECTION_UPMIX) && - cubeb_should_upmix(stream_params, mixer_params)) { - cubeb_upmix(frames, in, input_buffer_length, out, output_buffer_length, stream_params, mixer_params); - } - } - - ~cubeb_mixer_impl() {}; - - unsigned char const direction; -}; - -cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format, - unsigned char direction) -{ - assert(direction & CUBEB_MIXER_DIRECTION_DOWNMIX || - direction & CUBEB_MIXER_DIRECTION_UPMIX); - switch(format) { - case CUBEB_SAMPLE_S16NE: - return new cubeb_mixer_impl(direction); - case CUBEB_SAMPLE_FLOAT32NE: - return new cubeb_mixer_impl(direction); - default: - assert(false); - return nullptr; - } -} - -void cubeb_mixer_destroy(cubeb_mixer * mixer) -{ - delete mixer; -} - -void cubeb_mixer_mix(cubeb_mixer * mixer, long frames, - void * input_buffer, unsigned long input_buffer_length, - void * output_buffer, unsigned long output_buffer_length, - cubeb_stream_params const * stream_params, - cubeb_stream_params const * mixer_params) -{ - assert(mixer); - mixer->mix(frames, input_buffer, input_buffer_length, output_buffer, output_buffer_length, - stream_params, mixer_params); -} diff --git a/Externals/cubeb/src/cubeb_mixer.h b/Externals/cubeb/src/cubeb_mixer.h deleted file mode 100644 index 51fb9088a8..0000000000 --- a/Externals/cubeb/src/cubeb_mixer.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_MIXER -#define CUBEB_MIXER - -#include "cubeb/cubeb.h" // for cubeb_channel_layout ,CUBEB_CHANNEL_LAYOUT_MAPS and cubeb_stream_params. -#include - -#if defined(__cplusplus) -extern "C" { -#endif - -typedef enum { - CHANNEL_INVALID = -1, - CHANNEL_MONO = 0, - CHANNEL_LEFT, - CHANNEL_RIGHT, - CHANNEL_CENTER, - CHANNEL_LS, - CHANNEL_RS, - CHANNEL_RLS, - CHANNEL_RCENTER, - CHANNEL_RRS, - CHANNEL_LFE, - CHANNEL_UNMAPPED, - CHANNEL_MAX = 256 // Max number of supported channels. -} cubeb_channel; - -static cubeb_channel const CHANNEL_INDEX_TO_ORDER[CUBEB_LAYOUT_MAX][CHANNEL_MAX] = { - { CHANNEL_INVALID }, // UNDEFINED - { CHANNEL_LEFT, CHANNEL_RIGHT }, // DUAL_MONO - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE }, // DUAL_MONO_LFE - { CHANNEL_MONO }, // MONO - { CHANNEL_MONO, CHANNEL_LFE }, // MONO_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT }, // STEREO - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE }, // STEREO_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER }, // 3F - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE }, // 3F_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_RCENTER }, // 2F1 - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_RCENTER }, // 2F1_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_RCENTER }, // 3F1 - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RCENTER }, // 3F1_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LS, CHANNEL_RS }, // 2F2 - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_LFE, CHANNEL_LS, CHANNEL_RS }, // 2F2_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LS, CHANNEL_RS }, // 3F2 - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_LS, CHANNEL_RS }, // 3F2_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RCENTER, CHANNEL_LS, CHANNEL_RS }, // 3F3R_LFE - { CHANNEL_LEFT, CHANNEL_RIGHT, CHANNEL_CENTER, CHANNEL_LFE, CHANNEL_RLS, CHANNEL_RRS, CHANNEL_LS, CHANNEL_RS } // 3F4_LFE - // When more channels are present, the stream is considered unmapped to a - // particular speaker set. -}; - -typedef struct { - unsigned int channels; - cubeb_channel map[CHANNEL_MAX]; -} cubeb_channel_map; - -cubeb_channel_layout cubeb_channel_map_to_layout(cubeb_channel_map const * channel_map); - -bool cubeb_should_upmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer); - -bool cubeb_should_downmix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer); - -bool cubeb_should_mix(cubeb_stream_params const * stream, cubeb_stream_params const * mixer); - -typedef enum { - CUBEB_MIXER_DIRECTION_DOWNMIX = 0x01, - CUBEB_MIXER_DIRECTION_UPMIX = 0x02, -} cubeb_mixer_direction; - -typedef struct cubeb_mixer cubeb_mixer; -cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format, - unsigned char direction); -void cubeb_mixer_destroy(cubeb_mixer * mixer); -void cubeb_mixer_mix(cubeb_mixer * mixer, long frames, - void * input_buffer, unsigned long input_buffer_length, - void * output_buffer, unsigned long output_buffer_length, - cubeb_stream_params const * stream_params, - cubeb_stream_params const * mixer_params); - -#if defined(__cplusplus) -} -#endif - -#endif // CUBEB_MIXER diff --git a/Externals/cubeb/src/cubeb_opensl.c b/Externals/cubeb/src/cubeb_opensl.c deleted file mode 100644 index 903906d046..0000000000 --- a/Externals/cubeb/src/cubeb_opensl.c +++ /dev/null @@ -1,1721 +0,0 @@ -/* - * Copyright © 2012 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#undef NDEBUG -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__ANDROID__) -#include -#include -#include "android/sles_definitions.h" -#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" - -#if defined(__ANDROID__) -#ifdef LOG -#undef LOG -#endif -//#define LOGGING_ENABLED -#ifdef LOGGING_ENABLED -#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) -#else -#define TIMESTAMP(...) -#endif - -#define ANDROID_VERSION_GINGERBREAD_MR1 10 -#define ANDROID_VERSION_LOLLIPOP 21 -#define ANDROID_VERSION_MARSHMALLOW 23 -#endif - -#define DEFAULT_SAMPLE_RATE 48000 - -static struct cubeb_ops const opensl_ops; - -struct cubeb { - struct cubeb_ops const * ops; - void * lib; - void * libmedia; - int32_t (* get_output_latency)(uint32_t * latency, int stream_type); - SLInterfaceID SL_IID_BUFFERQUEUE; - SLInterfaceID SL_IID_PLAY; -#if defined(__ANDROID__) - SLInterfaceID SL_IID_ANDROIDCONFIGURATION; - SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE; -#endif - SLInterfaceID SL_IID_VOLUME; - SLInterfaceID SL_IID_RECORD; - SLObjectItf engObj; - SLEngineItf eng; - SLObjectItf outmixObj; -}; - -#define NELEMS(A) (sizeof(A) / sizeof A[0]) -#define NBUFS 4 -#define AUDIO_STREAM_TYPE_MUSIC 3 - -struct cubeb_stream { - cubeb * context; - pthread_mutex_t mutex; - SLObjectItf playerObj; - SLPlayItf play; - SLBufferQueueItf bufq; - SLVolumeItf volume; - void ** queuebuf; - uint32_t queuebuf_capacity; - int queuebuf_idx; - long queuebuf_len; - long bytespersec; - long framesize; - /* Total number of played frames. - * Synchronized by stream::mutex lock. */ - long written; - /* Flag indicating draining. Synchronized - * by stream::mutex lock. */ - int draining; - /* Flags to determine in/out.*/ - uint32_t input_enabled; - uint32_t output_enabled; - /* Recorder abstract object. */ - SLObjectItf recorderObj; - /* Recorder Itf for input capture. */ - SLRecordItf recorderItf; - /* Buffer queue for input capture. */ - SLAndroidSimpleBufferQueueItf recorderBufferQueueItf; - /* Store input buffers. */ - void ** input_buffer_array; - /* The capacity of the array. - * On capture only can be small (4). - * On full duplex is calculated to - * store 1 sec of data buffers. */ - uint32_t input_array_capacity; - /* Current filled index of input buffer array. - * It is initiated to -1 indicating buffering - * have not started yet. */ - int input_buffer_index; - /* Length of input buffer.*/ - uint32_t input_buffer_length; - /* Input frame size */ - uint32_t input_frame_size; - /* Device sampling rate. If user rate is not - * accepted an compatible rate is set. If it is - * accepted this is equal to params.rate. */ - uint32_t input_device_rate; - /* Exchange input buffers between input - * and full duplex threads. */ - array_queue * input_queue; - /* Silent input buffer used on full duplex. */ - void * input_silent_buffer; - /* Number of input frames from the start of the stream*/ - uint32_t input_total_frames; - /* Flag to stop the execution of user callback and - * close all working threads. Synchronized by - * stream::mutex lock. */ - uint32_t shutdown; - /* Store user callback. */ - cubeb_data_callback data_callback; - /* Store state callback. */ - cubeb_state_callback state_callback; - /* User pointer for data & state callbacks*/ - void * user_ptr; - - cubeb_resampler * resampler; - unsigned int inputrate; - unsigned int output_configured_rate; - unsigned int latency_frames; - int64_t lastPosition; - int64_t lastPositionTimeStamp; - int64_t lastCompensativePosition; -}; - -/* Forward declaration. */ -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."); -#endif - return stm->draining; -} - -static void -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."); -#endif - assert(value == 0 || value == 1); - stm->draining = value; -} - -static void -opensl_notify_drained(cubeb_stream * stm) -{ - assert(stm); - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - int draining = opensl_get_draining(stm); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - if (draining) { - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - if (stm->play) { - LOG("stop player in play_callback"); - r = opensl_stop_player(stm); - assert(r == CUBEB_OK); - } - if (stm->recorderItf) { - r = opensl_stop_recorder(stm); - assert(r == CUBEB_OK); - } - } -} - -static uint32_t -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."); -#endif - return stm->shutdown; -} - -static void -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."); -#endif - assert(value == 0 || value == 1); - stm->shutdown = value; -} - -static void -play_callback(SLPlayItf caller, void * user_ptr, SLuint32 event) -{ - cubeb_stream * stm = user_ptr; - int draining; - assert(stm); - switch (event) { - case SL_PLAYEVENT_HEADATMARKER: - opensl_notify_drained(stm); - break; - default: - break; - } -} - -static void -recorder_marker_callback (SLRecordItf caller, void * pContext, SLuint32 event) -{ - cubeb_stream * stm = pContext; - assert(stm); - - if (event == SL_RECORDEVENT_HEADATMARKER) { - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - int draining = opensl_get_draining(stm); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - if (draining) { - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - if (stm->recorderItf) { - r = opensl_stop_recorder(stm); - assert(r == CUBEB_OK); - } - if (stm->play) { - r = opensl_stop_player(stm); - assert(r == CUBEB_OK); - } - } - } -} - -static void -bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr) -{ - cubeb_stream * stm = user_ptr; - assert(stm); - SLBufferQueueState state; - SLresult res; - long written = 0; - - res = (*stm->bufq)->GetState(stm->bufq, &state); - assert(res == SL_RESULT_SUCCESS); - - if (state.count > 1) { - return; - } - - uint8_t *buf = stm->queuebuf[stm->queuebuf_idx]; - written = 0; - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - int draining = opensl_get_draining(stm); - uint32_t shutdown = opensl_get_shutdown(stm); - 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); - LOG("bufferqueue_callback: resampler fill returned %ld frames", written); - if (written < 0 || written * stm->framesize > stm->queuebuf_len) { - r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - opensl_set_shutdown(stm, 1); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - opensl_stop_player(stm); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - return; - } - } - - // 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); - 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; - - if (written > 0) { - pthread_mutex_lock(&stm->mutex); - stm->written += written; - pthread_mutex_unlock(&stm->mutex); - } - - if (!draining && written * stm->framesize < stm->queuebuf_len) { - 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; - opensl_set_draining(stm, 1); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - if (written_duration == 0) { - // since we didn't write any sample, it's not possible to reach the marker - // time and trigger the callback. We should initiative notify drained. - opensl_notify_drained(stm); - } 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); - } - return; - } -} - -static int -opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer) -{ - assert(stm); - - int current_index = stm->input_buffer_index; - void * last_buffer = NULL; - - if (current_index < 0) { - // This is the first enqueue - current_index = 0; - } else { - // 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 ) { - LOG("Enqueue recorder failed. Error code: %lu", res); - return CUBEB_ERROR; - } - // All good, update buffer and index. - stm->input_buffer_index = current_index; - if (last_filled_buffer) { - *last_filled_buffer = last_buffer; - } - return CUBEB_OK; -} - -// input data callback -void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context) -{ - assert(context); - cubeb_stream * stm = context; - assert(stm->recorderBufferQueueItf); - - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - uint32_t shutdown = opensl_get_shutdown(stm); - int draining = opensl_get_draining(stm); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - if (shutdown || draining) { - // According to the OpenSL ES 1.1 Specification, 8.14 SLBufferQueueItf - // page 184, 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. - r = opensl_enqueue_recorder(stm, NULL); - assert(r == CUBEB_OK); - return; - } - - // Enqueue next available buffer and get the last filled buffer. - void * input_buffer = NULL; - r = opensl_enqueue_recorder(stm, &input_buffer); - assert(r == CUBEB_OK); - 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); - // Error case - if (got < 0 || got > input_frame_count) { - r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - opensl_set_shutdown(stm, 1); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - r = opensl_stop_recorder(stm); - assert(r == CUBEB_OK); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - } - - // Advance total stream frames - stm->input_total_frames += got; - - if (got < input_frame_count) { - r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - 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); - return; - } -} - -void recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context) -{ - assert(context); - cubeb_stream * stm = context; - assert(stm->recorderBufferQueueItf); - - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - int draining = opensl_get_draining(stm); - uint32_t shutdown = opensl_get_shutdown(stm); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - 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. */ - LOG("Input shutdown %d or drain %d", shutdown, draining); - int r = opensl_enqueue_recorder(stm, NULL); - assert(r == CUBEB_OK); - return; - } - - // Enqueue next available buffer and get the last filled buffer. - void * input_buffer = NULL; - r = opensl_enqueue_recorder(stm, &input_buffer); - assert(r == CUBEB_OK); - assert(input_buffer); - - assert(stm->input_queue); - r = array_queue_push(stm->input_queue, input_buffer); - if (r == -1) { - LOG("Input queue is full, drop input ..."); - return; - } - - LOG("Input pushed in the queue, input array %zu", - array_queue_get_size(stm->input_queue)); -} - -static void -player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr) -{ - TIMESTAMP("ENTER"); - cubeb_stream * stm = user_ptr; - assert(stm); - SLresult res; - - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - int draining = opensl_get_draining(stm); - uint32_t shutdown = opensl_get_shutdown(stm); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - // Get output - void * output_buffer = NULL; - r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - output_buffer = stm->queuebuf[stm->queuebuf_idx]; - // Advance the output buffer queue index - stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity; - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - if (shutdown || draining) { - LOG("Shutdown/draining, send silent"); - // Set silent on buffer - memset(output_buffer, 0, stm->queuebuf_len); - - // Enqueue data in player buffer queue - res = (*stm->bufq)->Enqueue(stm->bufq, - output_buffer, - stm->queuebuf_len); - assert(res == SL_RESULT_SUCCESS); - return; - } - - // Get input. - void * input_buffer = array_queue_pop(stm->input_queue); - long input_frame_count = stm->input_buffer_length / stm->input_frame_size; - long frames_needed = stm->queuebuf_len / stm->framesize; - if (!input_buffer) { - LOG("Input hole set silent input buffer"); - input_buffer = stm->input_silent_buffer; - } - - long written = 0; - // Trigger user callback through resampler - 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)); - - if (written < 0 || written > frames_needed) { - // Error case - r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - opensl_set_shutdown(stm, 1); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - opensl_stop_player(stm); - opensl_stop_recorder(stm); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - memset(output_buffer, 0, stm->queuebuf_len); - - // Enqueue data in player buffer queue - res = (*stm->bufq)->Enqueue(stm->bufq, - output_buffer, - stm->queuebuf_len); - assert(res == SL_RESULT_SUCCESS); - return; - } - - // Advance total out written frames counter - r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - stm->written += written; - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - 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; - opensl_set_draining(stm, 1); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - // 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); - } - - // Keep sending silent data even in draining mode to prevent the audio - // back-end from being stopped automatically by OpenSL/ES. - memset((uint8_t *)output_buffer + written * stm->framesize, 0, - stm->queuebuf_len - written * stm->framesize); - - // Enqueue data in player buffer queue - res = (*stm->bufq)->Enqueue(stm->bufq, - output_buffer, - stm->queuebuf_len); - assert(res == SL_RESULT_SUCCESS); - TIMESTAMP("EXIT"); -} - -static void opensl_destroy(cubeb * ctx); - -#if defined(__ANDROID__) -#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP) -typedef int (system_property_get)(const char*, char*); - -static int -wrap_system_property_get(const char* name, char* value) -{ - 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"); - int ret = -1; - if (func) { - ret = func(name, value); - } - dlclose(libc); - return ret; -} -#endif - -static int -get_android_version(void) -{ - char version_string[PROP_VALUE_MAX]; - - memset(version_string, 0, PROP_VALUE_MAX); - -#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP) - int len = wrap_system_property_get("ro.build.version.sdk", version_string); -#else - int len = __system_property_get("ro.build.version.sdk", version_string); -#endif - if (len <= 0) { - LOG("Failed to get Android version!\n"); - return len; - } - - int version = (int)strtol(version_string, NULL, 10); - LOG("Android version %d", version); - return version; -} -#endif - -/*static*/ int -opensl_init(cubeb ** context, char const * context_name) -{ - cubeb * ctx; - -#if defined(__ANDROID__) - int android_version = get_android_version(); - if (android_version > 0 && android_version <= ANDROID_VERSION_GINGERBREAD_MR1) { - // Don't even attempt to run on Gingerbread and lower - return CUBEB_ERROR; - } -#endif - - *context = NULL; - - ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - - ctx->ops = &opensl_ops; - - ctx->lib = dlopen("libOpenSLES.so", RTLD_LAZY); - ctx->libmedia = dlopen("libmedia.so", RTLD_LAZY); - if (!ctx->lib || !ctx->libmedia) { - free(ctx); - return CUBEB_ERROR; - } - - /* Get the latency, in ms, from AudioFlinger */ - /* status_t AudioSystem::getOutputLatency(uint32_t* latency, - * audio_stream_type_t streamType) */ - /* First, try the most recent signature. */ - ctx->get_output_latency = - dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"); - if (!ctx->get_output_latency) { - /* in case of failure, try the legacy version. */ - /* status_t AudioSystem::getOutputLatency(uint32_t* latency, - * int streamType) */ - ctx->get_output_latency = - dlsym(ctx->libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji"); - if (!ctx->get_output_latency) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - } - - 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"); - ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME"); - 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"); -#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 || - !ctx->SL_IID_BUFFERQUEUE || -#if defined(__ANDROID__) - !ctx->SL_IID_ANDROIDCONFIGURATION || - !ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE || -#endif - !ctx->SL_IID_PLAY || - !ctx->SL_IID_RECORD) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - - const SLEngineOption opt[] = {{SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE}}; - - SLresult res; - res = cubeb_get_sles_engine(&ctx->engObj, 1, opt, 0, NULL, NULL); - - if (res != SL_RESULT_SUCCESS) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - - res = cubeb_realize_sles_engine(ctx->engObj); - if (res != SL_RESULT_SUCCESS) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - - res = (*ctx->engObj)->GetInterface(ctx->engObj, SL_IID_ENGINE, &ctx->eng); - if (res != SL_RESULT_SUCCESS) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - - const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX}; - const SLboolean reqom[] = {SL_BOOLEAN_TRUE}; - res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom); - if (res != SL_RESULT_SUCCESS) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - - res = (*ctx->outmixObj)->Realize(ctx->outmixObj, SL_BOOLEAN_FALSE); - if (res != SL_RESULT_SUCCESS) { - opensl_destroy(ctx); - return CUBEB_ERROR; - } - - *context = ctx; - - LOG("Cubeb init (%p) success", ctx); - return CUBEB_OK; -} - -static char const * -opensl_get_backend_id(cubeb * ctx) -{ - return "opensl"; -} - -static int -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 */ - *max_channels = 2; - - return CUBEB_OK; -} - -static int -opensl_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html - * We don't want to deal with JNI here (and we don't have Java on b2g anyways), - * so we just dlopen the library and get the two symbols we need. */ - int r; - void * libmedia; - uint32_t (*get_primary_output_samplingrate)(); - uint32_t (*get_output_samplingrate)(int * samplingRate, int streamType); - - libmedia = dlopen("libmedia.so", RTLD_LAZY); - if (!libmedia) { - return CUBEB_ERROR; - } - - /* uint32_t AudioSystem::getPrimaryOutputSamplingRate(void) */ - get_primary_output_samplingrate = - dlsym(libmedia, "_ZN7android11AudioSystem28getPrimaryOutputSamplingRateEv"); - if (!get_primary_output_samplingrate) { - /* fallback to - * status_t AudioSystem::getOutputSamplingRate(int* samplingRate, int streamType) - * if we cannot find getPrimaryOutputSamplingRate. */ - get_output_samplingrate = - dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPj19audio_stream_type_t"); - if (!get_output_samplingrate) { - /* Another signature exists, with a int instead of an audio_stream_type_t */ - get_output_samplingrate = - dlsym(libmedia, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"); - if (!get_output_samplingrate) { - dlclose(libmedia); - return CUBEB_ERROR; - } - } - } - - if (get_primary_output_samplingrate) { - *rate = get_primary_output_samplingrate(); - } else { - /* We don't really know about the type, here, so we just pass music. */ - r = get_output_samplingrate((int *) rate, AUDIO_STREAM_TYPE_MUSIC); - if (r) { - dlclose(libmedia); - return CUBEB_ERROR; - } - } - - dlclose(libmedia); - - /* Depending on which method we called above, we can get a zero back, yet have - * a non-error return value, especially if the audio system is not - * ready/shutting down (i.e. when we can't get our hand on the AudioFlinger - * thread). */ - if (*rate == 0) { - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -opensl_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) -{ - /* https://android.googlesource.com/platform/ndk.git/+/master/docs/opensles/index.html - * We don't want to deal with JNI here (and we don't have Java on b2g anyways), - * so we just dlopen the library and get the two symbols we need. */ - - int r; - void * libmedia; - size_t (*get_primary_output_frame_count)(void); - int (*get_output_frame_count)(size_t * frameCount, int streamType); - uint32_t primary_sampling_rate; - size_t primary_buffer_size; - - r = opensl_get_preferred_sample_rate(ctx, &primary_sampling_rate); - - if (r) { - return CUBEB_ERROR; - } - - libmedia = dlopen("libmedia.so", RTLD_LAZY); - if (!libmedia) { - return CUBEB_ERROR; - } - - /* JB variant */ - /* size_t AudioSystem::getPrimaryOutputFrameCount(void) */ - get_primary_output_frame_count = - dlsym(libmedia, "_ZN7android11AudioSystem26getPrimaryOutputFrameCountEv"); - if (!get_primary_output_frame_count) { - /* ICS variant */ - /* status_t AudioSystem::getOutputFrameCount(int* frameCount, int streamType) */ - get_output_frame_count = - dlsym(libmedia, "_ZN7android11AudioSystem19getOutputFrameCountEPii"); - if (!get_output_frame_count) { - dlclose(libmedia); - return CUBEB_ERROR; - } - } - - if (get_primary_output_frame_count) { - primary_buffer_size = get_primary_output_frame_count(); - } else { - if (get_output_frame_count(&primary_buffer_size, AUDIO_STREAM_TYPE_MUSIC) != 0) { - return CUBEB_ERROR; - } - } - - /* To get a fast track in Android's mixer, we need to be at the native - * samplerate, which is device dependant. Some devices might be able to - * resample when playing a fast track, but it's pretty rare. */ - *latency_frames = primary_buffer_size; - - dlclose(libmedia); - - return CUBEB_OK; -} - -static void -opensl_destroy(cubeb * ctx) -{ - if (ctx->outmixObj) - (*ctx->outmixObj)->Destroy(ctx->outmixObj); - if (ctx->engObj) - cubeb_destroy_sles_engine(&ctx->engObj); - dlclose(ctx->lib); - dlclose(ctx->libmedia); - free(ctx); -} - -static void opensl_stream_destroy(cubeb_stream * stm); - -static int -opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params) -{ - assert(format); - assert(params); - - format->formatType = SL_DATAFORMAT_PCM; - format->numChannels = params->channels; - // samplesPerSec is in milliHertz - 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; - - 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; - } - return CUBEB_OK; -} - -static int -opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params) -{ - assert(stm); - assert(params); - - SLDataLocator_AndroidSimpleBufferQueue lDataLocatorOut; - lDataLocatorOut.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; - lDataLocatorOut.numBuffers = NBUFS; - - SLDataFormat_PCM lDataFormat; - int r = opensl_set_format(&lDataFormat, params); - if (r != CUBEB_OK) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - /* For now set device rate to params rate. */ - stm->input_device_rate = params->rate; - - SLDataSink lDataSink; - lDataSink.pLocator = &lDataLocatorOut; - lDataSink.pFormat = &lDataFormat; - - SLDataLocator_IODevice lDataLocatorIn; - lDataLocatorIn.locatorType = SL_DATALOCATOR_IODEVICE; - lDataLocatorIn.deviceType = SL_IODEVICE_AUDIOINPUT; - lDataLocatorIn.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT; - lDataLocatorIn.device = NULL; - - SLDataSource lDataSource; - lDataSource.pLocator = &lDataLocatorIn; - lDataSource.pFormat = NULL; - - const SLuint32 lSoundRecorderIIDCount = 2; - const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD, - stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; - const SLboolean lSoundRecorderReqs[] = { 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, - lSoundRecorderIIDCount, - 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 { - // The output preferred rate is used for input only scenario. This is - // the correct rate to use to get a fast track for input only. - r = opensl_get_preferred_sample_rate(stm->context, &stm->input_device_rate); - if (r != CUBEB_OK) { - // If everything else fail use a safe choice for Android. - 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, - lSoundRecorderIIDCount, - lSoundRecorderIIDs, - lSoundRecorderReqs); - - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to create recorder. Error code: %lu", res); - return CUBEB_ERROR; - } - } - - // realize the audio recorder - res = (*stm->recorderObj)->Realize(stm->recorderObj, SL_BOOLEAN_FALSE); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to realize recorder. Error code: %lu", res); - return CUBEB_ERROR; - } - // get the record interface - 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); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to register recorder marker callback. Error code: %lu", res); - return CUBEB_ERROR; - } - - (*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)0); - - 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); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to get recorder (android) buffer queue interface. Error code: %lu", res); - return CUBEB_ERROR; - } - - // register callback on record (input) buffer queue - slAndroidSimpleBufferQueueCallback rec_callback = recorder_callback; - if (stm->output_enabled) { - // Register full duplex callback instead. - rec_callback = recorder_fullduplex_callback; - } - 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); - return CUBEB_ERROR; - } - - // Calculate length of input buffer according to requested latency - stm->input_frame_size = params->channels * sizeof(int16_t); - stm->input_buffer_length = (stm->input_frame_size * stm->latency_frames); - - // Calculate the capacity of input array - 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; - } - // Allocate input array - 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) { - stm->input_buffer_array[i] = calloc(1, stm->input_buffer_length); - } - - // On full duplex allocate input queue and silent buffer - if (stm->output_enabled) { - stm->input_queue = array_queue_create(stm->input_array_capacity); - assert(stm->input_queue); - stm->input_silent_buffer = calloc(1, stm->input_buffer_length); - assert(stm->input_silent_buffer); - } - - // Enqueue buffer to start rolling once recorder started - r = opensl_enqueue_recorder(stm, NULL); - if (r != CUBEB_OK) { - return r; - } - - LOG("Cubeb stream init recorder success"); - - return CUBEB_OK; -} - -static int -opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) { - assert(stm); - assert(params); - - stm->inputrate = params->rate; - stm->framesize = params->channels * sizeof(int16_t); - stm->lastPosition = -1; - stm->lastPositionTimeStamp = 0; - stm->lastCompensativePosition = -1; - - SLDataFormat_PCM format; - int r = opensl_set_format(&format, params); - if (r != CUBEB_OK) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - SLDataLocator_BufferQueue loc_bufq; - loc_bufq.locatorType = SL_DATALOCATOR_BUFFERQUEUE; - loc_bufq.numBuffers = NBUFS; - SLDataSource source; - source.pLocator = &loc_bufq; - source.pFormat = &format; - - SLDataLocator_OutputMix loc_outmix; - loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; - loc_outmix.outputMix = stm->context->outmixObj; - SLDataSink sink; - sink.pLocator = &loc_outmix; - sink.pFormat = NULL; - -#if defined(__ANDROID__) - const SLInterfaceID ids[] = {stm->context->SL_IID_BUFFERQUEUE, - stm->context->SL_IID_VOLUME, - stm->context->SL_IID_ANDROIDCONFIGURATION}; - const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; -#else - const SLInterfaceID ids[] = {ctx->SL_IID_BUFFERQUEUE, ctx->SL_IID_VOLUME}; - const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; -#endif - assert(NELEMS(ids) == NELEMS(req)); - - unsigned int latency_frames = stm->latency_frames; - uint32_t preferred_sampling_rate = stm->inputrate; -#if defined(__ANDROID__) - if (get_android_version() >= ANDROID_VERSION_MARSHMALLOW) { - // Reset preferred samping rate to trigger fallback to native sampling rate. - preferred_sampling_rate = 0; - if (opensl_get_min_latency(stm->context, *params, &latency_frames) != CUBEB_OK) { - // Default to AudioFlinger's advertised fast track latency of 10ms. - latency_frames = 440; - } - stm->latency_frames = latency_frames; - } -#endif - - 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); - } - - // Sample rate not supported? Try again with primary sample rate! - if (res == SL_RESULT_CONTENT_UNSUPPORTED) { - if (opensl_get_preferred_sample_rate(stm->context, &preferred_sampling_rate)) { - // If fail default is used - preferred_sampling_rate = DEFAULT_SAMPLE_RATE; - } - - format.samplesPerSec = preferred_sampling_rate * 1000; - res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng, - &stm->playerObj, - &source, - &sink, - NELEMS(ids), - ids, - req); - } - - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to create audio player. Error code: %lu", res); - return CUBEB_ERROR; - } - - stm->output_configured_rate = preferred_sampling_rate; - stm->bytespersec = stm->output_configured_rate * stm->framesize; - stm->queuebuf_len = stm->framesize * latency_frames; - - // Calculate the capacity of input array - 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; - } - // Allocate input array - 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]); - } - - res = (*stm->playerObj)->Realize(stm->playerObj, SL_BOOLEAN_FALSE); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to realize player object. Error code: %lu", res); - return CUBEB_ERROR; - } - - 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); - 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); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to get volume interface. Error code: %lu", res); - return CUBEB_ERROR; - } - - res = (*stm->play)->RegisterCallback(stm->play, play_callback, stm); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to register play callback. Error code: %lu", res); - return CUBEB_ERROR; - } - - // Work around wilhelm/AudioTrack badness, bug 1221228 - (*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0); - - 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; - } - - slBufferQueueCallback player_callback = bufferqueue_callback; - if (stm->input_enabled) { - player_callback = player_fullduplex_callback; - } - res = (*stm->bufq)->RegisterCallback(stm->bufq, player_callback, stm); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to register bufferqueue callback. Error code: %lu", res); - return CUBEB_ERROR; - } - - { - // Enqueue a silent frame so once the player becomes playing, the frame - // 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++]; - memset(buf, 0, stm->framesize); - res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->framesize); - assert(res == SL_RESULT_SUCCESS); - } - - LOG("Cubeb stream init playback success"); - return CUBEB_OK; -} - -static int -opensl_validate_stream_param(cubeb_stream_params * stream_params) -{ - if ((stream_params && - (stream_params->channels < 1 || stream_params->channels > 32))) { - return CUBEB_ERROR_INVALID_FORMAT; - } - return CUBEB_OK; -} - -static int -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_stream * stm; - - assert(ctx); - if (input_device || output_device) { - 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) { - LOG("Output stream params not valid"); - return r; - } - r = opensl_validate_stream_param(input_stream_params); - if(r != CUBEB_OK) { - LOG("Input stream params not valid"); - return r; - } - - stm = calloc(1, sizeof(*stm)); - assert(stm); - - stm->context = ctx; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->latency_frames = latency_frames; - stm->input_enabled = (input_stream_params) ? 1 : 0; - stm->output_enabled = (output_stream_params) ? 1 : 0; - stm->shutdown = 1; - -#ifdef DEBUG - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); - r = pthread_mutex_init(&stm->mutex, &attr); -#else - r = pthread_mutex_init(&stm->mutex, NULL); -#endif - assert(r == 0); - - if (output_stream_params) { - 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->latency_frames); - r = opensl_configure_playback(stm, output_stream_params); - if (r != CUBEB_OK) { - opensl_stream_destroy(stm); - return r; - } - } - - if (input_stream_params) { - 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->latency_frames); - r = opensl_configure_capture(stm, input_stream_params); - if (r != CUBEB_OK) { - opensl_stream_destroy(stm); - return r; - } - } - - /* Configure resampler*/ - uint32_t target_sample_rate; - if (input_stream_params) { - target_sample_rate = input_stream_params->rate; - } else { - assert(output_stream_params); - target_sample_rate = output_stream_params->rate; - } - - // Use the actual configured rates for input - // and output. - cubeb_stream_params input_params; - if (input_stream_params) { - input_params = *input_stream_params; - input_params.rate = stm->input_device_rate; - } - cubeb_stream_params output_params; - if (output_stream_params) { - output_params = *output_stream_params; - 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); - if (!stm->resampler) { - LOG("Failed to create resampler"); - opensl_stream_destroy(stm); - return CUBEB_ERROR; - } - - *stream = stm; - LOG("Cubeb stream (%p) init success", stm); - return CUBEB_OK; -} - -static int -opensl_start_player(cubeb_stream * stm) -{ - assert(stm->playerObj); - SLuint32 playerState; - (*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) { - LOG("Failed to start player. Error code: %lu", res); - return CUBEB_ERROR; - } - } - return CUBEB_OK; -} - -static int -opensl_start_recorder(cubeb_stream * stm) -{ - assert(stm->recorderObj); - 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) { - LOG("Failed to start recorder. Error code: %lu", res); - return CUBEB_ERROR; - } - } - return CUBEB_OK; -} - -static int -opensl_stream_start(cubeb_stream * stm) -{ - assert(stm); - - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - opensl_set_shutdown(stm, 0); - opensl_set_draining(stm, 0); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - if (stm->playerObj) { - r = opensl_start_player(stm); - if (r != CUBEB_OK) { - return r; - } - } - - if (stm->recorderObj) { - int r = opensl_start_recorder(stm); - if (r != CUBEB_OK) { - return r; - } - } - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); - LOG("Cubeb stream (%p) started", stm); - return CUBEB_OK; -} - -static int -opensl_stop_player(cubeb_stream * stm) -{ - assert(stm->playerObj); - assert(stm->shutdown || stm->draining); - - SLresult res = (*stm->play)->SetPlayState(stm->play, SL_PLAYSTATE_PAUSED); - if (res != SL_RESULT_SUCCESS) { - LOG("Failed to stop player. Error code: %lu", res); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -static int -opensl_stop_recorder(cubeb_stream * stm) -{ - assert(stm->recorderObj); - assert(stm->shutdown || stm->draining); - - 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; - } - - return CUBEB_OK; -} - -static int -opensl_stream_stop(cubeb_stream * stm) -{ - assert(stm); - - int r = pthread_mutex_lock(&stm->mutex); - assert(r == 0); - opensl_set_shutdown(stm, 1); - r = pthread_mutex_unlock(&stm->mutex); - assert(r == 0); - - if (stm->playerObj) { - r = opensl_stop_player(stm); - if (r != CUBEB_OK) { - return r; - } - } - - if (stm->recorderObj) { - int r = opensl_stop_recorder(stm); - if (r != CUBEB_OK) { - return r; - } - } - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); - LOG("Cubeb stream (%p) stopped", stm); - return CUBEB_OK; -} - -static int -opensl_destroy_recorder(cubeb_stream * stm) -{ - assert(stm); - assert(stm->recorderObj); - - if (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; - } - stm->recorderBufferQueueItf = NULL; - for (uint32_t i = 0; i < stm->input_array_capacity; ++i) { - free(stm->input_buffer_array[i]); - } - } - - (*stm->recorderObj)->Destroy(stm->recorderObj); - stm->recorderObj = NULL; - stm->recorderItf = NULL; - - if (stm->input_queue) { - array_queue_destroy(stm->input_queue); - } - free(stm->input_silent_buffer); - - return CUBEB_OK; -} - -static void -opensl_stream_destroy(cubeb_stream * stm) -{ - assert(stm->draining || stm->shutdown); - - if (stm->playerObj) { - (*stm->playerObj)->Destroy(stm->playerObj); - stm->playerObj = NULL; - stm->play = NULL; - stm->bufq = NULL; - for (uint32_t i = 0; i < stm->queuebuf_capacity; ++i) { - free(stm->queuebuf[i]); - } - } - - if (stm->recorderObj) { - int r = opensl_destroy_recorder(stm); - assert(r == CUBEB_OK); - } - - if (stm->resampler) { - cubeb_resampler_destroy(stm->resampler); - } - - pthread_mutex_destroy(&stm->mutex); - - LOG("Cubeb stream (%p) destroyed", stm); - free(stm); -} - -static int -opensl_stream_get_position(cubeb_stream * stm, uint64_t * position) -{ - SLmillisecond msec; - uint64_t samplerate; - SLresult res; - int r; - uint32_t mixer_latency; - uint32_t compensation_msec = 0; - - res = (*stm->play)->GetPosition(stm->play, &msec); - if (res != SL_RESULT_SUCCESS) - return CUBEB_ERROR; - - struct timespec t; - clock_gettime(CLOCK_MONOTONIC, &t); - if(stm->lastPosition == msec) { - compensation_msec = - (t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000; - } else { - stm->lastPositionTimeStamp = t.tv_sec*1000000000LL + t.tv_nsec; - stm->lastPosition = msec; - } - - samplerate = stm->inputrate; - - r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC); - if (r) { - return CUBEB_ERROR; - } - - pthread_mutex_lock(&stm->mutex); - int64_t maximum_position = stm->written * (int64_t)stm->inputrate / stm->output_configured_rate; - pthread_mutex_unlock(&stm->mutex); - assert(maximum_position >= 0); - - if (msec > mixer_latency) { - int64_t unadjusted_position; - if (stm->lastCompensativePosition > msec + compensation_msec) { - // Over compensation, use lastCompensativePosition. - unadjusted_position = - samplerate * (stm->lastCompensativePosition - mixer_latency) / 1000; - } else { - unadjusted_position = - samplerate * (msec - mixer_latency + compensation_msec) / 1000; - stm->lastCompensativePosition = msec + compensation_msec; - } - *position = unadjusted_position < maximum_position ? - unadjusted_position : maximum_position; - } else { - *position = 0; - } - return CUBEB_OK; -} - -int -opensl_stream_get_latency(cubeb_stream * stm, uint32_t * latency) -{ - int r; - uint32_t mixer_latency; // The latency returned by AudioFlinger is in ms. - - /* audio_stream_type_t is an int, so this is okay. */ - r = stm->context->get_output_latency(&mixer_latency, AUDIO_STREAM_TYPE_MUSIC); - if (r) { - return CUBEB_ERROR; - } - - *latency = stm->latency_frames + // OpenSL latency - mixer_latency * stm->inputrate / 1000; // AudioFlinger latency - - return CUBEB_OK; -} - -int -opensl_stream_set_volume(cubeb_stream * stm, float volume) -{ - SLresult res; - SLmillibel max_level, millibels; - float unclamped_millibels; - - res = (*stm->volume)->GetMaxVolumeLevel(stm->volume, &max_level); - - if (res != SL_RESULT_SUCCESS) { - return CUBEB_ERROR; - } - - /* millibels are 100*dB, so the conversion from the volume's linear amplitude - * is 100 * 20 * log(volume). However we clamp the resulting value before - * passing it to lroundf() in order to prevent it from silently returning an - * erroneous value when the unclamped value exceeds the size of a long. */ - unclamped_millibels = 100.0f * 20.0f * log10f(fmaxf(volume, 0.0f)); - unclamped_millibels = fmaxf(unclamped_millibels, SL_MILLIBEL_MIN); - unclamped_millibels = fminf(unclamped_millibels, max_level); - - millibels = lroundf(unclamped_millibels); - - res = (*stm->volume)->SetVolumeLevel(stm->volume, millibels); - - if (res != SL_RESULT_SUCCESS) { - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -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 = opensl_get_min_latency, - .get_preferred_sample_rate = opensl_get_preferred_sample_rate, - .get_preferred_channel_layout = 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_set_volume = opensl_stream_set_volume, - .stream_set_panning = 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_osx_run_loop.cpp b/Externals/cubeb/src/cubeb_osx_run_loop.cpp deleted file mode 100644 index de4e43970c..0000000000 --- a/Externals/cubeb/src/cubeb_osx_run_loop.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#include -#include "cubeb_osx_run_loop.h" -#include "cubeb_log.h" -#include -#include -#include -#include - -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 - }; - - CFRunLoopRef run_loop = nullptr; - - OSStatus r; - 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 deleted file mode 100644 index 78cd68d09b..0000000000 --- a/Externals/cubeb/src/cubeb_osx_run_loop.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright © 2014 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -/* On OSX 10.6 and after, the notification callbacks from the audio hardware are - * called on the main thread. Setting the kAudioHardwarePropertyRunLoop property - * to null tells the OSX to use a separate thread for that. - * - * This has to be called only once per process, so it is in a separate header - * for easy integration in other code bases. */ -#if defined(__cplusplus) -extern "C" { -#endif - -void cubeb_set_coreaudio_notification_runloop(); - -#if defined(__cplusplus) -} -#endif diff --git a/Externals/cubeb/src/cubeb_panner.cpp b/Externals/cubeb/src/cubeb_panner.cpp deleted file mode 100644 index bd96ed6ef0..0000000000 --- a/Externals/cubeb/src/cubeb_panner.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright © 2014 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#define _USE_MATH_DEFINES -#include -#include - -#include "cubeb_panner.h" - -#ifndef M_PI -#define M_PI 3.14159263 -#endif - -/** - * We use a cos/sin law. - */ - -namespace { -template -void cubeb_pan_stereo_buffer(T * buf, uint32_t frames, float pan) -{ - if (pan == 0.0) { - return; - } - /* rescale in [0; 1] */ - pan += 1; - pan /= 2; - float left_gain = float(cos(pan * M_PI * 0.5)); - float right_gain = float(sin(pan * M_PI * 0.5)); - - /* In we are panning on the left, pan the right channel into the left one and - * vice-versa. */ - if (pan < 0.5) { - for (uint32_t i = 0; i < frames * 2; i+=2) { - buf[i] = T(buf[i] + buf[i + 1] * left_gain); - buf[i + 1] = T(buf[i + 1] * right_gain); - } - } else { - for (uint32_t i = 0; i < frames * 2; i+=2) { - buf[i] = T(buf[i] * left_gain); - buf[i + 1] = T(buf[i + 1] + buf[i] * right_gain); - } - } -} -} - -void cubeb_pan_stereo_buffer_float(float * buf, uint32_t frames, float pan) -{ - cubeb_pan_stereo_buffer(buf, frames, pan); -} - -void cubeb_pan_stereo_buffer_int(short * buf, uint32_t frames, float pan) -{ - cubeb_pan_stereo_buffer(buf, frames, pan); -} - diff --git a/Externals/cubeb/src/cubeb_panner.h b/Externals/cubeb/src/cubeb_panner.h deleted file mode 100644 index c61363b2bc..0000000000 --- a/Externals/cubeb/src/cubeb_panner.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright © 2014 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(CUBEB_PANNER) -#define CUBEB_PANNER - -#if defined(__cplusplus) -extern "C" { -#endif - -/** - * Pan an integer or an float stereo buffer according to a cos/sin pan law - * @param buf the buffer to pan - * @param frames the number of frames in `buf` - * @param pan a float in [-1.0; 1.0] - */ -void cubeb_pan_stereo_buffer_float(float * buf, uint32_t frames, float pan); -void cubeb_pan_stereo_buffer_int(short* buf, uint32_t frames, float pan); - -#if defined(__cplusplus) -} -#endif - -#endif diff --git a/Externals/cubeb/src/cubeb_pulse.c b/Externals/cubeb/src/cubeb_pulse.c deleted file mode 100644 index 73307cd03b..0000000000 --- a/Externals/cubeb/src/cubeb_pulse.c +++ /dev/null @@ -1,1584 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#undef NDEBUG -#include -#include -#include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" -#include "cubeb_mixer.h" -#include "cubeb_strings.h" -#include - -#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) \ - -#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x; -LIBPULSE_API_VISIT(MAKE_TYPEDEF); -#undef MAKE_TYPEDEF -#endif - -static struct cubeb_ops const pulse_ops; - -struct cubeb { - struct cubeb_ops const * ops; - void * libpulse; - pa_threaded_mainloop * mainloop; - pa_context * context; - pa_sink_info * default_sink_info; - char * context_name; - int error; - cubeb_device_collection_changed_callback collection_changed_callback; - void * collection_changed_user_ptr; - cubeb_strings * device_ids; -}; - -struct cubeb_stream { - cubeb * context; - pa_stream * output_stream; - pa_stream * input_stream; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * user_ptr; - pa_time_event * drain_timer; - pa_sample_spec output_sample_spec; - pa_sample_spec input_sample_spec; - int shutdown; - float volume; - cubeb_state state; -}; - -static const float PULSE_NO_GAIN = -1.0; - -enum cork_state { - UNCORK = 0, - CORK = 1 << 0, - NOTIFY = 1 << 1 -}; - -static int -intern_device_id(cubeb * ctx, char const ** id) -{ - char const * interned; - - assert(ctx); - assert(id); - - interned = cubeb_strings_intern(ctx->device_ids, *id); - if (!interned) { - return CUBEB_ERROR; - } - - *id = interned; - - return CUBEB_OK; -} - -static void -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(pa_sink_info)); - memcpy(ctx->default_sink_info, info, sizeof(pa_sink_info)); - } - WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); -} - -static void -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); - if (o) { - WRAP(pa_operation_unref)(o); - } -} - -static void -context_state_callback(pa_context * c, void * u) -{ - cubeb * ctx = u; - if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(c))) { - ctx->error = 1; - } - WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); -} - -static void -context_notify_callback(pa_context * c, void * u) -{ - (void)c; - cubeb * ctx = u; - WRAP(pa_threaded_mainloop_signal)(ctx->mainloop, 0); -} - -static void -stream_success_callback(pa_stream * s, int success, void * u) -{ - (void)s; - (void)success; - cubeb_stream * stm = u; - WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); -} - -static void -stream_state_change_callback(cubeb_stream * stm, cubeb_state s) -{ - stm->state = s; - stm->state_callback(stm, stm->user_ptr, s); -} - -static void -stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u) -{ - (void)a; - (void)tv; - cubeb_stream * stm = u; - assert(stm->drain_timer == e); - stream_state_change_callback(stm, CUBEB_STATE_DRAINED); - /* there's no pa_rttime_free, so use this instead. */ - a->time_free(stm->drain_timer); - stm->drain_timer = NULL; - WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); -} - -static void -stream_state_callback(pa_stream * s, void * u) -{ - cubeb_stream * stm = u; - if (!PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(s))) { - stream_state_change_callback(stm, CUBEB_STATE_ERROR); - } - WRAP(pa_threaded_mainloop_signal)(stm->context->mainloop, 0); -} - -static void -trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cubeb_stream * stm) -{ - void * buffer; - size_t size; - int r; - long got; - size_t towrite, read_offset; - size_t frame_size; - - frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec); - assert(nbytes % frame_size == 0); - - towrite = nbytes; - read_offset = 0; - while (towrite) { - size = towrite; - r = WRAP(pa_stream_begin_write)(s, &buffer, &size); - // 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); - if (got < 0) { - WRAP(pa_stream_cancel_write)(s); - stm->shutdown = 1; - return; - } - // If more iterations move offset of read buffer - if (input_data) { - size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec); - read_offset += (size / frame_size) * in_frame_size; - } - - if (stm->volume != PULSE_NO_GAIN) { - 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) { - short * b = buffer; - for (uint32_t i = 0; i < samples; i++) { - b[i] *= stm->volume; - } - } else { - float * b = buffer; - for (uint32_t i = 0; i < samples; i++) { - b[i] *= stm->volume; - } - } - } - - 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) { - pa_usec_t latency = 0; - r = WRAP(pa_stream_get_latency)(s, &latency, NULL); - if (r == -PA_ERR_NODATA) { - /* this needs a better guess. */ - latency = 100 * PA_USEC_PER_MSEC; - } - assert(r == 0 || r == -PA_ERR_NODATA); - /* 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->shutdown = 1; - return; - } - - towrite -= size; - } - - assert(towrite == 0); -} - -static int -read_from_input(pa_stream * s, void const ** buffer, size_t * size) -{ - size_t readable_size = WRAP(pa_stream_readable_size)(s); - if (readable_size > 0) { - if (WRAP(pa_stream_peek)(s, buffer, size) < 0) { - return -1; - } - } - return readable_size; -} - -static void -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) { - return; - } - - if (!stm->input_stream){ - // Output/playback only operation. - // Write directly to output - assert(!stm->input_stream && stm->output_stream); - trigger_user_callback(s, NULL, nbytes, stm); - } -} - -static void -stream_read_callback(pa_stream * s, size_t nbytes, void * u) -{ - LOGV("Input callback buffer size %zd", nbytes); - cubeb_stream * stm = u; - if (stm->shutdown) { - return; - } - - void const * read_data = NULL; - size_t read_size; - while (read_from_input(s, &read_data, &read_size) > 0) { - /* read_data can be NULL in case of a hole. */ - if (read_data) { - size_t in_frame_size = WRAP(pa_frame_size)(&stm->input_sample_spec); - size_t read_frames = read_size / in_frame_size; - - if (stm->output_stream) { - // input/capture + output/playback operation - size_t out_frame_size = WRAP(pa_frame_size)(&stm->output_sample_spec); - size_t write_size = read_frames * out_frame_size; - // Offer full duplex data for writing - 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) { - WRAP(pa_stream_cancel_write)(s); - stm->shutdown = 1; - break; - } - } - } - if (read_size > 0) { - WRAP(pa_stream_drop)(s); - } - - if (stm->shutdown) { - return; - } - } -} - -static int -wait_until_context_ready(cubeb * ctx) -{ - for (;;) { - pa_context_state_t state = WRAP(pa_context_get_state)(ctx->context); - if (!PA_CONTEXT_IS_GOOD(state)) - return -1; - if (state == PA_CONTEXT_READY) - break; - WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); - } - return 0; -} - -static int -wait_until_io_stream_ready(pa_stream * stream, pa_threaded_mainloop * mainloop) -{ - if (!stream || !mainloop){ - return -1; - } - for (;;) { - pa_stream_state_t state = WRAP(pa_stream_get_state)(stream); - if (!PA_STREAM_IS_GOOD(state)) - return -1; - if (state == PA_STREAM_READY) - break; - WRAP(pa_threaded_mainloop_wait)(mainloop); - } - return 0; -} - -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) { - return -1; - } - if(stm->input_stream && - wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) { - return -1; - } - return 0; -} - -static int -operation_wait(cubeb * ctx, pa_stream * stream, pa_operation * o) -{ - while (WRAP(pa_operation_get_state)(o) == PA_OPERATION_RUNNING) { - WRAP(pa_threaded_mainloop_wait)(ctx->mainloop); - if (!PA_CONTEXT_IS_GOOD(WRAP(pa_context_get_state)(ctx->context))) { - return -1; - } - if (stream && !PA_STREAM_IS_GOOD(WRAP(pa_stream_get_state)(stream))) { - return -1; - } - } - return 0; -} - -static void -cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state) -{ - pa_operation * o; - if (!io_stream) { - return; - } - 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); - } -} - -static void -stream_cork(cubeb_stream * stm, enum cork_state state) -{ - WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - cork_io_stream(stm, stm->output_stream, state); - cork_io_stream(stm, stm->input_stream, state); - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - - if (state & NOTIFY) { - stream_state_change_callback(stm, state & CORK ? CUBEB_STATE_STOPPED - : CUBEB_STATE_STARTED); - } -} - -static int -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); - if (o) { - r = operation_wait(stm->context, stm->output_stream, o); - WRAP(pa_operation_unref)(o); - } - if (r != 0) { - return r; - } - } - - if (stm->input_stream) { - 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); - } - } - - return r; -} - -static pa_channel_position_t -cubeb_channel_to_pa_channel(cubeb_channel channel) -{ - assert(channel != CHANNEL_INVALID); - - // This variable may be used for multiple times, so we should avoid to - // allocate it in stack, or it will be created and removed repeatedly. - // Use static to allocate this local variable in data space instead of stack. - static pa_channel_position_t map[CHANNEL_MAX] = { - // PA_CHANNEL_POSITION_INVALID, // CHANNEL_INVALID - PA_CHANNEL_POSITION_MONO, // CHANNEL_MONO - PA_CHANNEL_POSITION_FRONT_LEFT, // CHANNEL_LEFT - PA_CHANNEL_POSITION_FRONT_RIGHT, // CHANNEL_RIGHT - PA_CHANNEL_POSITION_FRONT_CENTER, // CHANNEL_CENTER - PA_CHANNEL_POSITION_SIDE_LEFT, // CHANNEL_LS - PA_CHANNEL_POSITION_SIDE_RIGHT, // CHANNEL_RS - PA_CHANNEL_POSITION_REAR_LEFT, // CHANNEL_RLS - PA_CHANNEL_POSITION_REAR_CENTER, // CHANNEL_RCENTER - PA_CHANNEL_POSITION_REAR_RIGHT, // CHANNEL_RRS - PA_CHANNEL_POSITION_LFE // CHANNEL_LFE - }; - - return map[channel]; -} - -static cubeb_channel -pa_channel_to_cubeb_channel(pa_channel_position_t channel) -{ - assert(channel != PA_CHANNEL_POSITION_INVALID); - switch(channel) { - case PA_CHANNEL_POSITION_MONO: return CHANNEL_MONO; - case PA_CHANNEL_POSITION_FRONT_LEFT: return CHANNEL_LEFT; - case PA_CHANNEL_POSITION_FRONT_RIGHT: return CHANNEL_RIGHT; - case PA_CHANNEL_POSITION_FRONT_CENTER: return CHANNEL_CENTER; - case PA_CHANNEL_POSITION_SIDE_LEFT: return CHANNEL_LS; - case PA_CHANNEL_POSITION_SIDE_RIGHT: return CHANNEL_RS; - case PA_CHANNEL_POSITION_REAR_LEFT: return CHANNEL_RLS; - case PA_CHANNEL_POSITION_REAR_CENTER: return CHANNEL_RCENTER; - case PA_CHANNEL_POSITION_REAR_RIGHT: return CHANNEL_RRS; - case PA_CHANNEL_POSITION_LFE: return CHANNEL_LFE; - default: return CHANNEL_INVALID; - } -} - -static void -layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm) -{ - assert(cm && layout != CUBEB_LAYOUT_UNDEFINED); - - WRAP(pa_channel_map_init)(cm); - cm->channels = CUBEB_CHANNEL_LAYOUT_MAPS[layout].channels; - for (uint8_t i = 0 ; i < cm->channels ; ++i) { - cm->map[i] = cubeb_channel_to_pa_channel(CHANNEL_INDEX_TO_ORDER[layout][i]); - } -} - -static cubeb_channel_layout -channel_map_to_layout(pa_channel_map * cm) -{ - cubeb_channel_map cubeb_map; - cubeb_map.channels = cm->channels; - for (uint32_t i = 0 ; i < cm->channels ; ++i) { - cubeb_map.map[i] = pa_channel_to_cubeb_channel(cm->map[i]); - } - return cubeb_channel_map_to_layout(&cubeb_map); -} - -static void pulse_context_destroy(cubeb * ctx); -static void pulse_destroy(cubeb * ctx); - -static int -pulse_context_init(cubeb * ctx) -{ - if (ctx->context) { - assert(ctx->error == 1); - pulse_context_destroy(ctx); - } - - 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_threaded_mainloop_lock)(ctx->mainloop); - WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL); - - if (wait_until_context_ready(ctx) != 0) { - WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); - pulse_context_destroy(ctx); - ctx->context = NULL; - return -1; - } - - WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); - - ctx->error = 0; - - return 0; -} - -/*static*/ int -pulse_init(cubeb ** context, char const * context_name) -{ - void * libpulse = NULL; - cubeb * ctx; - pa_operation * o; - - *context = NULL; - -#ifndef DISABLE_LIBPULSE_DLOPEN - libpulse = dlopen("libpulse.so.0", RTLD_LAZY); - if (!libpulse) { - return CUBEB_ERROR; - } - -#define LOAD(x) { \ - cubeb_##x = dlsym(libpulse, #x); \ - if (!cubeb_##x) { \ - dlclose(libpulse); \ - return CUBEB_ERROR; \ - } \ - } - - LIBPULSE_API_VISIT(LOAD); -#undef LOAD -#endif - - ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - - ctx->ops = &pulse_ops; - ctx->libpulse = libpulse; - if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) { - pulse_destroy(ctx); - return CUBEB_ERROR; - } - - ctx->mainloop = WRAP(pa_threaded_mainloop_new)(); - ctx->default_sink_info = NULL; - - WRAP(pa_threaded_mainloop_start)(ctx->mainloop); - - ctx->context_name = context_name ? strdup(context_name) : NULL; - if (pulse_context_init(ctx) != 0) { - pulse_destroy(ctx); - return CUBEB_ERROR; - } - - /* server_info_callback performs a second async query, which is - responsible for initializing default_sink_info and signalling the - mainloop to end the wait. */ - WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); - o = WRAP(pa_context_get_server_info)(ctx->context, server_info_callback, ctx); - if (o) { - operation_wait(ctx, NULL, o); - WRAP(pa_operation_unref)(o); - } - WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); - - *context = ctx; - - return CUBEB_OK; -} - -static char const * -pulse_get_backend_id(cubeb * ctx) -{ - (void)ctx; - return "pulse"; -} - -static int -pulse_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - (void)ctx; - assert(ctx && max_channels); - - if (!ctx->default_sink_info) - return CUBEB_ERROR; - - *max_channels = ctx->default_sink_info->channel_map.channels; - - return CUBEB_OK; -} - -static int -pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - assert(ctx && rate); - (void)ctx; - - if (!ctx->default_sink_info) - return CUBEB_ERROR; - - *rate = ctx->default_sink_info->sample_spec.rate; - - return CUBEB_OK; -} - -static int -pulse_get_preferred_channel_layout(cubeb * ctx, cubeb_channel_layout * layout) -{ - assert(ctx && layout); - (void)ctx; - - if (!ctx->default_sink_info) - return CUBEB_ERROR; - - *layout = channel_map_to_layout(&ctx->default_sink_info->channel_map); - - return CUBEB_OK; -} - -static int -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. - *latency_frames = 25 * params.rate / 1000; - - return CUBEB_OK; -} - -static void -pulse_context_destroy(cubeb * ctx) -{ - pa_operation * o; - - WRAP(pa_threaded_mainloop_lock)(ctx->mainloop); - o = WRAP(pa_context_drain)(ctx->context, context_notify_callback, ctx); - if (o) { - operation_wait(ctx, NULL, o); - WRAP(pa_operation_unref)(o); - } - WRAP(pa_context_set_state_callback)(ctx->context, NULL, NULL); - WRAP(pa_context_disconnect)(ctx->context); - WRAP(pa_context_unref)(ctx->context); - WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); -} - -static void -pulse_destroy(cubeb * ctx) -{ - free(ctx->context_name); - if (ctx->context) { - pulse_context_destroy(ctx); - } - - if (ctx->mainloop) { - WRAP(pa_threaded_mainloop_stop)(ctx->mainloop); - WRAP(pa_threaded_mainloop_free)(ctx->mainloop); - } - - if (ctx->device_ids) { - cubeb_strings_destroy(ctx->device_ids); - } - - if (ctx->libpulse) { - dlclose(ctx->libpulse); - } - free(ctx->default_sink_info); - free(ctx); -} - -static void pulse_stream_destroy(cubeb_stream * stm); - -static pa_sample_format_t -to_pulse_format(cubeb_sample_format format) -{ - switch (format) { - case CUBEB_SAMPLE_S16LE: - return PA_SAMPLE_S16LE; - case CUBEB_SAMPLE_S16BE: - return PA_SAMPLE_S16BE; - case CUBEB_SAMPLE_FLOAT32LE: - return PA_SAMPLE_FLOAT32LE; - case CUBEB_SAMPLE_FLOAT32BE: - return PA_SAMPLE_FLOAT32BE; - default: - return PA_SAMPLE_INVALID; - } -} - -static int -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_MAPS[stream_params->layout].channels == stream_params->channels)))); - *pa_stm = NULL; - pa_sample_spec ss; - ss.format = to_pulse_format(stream_params->format); - if (ss.format == PA_SAMPLE_INVALID) - return CUBEB_ERROR_INVALID_FORMAT; - ss.rate = stream_params->rate; - ss.channels = stream_params->channels; - - if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) { - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL); - } else { - pa_channel_map cm; - layout_to_channel_map(stream_params->layout, &cm); - *pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm); - } - return (*pa_stm == NULL) ? CUBEB_ERROR : CUBEB_OK; -} - -static pa_buffer_attr -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; - - 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, - 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_stream * stm; - pa_buffer_attr battr; - int r; - - assert(context); - - // If the connection failed for some reason, try to reconnect - if (context->error == 1 && pulse_context_init(context) != 0) { - return CUBEB_ERROR; - } - - *stream = NULL; - - stm = calloc(1, sizeof(*stm)); - assert(stm); - - stm->context = context; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->volume = PULSE_NO_GAIN; - stm->state = -1; - assert(stm->shutdown == 0); - - 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); - 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)); - - 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); - } - - // Set up input stream - if (input_stream_params) { - 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)); - - 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); - } - - r = wait_until_stream_ready(stm); - if (r == 0) { - /* force a timing update now, otherwise timing info does not become valid - until some point after initialization has completed. */ - r = stream_update_timing_info(stm); - } - - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - - if (r != 0) { - pulse_stream_destroy(stm); - return CUBEB_ERROR; - } - - if (g_cubeb_log_level) { - 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); - } - - 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); - } - } - - *stream = stm; - - return CUBEB_OK; -} - -static void -pulse_stream_destroy(cubeb_stream * stm) -{ - stream_cork(stm, CORK); - - WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - if (stm->output_stream) { - - 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_stream_set_state_callback)(stm->output_stream, NULL, NULL); - WRAP(pa_stream_set_write_callback)(stm->output_stream, NULL, NULL); - WRAP(pa_stream_disconnect)(stm->output_stream); - WRAP(pa_stream_unref)(stm->output_stream); - } - - if (stm->input_stream) { - WRAP(pa_stream_set_state_callback)(stm->input_stream, NULL, NULL); - WRAP(pa_stream_set_read_callback)(stm->input_stream, NULL, NULL); - WRAP(pa_stream_disconnect)(stm->input_stream); - WRAP(pa_stream_unref)(stm->input_stream); - } - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - - free(stm); -} - -static void -pulse_defer_event_cb(pa_mainloop_api * a, void * userdata) -{ - (void)a; - cubeb_stream * stm = userdata; - if (stm->shutdown) { - return; - } - size_t writable_size = WRAP(pa_stream_writable_size)(stm->output_stream); - trigger_user_callback(stm->output_stream, NULL, writable_size, stm); -} - -static int -pulse_stream_start(cubeb_stream * stm) -{ - stm->shutdown = 0; - stream_cork(stm, UNCORK | NOTIFY); - - if (stm->output_stream && !stm->input_stream) { - /* On output only case need to manually call user cb once in order to make - * 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_threaded_mainloop_unlock)(stm->context->mainloop); - } - - return CUBEB_OK; -} - -static int -pulse_stream_stop(cubeb_stream * stm) -{ - WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - stm->shutdown = 1; - // If draining is taking place wait to finish - while (stm->drain_timer) { - WRAP(pa_threaded_mainloop_wait)(stm->context->mainloop); - } - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - - stream_cork(stm, CORK | NOTIFY); - return CUBEB_OK; -} - -static int -pulse_stream_get_position(cubeb_stream * stm, uint64_t * position) -{ - int r, in_thread; - pa_usec_t r_usec; - uint64_t bytes; - - if (!stm || !stm->output_stream) { - return CUBEB_ERROR; - } - - in_thread = WRAP(pa_threaded_mainloop_in_thread)(stm->context->mainloop); - - if (!in_thread) { - WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - } - r = WRAP(pa_stream_get_time)(stm->output_stream, &r_usec); - if (!in_thread) { - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - } - - if (r != 0) { - return CUBEB_ERROR; - } - - bytes = WRAP(pa_usec_to_bytes)(r_usec, &stm->output_sample_spec); - *position = bytes / WRAP(pa_frame_size)(&stm->output_sample_spec); - - return CUBEB_OK; -} - -static int -pulse_stream_get_latency(cubeb_stream * stm, uint32_t * latency) -{ - pa_usec_t r_usec; - int negative, r; - - if (!stm || !stm->output_stream) { - return CUBEB_ERROR; - } - - r = WRAP(pa_stream_get_latency)(stm->output_stream, &r_usec, &negative); - assert(!negative); - if (r) { - return CUBEB_ERROR; - } - - *latency = r_usec * stm->output_sample_spec.rate / PA_USEC_PER_SEC; - return CUBEB_OK; -} - -static void -volume_success(pa_context *c, int success, void *userdata) -{ - (void)success; - (void)c; - cubeb_stream * stream = userdata; - assert(success); - WRAP(pa_threaded_mainloop_signal)(stream->context->mainloop, 0); -} - -static int -pulse_stream_set_volume(cubeb_stream * stm, float volume) -{ - uint32_t index; - pa_operation * op; - pa_volume_t vol; - pa_cvolume cvol; - const pa_sample_spec * ss; - cubeb * ctx; - - if (!stm->output_stream) { - return CUBEB_ERROR; - } - - WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - - /* if the pulse daemon is configured to use flat volumes, - * apply our own gain instead of changing the input volume on the sink. */ - ctx = stm->context; - if (ctx->default_sink_info && - (ctx->default_sink_info->flags & PA_SINK_FLAT_VOLUME)) { - stm->volume = volume; - } else { - ss = WRAP(pa_stream_get_sample_spec)(stm->output_stream); - - vol = WRAP(pa_sw_volume_from_linear)(volume); - WRAP(pa_cvolume_set)(&cvol, ss->channels, vol); - - index = WRAP(pa_stream_get_index)(stm->output_stream); - - 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); - } - } - - WRAP(pa_threaded_mainloop_unlock)(ctx->mainloop); - - return CUBEB_OK; -} - -struct sink_input_info_result { - pa_cvolume * cvol; - pa_threaded_mainloop * mainloop; -}; - -static void -sink_input_info_cb(pa_context * c, pa_sink_input_info const * i, int eol, void * u) -{ - struct sink_input_info_result * r = u; - if (!eol) { - *r->cvol = i->volume; - } - WRAP(pa_threaded_mainloop_signal)(r->mainloop, 0); -} - -static int -pulse_stream_set_panning(cubeb_stream * stm, float panning) -{ - const pa_channel_map * map; - pa_cvolume cvol; - uint32_t index; - pa_operation * op; - - if (!stm->output_stream) { - return CUBEB_ERROR; - } - - WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop); - - map = WRAP(pa_stream_get_channel_map)(stm->output_stream); - if (!WRAP(pa_channel_map_can_balance)(map)) { - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - return CUBEB_ERROR; - } - - index = WRAP(pa_stream_get_index)(stm->output_stream); - - struct sink_input_info_result r = { &cvol, stm->context->mainloop }; - op = WRAP(pa_context_get_sink_input_info)(stm->context->context, - index, - sink_input_info_cb, - &r); - if (op) { - operation_wait(stm->context, stm->output_stream, op); - WRAP(pa_operation_unref)(op); - } - - WRAP(pa_cvolume_set_balance)(&cvol, map, panning); - - op = WRAP(pa_context_set_sink_input_volume)(stm->context->context, - index, &cvol, volume_success, - stm); - if (op) { - operation_wait(stm->context, stm->output_stream, op); - WRAP(pa_operation_unref)(op); - } - - WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop); - - return CUBEB_OK; -} - -typedef struct { - char * default_sink_name; - char * default_source_name; - - cubeb_device_info * devinfo; - uint32_t max; - uint32_t count; - cubeb * context; -} pulse_dev_list_data; - -static cubeb_device_fmt -pulse_format_to_cubeb_format(pa_sample_format_t format) -{ - switch (format) { - case PA_SAMPLE_S16LE: - return CUBEB_DEVICE_FMT_S16LE; - case PA_SAMPLE_S16BE: - return CUBEB_DEVICE_FMT_S16BE; - case PA_SAMPLE_FLOAT32LE: - return CUBEB_DEVICE_FMT_F32LE; - case PA_SAMPLE_FLOAT32BE: - return CUBEB_DEVICE_FMT_F32BE; - default: - return CUBEB_DEVICE_FMT_F32NE; - } -} - -static void -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); - } -} - -static cubeb_device_state -pulse_get_state_from_sink_port(pa_sink_port_info * info) -{ - if (info != NULL) { -#if PA_CHECK_VERSION(2, 0, 0) - if (info->available == PA_PORT_AVAILABLE_NO) - return CUBEB_DEVICE_STATE_UNPLUGGED; - else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ -#endif - return CUBEB_DEVICE_STATE_ENABLED; - } - - return CUBEB_DEVICE_STATE_ENABLED; -} - -static void -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; - char const * prop = NULL; - char const * device_id = NULL; - - (void)context; - - if (eol) { - WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); - return; - } - - if (info == NULL) - return; - - device_id = info->name; - if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) { - assert(false); - return; - } - - pulse_ensure_dev_list_data_list_size(list_data); - devinfo = &list_data->devinfo[list_data->count]; - memset(devinfo, 0, sizeof(cubeb_device_info)); - - devinfo->device_id = 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) - devinfo->group_id = strdup(prop); - prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); - if (prop) - devinfo->vendor_name = strdup(prop); - - 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->format = CUBEB_DEVICE_FMT_ALL; - 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; - devinfo->default_rate = info->sample_spec.rate; - - devinfo->latency_lo = 0; - devinfo->latency_hi = 0; - - list_data->count += 1; -} - -static cubeb_device_state -pulse_get_state_from_source_port(pa_source_port_info * info) -{ - if (info != NULL) { -#if PA_CHECK_VERSION(2, 0, 0) - if (info->available == PA_PORT_AVAILABLE_NO) - return CUBEB_DEVICE_STATE_UNPLUGGED; - else /*if (info->available == PA_PORT_AVAILABLE_YES) + UNKNOWN */ -#endif - return CUBEB_DEVICE_STATE_ENABLED; - } - - return CUBEB_DEVICE_STATE_ENABLED; -} - -static void -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; - char const * prop = NULL; - char const * device_id = NULL; - - (void)context; - - if (eol) { - WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); - return; - } - - device_id = info->name; - if (intern_device_id(list_data->context, &device_id) != CUBEB_OK) { - assert(false); - return; - } - - pulse_ensure_dev_list_data_list_size(list_data); - devinfo = &list_data->devinfo[list_data->count]; - memset(devinfo, 0, sizeof(cubeb_device_info)); - - devinfo->device_id = 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) - devinfo->group_id = strdup(prop); - prop = WRAP(pa_proplist_gets)(info->proplist, "device.vendor.name"); - if (prop) - devinfo->vendor_name = strdup(prop); - - 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->format = CUBEB_DEVICE_FMT_ALL; - 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; - devinfo->default_rate = info->sample_spec.rate; - - devinfo->latency_lo = 0; - devinfo->latency_hi = 0; - - list_data->count += 1; -} - -static void -pulse_server_info_cb(pa_context * c, const pa_server_info * i, void * userdata) -{ - pulse_dev_list_data * list_data = userdata; - - (void)c; - - 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; - list_data->default_source_name = - i->default_source_name ? strdup(i->default_source_name) : NULL; - - WRAP(pa_threaded_mainloop_signal)(list_data->context->mainloop, 0); -} - -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 }; - 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); - if (o) { - operation_wait(context, NULL, o); - WRAP(pa_operation_unref)(o); - } - - if (type & CUBEB_DEVICE_TYPE_OUTPUT) { - o = WRAP(pa_context_get_sink_info_list)(context->context, - pulse_sink_info_cb, &user_data); - if (o) { - operation_wait(context, NULL, o); - WRAP(pa_operation_unref)(o); - } - } - - if (type & CUBEB_DEVICE_TYPE_INPUT) { - o = WRAP(pa_context_get_source_info_list)(context->context, - pulse_source_info_cb, &user_data); - if (o) { - operation_wait(context, NULL, o); - WRAP(pa_operation_unref)(o); - } - } - - WRAP(pa_threaded_mainloop_unlock)(context->mainloop); - - collection->device = user_data.devinfo; - collection->count = user_data.count; - - free(user_data.default_sink_name); - free(user_data.default_source_name); - return CUBEB_OK; -} - -static int -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(collection->device); - return CUBEB_OK; -} - -static int -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)); - if (*device == NULL) - return CUBEB_ERROR; - - if (stm->input_stream) { - const char * name = WRAP(pa_stream_get_device_name)(stm->input_stream); - (*device)->input_name = (name == NULL) ? NULL : strdup(name); - } - - if (stm->output_stream) { - const char * name = WRAP(pa_stream_get_device_name)(stm->output_stream); - (*device)->output_name = (name == NULL) ? NULL : strdup(name); - } - - return CUBEB_OK; -#else - return CUBEB_ERROR_NOT_SUPPORTED; -#endif -} - -static int -pulse_stream_device_destroy(cubeb_stream * stream, - cubeb_device * device) -{ - (void)stream; - free(device->input_name); - free(device->output_name); - free(device); - return CUBEB_OK; -} - -static void -pulse_subscribe_callback(pa_context * ctx, - pa_subscription_event_type_t t, - uint32_t index, void * userdata) -{ - (void)ctx; - cubeb * context = userdata; - - switch (t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) { - 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) { - LOG("Removing sink 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) { - LOG("Adding sink index %d", index); - } - if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK && - (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_SINK && - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - LOG("Adding source index %d", index); - } - } - - if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE || - (t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) { - context->collection_changed_callback(context, context->collection_changed_user_ptr); - } - break; - } -} - -static void -subscribe_success(pa_context *c, int success, void *userdata) -{ - (void)c; - cubeb * context = userdata; - assert(success); - WRAP(pa_threaded_mainloop_signal)(context->mainloop, 0); -} - -static int -pulse_register_device_collection_changed(cubeb * context, - cubeb_device_type devtype, - cubeb_device_collection_changed_callback collection_changed_callback, - void * user_ptr) -{ - context->collection_changed_callback = collection_changed_callback; - context->collection_changed_user_ptr = user_ptr; - - WRAP(pa_threaded_mainloop_lock)(context->mainloop); - - pa_subscription_mask_t mask; - if (context->collection_changed_callback == NULL) { - // Unregister subscription - WRAP(pa_context_set_subscribe_callback)(context->context, NULL, NULL); - mask = PA_SUBSCRIPTION_MASK_NULL; - } else { - WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context); - if (devtype == CUBEB_DEVICE_TYPE_INPUT) - mask = PA_SUBSCRIPTION_MASK_SOURCE; - else if (devtype == CUBEB_DEVICE_TYPE_OUTPUT) - mask = PA_SUBSCRIPTION_MASK_SINK; - else - mask = PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE; - } - - pa_operation * o; - o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context); - if (o == NULL) { - LOG("Context subscribe failed"); - return CUBEB_ERROR; - } - operation_wait(context, NULL, o); - WRAP(pa_operation_unref)(o); - - WRAP(pa_threaded_mainloop_unlock)(context->mainloop); - - return CUBEB_OK; -} - -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, - .get_preferred_channel_layout = pulse_get_preferred_channel_layout, - .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_set_volume = pulse_stream_set_volume, - .stream_set_panning = pulse_stream_set_panning, - .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 deleted file mode 100644 index 786d01341f..0000000000 --- a/Externals/cubeb/src/cubeb_resampler.cpp +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Copyright © 2014 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#ifndef NOMINMAX -#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" - -int -to_speex_quality(cubeb_resampler_quality q) -{ - switch(q) { - case CUBEB_RESAMPLER_QUALITY_VOIP: - return SPEEX_RESAMPLER_QUALITY_VOIP; - case CUBEB_RESAMPLER_QUALITY_DEFAULT: - return SPEEX_RESAMPLER_QUALITY_DEFAULT; - case CUBEB_RESAMPLER_QUALITY_DESKTOP: - return SPEEX_RESAMPLER_QUALITY_DESKTOP; - default: - assert(false); - return 0XFFFFFFFF; - } -} - -uint32_t min_buffered_audio_frame(uint32_t sample_rate) -{ - return sample_rate / 20; -} - -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) -{ -} - -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 && - *input_frames_count + static_cast(samples_to_frames(internal_input_buffer.length())) >= output_frames) || - (output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) || - (input_buffer && !output_buffer && output_frames == 0)); - - if (input_buffer) { - if (!output_buffer) { - output_frames = *input_frames_count; - } - internal_input_buffer.push(static_cast(input_buffer), - frames_to_samples(*input_frames_count)); - } - - long rv = data_callback(stream, user_ptr, internal_input_buffer.data(), - output_buffer, output_frames); - - if (input_buffer) { - internal_input_buffer.pop(nullptr, frames_to_samples(output_frames)); - *input_frames_count = output_frames; - drop_audio_if_needed(); - } - - return rv; -} - -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) { - // Add some delay on the processor that has the lowest delay so that the - // streams are synchronized. - uint32_t in_latency = input_processor->latency(); - uint32_t out_latency = output_processor->latency(); - if (in_latency > out_latency) { - uint32_t latency_diff = in_latency - out_latency; - output_processor->add_latency(latency_diff); - } else if (in_latency < out_latency) { - uint32_t latency_diff = out_latency - in_latency; - input_processor->add_latency(latency_diff); - } - fill_internal = &cubeb_resampler_speex::fill_internal_duplex; - } else if (input_processor) { - fill_internal = &cubeb_resampler_speex::fill_internal_input; - } 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) -{ - /* 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); - - long got = 0; - T * out_unprocessed = nullptr; - long output_frames_before_processing = 0; - - /* 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); - - out_unprocessed = - output_processor->input_buffer(output_frames_before_processing); - - got = data_callback(stream, user_ptr, - nullptr, out_unprocessed, - output_frames_before_processing); - - if (got < 0) { - return got; - } - - output_processor->written(got); - - /* Process the output. If not enough frames have been returned from the - * callback, drain the processors. */ - return output_processor->output(output_buffer, output_frames_needed); -} - -template -long -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. */ - T * resampled_input = nullptr; - 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. */ - input_processor->input(input_buffer, *input_frames_count); - resampled_input = input_processor->output(resampled_frame_count, (size_t*)input_frames_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. */ - return (*input_frames_count) * (got / resampled_frame_count); -} - -template -long -cubeb_resampler_speex -::fill_internal_duplex(T * in_buffer, long * input_frames_count, - T * out_buffer, long output_frames_needed) -{ - /* 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; - size_t output_frames_before_processing = 0; - /* The number of frames returned from the callback. */ - long got = 0; - - /* We need to determine how much frames to present to the consumer. - * - If we have a two way stream, but we're only resampling input, we resample - * the input to the number of output frames. - * - If we have a two way stream, but we're only resampling the output, we - * resize the input buffer of the output resampler to the number of input - * frames, and we resample it afterwards. - * - If we resample both ways, we resample the input to the number of frames - * we would need to pass down to the consumer (before resampling the output), - * get the output data, and resample it to the number of frames needed by the - * 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 */ - out_unprocessed = - output_processor->input_buffer(output_frames_before_processing); - - if (in_buffer) { - /* process the input, and present exactly `output_frames_needed` in the - * callback. */ - input_processor->input(in_buffer, *input_frames_count); - resampled_input = - input_processor->output(output_frames_before_processing, (size_t*)input_frames_count); - } else { - resampled_input = nullptr; - } - - got = data_callback(stream, user_ptr, - resampled_input, out_unprocessed, - output_frames_before_processing); - - if (got < 0) { - return got; - } - - output_processor->written(got); - - input_processor->drop_audio_if_needed(); - - /* Process the output. If not enough frames have been returned from the - * callback, drain the processors. */ - got = output_processor->output(out_buffer, output_frames_needed); - - output_processor->drop_audio_if_needed(); - - return got; -} - -/* Resampler C API */ - -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_sample_format format; - - assert(input_params || output_params); - - if (input_params) { - format = input_params->format; - } else { - 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; - } -} - -long -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); -} - -void -cubeb_resampler_destroy(cubeb_resampler * resampler) -{ - delete resampler; -} - -long -cubeb_resampler_latency(cubeb_resampler * resampler) -{ - return resampler->latency(); -} diff --git a/Externals/cubeb/src/cubeb_resampler.h b/Externals/cubeb/src/cubeb_resampler.h deleted file mode 100644 index 020ccc17ab..0000000000 --- a/Externals/cubeb/src/cubeb_resampler.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright © 2014 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#ifndef CUBEB_RESAMPLER_H -#define CUBEB_RESAMPLER_H - -#include "cubeb/cubeb.h" - -#if defined(__cplusplus) -extern "C" { -#endif - -typedef struct cubeb_resampler cubeb_resampler; - -typedef enum { - CUBEB_RESAMPLER_QUALITY_VOIP, - CUBEB_RESAMPLER_QUALITY_DEFAULT, - CUBEB_RESAMPLER_QUALITY_DESKTOP -} cubeb_resampler_quality; - -/** - * Create a resampler to adapt the requested sample rate into something that - * is accepted by the audio backend. - * @param stream A cubeb_stream instance supplied to the data callback. - * @param params Used to calculate bytes per frame and buffer size for resampling. - * @param target_rate The sampling rate after resampling. - * @param callback A callback to request data for resampling. - * @param user_ptr User data supplied to the data callback. - * @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); - -/** - * Fill the buffer with frames acquired using the data callback. Resampling will - * happen if necessary. - * @param resampler A cubeb_resampler instance. - * @param input_buffer A buffer of input samples - * @param input_frame_count The size of the buffer. Returns the number of frames - * consumed. - * @param buffer The buffer to be filled. - * @param frames_needed Number of frames that should be produced. - * @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); - -/** - * Destroy a cubeb_resampler. - * @param resampler A cubeb_resampler instance. - */ -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); - -#if defined(__cplusplus) -} -#endif - -#endif /* CUBEB_RESAMPLER_H */ diff --git a/Externals/cubeb/src/cubeb_resampler_internal.h b/Externals/cubeb/src/cubeb_resampler_internal.h deleted file mode 100644 index ca08ec5714..0000000000 --- a/Externals/cubeb/src/cubeb_resampler_internal.h +++ /dev/null @@ -1,601 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(CUBEB_RESAMPLER_INTERNAL) -#define CUBEB_RESAMPLER_INTERNAL - -#include -#include -#include -#include -#ifdef CUBEB_GECKO_BUILD -#include "mozilla/UniquePtr.h" -// In libc++, symbols such as std::unique_ptr may be defined in std::__1. -// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros -// will expand to the correct namespace. -#ifdef _LIBCPP_BEGIN_NAMESPACE_STD -#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD -#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD -#else -#define MOZ_BEGIN_STD_NAMESPACE namespace std { -#define MOZ_END_STD_NAMESPACE } -#endif -MOZ_BEGIN_STD_NAMESPACE - 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 - -/* 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); - -int to_speex_quality(cubeb_resampler_quality q); - -struct cubeb_resampler { - virtual long fill(void * input_buffer, long * input_frames_count, - void * output_buffer, long frames_needed) = 0; - virtual long latency() = 0; - virtual ~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) - {} -protected: - size_t frames_to_samples(size_t frames) - { - return frames * channels; - } - size_t samples_to_frames(size_t samples) - { - assert(!(samples % channels)); - return samples / channels; - } - /** The number of channel of the audio buffers to be resampled. */ - const uint32_t channels; -}; - -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); - - virtual long fill(void * input_buffer, long * input_frames_count, - void * output_buffer, long output_frames); - - 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)); - } - } - -private: - cubeb_stream * const stream; - const cubeb_data_callback data_callback; - void * const user_ptr; - /* This allows to buffer some input to account for the fact that we buffer - * some inputs. */ - auto_array internal_input_buffer; - uint32_t sample_rate; -}; - -/** 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 -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); - - virtual ~cubeb_resampler_speex(); - - virtual long fill(void * input_buffer, long * input_frames_count, - void * output_buffer, long output_frames_needed); - - virtual long latency() - { - if (input_processor && output_processor) { - assert(input_processor->latency() == output_processor->latency()); - return input_processor->latency(); - } else if (input_processor) { - return input_processor->latency(); - } else { - return output_processor->latency(); - } - } - -private: - 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); - long fill_internal_input(T * input_buffer, long * input_frames_count, - T * output_buffer, long output_frames_needed); - long fill_internal_output(T * input_buffer, long * input_frames_count, - T * output_buffer, long output_frames_needed); - - std::unique_ptr input_processor; - std::unique_ptr output_processor; - processing_callback fill_internal; - cubeb_stream * const stream; - const cubeb_data_callback data_callback; - void * const user_ptr; -}; - -/** Handles one way of a (possibly) duplex resampler, working on interleaved - * 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 { -public: - /** The sample type of this resampler, either 16-bit integers or 32-bit - * floats. */ - typedef T sample_type; - /** Construct a resampler resampling from #source_rate to #target_rate, that - * can be arbitrary, strictly positive number. - * @parameter channels The number of channels this resampler will resample. - * @parameter source_rate The sample-rate of the audio input. - * @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) - { - int r; - speex_resampler = speex_resampler_init(channels, source_rate, - target_rate, quality, &r); - assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure"); - } - - /** Destructor, deallocate the resampler */ - virtual ~cubeb_resampler_speex_one_way() - { - speex_resampler_destroy(speex_resampler); - } - - /** Sometimes, it is necessary to add latency on one way of a two-way - * resampler so that the stream are synchronized. This must be called only on - * a fresh resampler, otherwise, silent samples will be inserted in the - * stream. - * @param frames the number of frames of latency to add. */ - void add_latency(size_t frames) - { - additional_latency += frames; - resampling_in_buffer.push_silence(frames_to_samples(frames)); - } - - /* Fill the resampler with `input_frame_count` frames. */ - void input(T * input_buffer, size_t input_frame_count) - { - resampling_in_buffer.push(input_buffer, - frames_to_samples(input_frame_count)); - } - - /** Outputs exactly `output_frame_count` into `output_buffer`. - * `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); - - /* This shifts back any unresampled samples to the beginning of the input - buffer. */ - resampling_in_buffer.pop(nullptr, frames_to_samples(in_len)); - - return out_len; - } - - size_t output_for_input(uint32_t input_frames) - { - 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. */ - T * output(size_t output_frame_count, size_t * input_frames_used) - { - if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) { - resampling_out_buffer.reserve(frames_to_samples(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, - resampling_out_buffer.data(), &out_len); - - assert(out_len == output_frame_count); - - /* This shifts back any unresampled samples to the beginning of the input - buffer. */ - resampling_in_buffer.pop(nullptr, frames_to_samples(in_len)); - *input_frames_used = in_len; - - return resampling_out_buffer.data(); - } - - /** Get the latency of the resampler, in output frames. */ - uint32_t latency() const - { - /* The documentation of the resampler talks about "samples" here, but it - * 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; - - assert(latency >= 0); - - return latency; - } - - /** Returns the number of frames to pass in the input of the resampler to have - * exactly `output_frame_count` resampled frames. This can return a number - * slightly bigger than what is strictly necessary, but it guaranteed that the - * number of output frames will be exactly equal. */ - uint32_t input_needed_for_output(uint32_t output_frame_count) - { - 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; - if (input_frames_needed < 0) { - return 0; - } - return (uint32_t)ceilf(input_frames_needed); - } - - /** Returns a pointer to the input buffer, that contains empty space for at - * least `frame_count` elements. This is useful so that consumer can directly - * write into the input buffer of the resampler. The pointer returned is - * adjusted so that leftover data are not overwritten. - */ - T * input_buffer(size_t frame_count) - { - leftover_samples = resampling_in_buffer.length(); - resampling_in_buffer.reserve(leftover_samples + - frames_to_samples(frame_count)); - return resampling_in_buffer.data() + leftover_samples; - } - - /** This method works with `input_buffer`, and allows to inform the processor - how much frames have been written in the provided buffer. */ - void written(size_t written_frames) - { - resampling_in_buffer.set_length(leftover_samples + - frames_to_samples(written_frames)); - } - - void drop_audio_if_needed() - { - // Keep at most 100ms buffered. - uint32_t available = samples_to_frames(resampling_in_buffer.length()); - uint32_t to_keep = min_buffered_audio_frame(source_rate); - if (available > to_keep) { - resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep)); - } - } -private: - /** Wrapper for the speex resampling functions to have a typed - * interface. */ - void speex_resample(float * input_buffer, uint32_t * input_frame_count, - float * output_buffer, uint32_t * output_frame_count) - { -#ifndef NDEBUG - int rv; - rv = -#endif - speex_resampler_process_interleaved_float(speex_resampler, - input_buffer, - input_frame_count, - output_buffer, - output_frame_count); - assert(rv == RESAMPLER_ERR_SUCCESS); - } - - void speex_resample(short * input_buffer, uint32_t * input_frame_count, - short * output_buffer, uint32_t * output_frame_count) - { -#ifndef NDEBUG - int rv; - rv = -#endif - 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. */ - SpeexResamplerState * speex_resampler; - /** Source rate / target rate. */ - const float resampling_ratio; - const uint32_t source_rate; - /** Storage for the input frames, to be resampled. Also contains - * any unresampled frames after resampling. */ - auto_array resampling_in_buffer; - /* Storage for the resampled frames, to be passed back to the caller. */ - auto_array resampling_out_buffer; - /** Additional latency inserted into the pipeline for synchronisation. */ - uint32_t additional_latency; - /** When `input_buffer` is called, this allows tracking the number of samples - that were in the buffer. */ - uint32_t leftover_samples; -}; - -/** This class allows delaying an audio stream by `frames` frames. */ -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 */ - delay_line(uint32_t frames, uint32_t channels, uint32_t 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); - } - /* Add some latency to the delay line. - * @param frames the number of frames of latency to add. */ - void add_latency(size_t frames) - { - length += frames; - delay_input_buffer.push_silence(frames_to_samples(frames)); - } - /** Push some frames into the delay line. - * @parameter buffer the frames to push. - * @parameter frame_count the number of frames in #buffer. */ - void input(T * buffer, uint32_t frame_count) - { - delay_input_buffer.push(buffer, frames_to_samples(frame_count)); - } - /** Pop some frames from the internal buffer, into a internal output buffer. - * @parameter frames_needed the number of frames to be returned. - * @return a buffer containing the delayed frames. The consumer should not - * hold onto the pointer. */ - T * output(uint32_t frames_needed, size_t * input_frames_used) - { - if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) { - delay_output_buffer.reserve(frames_to_samples(frames_needed)); - } - - delay_output_buffer.clear(); - delay_output_buffer.push(delay_input_buffer.data(), - frames_to_samples(frames_needed)); - delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed)); - *input_frames_used = frames_needed; - - return delay_output_buffer.data(); - } - /** Get a pointer to the first writable location in the input buffer> - * @parameter frames_needed the number of frames the user needs to write into - * the buffer. - * @returns a pointer to a location in the input buffer where #frames_needed - * can be writen. */ - T * input_buffer(uint32_t frames_needed) - { - leftover_samples = delay_input_buffer.length(); - 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 - how much frames have been written in the provided buffer. */ - void written(size_t frames_written) - { - delay_input_buffer.set_length(leftover_samples + - frames_to_samples(frames_written)); - } - /** Drains the delay line, emptying the buffer. - * @parameter output_buffer the buffer in which the frames are written. - * @parameter frames_needed the maximum number of frames to write. - * @return the actual number of frames written. */ - size_t output(T * output_buffer, uint32_t frames_needed) - { - uint32_t in_len = samples_to_frames(delay_input_buffer.length()); - uint32_t out_len = frames_needed; - - uint32_t to_pop = std::min(in_len, out_len); - - delay_input_buffer.pop(output_buffer, frames_to_samples(to_pop)); - - return to_pop; - } - /** Returns the number of frames one needs to input into the delay line to get - * #frames_needed frames back. - * @parameter frames_needed the number of frames one want to write into the - * delay_line - * @returns the number of frames one will get. */ - size_t input_needed_for_output(uint32_t frames_needed) - { - 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; - } - /** The number of frames this delay line delays the stream by. - * @returns The number of frames of delay. */ - size_t latency() - { - return length; - } - - void drop_audio_if_needed() - { - size_t available = samples_to_frames(delay_input_buffer.length()); - uint32_t to_keep = min_buffered_audio_frame(sample_rate); - if (available > to_keep) { - delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep)); - } - } -private: - /** The length, in frames, of this delay line */ - uint32_t length; - /** When `input_buffer` is called, this allows tracking the number of samples - that where in the buffer. */ - uint32_t leftover_samples; - /** The input buffer, where the delay is applied. */ - auto_array delay_input_buffer; - /** The output buffer. This is only ever used if using the ::output with a - * single argument. */ - auto_array delay_output_buffer; - uint32_t sample_rate; -}; - -/** This sits behind the C API and is more typed. */ -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_resampler_quality quality) -{ - std::unique_ptr> input_resampler = nullptr; - std::unique_ptr> output_resampler = nullptr; - std::unique_ptr> input_delay = nullptr; - std::unique_ptr> output_delay = nullptr; - - assert((input_params || output_params) && - "need at least one valid parameter pointer."); - - /* All the streams we have have a sample rate that matches the target - 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)) || - (input_params && !output_params && (input_params->rate == target_rate)) || - (output_params && !input_params && (output_params->rate == 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))); - 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))); - if (!input_resampler) { - return NULL; - } - } - - /* If we resample only one direction but we have a duplex stream, insert a - * delay line with a length equal to the resampler latency of the - * other direction so that the streams are synchronized. */ - if (input_resampler && !output_resampler && input_params && output_params) { - output_delay.reset(new delay_line(input_resampler->latency(), - output_params->channels, - output_params->rate)); - if (!output_delay) { - return NULL; - } - } 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)); - if (!input_delay) { - return NULL; - } - } - - if (input_resampler && output_resampler) { - return new cubeb_resampler_speex, - cubeb_resampler_speex_one_way> - (input_resampler.release(), - output_resampler.release(), - stream, callback, user_ptr); - } else if (input_resampler) { - return new cubeb_resampler_speex, - delay_line> - (input_resampler.release(), - output_delay.release(), - stream, callback, user_ptr); - } else { - return new cubeb_resampler_speex, - cubeb_resampler_speex_one_way> - (input_delay.release(), - output_resampler.release(), - stream, callback, user_ptr); - } -} - -#endif /* CUBEB_RESAMPLER_INTERNAL */ diff --git a/Externals/cubeb/src/cubeb_ring_array.h b/Externals/cubeb/src/cubeb_ring_array.h deleted file mode 100644 index 51b3b321a3..0000000000 --- a/Externals/cubeb/src/cubeb_ring_array.h +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_RING_ARRAY_H -#define CUBEB_RING_ARRAY_H - -#include "cubeb_utils.h" - -/** Ring array of pointers is used to hold buffers. In case that - asynchronous producer/consumer callbacks do not arrive in a - repeated order the ring array stores the buffers and fetch - 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. */ -} ring_array; - -static int -single_audiobuffer_init(AudioBuffer * buffer, - uint32_t bytesPerFrame, - uint32_t channelsPerFrame, - uint32_t frames) -{ - assert(buffer); - assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0); - - size_t size = bytesPerFrame * frames; - buffer->mData = operator new(size); - if (buffer->mData == NULL) { - return CUBEB_ERROR; - } - PodZero(static_cast(buffer->mData), size); - - buffer->mNumberChannels = channelsPerFrame; - buffer->mDataByteSize = size; - - return CUBEB_OK; -} - -/** Initialize the ring array. - @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) -{ - assert(ra); - if (capacity == 0 || bytesPerFrame == 0 || - channelsPerFrame == 0 || framesPerBuffer == 0) { - return CUBEB_ERROR_INVALID_PARAMETER; - } - ra->capacity = capacity; - ra->tail = 0; - ra->count = 0; - - ra->buffer_array = new AudioBuffer[ra->capacity]; - PodZero(ra->buffer_array, ra->capacity); - if (ra->buffer_array == NULL) { - return CUBEB_ERROR; - } - - for (unsigned int i = 0; i < ra->capacity; ++i) { - if (single_audiobuffer_init(&ra->buffer_array[i], - bytesPerFrame, - channelsPerFrame, - framesPerBuffer) != CUBEB_OK) { - return CUBEB_ERROR; - } - } - - return CUBEB_OK; -} - -/** Destroy the ring array. - @param ra The ring_array pointer.*/ -void -ring_array_destroy(ring_array * ra) -{ - assert(ra); - if (ra->buffer_array == NULL){ - return; - } - for (unsigned int i = 0; i < ra->capacity; ++i) { - if (ra->buffer_array[i].mData) { - operator delete(ra->buffer_array[i].mData); - } - } - 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. */ -AudioBuffer * -ring_array_get_free_buffer(ring_array * ra) -{ - assert(ra && ra->buffer_array); - assert(ra->buffer_array[0].mData != NULL); - if (ra->count == ra->capacity) { - return NULL; - } - - assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail); - AudioBuffer * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity]; - - ++ra->count; - assert(ra->count <= ra->capacity); - - return ret; -} - -/** Get the next available buffer with data. - @param ra The ring_array pointer. - @retval Pointer of the next in order data buffer or NULL if empty. */ -AudioBuffer * -ring_array_get_data_buffer(ring_array * ra) -{ - assert(ra && ra->buffer_array); - assert(ra->buffer_array[0].mData != NULL); - - if (ra->count == 0) { - return NULL; - } - AudioBuffer * ret = &ra->buffer_array[ra->tail]; - - ra->tail = (ra->tail + 1) % ra->capacity; - assert(ra->tail < ra->capacity); - - assert(ra->count > 0); - --ra->count; - - return ret; -} - -/** When array is empty get the first allocated buffer in the array. - @param ra The ring_array pointer. - @retval If arrays is empty, pointer of the allocated space else NULL. */ -AudioBuffer * -ring_array_get_dummy_buffer(ring_array * ra) -{ - assert(ra && ra->buffer_array); - assert(ra->capacity > 0); - if (ra->count > 0) { - return NULL; - } - return &ra->buffer_array[0]; -} - -#endif //CUBEB_RING_ARRAY_H diff --git a/Externals/cubeb/src/cubeb_ringbuffer.h b/Externals/cubeb/src/cubeb_ringbuffer.h deleted file mode 100644 index b6696e886d..0000000000 --- a/Externals/cubeb/src/cubeb_ringbuffer.h +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_RING_BUFFER_H -#define CUBEB_RING_BUFFER_H - -#include "cubeb_utils.h" -#include -#include -#include -#include -#include - -/** - * 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 - * structure on one thread. - * - * The role for the producer and the consumer must be constant, i.e., the - * producer should always be on one thread and the consumer should always be on - * another thread. - * - * Some words about the inner workings of this class: - * - Capacity is fixed. Only one allocation is performed, in the constructor. - * When reading and writing, the return value of the method allows checking if - * the ring buffer is empty or full. - * - We always keep the read index at least one element ahead of the write - * index, so we can distinguish between an empty and a full ring buffer: an - * empty ring buffer is when the write index is at the same position as the - * read index. A full buffer is when the write index is exactly one position - * before the read index. - * - We synchronize updates to the read index after having read the data, and - * the write index after having written the data. This means that the each - * thread can only touch a portion of the buffer that is not touched by the - * other thread. - * - Callers are expected to provide buffers. When writing to the queue, - * elements are copied into the internal storage from the buffer passed in. - * When reading from the queue, the user is expected to provide a buffer. - * Because this is a ring buffer, data might not be contiguous in memory, - * providing an external buffer to copy into is an easy way to have linear - * data for further processing. - */ -template -class ring_buffer_base -{ -public: - /** - * Constructor for a ring buffer. - * - * This performs an allocation, but is the only allocation that will happen - * for the life time of a `ring_buffer_base`. - * - * @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) - { - assert(storage_capacity() < - std::numeric_limits::max() / 2 && - "buffer too large for the type of index used."); - assert(capacity_ > 0); - - data_.reset(new T[storage_capacity()]); - /* If this queue is using atomics, initializing those members as the last - * action in the constructor acts as a full barrier, and allow capacity() to - * be thread-safe. */ - write_index_ = 0; - read_index_ = 0; - } - /** - * Push `count` zero or default constructed elements in the array. - * - * Only safely called on the producer thread. - * - * @param count The number of elements to enqueue. - * @return The number of element enqueued. - */ - int enqueue_default(int count) - { - return enqueue(nullptr, count); - } - /** - * @brief Put an element in the queue - * - * Only safely called on the producer thread. - * - * @param element The element to put in the queue. - * - * @return 1 if the element was inserted, 0 otherwise. - */ - 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. - * @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. - */ - int enqueue(T * elements, int count) - { -#ifndef NDEBUG - 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); - - if (full_internal(rd_idx, wr_idx)) { - return 0; - } - - 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); - /* Second part, from the beginning of the array */ - int second_part = to_write - first_part; - - if (elements) { - Copy(data_.get() + wr_idx, elements, first_part); - Copy(data_.get(), elements + first_part, second_part); - } else { - ConstructDefault(data_.get() + wr_idx, first_part); - ConstructDefault(data_.get(), second_part); - } - - write_index_.store(increment_index(wr_idx, to_write), std::memory_order::memory_order_release); - - return to_write; - } - /** - * Retrieve at most `count` elements from the ring buffer, and copy them to - * `elements`, if non-null. - * - * Only safely called on the consumer side. - * - * @param elements A pointer to a buffer with space for at least `count` - * elements. If `elements` is `nullptr`, `count` element will be discarded. - * @param count The maximum number of elements to dequeue. - * @return The number of elements written to `elements`. - */ - int dequeue(T * elements, int count) - { -#ifndef NDEBUG - 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); - - if (empty_internal(rd_idx, wr_idx)) { - return 0; - } - - 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; - - if (elements) { - Copy(elements, data_.get() + rd_idx, first_part); - Copy(elements + first_part, data_.get(), second_part); - } - - read_index_.store(increment_index(rd_idx, to_read), std::memory_order::memory_order_relaxed); - - return to_read; - } - /** - * Get the number of available element for consuming. - * - * Only safely called on the consumer thread. - * - * @return The number of available elements for reading. - */ - int available_read() const - { -#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)); - } - /** - * Get the number of available elements for consuming. - * - * Only safely called on the producer thread. - * - * @return The number of empty slots in the buffer, available for writing. - */ - int available_write() const - { -#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)); - } - /** - * Get the total capacity, for this ring buffer. - * - * Can be called safely on any thread. - * - * @return The maximum capacity of this ring buffer. - */ - 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 - * asserts are disabled. - */ - void reset_thread_ids() - { -#ifndef NDEBUG - consumer_id = producer_id = std::thread::id(); -#endif - } -private: - /** Return true if the ring buffer is empty. - * - * @param read_index the read index to consider - * @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 - { - return write_index == read_index; - } - /** Return true if the ring buffer is full. - * - * This happens if the write index is exactly one element behind the read - * index. - * - * @param read_index the read index to consider - * @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 - { - return (write_index + 1) % storage_capacity() == read_index; - } - /** - * Return the size of the storage. It is one more than the number of elements - * that can be stored in the buffer. - * - * @return the number of elements that can be stored in the buffer. - */ - 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 - { - if (write_index >= read_index) { - return write_index - read_index; - } else { - return write_index + storage_capacity() - read_index; - } - } - /** - * Returns the number of empty elements, available for writing. - * - * @return the number of elements that can be written into the array. - */ - 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. */ - int rv = read_index - write_index - 1; - if (write_index >= read_index) { - rv += storage_capacity(); - } - return rv; - } - /** - * Increments an index, wrapping it around the storage. - * - * @param index a reference to the index to increment. - * @param increment the number by which `index` is incremented. - * @return the new index. - */ - int - increment_index(int index, int increment) const - { - assert(increment >= 0); - return (index + increment) % storage_capacity(); - } - /** - * @brief This allows checking that enqueue (resp. dequeue) are always called - * by the right thread. - * - * @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) - { - if (id == std::thread::id()) { - id = std::this_thread::get_id(); - return; - } - assert(id == std::this_thread::get_id()); - } -#endif - /** Index at which the oldest element is at, in samples. */ - std::atomic read_index_; - /** Index at which to write new elements. `write_index` is always at - * least one element ahead of `read_index_`. */ - std::atomic write_index_; - /** Maximum number of elements that can be stored in the ring buffer. */ - const int capacity_; - /** Data storage */ - std::unique_ptr data_; -#ifndef NDEBUG - /** The id of the only thread that is allowed to read from the queue. */ - mutable std::thread::id consumer_id; - /** The id of the only thread that is allowed to write from the queue. */ - mutable std::thread::id producer_id; -#endif -}; - -/** - * Adapter for `ring_buffer_base` that exposes an interface in frames. - */ -template -class audio_ring_buffer_base -{ -public: - /** - * @brief Constructor. - * - * @param channel_count Number of channels. - * @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)) - { - assert(channel_count > 0); - } - /** - * @brief Enqueue silence. - * - * Only safely called on the producer thread. - * - * @param frame_count The number of frames of silence to enqueue. - * @return The number of frames of silence actually written to the queue. - */ - int enqueue_default(int frame_count) - { - return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count))); - } - /** - * @brief Enqueue `frames_count` frames of audio. - * - * Only safely called from the producer thread. - * - * @param [in] frames If non-null, the frames to enqueue. - * Otherwise, silent frames are enqueued. - * @param frame_count The number of frames to enqueue. - * - * @return The number of frames enqueued - */ - - int enqueue(T * frames, int frame_count) - { - return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count))); - } - - /** - * @brief Removes `frame_count` frames from the buffer, and - * write them to `frames` if it is non-null. - * - * Only safely called on the consumer thread. - * - * @param frames If non-null, the frames are copied to `frames`. - * Otherwise, they are dropped. - * @param frame_count The number of frames to remove. - * - * @return The number of frames actually dequeud. - */ - int dequeue(T * frames, int 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. - * - * Only safely called on the consumer thread. - * - * @return The number of available frames of audio for reading. - */ - int available_read() const - { - return samples_to_frames(ring_buffer.available_read()); - } - /** - * Get the number of available frames of audio for consuming. - * - * Only safely called on the producer thread. - * - * @return The number of empty slots in the buffer, available for writing. - */ - int available_write() const - { - return samples_to_frames(ring_buffer.available_write()); - } - /** - * Get the total capacity, for this ring buffer. - * - * Can be called safely on any thread. - * - * @return The maximum capacity of this ring buffer. - */ - int capacity() const - { - return samples_to_frames(ring_buffer.capacity()); - } -private: - /** - * @brief Frames to samples conversion. - * - * @param frames The number of frames. - * - * @return A number of samples. - */ - int frames_to_samples(int frames) const - { - return frames * channel_count; - } - /** - * @brief Samples to frames conversion. - * - * @param samples The number of samples. - * - * @return A number of frames. - */ - 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. */ - ring_buffer_base ring_buffer; -}; - -/** - * Lock-free instantiation of the `ring_buffer_base` type. This is safe to use - * from two threads, one producer, one consumer (that never change role), - * without explicit synchronization. - */ -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 -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 deleted file mode 100644 index 8b359842b6..0000000000 --- a/Externals/cubeb/src/cubeb_sndio.c +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (c) 2011 Alexandre Ratchov - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" - -#if defined(CUBEB_SNDIO_DEBUG) -#define DPR(...) fprintf(stderr, __VA_ARGS__); -#else -#define DPR(...) do {} while(0) -#endif - -static struct cubeb_ops const sndio_ops; - -struct cubeb { - struct cubeb_ops const * ops; -}; - -struct cubeb_stream { - cubeb * context; - 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 active; /* cubec_start() called */ - int conv; /* need float->s16 conversion */ - unsigned char *buf; /* data is prepared here */ - unsigned int nfr; /* number of frames in buf */ - unsigned int bpf; /* bytes per frame */ - unsigned int pchan; /* number of play channels */ - uint64_t rdpos; /* frame number Joe hears right now */ - uint64_t wrpos; /* number of written frames */ - cubeb_data_callback data_cb; /* cb to preapare data */ - cubeb_state_callback state_cb; /* cb to notify about state changes */ - void *arg; /* user arg to {data,state}_cb */ -}; - -static void -float_to_s16(void *ptr, long nsamp) -{ - int16_t *dst = ptr; - float *src = ptr; - int s; - - while (nsamp-- > 0) { - s = lrintf(*(src++) * 32768); - if (s < -32768) - s = -32768; - else if (s > 32767) - s = 32767; - *(dst++) = s; - } -} - -static void -sndio_onmove(void *arg, int delta) -{ - cubeb_stream *s = (cubeb_stream *)arg; - - s->rdpos += delta * s->bpf; -} - -static void * -sndio_mainloop(void *arg) -{ -#define MAXFDS 8 - struct pollfd pfds[MAXFDS]; - cubeb_stream *s = arg; - int n, nfds, revents, state = CUBEB_STATE_STARTED; - size_t start = 0, end = 0; - long nfr; - - DPR("sndio_mainloop()\n"); - s->state_cb(s, s->arg, CUBEB_STATE_STARTED); - pthread_mutex_lock(&s->mtx); - if (!sio_start(s->hdl)) { - pthread_mutex_unlock(&s->mtx); - return NULL; - } - DPR("sndio_mainloop(), started\n"); - - start = end = s->nfr; - for (;;) { - if (!s->active) { - DPR("sndio_mainloop() stopped\n"); - state = CUBEB_STATE_STOPPED; - break; - } - if (start == end) { - if (end < s->nfr) { - DPR("sndio_mainloop() drained\n"); - state = CUBEB_STATE_DRAINED; - break; - } - pthread_mutex_unlock(&s->mtx); - nfr = s->data_cb(s, s->arg, NULL, s->buf, s->nfr); - pthread_mutex_lock(&s->mtx); - if (nfr < 0) { - DPR("sndio_mainloop() cb err\n"); - state = CUBEB_STATE_ERROR; - break; - } - if (s->conv) - float_to_s16(s->buf, nfr * s->pchan); - start = 0; - end = nfr * s->bpf; - } - if (end == 0) - continue; - nfds = sio_pollfd(s->hdl, pfds, POLLOUT); - if (nfds > 0) { - pthread_mutex_unlock(&s->mtx); - n = poll(pfds, nfds, -1); - pthread_mutex_lock(&s->mtx); - if (n < 0) - continue; - } - revents = sio_revents(s->hdl, pfds); - if (revents & POLLHUP) - break; - if (revents & POLLOUT) { - n = sio_write(s->hdl, s->buf + start, end - start); - if (n == 0) { - DPR("sndio_mainloop() werr\n"); - state = CUBEB_STATE_ERROR; - break; - } - s->wrpos += n; - start += n; - } - } - sio_stop(s->hdl); - s->rdpos = s->wrpos; - pthread_mutex_unlock(&s->mtx); - s->state_cb(s, s->arg, state); - return NULL; -} - -/*static*/ int -sndio_init(cubeb **context, char const *context_name) -{ - DPR("sndio_init(%s)\n", context_name); - *context = malloc(sizeof(*context)); - (*context)->ops = &sndio_ops; - (void)context_name; - return CUBEB_OK; -} - -static char const * -sndio_get_backend_id(cubeb *context) -{ - return "sndio"; -} - -static void -sndio_destroy(cubeb *context) -{ - DPR("sndio_destroy()\n"); - free(context); -} - -static int -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_stream *s; - struct sio_par wpar, rpar; - DPR("sndio_stream_init(%s)\n", stream_name); - size_t size; - - assert(!input_stream_params && "not supported."); - if (input_device || output_device) { - /* Device selection not yet implemented. */ - return CUBEB_ERROR_DEVICE_UNAVAILABLE; - } - - s = malloc(sizeof(cubeb_stream)); - if (s == NULL) - return CUBEB_ERROR; - s->context = context; - s->hdl = sio_open(NULL, SIO_PLAY, 1); - if (s->hdl == NULL) { - free(s); - DPR("sndio_stream_init(), sio_open() failed\n"); - return CUBEB_ERROR; - } - sio_initpar(&wpar); - wpar.sig = 1; - wpar.bits = 16; - switch (output_stream_params->format) { - case CUBEB_SAMPLE_S16LE: - wpar.le = 1; - break; - case CUBEB_SAMPLE_S16BE: - wpar.le = 0; - break; - case CUBEB_SAMPLE_FLOAT32NE: - wpar.le = SIO_LE_NATIVE; - break; - default: - sio_close(s->hdl); - free(s); - DPR("sndio_stream_init() unsupported format\n"); - return CUBEB_ERROR_INVALID_FORMAT; - } - wpar.rate = output_stream_params->rate; - wpar.pchan = output_stream_params->channels; - wpar.appbufsz = latency_frames; - if (!sio_setpar(s->hdl, &wpar) || !sio_getpar(s->hdl, &rpar)) { - sio_close(s->hdl); - free(s); - DPR("sndio_stream_init(), sio_setpar() failed\n"); - return CUBEB_ERROR; - } - if (rpar.bits != wpar.bits || rpar.le != wpar.le || - rpar.sig != wpar.sig || rpar.rate != wpar.rate || - rpar.pchan != wpar.pchan) { - sio_close(s->hdl); - free(s); - DPR("sndio_stream_init() unsupported params\n"); - return CUBEB_ERROR_INVALID_FORMAT; - } - sio_onmove(s->hdl, sndio_onmove, s); - s->active = 0; - s->nfr = rpar.round; - s->bpf = rpar.bps * rpar.pchan; - s->pchan = rpar.pchan; - s->data_cb = data_callback; - s->state_cb = state_callback; - s->arg = user_ptr; - s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER; - s->rdpos = s->wrpos = 0; - if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) { - s->conv = 1; - size = rpar.round * rpar.pchan * sizeof(float); - } else { - s->conv = 0; - size = rpar.round * rpar.pchan * rpar.bps; - } - s->buf = malloc(size); - if (s->buf == NULL) { - sio_close(s->hdl); - free(s); - return CUBEB_ERROR; - } - *stream = s; - DPR("sndio_stream_init() end, ok\n"); - (void)context; - (void)stream_name; - return CUBEB_OK; -} - -static int -sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - assert(ctx && max_channels); - - *max_channels = 8; - - return CUBEB_OK; -} - -static int -sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - // XXX Not yet implemented. - *rate = 44100; - - return CUBEB_OK; -} - -static int -sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) -{ - // XXX Not yet implemented. - *latency_frames = 2048; - - return CUBEB_OK; -} - -static void -sndio_stream_destroy(cubeb_stream *s) -{ - DPR("sndio_stream_destroy()\n"); - sio_close(s->hdl); - free(s); -} - -static int -sndio_stream_start(cubeb_stream *s) -{ - int err; - - DPR("sndio_stream_start()\n"); - s->active = 1; - err = pthread_create(&s->th, NULL, sndio_mainloop, s); - if (err) { - s->active = 0; - return CUBEB_ERROR; - } - return CUBEB_OK; -} - -static int -sndio_stream_stop(cubeb_stream *s) -{ - void *dummy; - - DPR("sndio_stream_stop()\n"); - if (s->active) { - s->active = 0; - pthread_join(s->th, &dummy); - } - return CUBEB_OK; -} - -static int -sndio_stream_get_position(cubeb_stream *s, uint64_t *p) -{ - pthread_mutex_lock(&s->mtx); - DPR("sndio_stream_get_position() %lld\n", s->rdpos); - *p = s->rdpos / s->bpf; - pthread_mutex_unlock(&s->mtx); - return CUBEB_OK; -} - -static int -sndio_stream_set_volume(cubeb_stream *s, float volume) -{ - DPR("sndio_stream_set_volume(%f)\n", volume); - pthread_mutex_lock(&s->mtx); - sio_setvol(s->hdl, SIO_MAXVOL * volume); - pthread_mutex_unlock(&s->mtx); - return CUBEB_OK; -} - -int -sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency) -{ - // http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open - // in the "Measuring the latency and buffers usage" paragraph. - *latency = (stm->wrpos - stm->rdpos) / stm->bpf; - 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, - .get_preferred_channel_layout = NULL, - .enumerate_devices = NULL, - .device_collection_destroy = NULL, - .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_panning = 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 deleted file mode 100644 index 79d7d21b38..0000000000 --- a/Externals/cubeb/src/cubeb_strings.c +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#include "cubeb_strings.h" - -#include -#include -#include - -#define CUBEB_STRINGS_INLINE_COUNT 4 - -struct cubeb_strings { - uint32_t size; - uint32_t count; - char ** data; - char * small_store[CUBEB_STRINGS_INLINE_COUNT]; -}; - -int -cubeb_strings_init(cubeb_strings ** strings) -{ - cubeb_strings* strs = NULL; - - if (!strings) { - return CUBEB_ERROR; - } - - strs = calloc(1, sizeof(cubeb_strings)); - assert(strs); - - if (!strs) { - return CUBEB_ERROR; - } - - strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]); - strs->count = 0; - strs->data = strs->small_store; - - *strings = strs; - - return CUBEB_OK; -} - -void -cubeb_strings_destroy(cubeb_strings * strings) -{ - char ** sp = NULL; - char ** se = NULL; - - if (!strings) { - return; - } - - sp = strings->data; - se = sp + strings->count; - - for ( ; sp != se; sp++) { - if (*sp) { - free(*sp); - } - } - - if (strings->data != strings->small_store) { - free(strings->data); - } - - free(strings); -} - -/** Look for string in string storage. - @param strings Opaque pointer to interned string storage. - @param s String to look up. - @retval Read-only string or NULL if not found. */ -static char const * -cubeb_strings_lookup(cubeb_strings * strings, char const * s) -{ - char ** sp = NULL; - char ** se = NULL; - - if (!strings || !s) { - return NULL; - } - - sp = strings->data; - se = sp + strings->count; - - for ( ; sp != se; sp++) { - if (*sp && strcmp(*sp, s) == 0) { - return *sp; - } - } - - return NULL; -} - -static char const * -cubeb_strings_push(cubeb_strings * strings, char const * s) -{ - char * is = NULL; - - if (strings->count == strings->size) { - char ** new_data; - uint32_t value_size = sizeof(char const *); - uint32_t new_size = strings->size * 2; - if (!new_size || value_size > (uint32_t)-1 / new_size) { - // overflow - return NULL; - } - - if (strings->small_store == strings->data) { - // First time heap allocation. - new_data = malloc(new_size * value_size); - if (new_data) { - memcpy(new_data, strings->small_store, sizeof(strings->small_store)); - } - } else { - new_data = realloc(strings->data, new_size * value_size); - } - - if (!new_data) { - // out of memory - return NULL; - } - - strings->size = new_size; - strings->data = new_data; - } - - is = strdup(s); - strings->data[strings->count++] = is; - - return is; -} - -char const * -cubeb_strings_intern(cubeb_strings * strings, char const * s) -{ - char const * is = NULL; - - if (!strings || !s) { - return NULL; - } - - is = cubeb_strings_lookup(strings, s); - if (is) { - return is; - } - - return cubeb_strings_push(strings, s); -} - diff --git a/Externals/cubeb/src/cubeb_strings.h b/Externals/cubeb/src/cubeb_strings.h deleted file mode 100644 index a918a01c5d..0000000000 --- a/Externals/cubeb/src/cubeb_strings.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#ifndef CUBEB_STRINGS_H -#define CUBEB_STRINGS_H - -#include "cubeb/cubeb.h" - -#if defined(__cplusplus) -extern "C" { -#endif - -/** Opaque handle referencing interned string storage. */ -typedef struct cubeb_strings cubeb_strings; - -/** Initialize an interned string structure. - @param strings An out param where an opaque pointer to the - 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); - -/** 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); - -/** Add string to internal storage. - @param strings Opaque pointer to interned string storage. - @param s String to add to storage. - @retval CUBEB_OK - @retval CUBEB_ERROR - */ -CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s); - -#if defined(__cplusplus) -} -#endif - -#endif // !CUBEB_STRINGS_H diff --git a/Externals/cubeb/src/cubeb_utils.h b/Externals/cubeb/src/cubeb_utils.h deleted file mode 100644 index dc08fec992..0000000000 --- a/Externals/cubeb/src/cubeb_utils.h +++ /dev/null @@ -1,339 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(CUBEB_UTILS) -#define CUBEB_UTILS - -#include "cubeb/cubeb.h" - -#ifdef __cplusplus - -#include -#include -#include -#include -#include -#if defined(_WIN32) -#include "cubeb_utils_win.h" -#else -#include "cubeb_utils_unix.h" -#endif - -/** Similar to memcpy, but accounts for the size of an element. */ -template -void PodCopy(T * destination, const T * source, size_t count) -{ - static_assert(std::is_trivial::value, "Requires trivial type"); - assert(destination && source); - memcpy(destination, source, count * sizeof(T)); -} - -/** Similar to memmove, but accounts for the size of an element. */ -template -void PodMove(T * destination, const T * source, size_t count) -{ - static_assert(std::is_trivial::value, "Requires trivial type"); - assert(destination && source); - memmove(destination, source, count * sizeof(T)); -} - -/** Similar to a memset to zero, but accounts for the size of an element. */ -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)); -} - -namespace { -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) -{ - PodCopy(destination, source, count); -} -} - -/** - * 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) -{ - assert(destination && source); - Copy(destination, source, count, typename std::is_trivial::type()); -} - -namespace { -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) -{ - PodZero(destination, count); -} -} - -/** - * 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) -{ - assert(destination); - ConstructDefault(destination, count, - typename std::is_arithmetic::type()); -} - -template -class auto_array -{ -public: - explicit auto_array(uint32_t capacity = 0) - : data_(capacity ? new T[capacity] : nullptr) - , capacity_(capacity) - , length_(0) - {} - - ~auto_array() - { - delete [] data_; - } - - /** Get a constant pointer to the underlying data. */ - T * data() const - { - return data_; - } - - T * end() const - { - return data_ + length_; - } - - const T& at(size_t index) const - { - assert(index < length_ && "out of range"); - return data_[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_; - } - - /** Get how much elements this auto_array contains. */ - size_t length() const - { - return length_; - } - - /** Keeps the storage, but removes all the elements from the array. */ - 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. - */ - bool reserve(size_t new_capacity) - { - if (new_capacity < length_) { - return false; - } - T * new_data = new T[new_capacity]; - if (data_ && length_) { - PodCopy(new_data, data_, length_); - } - capacity_ = new_capacity; - 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. - */ - void push(const T * elements, size_t length) - { - if (length_ + length > capacity_) { - reserve(length_ + length); - } - PodCopy(data_ + length_, elements, length); - length_ += length; - } - - /** Append `length` zero-ed elements to the end of the array, resizing the - * array if needed. - * @parameter length the number of elements to append to the array. - */ - void push_silence(size_t length) - { - if (length_ + length > capacity_) { - reserve(length + length_); - } - PodZero(data_ + length_, length); - length_ += length; - } - - /** Prepend `length` zero-ed elements to the end of the array, resizing the - * array if needed. - * @parameter length the number of elements to prepend to the array. - */ - void push_front_silence(size_t length) - { - if (length_ + length > capacity_) { - reserve(length + length_); - } - PodMove(data_ + length, data_, length_); - PodZero(data_, length); - length_ += length; - } - - /** Return the number of free elements in the array. */ - 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. */ - bool pop(T * elements, size_t length) - { - if (length > length_) { - return false; - } - if (elements) { - PodCopy(elements, data_, length); - } - PodMove(data_, data_ + length, length_ - length); - - length_ -= length; - - return true; - } - - void set_length(size_t length) - { - assert(length <= capacity_); - length_ = length; - } - -private: - /** The underlying storage */ - T * data_; - /** The size, in number of elements, of the storage. */ - size_t capacity_; - /** The number of elements the array contains. */ - size_t length_; -}; - -struct auto_array_wrapper { - virtual void push(void * elements, size_t length) = 0; - virtual size_t length() = 0; - virtual void push_silence(size_t length) = 0; - virtual bool pop(size_t length) = 0; - virtual void * data() = 0; - virtual void * end() = 0; - virtual void clear() = 0; - virtual bool reserve(size_t capacity) = 0; - virtual void set_length(size_t length) = 0; - virtual ~auto_array_wrapper() {} -}; - -template -struct auto_array_wrapper_impl : public auto_array_wrapper { - auto_array_wrapper_impl() {} - - explicit auto_array_wrapper_impl(uint32_t size) - : ar(size) - {} - - void push(void * elements, size_t length) override { - ar.push(static_cast(elements), length); - } - - size_t length() override { - return ar.length(); - } - - void push_silence(size_t length) override { - ar.push_silence(length); - } - - bool pop(size_t length) override { - return ar.pop(nullptr, length); - } - - void * data() override { - return ar.data(); - } - - void * end() override { - return ar.end(); - } - - void clear() override { - ar.clear(); - } - - bool reserve(size_t capacity) override { - return ar.reserve(capacity); - } - - void set_length(size_t length) override { - ar.set_length(length); - } - - ~auto_array_wrapper_impl() { - ar.clear(); - } - -private: - auto_array ar; -}; - -using auto_lock = std::lock_guard; -#endif // __cplusplus - -#endif /* CUBEB_UTILS */ diff --git a/Externals/cubeb/src/cubeb_utils_unix.h b/Externals/cubeb/src/cubeb_utils_unix.h deleted file mode 100644 index 4876d015fe..0000000000 --- a/Externals/cubeb/src/cubeb_utils_unix.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(CUBEB_UTILS_UNIX) -#define CUBEB_UTILS_UNIX - -#include -#include -#include - -/* This wraps a critical section to track the owner in debug mode. */ -class owned_critical_section -{ -public: - owned_critical_section() - { - pthread_mutexattr_t attr; - pthread_mutexattr_init(&attr); -#ifndef NDEBUG - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); -#else - pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); -#endif - -#ifndef NDEBUG - int r = -#endif - pthread_mutex_init(&mutex, &attr); -#ifndef NDEBUG - assert(r == 0); -#endif - - pthread_mutexattr_destroy(&attr); - } - - ~owned_critical_section() - { -#ifndef NDEBUG - int r = -#endif - pthread_mutex_destroy(&mutex); -#ifndef NDEBUG - assert(r == 0); -#endif - } - - void lock() - { -#ifndef NDEBUG - int r = -#endif - pthread_mutex_lock(&mutex); -#ifndef NDEBUG - assert(r == 0 && "Deadlock"); -#endif - } - - void unlock() - { -#ifndef NDEBUG - int r = -#endif - pthread_mutex_unlock(&mutex); -#ifndef NDEBUG - assert(r == 0 && "Unlocking unlocked mutex"); -#endif - } - - void assert_current_thread_owns() - { -#ifndef NDEBUG - int r = pthread_mutex_lock(&mutex); - assert(r == EDEADLK); -#endif - } - -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&); -}; - -#endif /* CUBEB_UTILS_UNIX */ diff --git a/Externals/cubeb/src/cubeb_utils_win.h b/Externals/cubeb/src/cubeb_utils_win.h deleted file mode 100644 index 0112ad6d3c..0000000000 --- a/Externals/cubeb/src/cubeb_utils_win.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright © 2016 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ - -#if !defined(CUBEB_UTILS_WIN) -#define CUBEB_UTILS_WIN - -#include -#include "cubeb-internal.h" - -/* 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 -{ -public: - owned_critical_section() -#ifndef NDEBUG - : owner(0) -#endif - { - InitializeCriticalSection(&critical_section); - } - - ~owned_critical_section() - { - DeleteCriticalSection(&critical_section); - } - - void lock() - { - EnterCriticalSection(&critical_section); -#ifndef NDEBUG - XASSERT(owner != GetCurrentThreadId() && "recursive locking"); - owner = GetCurrentThreadId(); -#endif - } - - void unlock() - { -#ifndef NDEBUG - /* GetCurrentThreadId cannot return 0: it is not a the valid thread id */ - owner = 0; -#endif - LeaveCriticalSection(&critical_section); - } - - /* This is guaranteed to have the good behaviour if it succeeds. The behaviour - is undefined otherwise. */ - void assert_current_thread_owns() - { -#ifndef NDEBUG - /* This implies owner != 0, because GetCurrentThreadId cannot return 0. */ - XASSERT(owner == GetCurrentThreadId()); -#endif - } - -private: - CRITICAL_SECTION critical_section; -#ifndef NDEBUG - DWORD owner; -#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&); -}; - -#endif /* CUBEB_UTILS_WIN */ diff --git a/Externals/cubeb/src/cubeb_wasapi.cpp b/Externals/cubeb/src/cubeb_wasapi.cpp deleted file mode 100644 index b18662d0a1..0000000000 --- a/Externals/cubeb/src/cubeb_wasapi.cpp +++ /dev/null @@ -1,2418 +0,0 @@ -/* - * Copyright © 2013 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#define _WIN32_WINNT 0x0600 -#define NOMINMAX - -#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_mixer.h" -#include "cubeb_resampler.h" -#include "cubeb_strings.h" -#include "cubeb_utils.h" - -#ifndef PKEY_Device_FriendlyName -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 -#endif - -namespace { -struct com_heap_ptr_deleter { - void operator()(void * ptr) const noexcept { - CoTaskMemFree(ptr); - } -}; - -template -using com_heap_ptr = std::unique_ptr; - -template -constexpr size_t -ARRAY_LENGTH(T(&)[N]) -{ - return N; -} - -template -class no_addref_release : public T { - ULONG STDMETHODCALLTYPE AddRef() = 0; - ULONG STDMETHODCALLTYPE Release() = 0; -}; - -template -class com_ptr { -public: - com_ptr() noexcept = default; - - com_ptr(com_ptr const & other) noexcept = delete; - com_ptr & operator=(com_ptr const & other) noexcept = delete; - T ** operator&() const noexcept = delete; - - ~com_ptr() noexcept { - release(); - } - - com_ptr(com_ptr && other) noexcept - : ptr(other.ptr) - { - other.ptr = nullptr; - } - - com_ptr & operator=(com_ptr && other) noexcept { - if (ptr != other.ptr) { - release(); - ptr = other.ptr; - other.ptr = nullptr; - } - return *this; - } - - explicit operator bool() const noexcept { - return nullptr != ptr; - } - - no_addref_release * operator->() const noexcept { - return static_cast *>(ptr); - } - - T * get() const noexcept { - return ptr; - } - - T ** receive() noexcept { - XASSERT(ptr == nullptr); - return &ptr; - } - - void ** receive_vpp() noexcept { - return reinterpret_cast(receive()); - } - - com_ptr & operator=(std::nullptr_t) noexcept { - release(); - return *this; - } - - void reset(T * p = nullptr) noexcept { - release(); - ptr = p; - } - -private: - void release() noexcept { - T * temp = ptr; - - if (temp) { - ptr = nullptr; - temp->Release(); - } - } - - T * ptr = nullptr; -}; - -struct auto_com { - auto_com() { - result = CoInitializeEx(NULL, COINIT_MULTITHREADED); - } - ~auto_com() { - if (result == RPC_E_CHANGED_MODE) { - // This is not an error, COM was not initialized by this function, so it is - // not necessary to uninit it. - LOG("COM was already initialized in STA."); - } else if (result == S_FALSE) { - // This is not an error. We are allowed to call CoInitializeEx more than - // once, as long as it is matches by an CoUninitialize call. - // We do that in the dtor which is guaranteed to be called. - LOG("COM was already initialized in MTA"); - } - if (SUCCEEDED(result)) { - CoUninitialize(); - } - } - bool ok() { - return result == RPC_E_CHANGED_MODE || SUCCEEDED(result); - } -private: - HRESULT result; -}; - -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); -static char const * wstr_to_utf8(wchar_t const * str); -static std::unique_ptr utf8_to_wstr(char const * str); - -} - -struct cubeb { - cubeb_ops const * ops = &wasapi_ops; - cubeb_strings * device_ids; -}; - -class wasapi_endpoint_notification_client; - -/* We have three possible callbacks we can use with a stream: - * - input only - * - output only - * - synchronized input and output - * - * Returns true when we should continue to play, false otherwise. - */ -typedef bool (*wasapi_refill_callback)(cubeb_stream * stm); - -struct cubeb_stream { - cubeb * context = nullptr; - /* 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_params output_mix_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED }; - /* 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_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED }; - /* The input and output device, or NULL for default. */ - std::unique_ptr input_device; - std::unique_ptr output_device; - /* The latency initially requested for this stream, in frames. */ - unsigned latency = 0; - cubeb_state_callback state_callback = nullptr; - cubeb_data_callback data_callback = nullptr; - wasapi_refill_callback refill_callback = nullptr; - void * user_ptr = nullptr; - /* Lifetime considerations: - - client, render_client, audio_clock and audio_stream_volume are interface - pointer to the IAudioClient. - - The lifetime for device_enumerator and notification_client, resampler, - mix_buffer are the same as the cubeb_stream instance. */ - - /* Main handle on the WASAPI stream. */ - com_ptr output_client; - /* Interface pointer to use the event-driven interface. */ - com_ptr render_client; - /* Interface pointer to use the volume facilities. */ - com_ptr audio_stream_volume; - /* Interface pointer to use the stream audio clock. */ - com_ptr audio_clock; - /* Frames written to the stream since it was opened. Reset on device - change. Uses mix_params.rate. */ - UINT64 frames_written = 0; - /* Frames written to the (logical) stream since it was first - created. Updated on device change. Uses stream_params.rate. */ - UINT64 total_frames_written = 0; - /* Last valid reported stream position. Used to ensure the position - reported by stream_get_position increases monotonically. */ - UINT64 prev_position = 0; - /* Device enumerator to be able to be notified when the default - device change. */ - com_ptr device_enumerator; - /* Device notification client, to be able to be notified when the default - audio device changes and route the audio to the new default audio output - device */ - com_ptr notification_client; - /* Main andle to the WASAPI capture stream. */ - com_ptr input_client; - /* Interface to use the event driven capture interface */ - com_ptr capture_client; - /* This event is set by the stream_stop and stream_destroy - function, so the render loop can exit properly. */ - HANDLE shutdown_event = 0; - /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required. - The reconfiguration is handled by the render loop thread. */ - HANDLE reconfigure_event = 0; - /* This is set by WASAPI when we should refill the stream. */ - HANDLE refill_event = 0; - /* This is set by WASAPI when we should read from the input stream. In - * practice, we read from the input stream in the output callback, so - * this is not used, but it is necessary to start getting input data. */ - HANDLE input_available_event = 0; - /* Each cubeb_stream has its own thread. */ - HANDLE thread = 0; - /* The lock protects all members that are touched by the render thread or - change during a device reset, including: audio_clock, audio_stream_volume, - client, frames_written, mix_params, total_frames_written, prev_position. */ - owned_critical_section stream_reset_lock; - /* Maximum number of frames that can be passed down in a callback. */ - uint32_t input_buffer_frame_count = 0; - /* 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 }; - /* Mixer interface */ - std::unique_ptr mixer = { nullptr, cubeb_mixer_destroy }; - /* A buffer for up/down mixing multi-channel audio. */ - std::vector mix_buffer; - /* WASAPI input works in "packets". We re-linearize the audio packets - * into this buffer before handing it to the resampler. */ - std::unique_ptr linear_input_buffer; - /* Bytes per sample. This multiplied by the number of channels is the number - * of bytes per frame. */ - size_t bytes_per_sample = 0; - /* WAVEFORMATEXTENSIBLE sub-format: either PCM or float. */ - GUID waveformatextensible_sub_format = GUID_NULL; - /* Stream volume. Set via stream_set_volume and used to reset volume on - device changes. */ - float volume = 1.0; - /* True if the stream is draining. */ - 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; -}; - -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 - Release() - { - ULONG ulRef = InterlockedDecrement(&ref_count); - if (0 == ulRef) { - delete this; - } - return ulRef; - } - - HRESULT STDMETHODCALLTYPE - QueryInterface(REFIID riid, VOID **ppvInterface) - { - if (__uuidof(IUnknown) == riid) { - AddRef(); - *ppvInterface = (IUnknown*)this; - } else if (__uuidof(IMMNotificationClient) == riid) { - AddRef(); - *ppvInterface = (IMMNotificationClient*)this; - } else { - *ppvInterface = NULL; - return E_NOINTERFACE; - } - return S_OK; - } - - wasapi_endpoint_notification_client(HANDLE event) - : ref_count(1) - , reconfigure_event(event) - { } - - virtual ~wasapi_endpoint_notification_client() - { } - - HRESULT STDMETHODCALLTYPE - OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR device_id) - { - LOG("Audio device default changed."); - - /* we only support a single stream type for now. */ - if (flow != eRender && role != eConsole) { - return S_OK; - } - - BOOL ok = SetEvent(reconfigure_event); - if (!ok) { - LOG("SetEvent on reconfigure_event failed: %lx", GetLastError()); - } - - return S_OK; - } - - /* The remaining methods are not implemented, they simply log when called (if - log is enabled), for debugging. */ - HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR device_id) - { - LOG("Audio device added."); - return S_OK; - }; - - HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR device_id) - { - LOG("Audio device removed."); - return S_OK; - } - - HRESULT STDMETHODCALLTYPE - OnDeviceStateChanged(LPCWSTR device_id, DWORD new_state) - { - LOG("Audio device state changed."); - return S_OK; - } - - HRESULT STDMETHODCALLTYPE - OnPropertyValueChanged(LPCWSTR device_id, const PROPERTYKEY key) - { - LOG("Audio device property value changed."); - return S_OK; - } -private: - /* refcount for this instance, necessary to implement MSCOM semantics. */ - LONG ref_count; - HANDLE reconfigure_event; -}; - -namespace { - -char const * -intern_device_id(cubeb * ctx, wchar_t const * id) -{ - XASSERT(id); - - char const * tmp = wstr_to_utf8(id); - if (!tmp) - return nullptr; - - char const * interned = cubeb_strings_intern(ctx->device_ids, tmp); - - free((void *) tmp); - - return interned; -} - -bool has_input(cubeb_stream * stm) -{ - return stm->input_stream_params.rate != 0; -} - -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) -{ - 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 */ -#define MASK_DUAL_MONO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) -#define MASK_DUAL_MONO_LFE (MASK_DUAL_MONO | SPEAKER_LOW_FREQUENCY) -#define MASK_MONO (KSAUDIO_SPEAKER_MONO) -#define MASK_MONO_LFE (MASK_MONO | SPEAKER_LOW_FREQUENCY) -#define MASK_STEREO (KSAUDIO_SPEAKER_STEREO) -#define MASK_STEREO_LFE (MASK_STEREO | SPEAKER_LOW_FREQUENCY) -#define MASK_3F (MASK_STEREO | SPEAKER_FRONT_CENTER) -#define MASK_3F_LFE (MASK_3F | SPEAKER_LOW_FREQUENCY) -#define MASK_2F1 (MASK_STEREO | SPEAKER_BACK_CENTER) -#define MASK_2F1_LFE (MASK_2F1 | SPEAKER_LOW_FREQUENCY) -#define MASK_3F1 (KSAUDIO_SPEAKER_SURROUND) -#define MASK_3F1_LFE (MASK_3F1 | SPEAKER_LOW_FREQUENCY) -#define MASK_2F2 (MASK_STEREO | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) -#define MASK_2F2_LFE (MASK_2F2 | SPEAKER_LOW_FREQUENCY) -#define MASK_3F2 (MASK_3F | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) -#define MASK_3F2_LFE (KSAUDIO_SPEAKER_5POINT1_SURROUND) -#define MASK_3F3R_LFE (MASK_3F2_LFE | SPEAKER_BACK_CENTER) -#define MASK_3F4_LFE (KSAUDIO_SPEAKER_7POINT1_SURROUND) - -static DWORD -channel_layout_to_mask(cubeb_channel_layout layout) -{ - XASSERT(layout < CUBEB_LAYOUT_MAX && "invalid conversion."); - - // This variable may be used for multiple times, so we should avoid to - // allocate it in stack, or it will be created and removed repeatedly. - // Use static to allocate this local variable in data space instead of stack. - static DWORD map[CUBEB_LAYOUT_MAX] = { - KSAUDIO_SPEAKER_DIRECTOUT, // CUBEB_LAYOUT_UNDEFINED - MASK_DUAL_MONO, // CUBEB_LAYOUT_DUAL_MONO - MASK_DUAL_MONO_LFE, // CUBEB_LAYOUT_DUAL_MONO_LFE - MASK_MONO, // CUBEB_LAYOUT_MONO - MASK_MONO_LFE, // CUBEB_LAYOUT_MONO_LFE - MASK_STEREO, // CUBEB_LAYOUT_STEREO - MASK_STEREO_LFE, // CUBEB_LAYOUT_STEREO_LFE - MASK_3F, // CUBEB_LAYOUT_3F - MASK_3F_LFE, // CUBEB_LAYOUT_3F_LFE - MASK_2F1, // CUBEB_LAYOUT_2F1 - MASK_2F1_LFE, // CUBEB_LAYOUT_2F1_LFE - MASK_3F1, // CUBEB_LAYOUT_3F1 - MASK_3F1_LFE, // CUBEB_LAYOUT_3F1_LFE - MASK_2F2, // CUBEB_LAYOUT_2F2 - MASK_2F2_LFE, // CUBEB_LAYOUT_2F2_LFE - MASK_3F2, // CUBEB_LAYOUT_3F2 - MASK_3F2_LFE, // CUBEB_LAYOUT_3F2_LFE - MASK_3F3R_LFE, // CUBEB_LAYOUT_3F3R_LFE - MASK_3F4_LFE, // CUBEB_LAYOUT_3F4_LFE - }; - return map[layout]; -} - -cubeb_channel_layout -mask_to_channel_layout(WAVEFORMATEX const * fmt) -{ - DWORD mask = 0; - - if (fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE const * ext = reinterpret_cast(fmt); - mask = ext->dwChannelMask; - } else if (fmt->wFormatTag == WAVE_FORMAT_PCM || - fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) { - if (fmt->nChannels == 1) { - mask = MASK_MONO; - } else if (fmt->nChannels == 2) { - mask = MASK_STEREO; - } - } - - switch (mask) { - // MASK_DUAL_MONO(_LFE) is same as STEREO(_LFE), so we skip it. - case MASK_MONO: return CUBEB_LAYOUT_MONO; - case MASK_MONO_LFE: return CUBEB_LAYOUT_MONO_LFE; - case MASK_STEREO: return CUBEB_LAYOUT_STEREO; - case MASK_STEREO_LFE: return CUBEB_LAYOUT_STEREO_LFE; - case MASK_3F: return CUBEB_LAYOUT_3F; - case MASK_3F_LFE: return CUBEB_LAYOUT_3F_LFE; - case MASK_2F1: return CUBEB_LAYOUT_2F1; - case MASK_2F1_LFE: return CUBEB_LAYOUT_2F1_LFE; - case MASK_3F1: return CUBEB_LAYOUT_3F1; - case MASK_3F1_LFE: return CUBEB_LAYOUT_3F1_LFE; - case MASK_2F2: return CUBEB_LAYOUT_2F2; - case MASK_2F2_LFE: return CUBEB_LAYOUT_2F2_LFE; - case MASK_3F2: return CUBEB_LAYOUT_3F2; - case MASK_3F2_LFE: return CUBEB_LAYOUT_3F2_LFE; - case MASK_3F3R_LFE: return CUBEB_LAYOUT_3F3R_LFE; - case MASK_3F4_LFE: return CUBEB_LAYOUT_3F4_LFE; - default: return CUBEB_LAYOUT_UNDEFINED; - } -} - -uint32_t -get_rate(cubeb_stream * stm) -{ - return has_input(stm) ? stm->input_stream_params.rate - : stm->output_stream_params.rate; -} - -uint32_t -hns_to_frames(uint32_t rate, REFERENCE_TIME hns) -{ - return std::ceil(hns / 10000000.0 * rate); -} - -uint32_t -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)); -} - -/* This returns the size of a frame in the stream, before the eventual upmix - occurs. */ -static size_t -frames_to_bytes_before_mix(cubeb_stream * stm, size_t frames) -{ - // This is called only when we has a output client. - XASSERT(has_output(stm)); - return stm->output_stream_params.channels * stm->bytes_per_sample * frames; -} - -/* This function handles the processing of the input and output audio, - * converting it to rate and channel layout specified at initialization. - * It then calls the data callback, via the resampler. */ -long -refill(cubeb_stream * stm, void * input_buffer, long input_frames_count, - void * output_buffer, long output_frames_needed) -{ - /* If we need to upmix after resampling, resample into the mix buffer to - avoid a copy. */ - void * dest = nullptr; - if (has_output(stm)) { - if (cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) { - dest = stm->mix_buffer.data(); - } else { - dest = output_buffer; - } - } - - 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); - - { - auto_lock lock(stm->stream_reset_lock); - stm->frames_written += out_frames; - } - - /* Go in draining mode if we got fewer frames than requested. */ - if (out_frames < output_frames_needed) { - LOG("start draining."); - stm->draining = true; - } - - /* 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)); - - if (has_output(stm) && cubeb_should_mix(&stm->output_stream_params, &stm->output_mix_params)) { - XASSERT(dest == stm->mix_buffer.data()); - unsigned long dest_len = out_frames * stm->output_stream_params.channels; - XASSERT(dest_len <= stm->mix_buffer.size() / stm->bytes_per_sample); - unsigned long output_buffer_len = out_frames * stm->output_mix_params.channels; - cubeb_mixer_mix(stm->mixer.get(), out_frames, - dest, dest_len, output_buffer, output_buffer_len, - &stm->output_stream_params, &stm->output_mix_params); - } - - return out_frames; -} - -/* 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. */ -bool get_input_buffer(cubeb_stream * stm) -{ - HRESULT hr; - UINT32 padding_in; - - XASSERT(has_input(stm)); - - hr = stm->input_client->GetCurrentPadding(&padding_in); - if (FAILED(hr)) { - LOG("Failed to get padding"); - return false; - } - XASSERT(padding_in <= stm->input_buffer_frame_count); - UINT32 total_available_input = padding_in; - - BYTE * input_packet = NULL; - DWORD flags; - UINT64 dev_pos; - UINT32 next; - /* Get input packets until we have captured enough frames, and put them in a - * contiguous buffer. */ - uint32_t offset = 0; - while (offset != total_available_input) { - hr = stm->capture_client->GetNextPacketSize(&next); - if (FAILED(hr)) { - LOG("cannot get next packet size: %lx", hr); - return false; - } - /* This can happen if the capture stream has stopped. Just return in this - * case. */ - if (!next) { - break; - } - - UINT32 packet_size; - hr = stm->capture_client->GetBuffer(&input_packet, - &packet_size, - &flags, - &dev_pos, - NULL); - if (FAILED(hr)) { - LOG("GetBuffer failed for capture: %lx", hr); - return false; - } - XASSERT(packet_size == next); - if (flags & AUDCLNT_BUFFERFLAGS_SILENT) { - LOG("insert silence: ps=%u", packet_size); - stm->linear_input_buffer->push_silence(packet_size * stm->input_stream_params.channels); - } else { - if (cubeb_should_mix(&stm->input_mix_params, &stm->input_stream_params)) { - bool ok = stm->linear_input_buffer->reserve(stm->linear_input_buffer->length() + - packet_size * stm->input_stream_params.channels); - XASSERT(ok); - unsigned long input_packet_length = packet_size * stm->input_mix_params.channels; - unsigned long linear_input_buffer_length = packet_size * stm->input_stream_params.channels; - cubeb_mixer_mix(stm->mixer.get(), packet_size, - input_packet, input_packet_length, - stm->linear_input_buffer->end(), linear_input_buffer_length, - &stm->input_mix_params, - &stm->input_stream_params); - stm->linear_input_buffer->set_length(stm->linear_input_buffer->length() + linear_input_buffer_length); - } else { - stm->linear_input_buffer->push(input_packet, - packet_size * stm->input_stream_params.channels); - } - } - hr = stm->capture_client->ReleaseBuffer(packet_size); - if (FAILED(hr)) { - LOG("FAILED to release intput buffer"); - return false; - } - offset += packet_size; - } - - XASSERT(stm->linear_input_buffer->length() >= total_available_input && - offset == total_available_input); - - return true; -} - -/* 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) -{ - UINT32 padding_out; - HRESULT hr; - - XASSERT(has_output(stm)); - - hr = stm->output_client->GetCurrentPadding(&padding_out); - if (FAILED(hr)) { - LOG("Failed to get padding: %lx", hr); - return false; - } - XASSERT(padding_out <= stm->output_buffer_frame_count); - - if (stm->draining) { - if (padding_out == 0) { - LOG("Draining finished."); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - return false; - } - LOG("Draining."); - return true; - } - - frame_count = stm->output_buffer_frame_count - padding_out; - BYTE * output_buffer; - - hr = stm->render_client->GetBuffer(frame_count, &output_buffer); - if (FAILED(hr)) { - LOG("cannot get render buffer"); - return false; - } - - buffer = output_buffer; - - return true; -} - -/** - * This function gets input data from a input device, and pass it along with an - * output buffer to the resamplers. */ -bool -refill_callback_duplex(cubeb_stream * stm) -{ - HRESULT hr; - void * output_buffer = nullptr; - size_t output_frames = 0; - size_t input_frames; - bool rv; - - XASSERT(has_input(stm) && has_output(stm)); - - rv = get_input_buffer(stm); - if (!rv) { - return rv; - } - - input_frames = stm->linear_input_buffer->length() / stm->input_stream_params.channels; - if (!input_frames) { - return true; - } - - rv = get_output_buffer(stm, output_buffer, output_frames); - if (!rv) { - hr = stm->render_client->ReleaseBuffer(output_frames, 0); - return rv; - } - - /* This can only happen when debugging, and having breakpoints set in the - * callback in a way that it makes the stream underrun. */ - if (output_frames == 0) { - return true; - } - - - ALOGV("Duplex callback: input frames: %Iu, output frames: %Iu", - input_frames, output_frames); - - refill(stm, - stm->linear_input_buffer->data(), - input_frames, - output_buffer, - output_frames); - - stm->linear_input_buffer->clear(); - - hr = stm->render_client->ReleaseBuffer(output_frames, 0); - if (FAILED(hr)) { - LOG("failed to release buffer: %lx", hr); - return false; - } - return true; -} - -bool -refill_callback_input(cubeb_stream * stm) -{ - bool rv; - size_t input_frames; - - XASSERT(has_input(stm) && !has_output(stm)); - - rv = get_input_buffer(stm); - if (!rv) { - return rv; - } - - 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); - - XASSERT(read >= 0); - - stm->linear_input_buffer->clear(); - - return !stm->draining; -} - -bool -refill_callback_output(cubeb_stream * stm) -{ - bool rv; - HRESULT hr; - void * output_buffer = nullptr; - size_t output_frames = 0; - - XASSERT(!has_input(stm) && has_output(stm)); - - rv = get_output_buffer(stm, output_buffer, output_frames); - if (!rv) { - return rv; - } - - if (stm->draining || output_frames == 0) { - return true; - } - - long got = refill(stm, - nullptr, - 0, - output_buffer, - output_frames); - - ALOGV("Output callback: output frames requested: %Iu, got %ld", - output_frames, got); - - XASSERT(got >= 0); - XASSERT((unsigned long) got == output_frames || stm->draining); - - hr = stm->render_client->ReleaseBuffer(got, 0); - if (FAILED(hr)) { - LOG("failed to release buffer: %lx", hr); - return false; - } - - return (unsigned long) got == output_frames || stm->draining; -} - -static unsigned int __stdcall -wasapi_stream_render_loop(LPVOID stream) -{ - cubeb_stream * stm = static_cast(stream); - std::atomic * emergency_bailout = stm->emergency_bailout; - - bool is_playing = true; - 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; - auto_com com; - if (!com.ok()) { - LOG("COM initialization failed on render_loop thread."); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - return 0; - } - - /* We could consider using "Pro Audio" here for WebAudio and - maybe WebRTC. */ - 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()); - } - - // This has already been nulled out, simply exit. - if (!emergency_bailout) { - is_playing = false; - } - - /* WaitForMultipleObjects timeout can trigger in cases where we don't want to - treat it as a timeout, such as across a system sleep/wake cycle. Trigger - the timeout error handling only when the timeout_limit is reached, which is - reset on each successful loop. */ - unsigned timeout_count = 0; - const unsigned timeout_limit = 5; - 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. - if (*emergency_bailout) { - delete emergency_bailout; - return 0; - } - DWORD waitResult = WaitForMultipleObjects(ARRAY_LENGTH(wait_array), - wait_array, - FALSE, - 1000); - if (*emergency_bailout) { - delete emergency_bailout; - return 0; - } - if (waitResult != WAIT_TIMEOUT) { - timeout_count = 0; - } - switch (waitResult) { - case WAIT_OBJECT_0: { /* shutdown */ - is_playing = false; - /* We don't check if the drain is actually finished here, we just want to - shutdown. */ - if (stm->draining) { - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - } - continue; - } - case WAIT_OBJECT_0 + 1: { /* reconfigure */ - XASSERT(stm->output_client || stm->input_client); - LOG("Reconfiguring the stream"); - /* Close the stream */ - if (stm->output_client) { - stm->output_client->Stop(); - LOG("Output stopped."); - } - if (stm->input_client) { - stm->input_client->Stop(); - LOG("Input stopped."); - } - { - 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. */ - int r = setup_wasapi_stream(stm); - if (r != CUBEB_OK) { - LOG("Error setting up the stream during reconfigure."); - /* Don't destroy the stream here, since we expect the caller to do - so after the error has propagated via the state callback. */ - is_playing = false; - hr = E_FAIL; - continue; - } - LOG("Stream setup successfuly."); - } - XASSERT(stm->output_client || stm->input_client); - if (stm->output_client) { - stm->output_client->Start(); - LOG("Output started after reconfigure."); - } - if (stm->input_client) { - stm->input_client->Start(); - LOG("Input started after reconfigure."); - } - break; - } - 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); - break; - case WAIT_TIMEOUT: - XASSERT(stm->shutdown_event == wait_array[0]); - if (++timeout_count >= timeout_limit) { - LOG("Render loop reached the timeout limit."); - is_playing = false; - hr = E_FAIL; - } - break; - default: - LOG("case %lu not handled in render loop.", waitResult); - abort(); - } - } - - if (FAILED(hr)) { - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - } - - if (mmcss_handle) { - AvRevertMmThreadCharacteristics(mmcss_handle); - } - - return 0; -} - -void wasapi_destroy(cubeb * context); - -HRESULT register_notification_client(cubeb_stream * stm) -{ - 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; - } - - stm->notification_client.reset(new wasapi_endpoint_notification_client(stm->reconfigure_event)); - - 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) -{ - XASSERT(stm); - HRESULT hr; - - if (!stm->device_enumerator) { - return S_OK; - } - - 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; - return S_OK; - } - - stm->notification_client = nullptr; - stm->device_enumerator = nullptr; - - return S_OK; -} - -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())); - if (FAILED(hr)) { - LOG("Could not get device enumerator: %lx", hr); - return hr; - } - - hr = enumerator->GetDevice(devid, device.receive()); - if (FAILED(hr)) { - LOG("Could not get device: %lx", hr); - return hr; - } - - return S_OK; -} - -HRESULT get_default_endpoint(com_ptr & device, EDataFlow direction) -{ - com_ptr enumerator; - 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; - } - hr = enumerator->GetDefaultAudioEndpoint(direction, eConsole, device.receive()); - if (FAILED(hr)) { - LOG("Could not get default audio endpoint: %lx", hr); - return hr; - } - - return ERROR_SUCCESS; -} - -double -current_stream_delay(cubeb_stream * stm) -{ - stm->stream_reset_lock.assert_current_thread_owns(); - - /* If the default audio endpoint went away during playback and we weren't - able to configure a new one, it's possible the caller may call this - before the error callback has propogated back. */ - if (!stm->audio_clock) { - return 0; - } - - UINT64 freq; - HRESULT hr = stm->audio_clock->GetFrequency(&freq); - if (FAILED(hr)) { - LOG("GetFrequency failed: %lx", hr); - return 0; - } - - UINT64 pos; - hr = stm->audio_clock->GetPosition(&pos, NULL); - if (FAILED(hr)) { - LOG("GetPosition failed: %lx", hr); - return 0; - } - - double cur_pos = static_cast(pos) / freq; - double max_pos = static_cast(stm->frames_written) / stm->output_mix_params.rate; - double delay = max_pos - cur_pos; - XASSERT(delay >= 0); - - return delay; -} - -int -stream_set_volume(cubeb_stream * stm, float volume) -{ - stm->stream_reset_lock.assert_current_thread_owns(); - - if (!stm->audio_stream_volume) { - return CUBEB_ERROR; - } - - uint32_t channels; - HRESULT hr = stm->audio_stream_volume->GetChannelCount(&channels); - if (hr != S_OK) { - LOG("could not get the channel count: %lx", hr); - return CUBEB_ERROR; - } - - /* up to 9.1 for now */ - if (channels > 10) { - return CUBEB_ERROR_NOT_SUPPORTED; - } - - float volumes[10]; - for (uint32_t i = 0; i < channels; i++) { - volumes[i] = volume; - } - - hr = stm->audio_stream_volume->SetAllVolumes(channels, volumes); - if (hr != S_OK) { - LOG("could not set the channels volume: %lx", hr); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} -} // namespace anonymous - -extern "C" { -int wasapi_init(cubeb ** context, char const * context_name) -{ - HRESULT hr; - auto_com com; - if (!com.ok()) { - return CUBEB_ERROR; - } - - /* 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 - support WASAPI. */ - com_ptr device; - hr = get_default_endpoint(device, eRender); - if (FAILED(hr)) { - LOG("Could not get device: %lx", hr); - return CUBEB_ERROR; - } - - cubeb * ctx = new cubeb(); - - ctx->ops = &wasapi_ops; - if (cubeb_strings_init(&ctx->device_ids) != CUBEB_OK) { - free(ctx); - return CUBEB_ERROR; - } - - *context = ctx; - - return CUBEB_OK; -} -} - -namespace { -bool stop_and_join_render_thread(cubeb_stream * stm) -{ - bool rv = true; - LOG("Stop and join render thread."); - if (!stm->thread) { - LOG("No thread present."); - return true; - } - - // If we've already leaked the thread, just return, - // there is not much we can do. - if (!stm->emergency_bailout.load()) { - return false; - } - - BOOL ok = SetEvent(stm->shutdown_event); - if (!ok) { - LOG("Destroy SetEvent failed: %lx", GetLastError()); - } - - /* Wait five seconds for the rendering thread to return. It's supposed to - * check its event loop very often, five seconds is rather conservative. */ - DWORD r = WaitForSingleObject(stm->thread, 5000); - if (r == WAIT_TIMEOUT) { - /* Something weird happened, leak the thread and continue the shutdown - * process. */ - *(stm->emergency_bailout) = true; - // We give the ownership to the rendering thread. - stm->emergency_bailout = nullptr; - LOG("Destroy WaitForSingleObject on thread timed out," - " leaking the thread: %lx", GetLastError()); - rv = false; - } - if (r == WAIT_FAILED) { - *(stm->emergency_bailout) = true; - // We give the ownership to the rendering thread. - stm->emergency_bailout = nullptr; - LOG("Destroy WaitForSingleObject on thread failed: %lx", GetLastError()); - rv = false; - } - - - // Only attempts to close and null out the thread and event if the - // WaitForSingleObject above succeeded, so that calling this function again - // attemps to clean up the thread and event each time. - if (rv) { - LOG("Closing thread."); - CloseHandle(stm->thread); - stm->thread = NULL; - - CloseHandle(stm->shutdown_event); - stm->shutdown_event = 0; - } - - return rv; -} - -void wasapi_destroy(cubeb * context) -{ - if (context->device_ids) { - cubeb_strings_destroy(context->device_ids); - } - - delete context; -} - -char const * wasapi_get_backend_id(cubeb * context) -{ - return "wasapi"; -} - -int -wasapi_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - HRESULT hr; - auto_com com; - if (!com.ok()) { - return CUBEB_ERROR; - } - - XASSERT(ctx && max_channels); - - com_ptr device; - hr = get_default_endpoint(device, eRender); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - - com_ptr client; - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp()); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - - WAVEFORMATEX * tmp = nullptr; - hr = client->GetMixFormat(&tmp); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - com_heap_ptr mix_format(tmp); - - *max_channels = mix_format->nChannels; - - return CUBEB_OK; -} - -int -wasapi_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames) -{ - HRESULT hr; - REFERENCE_TIME default_period; - auto_com com; - if (!com.ok()) { - return CUBEB_ERROR; - } - - if (params.format != CUBEB_SAMPLE_FLOAT32NE && params.format != CUBEB_SAMPLE_S16NE) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - com_ptr device; - hr = get_default_endpoint(device, eRender); - if (FAILED(hr)) { - LOG("Could not get default endpoint: %lx", hr); - return CUBEB_ERROR; - } - - com_ptr client; - 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; - } - - /* The second parameter is for exclusive mode, that we don't use. */ - hr = client->GetDevicePeriod(&default_period, NULL); - if (FAILED(hr)) { - LOG("Could not get device period: %lx", hr); - return CUBEB_ERROR; - } - - LOG("default device period: %I64d", default_period); - - /* 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 */ - - *latency_frames = hns_to_frames(params.rate, default_period); - - LOG("Minimum latency in frames: %u", *latency_frames); - - return CUBEB_OK; -} - -int -wasapi_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - HRESULT hr; - auto_com com; - if (!com.ok()) { - return CUBEB_ERROR; - } - - com_ptr device; - hr = get_default_endpoint(device, eRender); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - - com_ptr client; - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp()); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - - WAVEFORMATEX * tmp = nullptr; - hr = client->GetMixFormat(&tmp); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - com_heap_ptr mix_format(tmp); - - *rate = mix_format->nSamplesPerSec; - - LOG("Preferred sample rate for output: %u", *rate); - - return CUBEB_OK; -} - -int -wasapi_get_preferred_channel_layout(cubeb * context, cubeb_channel_layout * layout) -{ - HRESULT hr; - auto_com com; - if (!com.ok()) { - return CUBEB_ERROR; - } - - com_ptr device; - hr = get_default_endpoint(device, eRender); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - - com_ptr client; - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, client.receive_vpp()); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - - WAVEFORMATEX * tmp = nullptr; - hr = client->GetMixFormat(&tmp); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - com_heap_ptr mix_format(tmp); - - *layout = mask_to_channel_layout(mix_format.get()); - - LOG("Preferred channel layout: %s", CUBEB_CHANNEL_LAYOUT_MAPS[*layout].name); - - return CUBEB_OK; -} - -void wasapi_stream_destroy(cubeb_stream * stm); - -static void -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); - format_pcm->Samples.wValidBitsPerSample = format->wBitsPerSample; - } -} - -/* 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) -{ - 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*/ - if (mix_format->wFormatTag != WAVE_FORMAT_EXTENSIBLE) { - return; - } - - WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast(mix_format.get()); - - /* 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. - If the layout is not supported, we will get a closest settings below. */ - format_pcm->dwChannelMask = channel_layout_to_mask(stream_params->layout); - mix_format->nChannels = stream_params->channels; - waveformatex_update_derived_properties(mix_format.get()); - - /* Check if wasapi will accept our channel layout request. */ - WAVEFORMATEX * closest; - HRESULT hr = audio_client->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, - mix_format.get(), - &closest); - if (hr == S_FALSE) { - /* Channel layout not supported, but WASAPI gives us a suggestion. Use it, - and handle the eventual upmix/downmix ourselves. Ignore the subformat of - the suggestion, since it seems to always be IEEE_FLOAT. */ - LOG("Using WASAPI suggested format: channels: %d", closest->nChannels); - XASSERT(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE); - WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast(closest); - format_pcm->dwChannelMask = closest_pcm->dwChannelMask; - mix_format->nChannels = closest->nChannels; - waveformatex_update_derived_properties(mix_format.get()); - } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) { - /* Not supported, no suggestion. This should not happen, but it does in the - field with some sound cards. We restore the mix format, and let the rest - of the code figure out the right conversion path. */ - XASSERT(mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE); - *reinterpret_cast(mix_format.get()) = hw_mix_format; - } else if (hr == S_OK) { - LOG("Requested format accepted by WASAPI."); - } else { - LOG("IsFormatSupported unhandled error: %lx", hr); - } -} - -#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; - HRESULT hr; - - stm->stream_reset_lock.assert_current_thread_owns(); - bool try_again = false; - // This loops until we find a device that works, or we've exhausted all - // possibilities. - do { - if (devid) { - hr = get_endpoint(device, devid); - if (FAILED(hr)) { - LOG("Could not get %s endpoint, error: %lx\n", DIRECTION_NAME, hr); - return CUBEB_ERROR; - } - } else { - hr = get_default_endpoint(device, direction); - if (FAILED(hr)) { - LOG("Could not get default %s endpoint, error: %lx\n", DIRECTION_NAME, hr); - return CUBEB_ERROR; - } - } - - /* Get a client. We will get all other interfaces we need from - * this pointer. */ - hr = device->Activate(__uuidof(IAudioClient), - CLSCTX_INPROC_SERVER, - NULL, audio_client.receive_vpp()); - if (FAILED(hr)) { - LOG("Could not activate the device to get an audio" - " 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) { - LOG("Trying again with the default %s audio device.", DIRECTION_NAME); - devid = nullptr; - device = nullptr; - try_again = true; - } else { - return CUBEB_ERROR; - } - } else { - try_again = false; - } - } while (try_again); - - /* We have to distinguish between the format the mixer uses, - * and the format the stream we want to play uses. */ - WAVEFORMATEX * tmp = nullptr; - 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); - return CUBEB_ERROR; - } - com_heap_ptr mix_format(tmp); - - mix_format->wBitsPerSample = stm->bytes_per_sample * 8; - if (mix_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast(mix_format.get()); - format_pcm->SubFormat = stm->waveformatextensible_sub_format; - } - waveformatex_update_derived_properties(mix_format.get()); - /* Set channel layout only when there're more than two channels. Otherwise, - * use the default setting retrieved from the stream format of the audio - * engine's internal processing by GetMixFormat. */ - if (mix_format->nChannels > 2) { - handle_channel_layout(stm, direction, mix_format, stream_params); - } - - mix_params->format = stream_params->format; - mix_params->rate = mix_format->nSamplesPerSec; - mix_params->channels = mix_format->nChannels; - mix_params->layout = mask_to_channel_layout(mix_format.get()); - if (mix_params->layout == CUBEB_LAYOUT_UNDEFINED) { - LOG("Output using undefined layout!\n"); - } else if (mix_format->nChannels != CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels) { - // The CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].channels may be - // different from the mix_params->channels. 6 channel ouput with stereo - // layout is acceptable in Windows. If this happens, it should not downmix - // audio according to layout. - LOG("Channel count is different from the layout standard!\n"); - } - LOG("Setup requested=[f=%d r=%u c=%u l=%s] mix=[f=%d r=%u c=%u l=%s]", - stream_params->format, stream_params->rate, stream_params->channels, - CUBEB_CHANNEL_LAYOUT_MAPS[stream_params->layout].name, - mix_params->format, mix_params->rate, mix_params->channels, - CUBEB_CHANNEL_LAYOUT_MAPS[mix_params->layout].name); - - hr = audio_client->Initialize(AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK | - AUDCLNT_STREAMFLAGS_NOPERSIST, - frames_to_hns(stm, stm->latency), - 0, - mix_format.get(), - NULL); - if (FAILED(hr)) { - LOG("Unable to initialize audio client for %s: %lx.", DIRECTION_NAME, hr); - return CUBEB_ERROR; - } - - 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); - return CUBEB_ERROR; - } - // Input is up/down mixed when depacketized in get_input_buffer. - if (has_output(stm) && cubeb_should_mix(stream_params, mix_params)) { - stm->mix_buffer.resize(frames_to_bytes_before_mix(stm, *buffer_frame_count)); - } - - hr = audio_client->SetEventHandle(event); - if (FAILED(hr)) { - LOG("Could set the event handle for the %s client %lx.", - DIRECTION_NAME, hr); - return CUBEB_ERROR; - } - - hr = audio_client->GetService(riid, render_or_capture_client.receive_vpp()); - if (FAILED(hr)) { - LOG("Could not get the %s client %lx.", DIRECTION_NAME, hr); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -#undef DIRECTION_NAME - -int setup_wasapi_stream(cubeb_stream * stm) -{ - HRESULT hr; - int rv; - - stm->stream_reset_lock.assert_current_thread_owns(); - - auto_com com; - if (!com.ok()) { - LOG("Failure to initialize COM."); - return CUBEB_ERROR; - } - - 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.get()); - rv = setup_wasapi_stream_one_side(stm, - &stm->input_stream_params, - stm->input_device.get(), - eCapture, - __uuidof(IAudioCaptureClient), - stm->input_client, - &stm->input_buffer_frame_count, - stm->input_available_event, - stm->capture_client, - &stm->input_mix_params); - - // 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 - const int silent_buffer_count = 4; -#endif - stm->linear_input_buffer->push_silence(stm->input_buffer_frame_count * - stm->input_stream_params.channels * - silent_buffer_count); - - if (rv != CUBEB_OK) { - LOG("Failure to open the input side."); - return rv; - } - } - - if (has_output(stm)) { - LOG("(%p) Setup render: device=%p", stm, stm->output_device.get()); - rv = setup_wasapi_stream_one_side(stm, - &stm->output_stream_params, - stm->output_device.get(), - eRender, - __uuidof(IAudioRenderClient), - stm->output_client, - &stm->output_buffer_frame_count, - stm->refill_event, - stm->render_client, - &stm->output_mix_params); - if (rv != CUBEB_OK) { - LOG("Failure to open the output side."); - return rv; - } - - 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; - } - - XASSERT(stm->frames_written == 0); - hr = stm->output_client->GetService(__uuidof(IAudioClock), - stm->audio_clock.receive_vpp()); - if (FAILED(hr)) { - LOG("Could not get the IAudioClock: %lx", hr); - return CUBEB_ERROR; - } - - /* 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; - } - } - - /* If we have both input and output, we resample to - * the highest sample rate available. */ - int32_t target_sample_rate; - if (has_input(stm) && has_output(stm)) { - XASSERT(stm->input_stream_params.rate == stm->output_stream_params.rate); - target_sample_rate = stm->input_stream_params.rate; - } else if (has_input(stm)) { - target_sample_rate = stm->input_stream_params.rate; - } else { - XASSERT(has_output(stm)); - target_sample_rate = stm->output_stream_params.rate; - } - - LOG("Target sample rate: %d", target_sample_rate); - - /* If we are playing/capturing a mono stream, we only resample one channel, - and copy it over, so we are always resampling the number - of channels of the stream, not the number of channels - that WASAPI wants. */ - cubeb_stream_params input_params = stm->input_mix_params; - input_params.channels = stm->input_stream_params.channels; - 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, - CUBEB_RESAMPLER_QUALITY_DESKTOP)); - if (!stm->resampler) { - LOG("Could not get a resampler"); - return CUBEB_ERROR; - } - - XASSERT(has_input(stm) || has_output(stm)); - - if (has_input(stm) && has_output(stm)) { - stm->refill_callback = refill_callback_duplex; - } else if (has_input(stm)) { - stm->refill_callback = refill_callback_input; - } else if (has_output(stm)) { - stm->refill_callback = refill_callback_output; - } - - return CUBEB_OK; -} - -int -wasapi_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) -{ - HRESULT hr; - int rv; - auto_com com; - if (!com.ok()) { - return CUBEB_ERROR; - } - - XASSERT(context && stream && (input_stream_params || output_stream_params)); - - if (output_stream_params && input_stream_params && - output_stream_params->format != input_stream_params->format) { - return CUBEB_ERROR_INVALID_FORMAT; - } - - std::unique_ptr stm(new cubeb_stream(), wasapi_stream_destroy); - - stm->context = context; - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - if (input_stream_params) { - stm->input_stream_params = *input_stream_params; - stm->input_device = utf8_to_wstr(reinterpret_cast(input_device)); - // Make sure the layout matches the channel count. - XASSERT(stm->input_stream_params.layout == CUBEB_LAYOUT_UNDEFINED || - stm->input_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->input_stream_params.layout].channels); - } - if (output_stream_params) { - stm->output_stream_params = *output_stream_params; - stm->output_device = utf8_to_wstr(reinterpret_cast(output_device)); - // Make sure the layout matches the channel count. - XASSERT(stm->output_stream_params.layout == CUBEB_LAYOUT_UNDEFINED || - stm->output_stream_params.channels == CUBEB_CHANNEL_LAYOUT_MAPS[stm->output_stream_params.layout].channels); - } - - 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->mixer.reset(cubeb_mixer_create(output_stream_params ? output_stream_params->format : - input_stream_params->format, - CUBEB_MIXER_DIRECTION_DOWNMIX | CUBEB_MIXER_DIRECTION_UPMIX)); - - stm->latency = latency_frames; - - stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL); - if (!stm->reconfigure_event) { - LOG("Can't create the reconfigure event, error: %lx", GetLastError()); - return CUBEB_ERROR; - } - - /* Unconditionally create the two events so that the wait logic is simpler. */ - stm->refill_event = CreateEvent(NULL, 0, 0, NULL); - if (!stm->refill_event) { - LOG("Can't create the refill event, error: %lx", GetLastError()); - return CUBEB_ERROR; - } - - stm->input_available_event = CreateEvent(NULL, 0, 0, NULL); - if (!stm->input_available_event) { - LOG("Can't create the input available event , error: %lx", GetLastError()); - return CUBEB_ERROR; - } - - { - /* Locking here is not strictly necessary, because we don't have a - notification client that can reset the stream yet, but it lets us - assert that the lock is held in the function. */ - auto_lock lock(stm->stream_reset_lock); - rv = setup_wasapi_stream(stm.get()); - } - if (rv != CUBEB_OK) { - return rv; - } - - hr = register_notification_client(stm.get()); - if (FAILED(hr)) { - /* this is not fatal, we can still play audio, but we won't be able - to keep using the default audio endpoint if it changes. */ - LOG("failed to register notification client, %lx", hr); - } - - *stream = stm.release(); - - return CUBEB_OK; -} - -void close_wasapi_stream(cubeb_stream * stm) -{ - XASSERT(stm); - - stm->stream_reset_lock.assert_current_thread_owns(); - - stm->output_client = nullptr; - stm->render_client = nullptr; - - stm->input_client = nullptr; - stm->capture_client = nullptr; - - stm->audio_stream_volume = nullptr; - - 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->frames_written = 0; - - stm->resampler.reset(); - - stm->mix_buffer.clear(); -} - -void wasapi_stream_destroy(cubeb_stream * stm) -{ - XASSERT(stm); - - // Only free stm->emergency_bailout if we could join the thread. - // If we could not join the thread, stm->emergency_bailout is true - // and is still alive until the thread wakes up and exits cleanly. - if (stop_and_join_render_thread(stm)) { - delete stm->emergency_bailout.load(); - stm->emergency_bailout = nullptr; - } - - unregister_notification_client(stm); - - CloseHandle(stm->reconfigure_event); - CloseHandle(stm->refill_event); - CloseHandle(stm->input_available_event); - - // The variables intialized in wasapi_stream_init, - // must be destroyed in wasapi_stream_destroy. - stm->mixer.reset(); - stm->linear_input_buffer.reset(); - - { - auto_lock lock(stm->stream_reset_lock); - close_wasapi_stream(stm); - } - - delete stm; -} - -enum StreamDirection { - OUTPUT, - INPUT -}; - -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(); - if (hr == AUDCLNT_E_DEVICE_INVALIDATED) { - LOG("audioclient invalidated for %s device, reconfiguring", - dir == OUTPUT ? "output" : "input"); - - BOOL ok = ResetEvent(stm->reconfigure_event); - if (!ok) { - LOG("resetting reconfig event failed for %s stream: %lx", - dir == OUTPUT ? "output" : "input", GetLastError()); - } - - close_wasapi_stream(stm); - int r = setup_wasapi_stream(stm); - if (r != CUBEB_OK) { - LOG("reconfigure failed"); - return r; - } - - 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); - return CUBEB_ERROR; - } - } else if (FAILED(hr)) { - LOG("could not start the %s stream: %lx.", - dir == OUTPUT ? "output" : "input", hr); - return CUBEB_ERROR; - } - - return CUBEB_OK; -} - -int wasapi_stream_start(cubeb_stream * stm) -{ - auto_lock lock(stm->stream_reset_lock); - - XASSERT(stm && !stm->thread && !stm->shutdown_event); - XASSERT(stm->output_client || stm->input_client); - - stm->emergency_bailout = new std::atomic(false); - - if (stm->output_client) { - int rv = stream_start_one_side(stm, OUTPUT); - if (rv != CUBEB_OK) { - return rv; - } - } - - if (stm->input_client) { - int rv = stream_start_one_side(stm, INPUT); - if (rv != CUBEB_OK) { - return rv; - } - } - - stm->shutdown_event = CreateEvent(NULL, 0, 0, NULL); - if (!stm->shutdown_event) { - LOG("Can't create the shutdown event, error: %lx", GetLastError()); - return CUBEB_ERROR; - } - - cubeb_async_log_reset_threads(); - 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; - } - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); - - return CUBEB_OK; -} - -int wasapi_stream_stop(cubeb_stream * stm) -{ - XASSERT(stm); - HRESULT hr; - - { - auto_lock lock(stm->stream_reset_lock); - - if (stm->output_client) { - hr = stm->output_client->Stop(); - if (FAILED(hr)) { - LOG("could not stop AudioClient (output)"); - return CUBEB_ERROR; - } - } - - if (stm->input_client) { - hr = stm->input_client->Stop(); - if (FAILED(hr)) { - LOG("could not stop AudioClient (input)"); - return CUBEB_ERROR; - } - } - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); - } - - if (stop_and_join_render_thread(stm)) { - // This is null if we've given the pointer to the other thread - if (stm->emergency_bailout.load()) { - delete stm->emergency_bailout.load(); - stm->emergency_bailout = nullptr; - } - } else { - // If we could not join the thread, put the stream in error. - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - return CUBEB_ERROR; - } - - 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) -{ - XASSERT(stm && position); - auto_lock lock(stm->stream_reset_lock); - - if (!has_output(stm)) { - return CUBEB_ERROR; - } - - /* 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); - - /* 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))); - - *position = max_pos; - if (stream_delay <= *position) { - *position -= stream_delay; - } - - if (*position < stm->prev_position) { - *position = stm->prev_position; - } - stm->prev_position = *position; - - return CUBEB_OK; -} - -int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency) -{ - XASSERT(stm && latency); - - if (!has_output(stm)) { - return CUBEB_ERROR; - } - - auto_lock lock(stm->stream_reset_lock); - - /* The GetStreamLatency method only works if the - AudioClient has been initialized. */ - if (!stm->output_client) { - return CUBEB_ERROR; - } - - REFERENCE_TIME latency_hns; - HRESULT hr = stm->output_client->GetStreamLatency(&latency_hns); - if (FAILED(hr)) { - return CUBEB_ERROR; - } - *latency = hns_to_frames(stm, latency_hns); - - return CUBEB_OK; -} - -int wasapi_stream_set_volume(cubeb_stream * stm, float volume) -{ - auto_lock lock(stm->stream_reset_lock); - - if (!has_output(stm)) { - return CUBEB_ERROR; - } - - if (stream_set_volume(stm, volume) != CUBEB_OK) { - return CUBEB_ERROR; - } - - stm->volume = volume; - - return CUBEB_OK; -} - -static char const * -wstr_to_utf8(LPCWSTR str) -{ - int size = ::WideCharToMultiByte(CP_UTF8, 0, str, -1, nullptr, 0, NULL, NULL); - if (size <= 0) { - return nullptr; - } - - char * ret = static_cast(malloc(size)); - ::WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL); - return ret; -} - -static std::unique_ptr -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]); - ::MultiByteToWideChar(CP_UTF8, 0, str, -1, ret.get(), size); - return std::move(ret); -} - -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())) && - SUCCEEDED(devtopo->GetConnector(0, connector.receive()))) { - wchar_t * tmp = nullptr; - if (SUCCEEDED(connector->GetDeviceIdConnectedTo(&tmp))) { - com_heap_ptr filterid(tmp); - if (FAILED(enumerator->GetDevice(filterid.get(), ret.receive()))) - ret = NULL; - } - } - - return ret; -} - -static BOOL -wasapi_is_default_device(EDataFlow flow, ERole role, LPCWSTR device_id, - IMMDeviceEnumerator * enumerator) -{ - BOOL ret = FALSE; - com_ptr dev; - HRESULT hr; - - hr = enumerator->GetDefaultAudioEndpoint(flow, role, dev.receive()); - if (SUCCEEDED(hr)) { - wchar_t * tmp = nullptr; - if (SUCCEEDED(dev->GetId(&tmp))) { - com_heap_ptr defdevid(tmp); - ret = (wcscmp(defdevid.get(), device_id) == 0); - } - } - - return ret; -} - -int -wasapi_create_device(cubeb * ctx, cubeb_device_info& ret, IMMDeviceEnumerator * enumerator, IMMDevice * dev) -{ - com_ptr endpoint; - com_ptr devnode; - com_ptr client; - EDataFlow flow; - DWORD state = DEVICE_STATE_NOTPRESENT; - com_ptr propstore; - REFERENCE_TIME def_period, min_period; - HRESULT hr; - - struct prop_variant : public PROPVARIANT { - prop_variant() { PropVariantInit(this); } - ~prop_variant() { PropVariantClear(this); } - prop_variant(prop_variant const &) = delete; - prop_variant & operator=(prop_variant const &) = delete; - }; - - hr = dev->QueryInterface(IID_PPV_ARGS(endpoint.receive())); - if (FAILED(hr)) return CUBEB_ERROR; - - hr = endpoint->GetDataFlow(&flow); - if (FAILED(hr)) return CUBEB_ERROR; - - wchar_t * tmp = nullptr; - hr = dev->GetId(&tmp); - if (FAILED(hr)) return CUBEB_ERROR; - com_heap_ptr device_id(tmp); - - char const * device_id_intern = intern_device_id(ctx, device_id.get()); - if (!device_id_intern) { - return CUBEB_ERROR; - } - - hr = dev->OpenPropertyStore(STGM_READ, propstore.receive()); - if (FAILED(hr)) return CUBEB_ERROR; - - hr = dev->GetState(&state); - if (FAILED(hr)) return CUBEB_ERROR; - - ret.device_id = device_id_intern; - ret.devid = reinterpret_cast(ret.device_id); - prop_variant namevar; - hr = propstore->GetValue(PKEY_Device_FriendlyName, &namevar); - if (SUCCEEDED(hr)) - ret.friendly_name = wstr_to_utf8(namevar.pwszVal); - - devnode = wasapi_get_device_node(enumerator, dev); - if (devnode) { - com_ptr ps; - hr = devnode->OpenPropertyStore(STGM_READ, ps.receive()); - if (FAILED(hr)) return CUBEB_ERROR; - - prop_variant instancevar; - hr = ps->GetValue(PKEY_Device_InstanceId, &instancevar); - if (SUCCEEDED(hr)) { - ret.group_id = wstr_to_utf8(instancevar.pwszVal); - } - } - - 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); - 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); - - if (flow == eRender) ret.type = CUBEB_DEVICE_TYPE_OUTPUT; - else if (flow == eCapture) ret.type = CUBEB_DEVICE_TYPE_INPUT; - 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; - }; - - 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); - - 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); - - if (fmtvar.blob.cbSize >= sizeof(WAVEFORMATEX) + wfx->cbSize || - wfx->wFormatTag == WAVE_FORMAT_PCM) { - ret.max_rate = ret.min_rate = ret.default_rate = wfx->nSamplesPerSec; - ret.max_channels = wfx->nChannels; - } - } - } - - if (SUCCEEDED(dev->Activate(__uuidof(IAudioClient), CLSCTX_INPROC_SERVER, NULL, client.receive_vpp())) && - SUCCEEDED(client->GetDevicePeriod(&def_period, &min_period))) { - ret.latency_lo = hns_to_frames(ret.default_rate, min_period); - ret.latency_hi = hns_to_frames(ret.default_rate, def_period); - } else { - ret.latency_lo = 0; - ret.latency_hi = 0; - } - - return CUBEB_OK; -} - -static int -wasapi_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * out) -{ - auto_com com; - com_ptr enumerator; - com_ptr collection; - HRESULT hr; - UINT cc, i; - EDataFlow flow; - - if (!com.ok()) - return CUBEB_ERROR; - - 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; - - hr = enumerator->EnumAudioEndpoints(flow, DEVICE_STATEMASK_ALL, collection.receive()); - if (FAILED(hr)) { - LOG("Could not enumerate audio endpoints: %lx", hr); - return CUBEB_ERROR; - } - - hr = collection->GetCount(&cc); - if (FAILED(hr)) { - LOG("IMMDeviceCollection::GetCount() failed: %lx", hr); - return CUBEB_ERROR; - } - cubeb_device_info * devices = new cubeb_device_info[cc]; - if (!devices) - return CUBEB_ERROR; - - PodZero(devices, cc); - out->count = 0; - for (i = 0; i < cc; i++) { - com_ptr dev; - hr = collection->Item(i, dev.receive()); - if (FAILED(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) { - out->count += 1; - } - } - - out->device = devices; - return CUBEB_OK; -} - -static int -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]; - delete [] dev.friendly_name; - delete [] dev.group_id; - } - - delete [] collection->device; - return CUBEB_OK; -} - -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, - /*.get_preferred_channel_layout =*/ wasapi_get_preferred_channel_layout, - /*.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_set_volume =*/ wasapi_stream_set_volume, - /*.stream_set_panning =*/ NULL, - /*.stream_get_current_device =*/ NULL, - /*.stream_device_destroy =*/ NULL, - /*.stream_register_device_changed_callback =*/ NULL, - /*.register_device_collection_changed =*/ NULL -}; -} // namespace anonymous diff --git a/Externals/cubeb/src/cubeb_winmm.c b/Externals/cubeb/src/cubeb_winmm.c deleted file mode 100644 index ce7371a433..0000000000 --- a/Externals/cubeb/src/cubeb_winmm.c +++ /dev/null @@ -1,1062 +0,0 @@ -/* - * Copyright © 2011 Mozilla Foundation - * - * This program is made available under an ISC-style license. See the - * accompanying file LICENSE for details. - */ -#define __MSVCRT_VERSION__ 0x0700 -#undef WINVER -#define WINVER 0x0501 -#undef WIN32_LEAN_AND_MEAN - -#include -#include -#include -#include -#include -#include -#include -#include -#include "cubeb/cubeb.h" -#include "cubeb-internal.h" - -/* 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.*/ -#ifndef WAVE_FORMAT_48M08 -#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 */ -#endif -#ifndef WAVE_FORMAT_48S08 -#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 */ -#endif -#ifndef WAVE_FORMAT_96M08 -#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 */ -#endif -#ifndef WAVE_FORMAT_96S08 -#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 */ -#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 -#endif - -#ifndef DRVM_MAPPER -#define DRVM_MAPPER (0x2000) -#endif -#ifndef DRVM_MAPPER_PREFERRED_GET -#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) -#endif -#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET -#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 } }; - -struct cubeb_stream_item { - SLIST_ENTRY head; - cubeb_stream * stream; -}; - -static struct cubeb_ops const winmm_ops; - -struct cubeb { - struct cubeb_ops const * ops; - HANDLE event; - HANDLE thread; - int shutdown; - PSLIST_HEADER work; - CRITICAL_SECTION lock; - unsigned int active_streams; - unsigned int minimum_latency_ms; -}; - -struct cubeb_stream { - cubeb * context; - cubeb_stream_params params; - cubeb_data_callback data_callback; - cubeb_state_callback state_callback; - void * user_ptr; - WAVEHDR buffers[NBUFS]; - size_t buffer_size; - int next_buffer; - int free_buffers; - int shutdown; - int draining; - HANDLE event; - HWAVEOUT waveout; - CRITICAL_SECTION lock; - uint64_t written; - float soft_volume; -}; - -static size_t -bytes_per_frame(cubeb_stream_params params) -{ - size_t bytes; - - switch (params.format) { - case CUBEB_SAMPLE_S16LE: - bytes = sizeof(signed short); - break; - case CUBEB_SAMPLE_FLOAT32LE: - bytes = sizeof(float); - break; - default: - XASSERT(0); - } - - return bytes * params.channels; -} - -static WAVEHDR * -winmm_get_next_buffer(cubeb_stream * stm) -{ - WAVEHDR * hdr = NULL; - - XASSERT(stm->free_buffers > 0 && stm->free_buffers <= NBUFS); - hdr = &stm->buffers[stm->next_buffer]; - XASSERT(hdr->dwFlags & WHDR_PREPARED || - (hdr->dwFlags & WHDR_DONE && !(hdr->dwFlags & WHDR_INQUEUE))); - stm->next_buffer = (stm->next_buffer + 1) % NBUFS; - stm->free_buffers -= 1; - - return hdr; -} - -static void -winmm_refill_stream(cubeb_stream * stm) -{ - WAVEHDR * hdr; - long got; - long wanted; - MMRESULT r; - - EnterCriticalSection(&stm->lock); - stm->free_buffers += 1; - XASSERT(stm->free_buffers > 0 && stm->free_buffers <= NBUFS); - - if (stm->draining) { - LeaveCriticalSection(&stm->lock); - if (stm->free_buffers == NBUFS) { - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); - } - SetEvent(stm->event); - return; - } - - if (stm->shutdown) { - LeaveCriticalSection(&stm->lock); - SetEvent(stm->event); - return; - } - - hdr = winmm_get_next_buffer(stm); - - 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. */ - LeaveCriticalSection(&stm->lock); - got = stm->data_callback(stm, stm->user_ptr, NULL, hdr->lpData, wanted); - EnterCriticalSection(&stm->lock); - if (got < 0) { - LeaveCriticalSection(&stm->lock); - /* XXX handle this case */ - XASSERT(0); - return; - } else if (got < wanted) { - stm->draining = 1; - } - stm->written += got; - - XASSERT(hdr->dwFlags & WHDR_PREPARED); - - hdr->dwBufferLength = got * bytes_per_frame(stm->params); - XASSERT(hdr->dwBufferLength <= stm->buffer_size); - - if (stm->soft_volume != -1.0) { - if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) { - 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; - uint32_t i; - for (i = 0; i < got * stm->params.channels; i++) { - b[i] = (short) (b[i] * stm->soft_volume); - } - } - } - - r = waveOutWrite(stm->waveout, hdr, sizeof(*hdr)); - if (r != MMSYSERR_NOERROR) { - LeaveCriticalSection(&stm->lock); - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR); - return; - } - - LeaveCriticalSection(&stm->lock); -} - -static unsigned __stdcall -winmm_buffer_thread(void * user_ptr) -{ - cubeb * ctx = (cubeb *) user_ptr; - XASSERT(ctx); - - for (;;) { - DWORD r; - PSLIST_ENTRY item; - - r = WaitForSingleObject(ctx->event, INFINITE); - XASSERT(r == WAIT_OBJECT_0); - - /* Process work items in batches so that a single stream can't - starve the others by continuously adding new work to the top of - the work item stack. */ - item = InterlockedFlushSList(ctx->work); - while (item != NULL) { - PSLIST_ENTRY tmp = item; - winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream); - item = item->Next; - _aligned_free(tmp); - } - - if (ctx->shutdown) { - break; - } - } - - return 0; -} - -static void CALLBACK -winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2) -{ - 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); - XASSERT(item); - item->stream = stm; - InterlockedPushEntrySList(stm->context->work, &item->head); - - SetEvent(stm->context->event); -} - -static unsigned int -calculate_minimum_latency(void) -{ - OSVERSIONINFOEX osvi; - DWORDLONG mask; - - /* Running under Terminal Services results in underruns with low latency. */ - if (GetSystemMetrics(SM_REMOTESESSION) == TRUE) { - return 500; - } - - /* 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; - osvi.dwMinorVersion = 0; - - mask = 0; - 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) { - return 200; - } - - return 100; -} - -static void winmm_destroy(cubeb * ctx); - -/*static*/ int -winmm_init(cubeb ** context, char const * context_name) -{ - cubeb * ctx; - - XASSERT(context); - *context = NULL; - - /* Don't initialize a context if there are no devices available. */ - if (waveOutGetNumDevs() == 0) { - return CUBEB_ERROR; - } - - ctx = calloc(1, sizeof(*ctx)); - XASSERT(ctx); - - ctx->ops = &winmm_ops; - - ctx->work = _aligned_malloc(sizeof(*ctx->work), MEMORY_ALLOCATION_ALIGNMENT); - XASSERT(ctx->work); - InitializeSListHead(ctx->work); - - ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!ctx->event) { - winmm_destroy(ctx); - return CUBEB_ERROR; - } - - 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; - } - - SetThreadPriority(ctx->thread, THREAD_PRIORITY_TIME_CRITICAL); - - InitializeCriticalSection(&ctx->lock); - ctx->active_streams = 0; - - ctx->minimum_latency_ms = calculate_minimum_latency(); - - *context = ctx; - - return CUBEB_OK; -} - -static char const * -winmm_get_backend_id(cubeb * ctx) -{ - return "winmm"; -} - -static void -winmm_destroy(cubeb * ctx) -{ - DWORD r; - - XASSERT(ctx->active_streams == 0); - XASSERT(!InterlockedPopEntrySList(ctx->work)); - - DeleteCriticalSection(&ctx->lock); - - if (ctx->thread) { - ctx->shutdown = 1; - SetEvent(ctx->event); - r = WaitForSingleObject(ctx->thread, INFINITE); - XASSERT(r == WAIT_OBJECT_0); - CloseHandle(ctx->thread); - } - - if (ctx->event) { - CloseHandle(ctx->event); - } - - _aligned_free(ctx->work); - - free(ctx); -} - -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, - 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) -{ - MMRESULT r; - WAVEFORMATEXTENSIBLE wfx; - cubeb_stream * stm; - int i; - size_t bufsz; - - XASSERT(context); - XASSERT(stream); - - if (input_stream_params) { - /* Capture support not yet implemented. */ - return CUBEB_ERROR_NOT_SUPPORTED; - } - - if (input_device || output_device) { - /* Device selection not yet implemented. */ - return CUBEB_ERROR_DEVICE_UNAVAILABLE; - } - - *stream = NULL; - - memset(&wfx, 0, sizeof(wfx)); - if (output_stream_params->channels > 2) { - wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format); - } else { - wfx.Format.wFormatTag = WAVE_FORMAT_PCM; - if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE) { - wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - } - wfx.Format.cbSize = 0; - } - wfx.Format.nChannels = output_stream_params->channels; - wfx.Format.nSamplesPerSec = output_stream_params->rate; - - /* XXX fix channel mappings */ - wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; - - switch (output_stream_params->format) { - case CUBEB_SAMPLE_S16LE: - wfx.Format.wBitsPerSample = 16; - wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - break; - case CUBEB_SAMPLE_FLOAT32LE: - wfx.Format.wBitsPerSample = 32; - wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; - break; - default: - 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.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample; - - EnterCriticalSection(&context->lock); - /* CUBEB_STREAM_MAX is a horrible hack to avoid a situation where, when - many streams are active at once, a subset of them will not consume (via - playback) or release (via waveOutReset) their buffers. */ - if (context->active_streams >= CUBEB_STREAM_MAX) { - LeaveCriticalSection(&context->lock); - return CUBEB_ERROR; - } - context->active_streams += 1; - LeaveCriticalSection(&context->lock); - - stm = calloc(1, sizeof(*stm)); - XASSERT(stm); - - stm->context = context; - - stm->params = *output_stream_params; - - stm->data_callback = data_callback; - stm->state_callback = state_callback; - stm->user_ptr = user_ptr; - stm->written = 0; - - uint32_t latency_ms = latency_frames * 1000 / output_stream_params->rate; - - if (latency_ms < context->minimum_latency_ms) { - latency_ms = context->minimum_latency_ms; - } - - 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)); - } - XASSERT(bufsz % bytes_per_frame(stm->params) == 0); - - stm->buffer_size = bufsz; - - InitializeCriticalSection(&stm->lock); - - stm->event = CreateEvent(NULL, FALSE, FALSE, NULL); - if (!stm->event) { - winmm_stream_destroy(stm); - return CUBEB_ERROR; - } - - stm->soft_volume = -1.0; - - /* 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, - CALLBACK_FUNCTION); - if (r != MMSYSERR_NOERROR) { - winmm_stream_destroy(stm); - return CUBEB_ERROR; - } - - r = waveOutPause(stm->waveout); - if (r != MMSYSERR_NOERROR) { - winmm_stream_destroy(stm); - return CUBEB_ERROR; - } - - for (i = 0; i < NBUFS; ++i) { - WAVEHDR * hdr = &stm->buffers[i]; - - hdr->lpData = calloc(1, bufsz); - XASSERT(hdr->lpData); - hdr->dwBufferLength = bufsz; - hdr->dwFlags = 0; - - r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr)); - if (r != MMSYSERR_NOERROR) { - winmm_stream_destroy(stm); - return CUBEB_ERROR; - } - - winmm_refill_stream(stm); - } - - *stream = stm; - - return CUBEB_OK; -} - -static void -winmm_stream_destroy(cubeb_stream * stm) -{ - int i; - - if (stm->waveout) { - MMTIME time; - MMRESULT r; - int device_valid; - int enqueued; - - EnterCriticalSection(&stm->lock); - stm->shutdown = 1; - - waveOutReset(stm->waveout); - - /* Don't need this value, we just want the result to detect invalid - handle/no device errors than waveOutReset doesn't seem to report. */ - time.wType = TIME_SAMPLES; - r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); - device_valid = !(r == MMSYSERR_INVALHANDLE || r == MMSYSERR_NODRIVER); - - enqueued = NBUFS - stm->free_buffers; - LeaveCriticalSection(&stm->lock); - - /* Wait for all blocks to complete. */ - while (device_valid && enqueued > 0) { - DWORD rv = WaitForSingleObject(stm->event, INFINITE); - XASSERT(rv == WAIT_OBJECT_0); - - EnterCriticalSection(&stm->lock); - enqueued = NBUFS - stm->free_buffers; - LeaveCriticalSection(&stm->lock); - } - - EnterCriticalSection(&stm->lock); - - for (i = 0; i < NBUFS; ++i) { - if (stm->buffers[i].dwFlags & WHDR_PREPARED) { - waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i])); - } - } - - waveOutClose(stm->waveout); - - LeaveCriticalSection(&stm->lock); - } - - if (stm->event) { - CloseHandle(stm->event); - } - - DeleteCriticalSection(&stm->lock); - - for (i = 0; i < NBUFS; ++i) { - free(stm->buffers[i].lpData); - } - - EnterCriticalSection(&stm->context->lock); - XASSERT(stm->context->active_streams >= 1); - stm->context->active_streams -= 1; - LeaveCriticalSection(&stm->context->lock); - - free(stm); -} - -static int -winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels) -{ - XASSERT(ctx && max_channels); - - /* We don't support more than two channels in this backend. */ - *max_channels = 2; - - return CUBEB_OK; -} - -static int -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; - - return CUBEB_OK; -} - -static int -winmm_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) -{ - WAVEOUTCAPS woc; - MMRESULT r; - - r = waveOutGetDevCaps(WAVE_MAPPER, &woc, sizeof(WAVEOUTCAPS)); - if (r != MMSYSERR_NOERROR) { - return CUBEB_ERROR; - } - - /* Check if we support 48kHz, but not 44.1kHz. */ - if (!(woc.dwFormats & WAVE_FORMAT_4S16) && - woc.dwFormats & WAVE_FORMAT_48S16) { - *rate = 48000; - return CUBEB_OK; - } - /* Prefer 44.1kHz between 44.1kHz and 48kHz. */ - *rate = 44100; - - return CUBEB_OK; -} - -static int -winmm_stream_start(cubeb_stream * stm) -{ - MMRESULT r; - - EnterCriticalSection(&stm->lock); - r = waveOutRestart(stm->waveout); - LeaveCriticalSection(&stm->lock); - - if (r != MMSYSERR_NOERROR) { - return CUBEB_ERROR; - } - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); - - return CUBEB_OK; -} - -static int -winmm_stream_stop(cubeb_stream * stm) -{ - MMRESULT r; - - EnterCriticalSection(&stm->lock); - r = waveOutPause(stm->waveout); - LeaveCriticalSection(&stm->lock); - - if (r != MMSYSERR_NOERROR) { - return CUBEB_ERROR; - } - - stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); - - return CUBEB_OK; -} - -static int -winmm_stream_get_position(cubeb_stream * stm, uint64_t * position) -{ - MMRESULT r; - MMTIME time; - - EnterCriticalSection(&stm->lock); - time.wType = TIME_SAMPLES; - r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); - LeaveCriticalSection(&stm->lock); - - if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) { - return CUBEB_ERROR; - } - - *position = time.u.sample; - - return CUBEB_OK; -} - -static int -winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency) -{ - MMRESULT r; - MMTIME time; - uint64_t written; - - EnterCriticalSection(&stm->lock); - time.wType = TIME_SAMPLES; - r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); - written = stm->written; - LeaveCriticalSection(&stm->lock); - - if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) { - return CUBEB_ERROR; - } - - XASSERT(written - time.u.sample <= UINT32_MAX); - *latency = (uint32_t) (written - time.u.sample); - - return CUBEB_OK; -} - -static int -winmm_stream_set_volume(cubeb_stream * stm, float volume) -{ - EnterCriticalSection(&stm->lock); - stm->soft_volume = volume; - LeaveCriticalSection(&stm->lock); - 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) -static void -winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats) -{ - if (formats & MM_11025HZ_MASK) { - info->min_rate = 11025; - info->default_rate = 11025; - info->max_rate = 11025; - } - if (formats & MM_22050HZ_MASK) { - 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; - info->max_rate = 44100; - info->default_rate = 44100; - } - if (formats & MM_48000HZ_MASK) { - if (info->min_rate == 0) info->min_rate = 48000; - info->max_rate = 48000; - info->default_rate = 48000; - } - if (formats & MM_96000HZ_MASK) { - if (info->min_rate == 0) { - info->min_rate = 96000; - info->default_rate = 96000; - } - info->max_rate = 96000; - } -} - -#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) -{ - WAVEFORMATEXTENSIBLE wfx; - - if (formats & MM_S16_MASK) - *deffmt = *supfmt = CUBEB_DEVICE_FMT_S16LE; - else - *deffmt = *supfmt = 0; - - ZeroMemory(&wfx, sizeof(WAVEFORMATEXTENSIBLE)); - wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - 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.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) - *supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE); - - return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR; -} - -static char * -guid_to_cstr(LPGUID guid) -{ - char * ret = malloc(40); - if (!ret) { - 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]); - return ret; -} - -static cubeb_device_pref -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 && - 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 && - devid == compref) - ret |= CUBEB_DEVICE_PREF_VOICE; - - return ret; -} - -static char * -device_id_idx(UINT devid) -{ - char * ret = malloc(16); - if (!ret) { - return NULL; - } - _snprintf_s(ret, 16, _TRUNCATE, "%u", devid); - return ret; -} - -static void -winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, UINT devid) -{ - XASSERT(ret); - 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); - ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid); - - ret->type = CUBEB_DEVICE_TYPE_OUTPUT; - ret->state = CUBEB_DEVICE_STATE_ENABLED; - ret->preferred = winmm_query_preferred_out_device(devid); - - ret->max_channels = caps->wChannels; - winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); - - /* Hardcoded latency estimates... */ - ret->latency_lo = 100 * ret->default_rate / 1000; - ret->latency_hi = 200 * ret->default_rate / 1000; -} - -static void -winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, UINT devid) -{ - XASSERT(ret); - ret->devid = (cubeb_devid) devid; - ret->device_id = device_id_idx(devid); - ret->friendly_name = _strdup(caps->szPname); - ret->group_id = NULL; - ret->vendor_name = NULL; - - ret->type = CUBEB_DEVICE_TYPE_OUTPUT; - ret->state = CUBEB_DEVICE_STATE_ENABLED; - ret->preferred = winmm_query_preferred_out_device(devid); - - ret->max_channels = caps->wChannels; - winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); - - /* Hardcoded latency estimates... */ - ret->latency_lo = 100 * ret->default_rate / 1000; - ret->latency_hi = 200 * ret->default_rate / 1000; -} - -static cubeb_device_pref -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 && - 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 && - devid == compref) - ret |= CUBEB_DEVICE_PREF_VOICE; - - return ret; -} - -static void -winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, UINT devid) -{ - XASSERT(ret); - 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); - ret->vendor_name = guid_to_cstr(&caps->ManufacturerGuid); - - ret->type = CUBEB_DEVICE_TYPE_INPUT; - ret->state = CUBEB_DEVICE_STATE_ENABLED; - ret->preferred = winmm_query_preferred_in_device(devid); - - ret->max_channels = caps->wChannels; - winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); - - /* Hardcoded latency estimates... */ - ret->latency_lo = 100 * ret->default_rate / 1000; - ret->latency_hi = 200 * ret->default_rate / 1000; -} - -static void -winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UINT devid) -{ - XASSERT(ret); - ret->devid = (cubeb_devid) devid; - ret->device_id = device_id_idx(devid); - ret->friendly_name = _strdup(caps->szPname); - ret->group_id = NULL; - ret->vendor_name = NULL; - - ret->type = CUBEB_DEVICE_TYPE_INPUT; - ret->state = CUBEB_DEVICE_STATE_ENABLED; - ret->preferred = winmm_query_preferred_in_device(devid); - - ret->max_channels = caps->wChannels; - winmm_calculate_device_rate(ret, caps->dwFormats); - winmm_query_supported_formats(devid, caps->dwFormats, - &ret->format, &ret->default_format); - - /* Hardcoded latency estimates... */ - ret->latency_lo = 100 * ret->default_rate / 1000; - ret->latency_hi = 200 * ret->default_rate / 1000; -} - -static int -winmm_enumerate_devices(cubeb * context, cubeb_device_type type, - cubeb_device_collection * collection) -{ - UINT i, incount, outcount, total; - cubeb_device_info * devices; - cubeb_device_info * dev; - - outcount = waveOutGetNumDevs(); - incount = waveInGetNumDevs(); - total = outcount + incount; - - devices = calloc(total, sizeof(cubeb_device_info)); - collection->count = 0; - - if (type & CUBEB_DEVICE_TYPE_OUTPUT) { - WAVEOUTCAPSA woc; - WAVEOUTCAPS2A woc2; - - ZeroMemory(&woc, sizeof(woc)); - ZeroMemory(&woc2, sizeof(woc2)); - - for (i = 0; i < outcount; i++) { - dev = &devices[collection->count]; - 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) { - winmm_create_device_from_outcaps(dev, &woc, i); - collection->count += 1; - } - } - } - - if (type & CUBEB_DEVICE_TYPE_INPUT) { - WAVEINCAPSA wic; - WAVEINCAPS2A wic2; - - ZeroMemory(&wic, sizeof(wic)); - ZeroMemory(&wic2, sizeof(wic2)); - - for (i = 0; i < incount; i++) { - dev = &devices[collection->count]; - 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) { - winmm_create_device_from_incaps(dev, &wic, i); - collection->count += 1; - } - } - } - - collection->device = devices; - - return CUBEB_OK; -} - -static int -winmm_device_collection_destroy(cubeb * ctx, - cubeb_device_collection * collection) -{ - uint32_t i; - XASSERT(collection); - - (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(collection->device); - return CUBEB_OK; -} - -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, - /*.get_preferred_channel_layout =*/ NULL, - /*.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_set_volume =*/ winmm_stream_set_volume, - /*.stream_set_panning =*/ 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/speex/arch.h b/Externals/cubeb/src/speex/arch.h deleted file mode 100644 index 73a45a069f..0000000000 --- a/Externals/cubeb/src/speex/arch.h +++ /dev/null @@ -1,235 +0,0 @@ -/* 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/src/speex/fixed_generic.h b/Externals/cubeb/src/speex/fixed_generic.h deleted file mode 100644 index 12d27aac55..0000000000 --- a/Externals/cubeb/src/speex/fixed_generic.h +++ /dev/null @@ -1,110 +0,0 @@ -/* 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/src/speex/resample.c b/Externals/cubeb/src/speex/resample.c deleted file mode 100644 index 10cb06593d..0000000000 --- a/Externals/cubeb/src/speex/resample.c +++ /dev/null @@ -1,1240 +0,0 @@ -/* 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/src/speex/resample_neon.h b/Externals/cubeb/src/speex/resample_neon.h deleted file mode 100644 index 0acbd27b9a..0000000000 --- a/Externals/cubeb/src/speex/resample_neon.h +++ /dev/null @@ -1,201 +0,0 @@ -/* 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/src/speex/resample_sse.h b/Externals/cubeb/src/speex/resample_sse.h deleted file mode 100644 index fed5b8276f..0000000000 --- a/Externals/cubeb/src/speex/resample_sse.h +++ /dev/null @@ -1,128 +0,0 @@ -/* 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/Source/Core/AudioCommon/CubebStream.cpp b/Source/Core/AudioCommon/CubebStream.cpp index b8ccbbdaee..16ec40fcda 100644 --- a/Source/Core/AudioCommon/CubebStream.cpp +++ b/Source/Core/AudioCommon/CubebStream.cpp @@ -39,7 +39,7 @@ bool CubebStream::Init() m_stereo = !Config::ShouldUseDPL2Decoder(); - cubeb_stream_params params; + cubeb_stream_params params{}; params.rate = m_mixer->GetSampleRate(); if (m_stereo) { diff --git a/Source/VSProps/Base.Dolphin.props b/Source/VSProps/Base.Dolphin.props index 0cbbab9617..15e3b0f072 100644 --- a/Source/VSProps/Base.Dolphin.props +++ b/Source/VSProps/Base.Dolphin.props @@ -71,7 +71,7 @@ - avrt.lib;comctl32.lib;iphlpapi.lib;setupapi.lib;shlwapi.lib;winmm.lib;ws2_32.lib;%(AdditionalDependencies) + avrt.lib;comctl32.lib;iphlpapi.lib;ksuser.lib;setupapi.lib;shlwapi.lib;winmm.lib;ws2_32.lib;%(AdditionalDependencies) opengl32.lib;%(AdditionalDependencies) $(ExternalsDir)FFmpeg-bin\$(Platform)\lib;%(AdditionalLibraryDirectories)