mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-06-27 05:29:32 +00:00
Cubeb library is now a submodule.
This commit is contained in:
parent
bcc0e38e9f
commit
4dcdeb095d
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "thirdparty/cubeb"]
|
||||||
|
path = thirdparty/cubeb
|
||||||
|
url = https://github.com/DingusDevOrg/cubeb.git
|
1
thirdparty/cubeb
vendored
Submodule
1
thirdparty/cubeb
vendored
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 524a9c99b440e4c6e81588d3b5b39456ddc11d0c
|
2
thirdparty/cubeb/.gitignore
vendored
2
thirdparty/cubeb/.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
.vscode/
|
|
||||||
build/
|
|
6
thirdparty/cubeb/.gitmodules
vendored
6
thirdparty/cubeb/.gitmodules
vendored
|
@ -1,6 +0,0 @@
|
||||||
[submodule "googletest"]
|
|
||||||
path = googletest
|
|
||||||
url = https://github.com/google/googletest
|
|
||||||
[submodule "cmake/sanitizers-cmake"]
|
|
||||||
path = cmake/sanitizers-cmake
|
|
||||||
url = https://github.com/arsenm/sanitizers-cmake
|
|
68
thirdparty/cubeb/.travis.yml
vendored
68
thirdparty/cubeb/.travis.yml
vendored
|
@ -1,68 +0,0 @@
|
||||||
language: cpp
|
|
||||||
dist: xenial
|
|
||||||
sudo: required
|
|
||||||
addons:
|
|
||||||
apt:
|
|
||||||
packages:
|
|
||||||
- clang-3.8
|
|
||||||
- g++-5
|
|
||||||
- libpulse-dev
|
|
||||||
- libasound2-dev
|
|
||||||
- pulseaudio
|
|
||||||
- doxygen
|
|
||||||
- mingw-w64
|
|
||||||
- mingw-w64-tools
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- CMAKE_USE_ASAN=OFF
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- env: C_COMPILER=clang-3.8 CXX_COMPILER=clang++-3.8
|
|
||||||
- env: C_COMPILER=clang-3.8 CXX_COMPILER=clang++-3.8 CMAKE_USE_ASAN=ON
|
|
||||||
- env: C_COMPILER=gcc-5 CXX_COMPILER=g++-5
|
|
||||||
- env: SCAN_BUILD=true
|
|
||||||
- env: CROSS=mingw
|
|
||||||
- env: CROSS=android API_LEVEL=android-16
|
|
||||||
- os: osx
|
|
||||||
- os: osx
|
|
||||||
env: SCAN_BUILD=true
|
|
||||||
- os: windows
|
|
||||||
before_install:
|
|
||||||
- if [[ -n $SCAN_BUILD ]]; then
|
|
||||||
if [[ $TRAVIS_OS_NAME = "osx" ]]; then
|
|
||||||
. scan-build-install.sh;
|
|
||||||
else
|
|
||||||
export SCAN_BUILD_PATH=/usr/share/clang/scan-build-3.8/bin/scan-build;
|
|
||||||
fi;
|
|
||||||
export SCAN_BUILD_PATH="$SCAN_BUILD_PATH -o scan_results";
|
|
||||||
fi
|
|
||||||
- if [[ $CROSS = "android" ]]; then
|
|
||||||
NDK=android-ndk-r19c;
|
|
||||||
curl -LO https://dl.google.com/android/repository/$NDK-linux-x86_64.zip;
|
|
||||||
unzip -q $NDK-linux-x86_64.zip;
|
|
||||||
rm $NDK-linux-x86_64.zip;
|
|
||||||
export ANDROID_NDK=$(pwd)/$NDK;
|
|
||||||
fi
|
|
||||||
before_script:
|
|
||||||
- if [[ -n $C_COMPILER ]]; then
|
|
||||||
export CC=$C_COMPILER;
|
|
||||||
fi
|
|
||||||
- if [[ -n $CXX_COMPILER ]]; then
|
|
||||||
export CXX=$CXX_COMPILER;
|
|
||||||
fi
|
|
||||||
- if [[ -n $CROSS ]]; then
|
|
||||||
export TOOLCHAIN=-DCMAKE_TOOLCHAIN_FILE=../cmake/toolchain-cross-$CROSS.cmake;
|
|
||||||
fi
|
|
||||||
- if [[ $CROSS = "android" ]]; then
|
|
||||||
export TOOLCHAIN="-DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK/build/cmake/android.toolchain.cmake -DANDROID_NATIVE_API_LEVEL=$API_LEVEL";
|
|
||||||
fi
|
|
||||||
- mkdir build && cd build
|
|
||||||
- $SCAN_BUILD_PATH cmake -DCMAKE_BUILD_TYPE=Debug -DSANITIZE_ADDRESS=$CMAKE_USE_ASAN $TOOLCHAIN ..;
|
|
||||||
script:
|
|
||||||
- $SCAN_BUILD_PATH cmake --build .
|
|
||||||
- if [[ $TRAVIS_OS_NAME = "linux" && -z $CROSS ]]; then
|
|
||||||
ctest -V;
|
|
||||||
fi
|
|
||||||
- if [[ -n $SCAN_BUILD ]]; then
|
|
||||||
rmdir scan_results || ( echo "scan-build detected bugs!" && exit 1 );
|
|
||||||
fi
|
|
16
thirdparty/cubeb/AUTHORS
vendored
16
thirdparty/cubeb/AUTHORS
vendored
|
@ -1,16 +0,0 @@
|
||||||
Matthew Gregan <kinetik@flim.org>
|
|
||||||
Alexandre Ratchov <alex@caoua.org>
|
|
||||||
Michael Wu <mwu@mozilla.com>
|
|
||||||
Paul Adenot <paul@paul.cx>
|
|
||||||
David Richards <drichards@mozilla.com>
|
|
||||||
Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
|
|
||||||
KO Myung-Hun <komh@chollian.net>
|
|
||||||
Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
|
|
||||||
Alex Chronopoulos <achronop@gmail.com>
|
|
||||||
Jan Beich <jbeich@FreeBSD.org>
|
|
||||||
Vito Caputo <vito.caputo@coreos.com>
|
|
||||||
Landry Breuil <landry@openbsd.org>
|
|
||||||
Jacek Caban <jacek@codeweavers.com>
|
|
||||||
Paul Hancock <Paul.Hancock.17041993@live.com>
|
|
||||||
Ted Mielczarek <ted@mielczarek.org>
|
|
||||||
Chun-Min Chang <chun.m.chang@gmail.com>
|
|
334
thirdparty/cubeb/CMakeLists.txt
vendored
334
thirdparty/cubeb/CMakeLists.txt
vendored
|
@ -1,334 +0,0 @@
|
||||||
# TODO
|
|
||||||
# - backend selection via command line, rather than simply detecting headers.
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
|
|
||||||
project(cubeb
|
|
||||||
VERSION 0.0.0)
|
|
||||||
|
|
||||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
|
||||||
option(BUILD_TESTS "Build tests" ON)
|
|
||||||
option(BUILD_RUST_LIBS "Build rust backends" OFF)
|
|
||||||
option(BUILD_TOOLS "Build tools" ON)
|
|
||||||
|
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
|
||||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
|
|
||||||
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(POLICY CMP0063)
|
|
||||||
cmake_policy(SET CMP0063 NEW)
|
|
||||||
endif()
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
|
|
||||||
if(NOT COMMAND add_sanitizers)
|
|
||||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake")
|
|
||||||
find_package(Sanitizers)
|
|
||||||
if(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()
|
|
||||||
|
|
||||||
if(BUILD_TESTS)
|
|
||||||
if(NOT TARGET gtest_main)
|
|
||||||
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/googletest/CMakeLists.txt")
|
|
||||||
message(FATAL_ERROR "Could not find googletest: run\n\tgit submodule update --init --recursive\nin base git checkout")
|
|
||||||
endif()
|
|
||||||
add_definitions(-DGTEST_HAS_TR1_TUPLE=0)
|
|
||||||
set(gtest_force_shared_crt ON CACHE BOOL "")
|
|
||||||
add_subdirectory(googletest)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (BUILD_RUST_LIBS)
|
|
||||||
if(EXISTS "${PROJECT_SOURCE_DIR}/src/cubeb-pulse-rs")
|
|
||||||
set(USE_PULSE_RUST 1)
|
|
||||||
endif()
|
|
||||||
if(EXISTS "${PROJECT_SOURCE_DIR}/src/cubeb-coreaudio-rs")
|
|
||||||
set(USE_AUDIOUNIT_RUST 1)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# 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")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(cubeb
|
|
||||||
src/cubeb.c
|
|
||||||
src/cubeb_mixer.cpp
|
|
||||||
src/cubeb_resampler.cpp
|
|
||||||
src/cubeb_log.cpp
|
|
||||||
src/cubeb_strings.c
|
|
||||||
src/cubeb_utils.cpp
|
|
||||||
$<TARGET_OBJECTS:speex>)
|
|
||||||
target_include_directories(cubeb
|
|
||||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>
|
|
||||||
)
|
|
||||||
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)
|
|
||||||
|
|
||||||
include(GenerateExportHeader)
|
|
||||||
generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h)
|
|
||||||
target_include_directories(cubeb
|
|
||||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/exports>
|
|
||||||
)
|
|
||||||
|
|
||||||
if(UNIX)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
else()
|
|
||||||
set(CMAKE_INSTALL_LIBDIR "lib")
|
|
||||||
set(CMAKE_INSTALL_BINDIR "bin")
|
|
||||||
set(CMAKE_INSTALL_DATADIR "share")
|
|
||||||
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATADIR}/doc")
|
|
||||||
set(CMAKE_INSTALL_INCLUDEDIR "include")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
|
|
||||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
|
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
|
||||||
write_basic_package_version_file(
|
|
||||||
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
|
||||||
COMPATIBILITY SameMajorVersion
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_package_config_file(
|
|
||||||
"Config.cmake.in"
|
|
||||||
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
|
|
||||||
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS cubeb
|
|
||||||
EXPORT "${PROJECT_NAME}Targets"
|
|
||||||
DESTINATION ${CMAKE_INSTALL_PREFIX}
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
|
||||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
|
||||||
)
|
|
||||||
install(
|
|
||||||
FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
|
||||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
|
||||||
)
|
|
||||||
install(
|
|
||||||
EXPORT "${PROJECT_NAME}Targets"
|
|
||||||
NAMESPACE "${PROJECT_NAME}::"
|
|
||||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(speex OBJECT
|
|
||||||
src/speex/resample.c)
|
|
||||||
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
|
|
||||||
target_compile_definitions(speex PRIVATE OUTSIDE_SPEEX)
|
|
||||||
target_compile_definitions(speex PRIVATE FLOATING_POINT)
|
|
||||||
target_compile_definitions(speex PRIVATE EXPORT=)
|
|
||||||
target_compile_definitions(speex PRIVATE RANDOM_PREFIX=speex)
|
|
||||||
|
|
||||||
include(CheckIncludeFiles)
|
|
||||||
|
|
||||||
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
|
|
||||||
if(USE_AUDIOUNIT)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_audiounit.cpp
|
|
||||||
src/cubeb_osx_run_loop.cpp)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT)
|
|
||||||
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(pulse/pulseaudio.h USE_PULSE)
|
|
||||||
if(USE_PULSE)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_pulse.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_PULSE)
|
|
||||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(alsa/asoundlib.h USE_ALSA)
|
|
||||||
if(USE_ALSA)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_alsa.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_ALSA)
|
|
||||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(jack/jack.h USE_JACK)
|
|
||||||
if(USE_JACK)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_jack.cpp)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_JACK)
|
|
||||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(audioclient.h USE_WASAPI)
|
|
||||||
if(USE_WASAPI)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_wasapi.cpp)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
|
|
||||||
target_link_libraries(cubeb PRIVATE avrt ole32)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files("windows.h;mmsystem.h" USE_WINMM)
|
|
||||||
if(USE_WINMM)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_winmm.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_WINMM)
|
|
||||||
target_link_libraries(cubeb PRIVATE winmm)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(SLES/OpenSLES.h USE_OPENSL)
|
|
||||||
if(USE_OPENSL)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_opensl.c
|
|
||||||
src/cubeb-jni.cpp)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_OPENSL)
|
|
||||||
target_link_libraries(cubeb PRIVATE OpenSLES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(android/log.h USE_AUDIOTRACK)
|
|
||||||
if(USE_AUDIOTRACK)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
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)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_sndio.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
|
|
||||||
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(sys/audioio.h USE_SUN)
|
|
||||||
if(USE_SUN)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_sun.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_SUN)
|
|
||||||
target_link_libraries(cubeb PRIVATE pthread)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_include_files(kai.h USE_KAI)
|
|
||||||
if(USE_KAI)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_kai.c)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_KAI)
|
|
||||||
target_link_libraries(cubeb PRIVATE kai)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(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 "${CMAKE_SOURCE_DIR}/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 "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs/target/debug/libcubeb_pulse.a"
|
|
||||||
optimized "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs/target/release/libcubeb_pulse.a" pulse)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(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 "${CMAKE_SOURCE_DIR}/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 "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs/target/debug/libcubeb_coreaudio.a"
|
|
||||||
optimized "${CMAKE_SOURCE_DIR}/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()
|
|
||||||
|
|
||||||
if(BUILD_TESTS)
|
|
||||||
enable_testing()
|
|
||||||
|
|
||||||
macro(cubeb_add_test NAME)
|
|
||||||
add_executable(test_${NAME} test/test_${NAME}.cpp)
|
|
||||||
target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include)
|
|
||||||
target_include_directories(test_${NAME} PRIVATE src)
|
|
||||||
target_link_libraries(test_${NAME} PRIVATE cubeb gtest_main)
|
|
||||||
add_test(${NAME} test_${NAME})
|
|
||||||
add_sanitizers(test_${NAME})
|
|
||||||
install(TARGETS test_${NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
|
|
||||||
endmacro(cubeb_add_test)
|
|
||||||
|
|
||||||
cubeb_add_test(sanity)
|
|
||||||
cubeb_add_test(tone)
|
|
||||||
cubeb_add_test(audio)
|
|
||||||
cubeb_add_test(record)
|
|
||||||
cubeb_add_test(devices)
|
|
||||||
cubeb_add_test(callback_ret)
|
|
||||||
|
|
||||||
add_executable(test_resampler test/test_resampler.cpp src/cubeb_resampler.cpp $<TARGET_OBJECTS:speex>)
|
|
||||||
target_include_directories(test_resampler PRIVATE ${gtest_SOURCE_DIR}/include)
|
|
||||||
target_include_directories(test_resampler PRIVATE src)
|
|
||||||
target_compile_definitions(test_resampler PRIVATE OUTSIDE_SPEEX)
|
|
||||||
target_compile_definitions(test_resampler PRIVATE FLOATING_POINT)
|
|
||||||
target_compile_definitions(test_resampler PRIVATE EXPORT=)
|
|
||||||
target_compile_definitions(test_resampler PRIVATE RANDOM_PREFIX=speex)
|
|
||||||
target_link_libraries(test_resampler PRIVATE cubeb gtest_main)
|
|
||||||
add_test(resampler test_resampler)
|
|
||||||
add_sanitizers(test_resampler)
|
|
||||||
install(TARGETS test_resampler DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
|
|
||||||
|
|
||||||
cubeb_add_test(duplex)
|
|
||||||
|
|
||||||
if (USE_WASAPI)
|
|
||||||
cubeb_add_test(overload_callback)
|
|
||||||
cubeb_add_test(loopback)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
cubeb_add_test(latency test_latency)
|
|
||||||
cubeb_add_test(ring_array)
|
|
||||||
|
|
||||||
cubeb_add_test(utils)
|
|
||||||
cubeb_add_test(ring_buffer)
|
|
||||||
cubeb_add_test(device_changed_callback)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(BUILD_TOOLS)
|
|
||||||
add_executable(cubeb-test tools/cubeb-test.cpp)
|
|
||||||
target_include_directories(cubeb-test PRIVATE src)
|
|
||||||
target_link_libraries(cubeb-test PRIVATE cubeb)
|
|
||||||
add_sanitizers(cubeb-test)
|
|
||||||
install(TARGETS cubeb-test DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
|
|
||||||
endif()
|
|
4
thirdparty/cubeb/Config.cmake.in
vendored
4
thirdparty/cubeb/Config.cmake.in
vendored
|
@ -1,4 +0,0 @@
|
||||||
@PACKAGE_INIT@
|
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake")
|
|
||||||
check_required_components(cubeb)
|
|
46
thirdparty/cubeb/INSTALL.md
vendored
46
thirdparty/cubeb/INSTALL.md
vendored
|
@ -1,46 +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, Microsoft Visual Studio
|
|
||||||
2017, or MinGW-w64 with Win32 threads (by passing `cmake -G` to generate the
|
|
||||||
appropriate build configuration).
|
|
||||||
|
|
||||||
## Microsoft Visual Studio 2015 or 2017 Command Line
|
|
||||||
|
|
||||||
CMake can be used from the command line by following the build steps at the top
|
|
||||||
of this file. CMake will select a default generator based on the environment,
|
|
||||||
or one can be specified with the `-G` argument.
|
|
||||||
|
|
||||||
## Microsoft Visual Studio 2017 IDE
|
|
||||||
|
|
||||||
Visual Studio 2017 adds in built support for CMake. CMake can be used from
|
|
||||||
within the IDE via the following steps:
|
|
||||||
|
|
||||||
- Navigate to `File -> Open -> Cmake...`
|
|
||||||
- Open `CMakeLists.txt` file in the root of the project.
|
|
||||||
|
|
||||||
Note, to generate the build in the cubeb dir CMake settings need to be updated
|
|
||||||
via: `CMake -> Change CMake Settings -> CMakeLists.txt`. The default
|
|
||||||
configuration used by Visual Studio will place the build in a different location
|
|
||||||
than the steps detailed at the top of this file.
|
|
||||||
|
|
||||||
## MinGW-w64
|
|
||||||
|
|
||||||
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 at the top of this file, but at step 3 run:
|
|
||||||
`cmake -G "MinGW Makefiles" ..`
|
|
||||||
- Continue the build steps at the top of this file.
|
|
13
thirdparty/cubeb/LICENSE
vendored
13
thirdparty/cubeb/LICENSE
vendored
|
@ -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.
|
|
8
thirdparty/cubeb/README.md
vendored
8
thirdparty/cubeb/README.md
vendored
|
@ -1,8 +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.
|
|
||||||
|
|
||||||
See [Backend Support](https://github.com/kinetiknz/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
|
|
||||||
|
|
||||||
Licensed under an ISC-style license. See LICENSE for details.
|
|
9
thirdparty/cubeb/appveyor.yml
vendored
9
thirdparty/cubeb/appveyor.yml
vendored
|
@ -1,9 +0,0 @@
|
||||||
version: 1.0.{build}
|
|
||||||
install:
|
|
||||||
- cmd: git submodule update --init --recursive
|
|
||||||
build_script:
|
|
||||||
- cmd: |-
|
|
||||||
cd c:\projects\cubeb
|
|
||||||
cmake .
|
|
||||||
cmake --build . --config Debug
|
|
||||||
|
|
1
thirdparty/cubeb/cmake/sanitizers-cmake
vendored
1
thirdparty/cubeb/cmake/sanitizers-cmake
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit aab6948fa863bc1cbe5d0850bc46b9ef02ed4c1a
|
|
|
@ -1,14 +0,0 @@
|
||||||
SET(CMAKE_SYSTEM_NAME Windows)
|
|
||||||
|
|
||||||
set(COMPILER_PREFIX "i686-w64-mingw32")
|
|
||||||
|
|
||||||
find_program(CMAKE_RC_COMPILER NAMES ${COMPILER_PREFIX}-windres)
|
|
||||||
find_program(CMAKE_C_COMPILER NAMES ${COMPILER_PREFIX}-gcc-posix)
|
|
||||||
find_program(CMAKE_CXX_COMPILER NAMES ${COMPILER_PREFIX}-g++-posix)
|
|
||||||
|
|
||||||
SET(CMAKE_FIND_ROOT_PATH /usr/${COMPILER_PREFIX})
|
|
||||||
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
|
||||||
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
|
||||||
|
|
36
thirdparty/cubeb/cubeb.supp
vendored
36
thirdparty/cubeb/cubeb.supp
vendored
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
12
thirdparty/cubeb/docs/Doxyfile.in
vendored
12
thirdparty/cubeb/docs/Doxyfile.in
vendored
|
@ -1,12 +0,0 @@
|
||||||
PROJECT_NAME = @PACKAGE@
|
|
||||||
PROJECT_NUMBER = @VERSION@
|
|
||||||
OUTPUT_DIRECTORY = .
|
|
||||||
JAVADOC_AUTOBRIEF = YES
|
|
||||||
OPTIMIZE_OUTPUT_FOR_C = YES
|
|
||||||
CASE_SENSE_NAMES = NO
|
|
||||||
SORT_MEMBER_DOCS = NO
|
|
||||||
QUIET = YES
|
|
||||||
WARN_NO_PARAMDOC = YES
|
|
||||||
INPUT = @CMAKE_CURRENT_SOURCE_DIR@/include/cubeb
|
|
||||||
GENERATE_HTML = YES
|
|
||||||
GENERATE_LATEX = NO
|
|
1
thirdparty/cubeb/googletest
vendored
1
thirdparty/cubeb/googletest
vendored
|
@ -1 +0,0 @@
|
||||||
Subproject commit 800f5422ac9d9e0ad59cd860a2ef3a679588acb4
|
|
668
thirdparty/cubeb/include/cubeb/cubeb.h
vendored
668
thirdparty/cubeb/include/cubeb/cubeb.h
vendored
|
@ -1,668 +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 <stdint.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include "cubeb_export.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/** @mainpage
|
|
||||||
|
|
||||||
@section intro Introduction
|
|
||||||
|
|
||||||
This is the documentation for the <tt>libcubeb</tt> C API.
|
|
||||||
<tt>libcubeb</tt> 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", NULL);
|
|
||||||
int rv;
|
|
||||||
uint32_t rate;
|
|
||||||
uint32_t latency_frames;
|
|
||||||
uint64_t ts;
|
|
||||||
|
|
||||||
rv = cubeb_get_preferred_sample_rate(app_ctx, &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;
|
|
||||||
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not get minimum latency");
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
|
||||||
input_params.rate = rate;
|
|
||||||
input_params.channels = 1;
|
|
||||||
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
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,
|
|
||||||
const void * input_buffer, void * output_buffer, long nframes)
|
|
||||||
{
|
|
||||||
const float * in = input_buffer;
|
|
||||||
float * out = output_buffer;
|
|
||||||
|
|
||||||
for (int i = 0; i < nframes; ++i) {
|
|
||||||
for (int c = 0; c < 2; ++c) {
|
|
||||||
out[2 * 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 <tt>libcubeb</tt> 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;
|
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
CHANNEL_UNKNOWN = 0,
|
|
||||||
CHANNEL_FRONT_LEFT = 1 << 0,
|
|
||||||
CHANNEL_FRONT_RIGHT = 1 << 1,
|
|
||||||
CHANNEL_FRONT_CENTER = 1 << 2,
|
|
||||||
CHANNEL_LOW_FREQUENCY = 1 << 3,
|
|
||||||
CHANNEL_BACK_LEFT = 1 << 4,
|
|
||||||
CHANNEL_BACK_RIGHT = 1 << 5,
|
|
||||||
CHANNEL_FRONT_LEFT_OF_CENTER = 1 << 6,
|
|
||||||
CHANNEL_FRONT_RIGHT_OF_CENTER = 1 << 7,
|
|
||||||
CHANNEL_BACK_CENTER = 1 << 8,
|
|
||||||
CHANNEL_SIDE_LEFT = 1 << 9,
|
|
||||||
CHANNEL_SIDE_RIGHT = 1 << 10,
|
|
||||||
CHANNEL_TOP_CENTER = 1 << 11,
|
|
||||||
CHANNEL_TOP_FRONT_LEFT = 1 << 12,
|
|
||||||
CHANNEL_TOP_FRONT_CENTER = 1 << 13,
|
|
||||||
CHANNEL_TOP_FRONT_RIGHT = 1 << 14,
|
|
||||||
CHANNEL_TOP_BACK_LEFT = 1 << 15,
|
|
||||||
CHANNEL_TOP_BACK_CENTER = 1 << 16,
|
|
||||||
CHANNEL_TOP_BACK_RIGHT = 1 << 17
|
|
||||||
} cubeb_channel;
|
|
||||||
|
|
||||||
typedef uint32_t cubeb_channel_layout;
|
|
||||||
// Some common layout definitions.
|
|
||||||
enum {
|
|
||||||
CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
|
|
||||||
CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
|
|
||||||
CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
|
|
||||||
CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_3F =
|
|
||||||
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
|
|
||||||
CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_2F1 =
|
|
||||||
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
|
|
||||||
CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
|
|
||||||
CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
|
|
||||||
CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
|
|
||||||
CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
|
|
||||||
CHANNEL_SIDE_RIGHT,
|
|
||||||
CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
|
|
||||||
CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
|
|
||||||
CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
|
|
||||||
CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
|
|
||||||
CHANNEL_SIDE_RIGHT,
|
|
||||||
CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
|
|
||||||
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
|
|
||||||
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
|
|
||||||
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Miscellaneous stream preferences. */
|
|
||||||
typedef enum {
|
|
||||||
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
|
|
||||||
CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
|
|
||||||
specified on the input params and an
|
|
||||||
output device to loopback from should
|
|
||||||
be passed in place of an input device. */
|
|
||||||
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
|
|
||||||
default device on OS
|
|
||||||
changes. */
|
|
||||||
CUBEB_STREAM_PREF_VOICE = 0x04 /**< This stream is going to transport voice data.
|
|
||||||
Depending on the backend and platform, this can
|
|
||||||
change the audio input or output devices
|
|
||||||
selected, as well as the quality of the stream,
|
|
||||||
for example to accomodate bluetooth SCO modes on
|
|
||||||
bluetooth devices. */
|
|
||||||
} cubeb_stream_prefs;
|
|
||||||
|
|
||||||
/** Stream format initialization parameters. */
|
|
||||||
typedef struct {
|
|
||||||
cubeb_sample_format format; /**< Requested sample format. One of
|
|
||||||
#cubeb_sample_format. */
|
|
||||||
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
|
|
||||||
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
|
|
||||||
cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
|
|
||||||
cubeb_stream_prefs prefs; /**< Requested preferences. */
|
|
||||||
} cubeb_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 If the stream has output, this is the number of frames written to
|
|
||||||
the output buffer. In this case, if this number is less than
|
|
||||||
nframes then the stream will start to drain. If the stream is
|
|
||||||
input only, then returning nframes indicates data has been read.
|
|
||||||
In this case, a value less than nframes will result in the stream
|
|
||||||
being stopped.
|
|
||||||
@retval CUBEB_ERROR on error, in which case the data callback will stop
|
|
||||||
and the stream will enter a shutdown state. */
|
|
||||||
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
|
|
||||||
void * user_ptr,
|
|
||||||
void const * input_buffer,
|
|
||||||
void * output_buffer,
|
|
||||||
long nframes);
|
|
||||||
|
|
||||||
/** 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.
|
|
||||||
|
|
||||||
Note: On Windows platforms, COM must be initialized in MTA mode on
|
|
||||||
any thread that will call the cubeb API.
|
|
||||||
|
|
||||||
@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);
|
|
||||||
|
|
||||||
/** 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);
|
|
||||||
|
|
||||||
/** Get the input latency for this stream, in frames. This is the number of
|
|
||||||
frames between the time the audio input devices records the data, and they
|
|
||||||
are available in the data callback.
|
|
||||||
This returns CUBEB_ERROR when the stream is output-only.
|
|
||||||
@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_input_latency(cubeb_stream * stream, uint32_t * latency);
|
|
||||||
/** Set the volume for a stream.
|
|
||||||
@param stream the stream for which to adjust the volume.
|
|
||||||
@param volume a float between 0.0 (muted) and 1.0 (maximum volume)
|
|
||||||
@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);
|
|
||||||
|
|
||||||
/** 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);
|
|
||||||
|
|
||||||
/** Return the user data pointer registered with the stream with cubeb_stream_init.
|
|
||||||
@param stream the stream for which to retrieve user data pointer.
|
|
||||||
@retval user data pointer */
|
|
||||||
CUBEB_EXPORT void * cubeb_stream_user_ptr(cubeb_stream * stream);
|
|
||||||
|
|
||||||
/** 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. Different callbacks and user pointers
|
|
||||||
can be registered for each devtype. The hybrid devtype
|
|
||||||
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
|
|
||||||
and will register the provided callback and user pointer in both sides.
|
|
||||||
@param callback a function called whenever the system device list changes.
|
|
||||||
Passing NULL allow to unregister a function. You have to unregister
|
|
||||||
first before you register a new callback.
|
|
||||||
@param user_ptr pointer to user specified data which will be present in
|
|
||||||
subsequent callbacks.
|
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
|
||||||
CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
|
|
||||||
cubeb_device_type devtype,
|
|
||||||
cubeb_device_collection_changed_callback callback,
|
|
||||||
void * user_ptr);
|
|
||||||
|
|
||||||
/** 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 */
|
|
15
thirdparty/cubeb/scan-build-install.sh
vendored
15
thirdparty/cubeb/scan-build-install.sh
vendored
|
@ -1,15 +0,0 @@
|
||||||
#!/bin/sh -x
|
|
||||||
|
|
||||||
CLANG_CHECKER_NAME=checker-278
|
|
||||||
|
|
||||||
cd ~
|
|
||||||
|
|
||||||
if [ ! -d ~/$CLANG_CHECKER_NAME ]
|
|
||||||
then
|
|
||||||
curl http://clang-analyzer.llvm.org/downloads/$CLANG_CHECKER_NAME.tar.bz2 -o ~/$CLANG_CHECKER_NAME.tar.bz2
|
|
||||||
tar -xf ~/$CLANG_CHECKER_NAME.tar.bz2
|
|
||||||
fi
|
|
||||||
|
|
||||||
export SCAN_BUILD_PATH=~/$CLANG_CHECKER_NAME/bin/scan-build
|
|
||||||
|
|
||||||
cd -
|
|
|
@ -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 <stdint.h>
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
#ifndef _CUBEB_OUTPUT_LATENCY_H_
|
|
||||||
#define _CUBEB_OUTPUT_LATENCY_H_
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "cubeb_media_library.h"
|
|
||||||
#include "../cubeb-jni.h"
|
|
||||||
|
|
||||||
struct output_latency_function {
|
|
||||||
media_lib * from_lib;
|
|
||||||
cubeb_jni * from_jni;
|
|
||||||
int version;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct output_latency_function output_latency_function;
|
|
||||||
|
|
||||||
const int ANDROID_JELLY_BEAN_MR1_4_2 = 17;
|
|
||||||
|
|
||||||
output_latency_function *
|
|
||||||
cubeb_output_latency_load_method(int version)
|
|
||||||
{
|
|
||||||
output_latency_function * ol = NULL;
|
|
||||||
ol = calloc(1, sizeof(output_latency_function));
|
|
||||||
|
|
||||||
ol->version = version;
|
|
||||||
|
|
||||||
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
|
|
||||||
ol->from_jni = cubeb_jni_init();
|
|
||||||
return ol;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol->from_lib = cubeb_load_media_library();
|
|
||||||
return ol;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
cubeb_output_latency_method_is_loaded(output_latency_function * ol)
|
|
||||||
{
|
|
||||||
assert(ol);
|
|
||||||
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
|
|
||||||
return !!ol->from_jni;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !!ol->from_lib;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cubeb_output_latency_unload_method(output_latency_function * ol)
|
|
||||||
{
|
|
||||||
if (!ol) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_jni) {
|
|
||||||
cubeb_jni_destroy(ol->from_jni);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ol->version <= ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_lib) {
|
|
||||||
cubeb_close_media_library(ol->from_lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(ol);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
cubeb_get_output_latency(output_latency_function * ol)
|
|
||||||
{
|
|
||||||
assert(cubeb_output_latency_method_is_loaded(ol));
|
|
||||||
|
|
||||||
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
|
|
||||||
return cubeb_get_output_latency_from_jni(ol->from_jni);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cubeb_get_output_latency_from_media_library(ol->from_lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _CUBEB_OUTPUT_LATENCY_H_
|
|
|
@ -1,62 +0,0 @@
|
||||||
#ifndef _CUBEB_MEDIA_LIBRARY_H_
|
|
||||||
#define _CUBEB_MEDIA_LIBRARY_H_
|
|
||||||
|
|
||||||
struct media_lib {
|
|
||||||
void * libmedia;
|
|
||||||
int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct media_lib media_lib;
|
|
||||||
|
|
||||||
media_lib *
|
|
||||||
cubeb_load_media_library()
|
|
||||||
{
|
|
||||||
media_lib ml = {0};
|
|
||||||
ml.libmedia = dlopen("libmedia.so", RTLD_LAZY);
|
|
||||||
if (!ml.libmedia) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the latency, in ms, from AudioFlinger. First, try the most recent signature.
|
|
||||||
// status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
|
|
||||||
ml.get_output_latency =
|
|
||||||
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
|
|
||||||
if (!ml.get_output_latency) {
|
|
||||||
// In case of failure, try the signature from legacy version.
|
|
||||||
// status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
|
|
||||||
ml.get_output_latency =
|
|
||||||
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
|
|
||||||
if (!ml.get_output_latency) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
media_lib * rv = NULL;
|
|
||||||
rv = calloc(1, sizeof(media_lib));
|
|
||||||
assert(rv);
|
|
||||||
*rv = ml;
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
cubeb_close_media_library(media_lib * ml)
|
|
||||||
{
|
|
||||||
dlclose(ml->libmedia);
|
|
||||||
ml->libmedia = NULL;
|
|
||||||
ml->get_output_latency = NULL;
|
|
||||||
free(ml);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t
|
|
||||||
cubeb_get_output_latency_from_media_library(media_lib * ml)
|
|
||||||
{
|
|
||||||
uint32_t latency = 0;
|
|
||||||
const int audio_stream_type_music = 3;
|
|
||||||
int32_t r = ml->get_output_latency(&latency, audio_stream_type_music);
|
|
||||||
if (r) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return latency;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _CUBEB_MEDIA_LIBRARY_H_
|
|
102
thirdparty/cubeb/src/android/sles_definitions.h
vendored
102
thirdparty/cubeb/src/android/sles_definitions.h
vendored
|
@ -1,102 +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)
|
|
||||||
/** uses the main microphone unprocessed */
|
|
||||||
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32) 0x00000005)
|
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/* 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)
|
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
/* Android AudioPlayer and AudioRecorder configuration */
|
|
||||||
/*---------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/** Audio Performance mode.
|
|
||||||
* Performance mode tells the framework how to configure the audio path
|
|
||||||
* for a player or recorder according to application performance and
|
|
||||||
* functional requirements.
|
|
||||||
* It affects the output or input latency based on acceptable tradeoffs on
|
|
||||||
* battery drain and use of pre or post processing effects.
|
|
||||||
* Performance mode should be set before realizing the object and should be
|
|
||||||
* read after realizing the object to check if the requested mode could be
|
|
||||||
* granted or not.
|
|
||||||
*/
|
|
||||||
/** Audio Performance mode key */
|
|
||||||
#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
|
|
||||||
|
|
||||||
/** Audio performance values */
|
|
||||||
/* No specific performance requirement. Allows HW and SW pre/post processing. */
|
|
||||||
#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000)
|
|
||||||
/* Priority given to latency. No HW or software pre/post processing.
|
|
||||||
* This is the default if no performance mode is specified. */
|
|
||||||
#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001)
|
|
||||||
/* Priority given to latency while still allowing HW pre and post processing. */
|
|
||||||
#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002)
|
|
||||||
/* Priority given to power saving if latency is not a concern.
|
|
||||||
* Allows HW and SW pre/post processing. */
|
|
||||||
#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003)
|
|
||||||
|
|
||||||
#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */
|
|
80
thirdparty/cubeb/src/cubeb-internal.h
vendored
80
thirdparty/cubeb/src/cubeb-internal.h
vendored
|
@ -1,80 +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 <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#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
|
|
||||||
|
|
||||||
struct cubeb_ops {
|
|
||||||
int (* init)(cubeb ** context, char const * context_name);
|
|
||||||
char const * (* get_backend_id)(cubeb * context);
|
|
||||||
int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels);
|
|
||||||
int (* get_min_latency)(cubeb * context,
|
|
||||||
cubeb_stream_params params,
|
|
||||||
uint32_t * latency_ms);
|
|
||||||
int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
|
|
||||||
int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
|
|
||||||
cubeb_device_collection * collection);
|
|
||||||
int (* device_collection_destroy)(cubeb * context,
|
|
||||||
cubeb_device_collection * collection);
|
|
||||||
void (* destroy)(cubeb * context);
|
|
||||||
int (* stream_init)(cubeb * context,
|
|
||||||
cubeb_stream ** stream,
|
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
|
||||||
cubeb_devid output_device,
|
|
||||||
cubeb_stream_params * output_stream_params,
|
|
||||||
unsigned int latency,
|
|
||||||
cubeb_data_callback data_callback,
|
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr);
|
|
||||||
void (* stream_destroy)(cubeb_stream * stream);
|
|
||||||
int (* stream_start)(cubeb_stream * stream);
|
|
||||||
int (* stream_stop)(cubeb_stream * stream);
|
|
||||||
int (* stream_reset_default_device)(cubeb_stream * stream);
|
|
||||||
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
|
|
||||||
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
|
||||||
int (* stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency);
|
|
||||||
int (* stream_set_volume)(cubeb_stream * stream, float volumes);
|
|
||||||
int (* stream_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 */
|
|
30
thirdparty/cubeb/src/cubeb-jni-instances.h
vendored
30
thirdparty/cubeb/src/cubeb-jni-instances.h
vendored
|
@ -1,30 +0,0 @@
|
||||||
#ifndef _CUBEB_JNI_INSTANCES_H_
|
|
||||||
#define _CUBEB_JNI_INSTANCES_H_
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The methods in this file offer a way to pass in the required
|
|
||||||
* JNI instances in the cubeb library. By default they return NULL.
|
|
||||||
* In this case part of the cubeb API that depends on JNI
|
|
||||||
* will return CUBEB_ERROR_NOT_SUPPORTED. Currently only one
|
|
||||||
* method depends on that:
|
|
||||||
*
|
|
||||||
* cubeb_stream_get_position()
|
|
||||||
*
|
|
||||||
* Users that want to use that cubeb API method must "override"
|
|
||||||
* the methods bellow to return a valid instance of JavaVM
|
|
||||||
* and application's Context object.
|
|
||||||
* */
|
|
||||||
|
|
||||||
JNIEnv *
|
|
||||||
cubeb_get_jni_env_for_thread()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobject
|
|
||||||
cubeb_jni_get_context_instance()
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //_CUBEB_JNI_INSTANCES_H_
|
|
68
thirdparty/cubeb/src/cubeb-jni.cpp
vendored
68
thirdparty/cubeb/src/cubeb-jni.cpp
vendored
|
@ -1,68 +0,0 @@
|
||||||
#include "jni.h"
|
|
||||||
#include <assert.h>
|
|
||||||
#include "cubeb-jni-instances.h"
|
|
||||||
|
|
||||||
#define AUDIO_STREAM_TYPE_MUSIC 3
|
|
||||||
|
|
||||||
struct cubeb_jni {
|
|
||||||
jobject s_audio_manager_obj = nullptr;
|
|
||||||
jclass s_audio_manager_class = nullptr;
|
|
||||||
jmethodID s_get_output_latency_id = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
cubeb_jni *
|
|
||||||
cubeb_jni_init()
|
|
||||||
{
|
|
||||||
jobject ctx_obj = cubeb_jni_get_context_instance();
|
|
||||||
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
|
|
||||||
if (!jni_env || !ctx_obj) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
|
|
||||||
assert(cubeb_jni_ptr);
|
|
||||||
|
|
||||||
// Find the audio manager object and make it global to call it from another method
|
|
||||||
jclass context_class = jni_env->FindClass("android/content/Context");
|
|
||||||
jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
|
|
||||||
jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field);
|
|
||||||
jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
|
||||||
jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
|
|
||||||
cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
|
|
||||||
|
|
||||||
// Make the audio manager class a global reference in order to preserve method id
|
|
||||||
jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager");
|
|
||||||
cubeb_jni_ptr->s_audio_manager_class = reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
|
|
||||||
cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I");
|
|
||||||
|
|
||||||
jni_env->DeleteLocalRef(ctx_obj);
|
|
||||||
jni_env->DeleteLocalRef(context_class);
|
|
||||||
jni_env->DeleteLocalRef(jstr);
|
|
||||||
jni_env->DeleteLocalRef(audio_manager_obj);
|
|
||||||
jni_env->DeleteLocalRef(audio_manager_class);
|
|
||||||
|
|
||||||
return cubeb_jni_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
|
|
||||||
{
|
|
||||||
assert(cubeb_jni_ptr);
|
|
||||||
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
|
|
||||||
return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C"
|
|
||||||
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
|
|
||||||
{
|
|
||||||
assert(cubeb_jni_ptr);
|
|
||||||
|
|
||||||
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
|
|
||||||
assert(jni_env);
|
|
||||||
|
|
||||||
jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_obj);
|
|
||||||
jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_class);
|
|
||||||
|
|
||||||
delete cubeb_jni_ptr;
|
|
||||||
}
|
|
10
thirdparty/cubeb/src/cubeb-jni.h
vendored
10
thirdparty/cubeb/src/cubeb-jni.h
vendored
|
@ -1,10 +0,0 @@
|
||||||
#ifndef _CUBEB_JNI_H_
|
|
||||||
#define _CUBEB_JNI_H_
|
|
||||||
|
|
||||||
typedef struct cubeb_jni cubeb_jni;
|
|
||||||
|
|
||||||
cubeb_jni * cubeb_jni_init();
|
|
||||||
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
|
|
||||||
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
|
|
||||||
|
|
||||||
#endif // _CUBEB_JNI_H_
|
|
43
thirdparty/cubeb/src/cubeb-sles.h
vendored
43
thirdparty/cubeb/src/cubeb-sles.h
vendored
|
@ -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 <SLES/OpenSLES.h>
|
|
||||||
|
|
||||||
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
|
|
1
thirdparty/cubeb/src/cubeb-speex-resampler.h
vendored
1
thirdparty/cubeb/src/cubeb-speex-resampler.h
vendored
|
@ -1 +0,0 @@
|
||||||
#include <speex/speex_resampler.h>
|
|
679
thirdparty/cubeb/src/cubeb.c
vendored
679
thirdparty/cubeb/src/cubeb.c
vendored
|
@ -1,679 +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 <assert.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#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 {
|
|
||||||
/*
|
|
||||||
* Note: All implementations of cubeb_stream must keep the following
|
|
||||||
* layout.
|
|
||||||
*/
|
|
||||||
struct cubeb * context;
|
|
||||||
void * user_ptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
#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_AUDIOUNIT_RUST)
|
|
||||||
int audiounit_rust_init(cubeb ** contet, 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_SUN)
|
|
||||||
int sun_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 > UINT8_MAX) {
|
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (input_stream_params) {
|
|
||||||
if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
|
|
||||||
input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) {
|
|
||||||
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, "audiounit-rust")) {
|
|
||||||
#if defined(USE_AUDIOUNIT_RUST)
|
|
||||||
init_oneshot = audiounit_rust_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, "sun")) {
|
|
||||||
#if defined(USE_SUN)
|
|
||||||
init_oneshot = sun_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_RUST)
|
|
||||||
pulse_rust_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_PULSE)
|
|
||||||
pulse_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_JACK)
|
|
||||||
jack_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_SNDIO)
|
|
||||||
sndio_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_ALSA)
|
|
||||||
alsa_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_AUDIOUNIT)
|
|
||||||
audiounit_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_AUDIOUNIT_RUST)
|
|
||||||
audiounit_rust_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_WASAPI)
|
|
||||||
wasapi_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_WINMM)
|
|
||||||
winmm_init,
|
|
||||||
#endif
|
|
||||||
#if defined(USE_SUN)
|
|
||||||
sun_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 || !data_callback || !state_callback) {
|
|
||||||
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_get_input_latency(cubeb_stream * stream, uint32_t * latency)
|
|
||||||
{
|
|
||||||
if (!stream || !latency) {
|
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stream->context->ops->stream_get_input_latency) {
|
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream->context->ops->stream_get_input_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_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void * cubeb_stream_user_ptr(cubeb_stream * stream)
|
|
||||||
{
|
|
||||||
if (!stream) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream->user_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
1453
thirdparty/cubeb/src/cubeb_alsa.c
vendored
1453
thirdparty/cubeb/src/cubeb_alsa.c
vendored
File diff suppressed because it is too large
Load Diff
97
thirdparty/cubeb/src/cubeb_array_queue.h
vendored
97
thirdparty/cubeb/src/cubeb_array_queue.h
vendored
|
@ -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 <assert.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#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
|
|
26
thirdparty/cubeb/src/cubeb_assert.h
vendored
26
thirdparty/cubeb/src/cubeb_assert.h
vendored
|
@ -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 <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
442
thirdparty/cubeb/src/cubeb_audiotrack.c
vendored
442
thirdparty/cubeb/src/cubeb_audiotrack.c
vendored
|
@ -1,442 +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 <assert.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <time.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
#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 {
|
|
||||||
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
|
||||||
cubeb * context;
|
|
||||||
void * user_ptr;
|
|
||||||
/**/
|
|
||||||
cubeb_stream_params params;
|
|
||||||
cubeb_data_callback data_callback;
|
|
||||||
cubeb_state_callback state_callback;
|
|
||||||
void * instance;
|
|
||||||
/* 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,
|
|
||||||
.enumerate_devices = NULL,
|
|
||||||
.device_collection_destroy = NULL,
|
|
||||||
.destroy = audiotrack_destroy,
|
|
||||||
.stream_init = audiotrack_stream_init,
|
|
||||||
.stream_destroy = audiotrack_stream_destroy,
|
|
||||||
.stream_start = audiotrack_stream_start,
|
|
||||||
.stream_stop = audiotrack_stream_stop,
|
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = audiotrack_stream_get_position,
|
|
||||||
.stream_get_latency = audiotrack_stream_get_latency,
|
|
||||||
.stream_get_input_latency = NULL,
|
|
||||||
.stream_set_volume = audiotrack_stream_set_volume,
|
|
||||||
.stream_get_current_device = NULL,
|
|
||||||
.stream_device_destroy = NULL,
|
|
||||||
.stream_register_device_changed_callback = NULL,
|
|
||||||
.register_device_collection_changed = NULL
|
|
||||||
};
|
|
3630
thirdparty/cubeb/src/cubeb_audiounit.cpp
vendored
3630
thirdparty/cubeb/src/cubeb_audiounit.cpp
vendored
File diff suppressed because it is too large
Load Diff
1076
thirdparty/cubeb/src/cubeb_jack.cpp
vendored
1076
thirdparty/cubeb/src/cubeb_jack.cpp
vendored
File diff suppressed because it is too large
Load Diff
370
thirdparty/cubeb/src/cubeb_kai.c
vendored
370
thirdparty/cubeb/src/cubeb_kai.c
vendored
|
@ -1,370 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2015 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <sys/fmutex.h>
|
|
||||||
|
|
||||||
#include <kai.h>
|
|
||||||
|
|
||||||
#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 {
|
|
||||||
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
|
||||||
cubeb * context;
|
|
||||||
void * user_ptr;
|
|
||||||
/**/
|
|
||||||
cubeb_stream_params params;
|
|
||||||
cubeb_data_callback data_callback;
|
|
||||||
cubeb_state_callback state_callback;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
// Loopback is unsupported
|
|
||||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
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_get_input_latency = */ NULL,
|
|
||||||
/*.stream_set_volume =*/ kai_stream_set_volume,
|
|
||||||
/*.stream_get_current_device =*/ NULL,
|
|
||||||
/*.stream_device_destroy =*/ NULL,
|
|
||||||
/*.stream_register_device_changed_callback=*/ NULL,
|
|
||||||
/*.register_device_collection_changed=*/ NULL
|
|
||||||
};
|
|
144
thirdparty/cubeb/src/cubeb_log.cpp
vendored
144
thirdparty/cubeb/src/cubeb_log.cpp
vendored
|
@ -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 <cstdarg>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <time.h>
|
|
||||||
#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<cubeb_log_message> 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();
|
|
||||||
}
|
|
54
thirdparty/cubeb/src/cubeb_log.h
vendored
54
thirdparty/cubeb/src/cubeb_log.h
vendored
|
@ -1,54 +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)))
|
|
||||||
#if defined(__FILE_NAME__)
|
|
||||||
#define __FILENAME__ __FILE_NAME__
|
|
||||||
#else
|
|
||||||
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define PRINTF_FORMAT(fmt, args)
|
|
||||||
#include <string.h>
|
|
||||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern cubeb_log_level g_cubeb_log_level;
|
|
||||||
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
|
|
||||||
void cubeb_async_log(const char * fmt, ...);
|
|
||||||
void cubeb_async_log_reset_threads();
|
|
||||||
|
|
||||||
#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", __FILENAME__, __LINE__, ##__VA_ARGS__); \
|
|
||||||
} \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
/* Asynchronous verbose logging, to log in real-time callbacks. */
|
|
||||||
#define ALOGV(fmt, ...) \
|
|
||||||
do { \
|
|
||||||
cubeb_async_log(fmt, ##__VA_ARGS__); \
|
|
||||||
} while(0)
|
|
||||||
|
|
||||||
#endif // CUBEB_LOG
|
|
663
thirdparty/cubeb/src/cubeb_mixer.cpp
vendored
663
thirdparty/cubeb/src/cubeb_mixer.cpp
vendored
|
@ -1,663 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2016 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*
|
|
||||||
* Adapted from code based on libswresample's rematrix.c
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define NOMINMAX
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cassert>
|
|
||||||
#include <climits>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
#include "cubeb_mixer.h"
|
|
||||||
#include "cubeb_utils.h"
|
|
||||||
|
|
||||||
#ifndef FF_ARRAY_ELEMS
|
|
||||||
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CHANNELS_MAX 32
|
|
||||||
#define FRONT_LEFT 0
|
|
||||||
#define FRONT_RIGHT 1
|
|
||||||
#define FRONT_CENTER 2
|
|
||||||
#define LOW_FREQUENCY 3
|
|
||||||
#define BACK_LEFT 4
|
|
||||||
#define BACK_RIGHT 5
|
|
||||||
#define FRONT_LEFT_OF_CENTER 6
|
|
||||||
#define FRONT_RIGHT_OF_CENTER 7
|
|
||||||
#define BACK_CENTER 8
|
|
||||||
#define SIDE_LEFT 9
|
|
||||||
#define SIDE_RIGHT 10
|
|
||||||
#define TOP_CENTER 11
|
|
||||||
#define TOP_FRONT_LEFT 12
|
|
||||||
#define TOP_FRONT_CENTER 13
|
|
||||||
#define TOP_FRONT_RIGHT 14
|
|
||||||
#define TOP_BACK_LEFT 15
|
|
||||||
#define TOP_BACK_CENTER 16
|
|
||||||
#define TOP_BACK_RIGHT 17
|
|
||||||
#define NUM_NAMED_CHANNELS 18
|
|
||||||
|
|
||||||
#ifndef M_SQRT1_2
|
|
||||||
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
|
|
||||||
#endif
|
|
||||||
#ifndef M_SQRT2
|
|
||||||
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
|
|
||||||
#endif
|
|
||||||
#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */
|
|
||||||
|
|
||||||
#define C30DB M_SQRT2
|
|
||||||
#define C15DB 1.189207115
|
|
||||||
#define C__0DB 1.0
|
|
||||||
#define C_15DB 0.840896415
|
|
||||||
#define C_30DB M_SQRT1_2
|
|
||||||
#define C_45DB 0.594603558
|
|
||||||
#define C_60DB 0.5
|
|
||||||
|
|
||||||
static cubeb_channel_layout
|
|
||||||
cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
|
|
||||||
{
|
|
||||||
if (l == CUBEB_LAYOUT_UNDEFINED) {
|
|
||||||
switch (c) {
|
|
||||||
case 1: return CUBEB_LAYOUT_MONO;
|
|
||||||
case 2: return CUBEB_LAYOUT_STEREO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
|
|
||||||
{
|
|
||||||
#if __GNUC__ || __clang__
|
|
||||||
return __builtin_popcount (x);
|
|
||||||
#else
|
|
||||||
x -= (x >> 1) & 0x55555555;
|
|
||||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
|
||||||
x = (x + (x >> 4)) & 0x0F0F0F0F;
|
|
||||||
x += x >> 8;
|
|
||||||
return (x + (x >> 16)) & 0x3F;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MixerContext {
|
|
||||||
MixerContext(cubeb_sample_format f,
|
|
||||||
uint32_t in_channels,
|
|
||||||
cubeb_channel_layout in,
|
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out)
|
|
||||||
: _format(f)
|
|
||||||
, _in_ch_layout(cubeb_channel_layout_check(in, in_channels))
|
|
||||||
, _out_ch_layout(cubeb_channel_layout_check(out, out_channels))
|
|
||||||
, _in_ch_count(in_channels)
|
|
||||||
, _out_ch_count(out_channels)
|
|
||||||
{
|
|
||||||
if (in_channels != cubeb_channel_layout_nb_channels(in) ||
|
|
||||||
out_channels != cubeb_channel_layout_nb_channels(out)) {
|
|
||||||
// Mismatch between channels and layout, aborting.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_valid = init() >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool even(cubeb_channel_layout layout)
|
|
||||||
{
|
|
||||||
if (!layout) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (layout & (layout - 1)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that the layout is sane (that is have symmetrical left/right
|
|
||||||
// channels), if not, layout will be treated as mono.
|
|
||||||
static cubeb_channel_layout clean_layout(cubeb_channel_layout layout)
|
|
||||||
{
|
|
||||||
if (layout && layout != CHANNEL_FRONT_LEFT && !(layout & (layout - 1))) {
|
|
||||||
LOG("Treating layout as mono");
|
|
||||||
return CHANNEL_FRONT_CENTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool sane_layout(cubeb_channel_layout layout)
|
|
||||||
{
|
|
||||||
if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!even(layout & (CHANNEL_FRONT_LEFT |
|
|
||||||
CHANNEL_FRONT_RIGHT))) { // no asymetric front
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!even(layout &
|
|
||||||
(CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!even(layout &
|
|
||||||
(CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int auto_matrix();
|
|
||||||
int init();
|
|
||||||
|
|
||||||
const cubeb_sample_format _format;
|
|
||||||
const cubeb_channel_layout _in_ch_layout; ///< input channel layout
|
|
||||||
const cubeb_channel_layout _out_ch_layout; ///< output channel layout
|
|
||||||
const uint32_t _in_ch_count; ///< input channel count
|
|
||||||
const uint32_t _out_ch_count; ///< output channel count
|
|
||||||
const float _surround_mix_level = C_30DB; ///< surround mixing level
|
|
||||||
const float _center_mix_level = C_30DB; ///< center mixing level
|
|
||||||
const float _lfe_mix_level = 1; ///< LFE mixing level
|
|
||||||
double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< floating point rematrixing coefficients
|
|
||||||
float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< single precision floating point rematrixing coefficients
|
|
||||||
int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< 17.15 fixed point rematrixing coefficients
|
|
||||||
uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }}; ///< Lists of input channels per output channel that have non zero rematrixing coefficients
|
|
||||||
bool _clipping = false; ///< Set to true if clipping detection is required
|
|
||||||
bool _valid = false; ///< Set to true if context is valid.
|
|
||||||
};
|
|
||||||
|
|
||||||
int MixerContext::auto_matrix()
|
|
||||||
{
|
|
||||||
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } };
|
|
||||||
double maxcoef = 0;
|
|
||||||
float maxval;
|
|
||||||
|
|
||||||
cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout);
|
|
||||||
cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout);
|
|
||||||
|
|
||||||
if (!sane_layout(in_ch_layout)) {
|
|
||||||
// Channel Not Supported
|
|
||||||
LOG("Input Layout %x is not supported", _in_ch_layout);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sane_layout(out_ch_layout)) {
|
|
||||||
LOG("Output Layout %x is not supported", _out_ch_layout);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < FF_ARRAY_ELEMS(matrix); i++) {
|
|
||||||
if (in_ch_layout & out_ch_layout & (1U << i)) {
|
|
||||||
matrix[i][i] = 1.0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_channel_layout unaccounted = in_ch_layout & ~out_ch_layout;
|
|
||||||
|
|
||||||
// Rematrixing is done via a matrix of coefficient that should be applied to
|
|
||||||
// all channels. Channels are treated as pair and must be symmetrical (if a
|
|
||||||
// left channel exists, the corresponding right should exist too) unless the
|
|
||||||
// output layout has similar layout. Channels are then mixed toward the front
|
|
||||||
// center or back center if they exist with a slight bias toward the front.
|
|
||||||
|
|
||||||
if (unaccounted & CHANNEL_FRONT_CENTER) {
|
|
||||||
if ((out_ch_layout & CUBEB_LAYOUT_STEREO) == CUBEB_LAYOUT_STEREO) {
|
|
||||||
if (in_ch_layout & CUBEB_LAYOUT_STEREO) {
|
|
||||||
matrix[FRONT_LEFT][FRONT_CENTER] += _center_mix_level;
|
|
||||||
matrix[FRONT_RIGHT][FRONT_CENTER] += _center_mix_level;
|
|
||||||
} else {
|
|
||||||
matrix[FRONT_LEFT][FRONT_CENTER] += M_SQRT1_2;
|
|
||||||
matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unaccounted & CUBEB_LAYOUT_STEREO) {
|
|
||||||
if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
|
||||||
matrix[FRONT_CENTER][FRONT_LEFT] += M_SQRT1_2;
|
|
||||||
matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
|
|
||||||
if (in_ch_layout & CHANNEL_FRONT_CENTER)
|
|
||||||
matrix[FRONT_CENTER][FRONT_CENTER] = _center_mix_level * M_SQRT2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unaccounted & CHANNEL_BACK_CENTER) {
|
|
||||||
if (out_ch_layout & CHANNEL_BACK_LEFT) {
|
|
||||||
matrix[BACK_LEFT][BACK_CENTER] += M_SQRT1_2;
|
|
||||||
matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
|
|
||||||
} else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
|
|
||||||
matrix[SIDE_LEFT][BACK_CENTER] += M_SQRT1_2;
|
|
||||||
matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
|
|
||||||
matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
|
|
||||||
matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
|
||||||
matrix[FRONT_CENTER][BACK_CENTER] +=
|
|
||||||
_surround_mix_level * M_SQRT1_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (unaccounted & CHANNEL_BACK_LEFT) {
|
|
||||||
if (out_ch_layout & CHANNEL_BACK_CENTER) {
|
|
||||||
matrix[BACK_CENTER][BACK_LEFT] += M_SQRT1_2;
|
|
||||||
matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
|
|
||||||
} else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
|
|
||||||
if (in_ch_layout & CHANNEL_SIDE_LEFT) {
|
|
||||||
matrix[SIDE_LEFT][BACK_LEFT] += M_SQRT1_2;
|
|
||||||
matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
|
|
||||||
} else {
|
|
||||||
matrix[SIDE_LEFT][BACK_LEFT] += 1.0;
|
|
||||||
matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
|
|
||||||
}
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
|
|
||||||
matrix[FRONT_LEFT][BACK_LEFT] += _surround_mix_level;
|
|
||||||
matrix[FRONT_RIGHT][BACK_RIGHT] += _surround_mix_level;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
|
||||||
matrix[FRONT_CENTER][BACK_LEFT] += _surround_mix_level * M_SQRT1_2;
|
|
||||||
matrix[FRONT_CENTER][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unaccounted & CHANNEL_SIDE_LEFT) {
|
|
||||||
if (out_ch_layout & CHANNEL_BACK_LEFT) {
|
|
||||||
/* if back channels do not exist in the input, just copy side
|
|
||||||
channels to back channels, otherwise mix side into back */
|
|
||||||
if (in_ch_layout & CHANNEL_BACK_LEFT) {
|
|
||||||
matrix[BACK_LEFT][SIDE_LEFT] += M_SQRT1_2;
|
|
||||||
matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
|
|
||||||
} else {
|
|
||||||
matrix[BACK_LEFT][SIDE_LEFT] += 1.0;
|
|
||||||
matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
|
|
||||||
}
|
|
||||||
} else if (out_ch_layout & CHANNEL_BACK_CENTER) {
|
|
||||||
matrix[BACK_CENTER][SIDE_LEFT] += M_SQRT1_2;
|
|
||||||
matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
|
|
||||||
matrix[FRONT_LEFT][SIDE_LEFT] += _surround_mix_level;
|
|
||||||
matrix[FRONT_RIGHT][SIDE_RIGHT] += _surround_mix_level;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
|
||||||
matrix[FRONT_CENTER][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2;
|
|
||||||
matrix[FRONT_CENTER][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unaccounted & CHANNEL_FRONT_LEFT_OF_CENTER) {
|
|
||||||
if (out_ch_layout & CHANNEL_FRONT_LEFT) {
|
|
||||||
matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0;
|
|
||||||
matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
|
||||||
matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += M_SQRT1_2;
|
|
||||||
matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* mix LFE into front left/right or center */
|
|
||||||
if (unaccounted & CHANNEL_LOW_FREQUENCY) {
|
|
||||||
if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
|
||||||
matrix[FRONT_CENTER][LOW_FREQUENCY] += _lfe_mix_level;
|
|
||||||
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
|
|
||||||
matrix[FRONT_LEFT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
|
|
||||||
matrix[FRONT_RIGHT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the conversion matrix.
|
|
||||||
for (uint32_t out_i = 0, i = 0; i < CHANNELS_MAX; i++) {
|
|
||||||
double sum = 0;
|
|
||||||
int in_i = 0;
|
|
||||||
if ((out_ch_layout & (1U << i)) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
|
|
||||||
if ((in_ch_layout & (1U << j)) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (i < FF_ARRAY_ELEMS(matrix) && j < FF_ARRAY_ELEMS(matrix[0])) {
|
|
||||||
_matrix[out_i][in_i] = matrix[i][j];
|
|
||||||
} else {
|
|
||||||
_matrix[out_i][in_i] =
|
|
||||||
i == j && (in_ch_layout & out_ch_layout & (1U << i));
|
|
||||||
}
|
|
||||||
sum += fabs(_matrix[out_i][in_i]);
|
|
||||||
in_i++;
|
|
||||||
}
|
|
||||||
maxcoef = std::max(maxcoef, sum);
|
|
||||||
out_i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_format == CUBEB_SAMPLE_S16NE) {
|
|
||||||
maxval = 1.0;
|
|
||||||
} else {
|
|
||||||
maxval = INT_MAX;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize matrix if needed.
|
|
||||||
if (maxcoef > maxval) {
|
|
||||||
maxcoef /= maxval;
|
|
||||||
for (uint32_t i = 0; i < CHANNELS_MAX; i++)
|
|
||||||
for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
|
|
||||||
_matrix[i][j] /= maxcoef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_format == CUBEB_SAMPLE_FLOAT32NE) {
|
|
||||||
for (uint32_t i = 0; i < FF_ARRAY_ELEMS(_matrix); i++) {
|
|
||||||
for (uint32_t j = 0; j < FF_ARRAY_ELEMS(_matrix[0]); j++) {
|
|
||||||
_matrix_flt[i][j] = _matrix[i][j];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int MixerContext::init()
|
|
||||||
{
|
|
||||||
int r = auto_matrix();
|
|
||||||
if (r) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if matrix operation would overflow
|
|
||||||
if (_format == CUBEB_SAMPLE_S16NE) {
|
|
||||||
int maxsum = 0;
|
|
||||||
for (uint32_t i = 0; i < _out_ch_count; i++) {
|
|
||||||
double rem = 0;
|
|
||||||
int sum = 0;
|
|
||||||
|
|
||||||
for (uint32_t j = 0; j < _in_ch_count; j++) {
|
|
||||||
double target = _matrix[i][j] * 32768 + rem;
|
|
||||||
int value = lrintf(target);
|
|
||||||
rem += target - value;
|
|
||||||
sum += std::abs(value);
|
|
||||||
}
|
|
||||||
maxsum = std::max(maxsum, sum);
|
|
||||||
}
|
|
||||||
if (maxsum > 32768) {
|
|
||||||
_clipping = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME quantize for integers
|
|
||||||
for (uint32_t i = 0; i < CHANNELS_MAX; i++) {
|
|
||||||
int ch_in = 0;
|
|
||||||
for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
|
|
||||||
_matrix32[i][j] = lrintf(_matrix[i][j] * 32768);
|
|
||||||
if (_matrix[i][j]) {
|
|
||||||
_matrix_ch[i][++ch_in] = j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_matrix_ch[i][0] = ch_in;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
|
|
||||||
void
|
|
||||||
sum2(TYPE_SAMPLE * out,
|
|
||||||
uint32_t stride_out,
|
|
||||||
const TYPE_SAMPLE * in1,
|
|
||||||
const TYPE_SAMPLE * in2,
|
|
||||||
uint32_t stride_in,
|
|
||||||
TYPE_COEFF coeff1,
|
|
||||||
TYPE_COEFF coeff2,
|
|
||||||
F&& operand,
|
|
||||||
uint32_t frames)
|
|
||||||
{
|
|
||||||
static_assert(
|
|
||||||
std::is_same<TYPE_COEFF,
|
|
||||||
typename std::result_of<F(TYPE_COEFF)>::type>::value,
|
|
||||||
"function must return the same type as used by matrix_coeff");
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
*out = operand(coeff1 * *in1 + coeff2 * *in2);
|
|
||||||
out += stride_out;
|
|
||||||
in1 += stride_in;
|
|
||||||
in2 += stride_in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
|
|
||||||
void
|
|
||||||
copy(TYPE_SAMPLE * out,
|
|
||||||
uint32_t stride_out,
|
|
||||||
const TYPE_SAMPLE * in,
|
|
||||||
uint32_t stride_in,
|
|
||||||
TYPE_COEFF coeff,
|
|
||||||
F&& operand,
|
|
||||||
uint32_t frames)
|
|
||||||
{
|
|
||||||
static_assert(
|
|
||||||
std::is_same<TYPE_COEFF,
|
|
||||||
typename std::result_of<F(TYPE_COEFF)>::type>::value,
|
|
||||||
"function must return the same type as used by matrix_coeff");
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
*out = operand(coeff * *in);
|
|
||||||
out += stride_out;
|
|
||||||
in += stride_in;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
|
|
||||||
static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
|
|
||||||
const TYPE_COEFF (&matrix_coeff)[COLS][COLS],
|
|
||||||
F&& aF, uint32_t frames)
|
|
||||||
{
|
|
||||||
static_assert(
|
|
||||||
std::is_same<TYPE_COEFF,
|
|
||||||
typename std::result_of<F(TYPE_COEFF)>::type>::value,
|
|
||||||
"function must return the same type as used by matrix_coeff");
|
|
||||||
|
|
||||||
for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
|
|
||||||
TYPE* out = aOut + out_i;
|
|
||||||
switch (s->_matrix_ch[out_i][0]) {
|
|
||||||
case 0:
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
out[i * s->_out_ch_count] = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 1: {
|
|
||||||
int in_i = s->_matrix_ch[out_i][1];
|
|
||||||
copy(out,
|
|
||||||
s->_out_ch_count,
|
|
||||||
aIn + in_i,
|
|
||||||
s->_in_ch_count,
|
|
||||||
matrix_coeff[out_i][in_i],
|
|
||||||
aF,
|
|
||||||
frames);
|
|
||||||
} break;
|
|
||||||
case 2:
|
|
||||||
sum2(out,
|
|
||||||
s->_out_ch_count,
|
|
||||||
aIn + s->_matrix_ch[out_i][1],
|
|
||||||
aIn + s->_matrix_ch[out_i][2],
|
|
||||||
s->_in_ch_count,
|
|
||||||
matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
|
|
||||||
matrix_coeff[out_i][s->_matrix_ch[out_i][2]],
|
|
||||||
aF,
|
|
||||||
frames);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
TYPE_COEFF v = 0;
|
|
||||||
for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
|
|
||||||
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
|
|
||||||
v +=
|
|
||||||
*(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
|
|
||||||
}
|
|
||||||
out[i * s->_out_ch_count] = aF(v);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct cubeb_mixer
|
|
||||||
{
|
|
||||||
cubeb_mixer(cubeb_sample_format format,
|
|
||||||
uint32_t in_channels,
|
|
||||||
cubeb_channel_layout in_layout,
|
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out_layout)
|
|
||||||
: _context(format, in_channels, in_layout, out_channels, out_layout)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void copy_and_trunc(size_t frames,
|
|
||||||
const T * input_buffer,
|
|
||||||
T * output_buffer) const
|
|
||||||
{
|
|
||||||
if (_context._in_ch_count <= _context._out_ch_count) {
|
|
||||||
// Not enough channels to copy, fill the gaps with silence.
|
|
||||||
if (_context._in_ch_count == 1 && _context._out_ch_count >= 2) {
|
|
||||||
// Special case for upmixing mono input to stereo and more. We will
|
|
||||||
// duplicate the mono channel to the first two channels. On most system,
|
|
||||||
// the first two channels are for left and right. It is commonly
|
|
||||||
// expected that mono will on both left+right channels
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
output_buffer[0] = output_buffer[1] = *input_buffer;
|
|
||||||
PodZero(output_buffer + 2, _context._out_ch_count - 2);
|
|
||||||
output_buffer += _context._out_ch_count;
|
|
||||||
input_buffer++;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
PodCopy(output_buffer, input_buffer, _context._in_ch_count);
|
|
||||||
output_buffer += _context._in_ch_count;
|
|
||||||
input_buffer += _context._in_ch_count;
|
|
||||||
PodZero(output_buffer, _context._out_ch_count - _context._in_ch_count);
|
|
||||||
output_buffer += _context._out_ch_count - _context._in_ch_count;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
|
||||||
PodCopy(output_buffer, input_buffer, _context._out_ch_count);
|
|
||||||
output_buffer += _context._out_ch_count;
|
|
||||||
input_buffer += _context._in_ch_count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int mix(size_t frames,
|
|
||||||
const void * input_buffer,
|
|
||||||
size_t input_buffer_size,
|
|
||||||
void * output_buffer,
|
|
||||||
size_t output_buffer_size) const
|
|
||||||
{
|
|
||||||
if (frames <= 0 || _context._out_ch_count == 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if output buffer is of sufficient size.
|
|
||||||
size_t size_read_needed =
|
|
||||||
frames * _context._in_ch_count * cubeb_sample_size(_context._format);
|
|
||||||
if (input_buffer_size < size_read_needed) {
|
|
||||||
// We don't have enough data to read!
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (output_buffer_size * _context._in_ch_count <
|
|
||||||
size_read_needed * _context._out_ch_count) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!valid()) {
|
|
||||||
// The channel layouts were invalid or unsupported, instead we will simply
|
|
||||||
// either drop the extra channels, or fill with silence the missing ones
|
|
||||||
if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
|
|
||||||
copy_and_trunc(frames,
|
|
||||||
static_cast<const float*>(input_buffer),
|
|
||||||
static_cast<float*>(output_buffer));
|
|
||||||
} else {
|
|
||||||
assert(_context._format == CUBEB_SAMPLE_S16NE);
|
|
||||||
copy_and_trunc(frames,
|
|
||||||
static_cast<const int16_t*>(input_buffer),
|
|
||||||
reinterpret_cast<int16_t*>(output_buffer));
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (_context._format)
|
|
||||||
{
|
|
||||||
case CUBEB_SAMPLE_FLOAT32NE: {
|
|
||||||
auto f = [](float x) { return x; };
|
|
||||||
return rematrix(&_context,
|
|
||||||
static_cast<float*>(output_buffer),
|
|
||||||
static_cast<const float*>(input_buffer),
|
|
||||||
_context._matrix_flt,
|
|
||||||
f,
|
|
||||||
frames);
|
|
||||||
}
|
|
||||||
case CUBEB_SAMPLE_S16NE:
|
|
||||||
if (_context._clipping) {
|
|
||||||
auto f = [](int x) {
|
|
||||||
int y = (x + 16384) >> 15;
|
|
||||||
// clip the signed integer value into the -32768,32767 range.
|
|
||||||
if ((y + 0x8000U) & ~0xFFFF) {
|
|
||||||
return (y >> 31) ^ 0x7FFF;
|
|
||||||
}
|
|
||||||
return y;
|
|
||||||
};
|
|
||||||
return rematrix(&_context,
|
|
||||||
static_cast<int16_t*>(output_buffer),
|
|
||||||
static_cast<const int16_t*>(input_buffer),
|
|
||||||
_context._matrix32,
|
|
||||||
f,
|
|
||||||
frames);
|
|
||||||
} else {
|
|
||||||
auto f = [](int x) { return (x + 16384) >> 15; };
|
|
||||||
return rematrix(&_context,
|
|
||||||
static_cast<int16_t*>(output_buffer),
|
|
||||||
static_cast<const int16_t*>(input_buffer),
|
|
||||||
_context._matrix32,
|
|
||||||
f,
|
|
||||||
frames);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return false if any of the input or ouput layout were invalid.
|
|
||||||
bool valid() const { return _context._valid; }
|
|
||||||
|
|
||||||
virtual ~cubeb_mixer(){};
|
|
||||||
|
|
||||||
MixerContext _context;
|
|
||||||
};
|
|
||||||
|
|
||||||
cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format,
|
|
||||||
uint32_t in_channels,
|
|
||||||
cubeb_channel_layout in_layout,
|
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out_layout)
|
|
||||||
{
|
|
||||||
return new cubeb_mixer(
|
|
||||||
format, in_channels, in_layout, out_channels, out_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void cubeb_mixer_destroy(cubeb_mixer * mixer)
|
|
||||||
{
|
|
||||||
delete mixer;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cubeb_mixer_mix(cubeb_mixer * mixer,
|
|
||||||
size_t frames,
|
|
||||||
const void * input_buffer,
|
|
||||||
size_t input_buffer_size,
|
|
||||||
void * output_buffer,
|
|
||||||
size_t output_buffer_size)
|
|
||||||
{
|
|
||||||
return mixer->mix(
|
|
||||||
frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size);
|
|
||||||
}
|
|
37
thirdparty/cubeb/src/cubeb_mixer.h
vendored
37
thirdparty/cubeb/src/cubeb_mixer.h
vendored
|
@ -1,37 +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 and cubeb_stream_params.
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct cubeb_mixer cubeb_mixer;
|
|
||||||
cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
|
|
||||||
uint32_t in_channels,
|
|
||||||
cubeb_channel_layout in_layout,
|
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out_layout);
|
|
||||||
void cubeb_mixer_destroy(cubeb_mixer * mixer);
|
|
||||||
int cubeb_mixer_mix(cubeb_mixer * mixer,
|
|
||||||
size_t frames,
|
|
||||||
const void * input_buffer,
|
|
||||||
size_t input_buffer_size,
|
|
||||||
void * output_buffer,
|
|
||||||
size_t output_buffer_size);
|
|
||||||
|
|
||||||
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // CUBEB_MIXER
|
|
1763
thirdparty/cubeb/src/cubeb_opensl.c
vendored
1763
thirdparty/cubeb/src/cubeb_opensl.c
vendored
File diff suppressed because it is too large
Load Diff
36
thirdparty/cubeb/src/cubeb_osx_run_loop.cpp
vendored
36
thirdparty/cubeb/src/cubeb_osx_run_loop.cpp
vendored
|
@ -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 <cubeb/cubeb.h>
|
|
||||||
#include "cubeb_osx_run_loop.h"
|
|
||||||
#include "cubeb_log.h"
|
|
||||||
#include <AudioUnit/AudioUnit.h>
|
|
||||||
#include <CoreAudio/AudioHardware.h>
|
|
||||||
#include <CoreAudio/HostTime.h>
|
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
|
22
thirdparty/cubeb/src/cubeb_osx_run_loop.h
vendored
22
thirdparty/cubeb/src/cubeb_osx_run_loop.h
vendored
|
@ -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
|
|
1606
thirdparty/cubeb/src/cubeb_pulse.c
vendored
1606
thirdparty/cubeb/src/cubeb_pulse.c
vendored
File diff suppressed because it is too large
Load Diff
388
thirdparty/cubeb/src/cubeb_resampler.cpp
vendored
388
thirdparty/cubeb/src/cubeb_resampler.cpp
vendored
|
@ -1,388 +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 <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#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<typename T>
|
|
||||||
passthrough_resampler<T>::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<typename T>
|
|
||||||
long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
|
|
||||||
void * output_buffer, long output_frames)
|
|
||||||
{
|
|
||||||
if (input_buffer) {
|
|
||||||
assert(input_frames_count);
|
|
||||||
}
|
|
||||||
assert((input_buffer && output_buffer) ||
|
|
||||||
(output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
|
|
||||||
(input_buffer && !output_buffer && output_frames == 0));
|
|
||||||
|
|
||||||
// When we have no pending input data and exactly as much input
|
|
||||||
// as output data, we don't need to copy it into the internal buffer
|
|
||||||
// and can directly forward it to the callback.
|
|
||||||
void * in_buf = input_buffer;
|
|
||||||
unsigned long pop_input_count = 0u;
|
|
||||||
if (input_buffer && !output_buffer) {
|
|
||||||
output_frames = *input_frames_count;
|
|
||||||
} else if(input_buffer) {
|
|
||||||
if (internal_input_buffer.length() != 0 ||
|
|
||||||
*input_frames_count < output_frames) {
|
|
||||||
// If we have pending input data left and have to first append the input
|
|
||||||
// so we can pass it as one pointer to the callback. Or this is a glitch.
|
|
||||||
// It can happen when system's performance is poor. Audible silence is
|
|
||||||
// being pushed at the end of the short input buffer. An improvement for
|
|
||||||
// the future is to resample to the output number of frames, when that happens.
|
|
||||||
internal_input_buffer.push(static_cast<T*>(input_buffer),
|
|
||||||
frames_to_samples(*input_frames_count));
|
|
||||||
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
|
|
||||||
// This is unxpected but it can happen when a glitch occurs. Fill the
|
|
||||||
// buffer with silence. First keep the actual number of input samples
|
|
||||||
// used without the silence.
|
|
||||||
pop_input_count = internal_input_buffer.length();
|
|
||||||
internal_input_buffer.push_silence(
|
|
||||||
frames_to_samples(output_frames) - internal_input_buffer.length());
|
|
||||||
} else {
|
|
||||||
pop_input_count = frames_to_samples(output_frames);
|
|
||||||
}
|
|
||||||
in_buf = internal_input_buffer.data();
|
|
||||||
} else if(*input_frames_count > output_frames) {
|
|
||||||
// In this case we have more input that we need output and
|
|
||||||
// fill the overflowing input into internal_input_buffer
|
|
||||||
// Since we have no other pending data, we can nonetheless
|
|
||||||
// pass the current input data directly to the callback
|
|
||||||
assert(pop_input_count == 0);
|
|
||||||
unsigned long samples_off = frames_to_samples(output_frames);
|
|
||||||
internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
|
|
||||||
frames_to_samples(*input_frames_count - output_frames));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
|
|
||||||
|
|
||||||
if (input_buffer) {
|
|
||||||
if (pop_input_count) {
|
|
||||||
internal_input_buffer.pop(nullptr, pop_input_count);
|
|
||||||
*input_frames_count = samples_to_frames(pop_input_count);
|
|
||||||
} else {
|
|
||||||
*input_frames_count = output_frames;
|
|
||||||
}
|
|
||||||
drop_audio_if_needed();
|
|
||||||
}
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Explicit instantiation of template class.
|
|
||||||
template class passthrough_resampler<float>;
|
|
||||||
template class passthrough_resampler<short>;
|
|
||||||
|
|
||||||
template<typename T, typename InputProcessor, typename OutputProcessor>
|
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|
||||||
::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<typename T, typename InputProcessor, typename OutputProcessor>
|
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|
||||||
::~cubeb_resampler_speex()
|
|
||||||
{ }
|
|
||||||
|
|
||||||
template<typename T, typename InputProcessor, typename OutputProcessor>
|
|
||||||
long
|
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|
||||||
::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<T*>(input_buffer);
|
|
||||||
T * out_buffer = reinterpret_cast<T*>(output_buffer);
|
|
||||||
return (this->*fill_internal)(in_buffer, input_frames_count,
|
|
||||||
out_buffer, output_frames_needed);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, typename InputProcessor, typename OutputProcessor>
|
|
||||||
long
|
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|
||||||
::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);
|
|
||||||
|
|
||||||
if (!draining) {
|
|
||||||
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 < output_frames_before_processing) {
|
|
||||||
draining = true;
|
|
||||||
|
|
||||||
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<typename T, typename InputProcessor, typename OutputProcessor>
|
|
||||||
long
|
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|
||||||
::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);
|
|
||||||
|
|
||||||
size_t frames_resampled = 0;
|
|
||||||
resampled_input = input_processor->output(resampled_frame_count, &frames_resampled);
|
|
||||||
*input_frames_count = frames_resampled;
|
|
||||||
|
|
||||||
long got = data_callback(stream, user_ptr,
|
|
||||||
resampled_input, nullptr, resampled_frame_count);
|
|
||||||
|
|
||||||
/* 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<typename T, typename InputProcessor, typename OutputProcessor>
|
|
||||||
long
|
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
|
||||||
::fill_internal_duplex(T * in_buffer, long * input_frames_count,
|
|
||||||
T * out_buffer, long output_frames_needed)
|
|
||||||
{
|
|
||||||
if (draining) {
|
|
||||||
// discard input and drain any signal remaining in the resampler.
|
|
||||||
return output_processor->output(out_buffer, output_frames_needed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The input data, after eventual resampling. This is passed to the callback. */
|
|
||||||
T * resampled_input = nullptr;
|
|
||||||
/* The output buffer passed down in the callback, that might be resampled. */
|
|
||||||
T * out_unprocessed = nullptr;
|
|
||||||
long 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);
|
|
||||||
|
|
||||||
size_t frames_resampled = 0;
|
|
||||||
resampled_input =
|
|
||||||
input_processor->output(output_frames_before_processing, &frames_resampled);
|
|
||||||
*input_frames_count = frames_resampled;
|
|
||||||
} else {
|
|
||||||
resampled_input = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
got = data_callback(stream, user_ptr,
|
|
||||||
resampled_input, out_unprocessed,
|
|
||||||
output_frames_before_processing);
|
|
||||||
|
|
||||||
if (got < output_frames_before_processing) {
|
|
||||||
draining = true;
|
|
||||||
|
|
||||||
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<short>(stream,
|
|
||||||
input_params,
|
|
||||||
output_params,
|
|
||||||
target_rate,
|
|
||||||
callback,
|
|
||||||
user_ptr,
|
|
||||||
quality);
|
|
||||||
case CUBEB_SAMPLE_FLOAT32NE:
|
|
||||||
return cubeb_resampler_create_internal<float>(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();
|
|
||||||
}
|
|
85
thirdparty/cubeb/src/cubeb_resampler.h
vendored
85
thirdparty/cubeb/src/cubeb_resampler.h
vendored
|
@ -1,85 +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 input_params Used to calculate bytes per frame and buffer size for
|
|
||||||
* resampling of the input side of the stream. NULL if input should not be
|
|
||||||
* resampled.
|
|
||||||
* @param output_params Used to calculate bytes per frame and buffer size for
|
|
||||||
* resampling of the output side of the stream. NULL if output should not be
|
|
||||||
* resampled.
|
|
||||||
* @param target_rate The sampling rate after resampling for the input side of
|
|
||||||
* the stream, and/or the sampling rate prior to resampling of the output side
|
|
||||||
* of the stream.
|
|
||||||
* @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 output_buffer The buffer to be filled.
|
|
||||||
* @param output_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 */
|
|
602
thirdparty/cubeb/src/cubeb_resampler_internal.h
vendored
602
thirdparty/cubeb/src/cubeb_resampler_internal.h
vendored
|
@ -1,602 +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 <cmath>
|
|
||||||
#include <cassert>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#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 <stdio.h>
|
|
||||||
|
|
||||||
/* 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) const
|
|
||||||
{
|
|
||||||
return frames * channels;
|
|
||||||
}
|
|
||||||
size_t samples_to_frames(size_t samples) const
|
|
||||||
{
|
|
||||||
assert(!(samples % channels));
|
|
||||||
return samples / channels;
|
|
||||||
}
|
|
||||||
/** The number of channel of the audio buffers to be resampled. */
|
|
||||||
const uint32_t channels;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
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<T> 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<typename T, typename InputProcessing, typename OutputProcessing>
|
|
||||||
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<InputProcessing> input_processor;
|
|
||||||
std::unique_ptr<OutputProcessing> output_processor;
|
|
||||||
processing_callback fill_internal;
|
|
||||||
cubeb_stream * const stream;
|
|
||||||
const cubeb_data_callback data_callback;
|
|
||||||
void * const user_ptr;
|
|
||||||
bool draining = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** 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<typename T>
|
|
||||||
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<float>(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) const
|
|
||||||
{
|
|
||||||
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<T> resampling_in_buffer;
|
|
||||||
/* Storage for the resampled frames, to be passed back to the caller. */
|
|
||||||
auto_array<T> 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<typename T>
|
|
||||||
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) const
|
|
||||||
{
|
|
||||||
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<T> delay_input_buffer;
|
|
||||||
/** The output buffer. This is only ever used if using the ::output with a
|
|
||||||
* single argument. */
|
|
||||||
auto_array<T> delay_output_buffer;
|
|
||||||
uint32_t sample_rate;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** This sits behind the C API and is more typed. */
|
|
||||||
template<typename T>
|
|
||||||
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<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
|
|
||||||
std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
|
|
||||||
std::unique_ptr<delay_line<T>> input_delay = nullptr;
|
|
||||||
std::unique_ptr<delay_line<T>> 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<T>(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<T>(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<T>(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<T>(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<T>(output_resampler->latency(),
|
|
||||||
input_params->channels,
|
|
||||||
output_params->rate));
|
|
||||||
if (!input_delay) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input_resampler && output_resampler) {
|
|
||||||
return new cubeb_resampler_speex<T,
|
|
||||||
cubeb_resampler_speex_one_way<T>,
|
|
||||||
cubeb_resampler_speex_one_way<T>>
|
|
||||||
(input_resampler.release(),
|
|
||||||
output_resampler.release(),
|
|
||||||
stream, callback, user_ptr);
|
|
||||||
} else if (input_resampler) {
|
|
||||||
return new cubeb_resampler_speex<T,
|
|
||||||
cubeb_resampler_speex_one_way<T>,
|
|
||||||
delay_line<T>>
|
|
||||||
(input_resampler.release(),
|
|
||||||
output_delay.release(),
|
|
||||||
stream, callback, user_ptr);
|
|
||||||
} else {
|
|
||||||
return new cubeb_resampler_speex<T,
|
|
||||||
delay_line<T>,
|
|
||||||
cubeb_resampler_speex_one_way<T>>
|
|
||||||
(input_delay.release(),
|
|
||||||
output_resampler.release(),
|
|
||||||
stream, callback, user_ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* CUBEB_RESAMPLER_INTERNAL */
|
|
159
thirdparty/cubeb/src/cubeb_ring_array.h
vendored
159
thirdparty/cubeb/src/cubeb_ring_array.h
vendored
|
@ -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<char*>(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
|
|
495
thirdparty/cubeb/src/cubeb_ringbuffer.h
vendored
495
thirdparty/cubeb/src/cubeb_ringbuffer.h
vendored
|
@ -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 <algorithm>
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstdint>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 <typename T>
|
|
||||||
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<int>::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<int> read_index_;
|
|
||||||
/** Index at which to write new elements. `write_index` is always at
|
|
||||||
* least one element ahead of `read_index_`. */
|
|
||||||
std::atomic<int> write_index_;
|
|
||||||
/** Maximum number of elements that can be stored in the ring buffer. */
|
|
||||||
const int capacity_;
|
|
||||||
/** Data storage */
|
|
||||||
std::unique_ptr<T[]> 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 <typename T>
|
|
||||||
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<T> 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<typename T>
|
|
||||||
using lock_free_queue = ring_buffer_base<T>;
|
|
||||||
/**
|
|
||||||
* 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<typename T>
|
|
||||||
using lock_free_audio_ring_buffer = audio_ring_buffer_base<T>;
|
|
||||||
|
|
||||||
#endif // CUBEB_RING_BUFFER_H
|
|
669
thirdparty/cubeb/src/cubeb_sndio.c
vendored
669
thirdparty/cubeb/src/cubeb_sndio.c
vendored
|
@ -1,669 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <poll.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sndio.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#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
|
|
||||||
|
|
||||||
#ifdef DISABLE_LIBSNDIO_DLOPEN
|
|
||||||
#define WRAP(x) x
|
|
||||||
#else
|
|
||||||
#define WRAP(x) cubeb_##x
|
|
||||||
#define LIBSNDIO_API_VISIT(X) \
|
|
||||||
X(sio_close) \
|
|
||||||
X(sio_eof) \
|
|
||||||
X(sio_getpar) \
|
|
||||||
X(sio_initpar) \
|
|
||||||
X(sio_nfds) \
|
|
||||||
X(sio_onmove) \
|
|
||||||
X(sio_open) \
|
|
||||||
X(sio_pollfd) \
|
|
||||||
X(sio_read) \
|
|
||||||
X(sio_revents) \
|
|
||||||
X(sio_setpar) \
|
|
||||||
X(sio_start) \
|
|
||||||
X(sio_stop) \
|
|
||||||
X(sio_write) \
|
|
||||||
|
|
||||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
|
||||||
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
|
|
||||||
#undef MAKE_TYPEDEF
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct cubeb_ops const sndio_ops;
|
|
||||||
|
|
||||||
struct cubeb {
|
|
||||||
struct cubeb_ops const * ops;
|
|
||||||
void * libsndio;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cubeb_stream {
|
|
||||||
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
|
||||||
cubeb * context;
|
|
||||||
void * arg; /* user arg to {data,state}_cb */
|
|
||||||
/**/
|
|
||||||
pthread_t th; /* to run real-time audio i/o */
|
|
||||||
pthread_mutex_t mtx; /* protects hdl and pos */
|
|
||||||
struct sio_hdl *hdl; /* link us to sndio */
|
|
||||||
int mode; /* bitmap of SIO_{PLAY,REC} */
|
|
||||||
int active; /* cubec_start() called */
|
|
||||||
int conv; /* need float->s16 conversion */
|
|
||||||
unsigned char *rbuf; /* rec data consumed from here */
|
|
||||||
unsigned char *pbuf; /* play data is prepared here */
|
|
||||||
unsigned int nfr; /* number of frames in ibuf and obuf */
|
|
||||||
unsigned int rbpf; /* rec bytes per frame */
|
|
||||||
unsigned int pbpf; /* play bytes per frame */
|
|
||||||
unsigned int rchan; /* number of rec channels */
|
|
||||||
unsigned int pchan; /* number of play channels */
|
|
||||||
unsigned int nblks; /* number of blocks in the buffer */
|
|
||||||
uint64_t hwpos; /* frame number Joe hears right now */
|
|
||||||
uint64_t swpos; /* number of frames produced/consumed */
|
|
||||||
cubeb_data_callback data_cb; /* cb to preapare data */
|
|
||||||
cubeb_state_callback state_cb; /* cb to notify about state changes */
|
|
||||||
float volume; /* current volume */
|
|
||||||
};
|
|
||||||
|
|
||||||
static void
|
|
||||||
s16_setvol(void *ptr, long nsamp, float volume)
|
|
||||||
{
|
|
||||||
int16_t *dst = ptr;
|
|
||||||
int32_t mult = volume * 32768;
|
|
||||||
int32_t s;
|
|
||||||
|
|
||||||
while (nsamp-- > 0) {
|
|
||||||
s = *dst;
|
|
||||||
s = (s * mult) >> 15;
|
|
||||||
*(dst++) = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
float_to_s16(void *ptr, long nsamp, float volume)
|
|
||||||
{
|
|
||||||
int16_t *dst = ptr;
|
|
||||||
float *src = ptr;
|
|
||||||
float mult = volume * 32768;
|
|
||||||
int s;
|
|
||||||
|
|
||||||
while (nsamp-- > 0) {
|
|
||||||
s = lrintf(*(src++) * mult);
|
|
||||||
if (s < -32768)
|
|
||||||
s = -32768;
|
|
||||||
else if (s > 32767)
|
|
||||||
s = 32767;
|
|
||||||
*(dst++) = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
s16_to_float(void *ptr, long nsamp)
|
|
||||||
{
|
|
||||||
int16_t *src = ptr;
|
|
||||||
float *dst = ptr;
|
|
||||||
|
|
||||||
src += nsamp;
|
|
||||||
dst += nsamp;
|
|
||||||
while (nsamp-- > 0)
|
|
||||||
*(--dst) = (1. / 32768) * *(--src);
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *
|
|
||||||
sndio_get_device()
|
|
||||||
{
|
|
||||||
#ifdef __linux__
|
|
||||||
/*
|
|
||||||
* On other platforms default to sndio devices,
|
|
||||||
* so cubebs other backends can be used instead.
|
|
||||||
*/
|
|
||||||
const char *dev = getenv("AUDIODEVICE");
|
|
||||||
if (dev == NULL || *dev == '\0')
|
|
||||||
return "snd/0";
|
|
||||||
return dev;
|
|
||||||
#else
|
|
||||||
return SIO_DEVANY;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sndio_onmove(void *arg, int delta)
|
|
||||||
{
|
|
||||||
cubeb_stream *s = (cubeb_stream *)arg;
|
|
||||||
|
|
||||||
s->hwpos += delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
sndio_mainloop(void *arg)
|
|
||||||
{
|
|
||||||
struct pollfd *pfds;
|
|
||||||
cubeb_stream *s = arg;
|
|
||||||
int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED;
|
|
||||||
size_t pstart = 0, pend = 0, rstart = 0, rend = 0;
|
|
||||||
long nfr;
|
|
||||||
|
|
||||||
nfds = WRAP(sio_nfds)(s->hdl);
|
|
||||||
pfds = calloc(nfds, sizeof (struct pollfd));
|
|
||||||
if (pfds == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
DPR("sndio_mainloop()\n");
|
|
||||||
s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
|
|
||||||
pthread_mutex_lock(&s->mtx);
|
|
||||||
if (!WRAP(sio_start)(s->hdl)) {
|
|
||||||
pthread_mutex_unlock(&s->mtx);
|
|
||||||
free(pfds);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
DPR("sndio_mainloop(), started\n");
|
|
||||||
|
|
||||||
if (s->mode & SIO_PLAY) {
|
|
||||||
pstart = pend = s->nfr * s->pbpf;
|
|
||||||
prime = s->nblks;
|
|
||||||
if (s->mode & SIO_REC) {
|
|
||||||
memset(s->rbuf, 0, s->nfr * s->rbpf);
|
|
||||||
rstart = rend = s->nfr * s->rbpf;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
prime = 0;
|
|
||||||
rstart = 0;
|
|
||||||
rend = s->nfr * s->rbpf;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (!s->active) {
|
|
||||||
DPR("sndio_mainloop() stopped\n");
|
|
||||||
state = CUBEB_STATE_STOPPED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* do we have a complete block? */
|
|
||||||
if ((!(s->mode & SIO_PLAY) || pstart == pend) &&
|
|
||||||
(!(s->mode & SIO_REC) || rstart == rend)) {
|
|
||||||
|
|
||||||
if (eof) {
|
|
||||||
DPR("sndio_mainloop() drained\n");
|
|
||||||
state = CUBEB_STATE_DRAINED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((s->mode & SIO_REC) && s->conv)
|
|
||||||
s16_to_float(s->rbuf, s->nfr * s->rchan);
|
|
||||||
|
|
||||||
/* invoke call-back, it returns less that s->nfr if done */
|
|
||||||
pthread_mutex_unlock(&s->mtx);
|
|
||||||
nfr = s->data_cb(s, s->arg, s->rbuf, s->pbuf, s->nfr);
|
|
||||||
pthread_mutex_lock(&s->mtx);
|
|
||||||
if (nfr < 0) {
|
|
||||||
DPR("sndio_mainloop() cb err\n");
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
s->swpos += nfr;
|
|
||||||
|
|
||||||
/* was this last call-back invocation (aka end-of-stream) ? */
|
|
||||||
if (nfr < s->nfr) {
|
|
||||||
|
|
||||||
if (!(s->mode & SIO_PLAY) || nfr == 0) {
|
|
||||||
state = CUBEB_STATE_DRAINED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* need to write (aka drain) the partial play block we got */
|
|
||||||
pend = nfr * s->pbpf;
|
|
||||||
eof = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prime > 0)
|
|
||||||
prime--;
|
|
||||||
|
|
||||||
if (s->mode & SIO_PLAY) {
|
|
||||||
if (s->conv)
|
|
||||||
float_to_s16(s->pbuf, nfr * s->pchan, s->volume);
|
|
||||||
else
|
|
||||||
s16_setvol(s->pbuf, nfr * s->pchan, s->volume);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s->mode & SIO_REC)
|
|
||||||
rstart = 0;
|
|
||||||
if (s->mode & SIO_PLAY)
|
|
||||||
pstart = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
events = 0;
|
|
||||||
if ((s->mode & SIO_REC) && rstart < rend && prime == 0)
|
|
||||||
events |= POLLIN;
|
|
||||||
if ((s->mode & SIO_PLAY) && pstart < pend)
|
|
||||||
events |= POLLOUT;
|
|
||||||
nfds = WRAP(sio_pollfd)(s->hdl, pfds, events);
|
|
||||||
|
|
||||||
if (nfds > 0) {
|
|
||||||
pthread_mutex_unlock(&s->mtx);
|
|
||||||
n = poll(pfds, nfds, -1);
|
|
||||||
pthread_mutex_lock(&s->mtx);
|
|
||||||
if (n < 0)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
revents = WRAP(sio_revents)(s->hdl, pfds);
|
|
||||||
|
|
||||||
if (revents & POLLHUP) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revents & POLLOUT) {
|
|
||||||
n = WRAP(sio_write)(s->hdl, s->pbuf + pstart, pend - pstart);
|
|
||||||
if (n == 0 && WRAP(sio_eof)(s->hdl)) {
|
|
||||||
DPR("sndio_mainloop() werr\n");
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pstart += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (revents & POLLIN) {
|
|
||||||
n = WRAP(sio_read)(s->hdl, s->rbuf + rstart, rend - rstart);
|
|
||||||
if (n == 0 && WRAP(sio_eof)(s->hdl)) {
|
|
||||||
DPR("sndio_mainloop() rerr\n");
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rstart += n;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* skip rec block, if not recording (yet) */
|
|
||||||
if (prime > 0 && (s->mode & SIO_REC))
|
|
||||||
rstart = rend;
|
|
||||||
}
|
|
||||||
WRAP(sio_stop)(s->hdl);
|
|
||||||
s->hwpos = s->swpos;
|
|
||||||
pthread_mutex_unlock(&s->mtx);
|
|
||||||
s->state_cb(s, s->arg, state);
|
|
||||||
free(pfds);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*static*/ int
|
|
||||||
sndio_init(cubeb **context, char const *context_name)
|
|
||||||
{
|
|
||||||
void * libsndio = NULL;
|
|
||||||
struct sio_hdl *hdl;
|
|
||||||
|
|
||||||
assert(context);
|
|
||||||
|
|
||||||
#ifndef DISABLE_LIBSNDIO_DLOPEN
|
|
||||||
libsndio = dlopen("libsndio.so.7.0", RTLD_LAZY);
|
|
||||||
if (!libsndio) {
|
|
||||||
libsndio = dlopen("libsndio.so", RTLD_LAZY);
|
|
||||||
if (!libsndio) {
|
|
||||||
DPR("sndio_init(%s) failed dlopen(libsndio.so)\n", context_name);
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LOAD(x) { \
|
|
||||||
cubeb_##x = dlsym(libsndio, #x); \
|
|
||||||
if (!cubeb_##x) { \
|
|
||||||
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
|
|
||||||
dlclose(libsndio); \
|
|
||||||
return CUBEB_ERROR; \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
LIBSNDIO_API_VISIT(LOAD);
|
|
||||||
#undef LOAD
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* test if sndio works */
|
|
||||||
hdl = WRAP(sio_open)(sndio_get_device(), SIO_PLAY, 1);
|
|
||||||
if (hdl == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
WRAP(sio_close)(hdl);
|
|
||||||
|
|
||||||
DPR("sndio_init(%s)\n", context_name);
|
|
||||||
*context = malloc(sizeof(**context));
|
|
||||||
if (*context == NULL)
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
(*context)->libsndio = libsndio;
|
|
||||||
(*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");
|
|
||||||
if (context->libsndio)
|
|
||||||
dlclose(context->libsndio);
|
|
||||||
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;
|
|
||||||
cubeb_sample_format format;
|
|
||||||
int rate;
|
|
||||||
size_t bps;
|
|
||||||
|
|
||||||
DPR("sndio_stream_init(%s)\n", stream_name);
|
|
||||||
|
|
||||||
s = malloc(sizeof(cubeb_stream));
|
|
||||||
if (s == NULL)
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
memset(s, 0, sizeof(cubeb_stream));
|
|
||||||
s->mode = 0;
|
|
||||||
if (input_stream_params) {
|
|
||||||
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
|
||||||
DPR("sndio_stream_init(), loopback not supported\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
s->mode |= SIO_REC;
|
|
||||||
format = input_stream_params->format;
|
|
||||||
rate = input_stream_params->rate;
|
|
||||||
}
|
|
||||||
if (output_stream_params) {
|
|
||||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
|
||||||
DPR("sndio_stream_init(), loopback not supported\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
s->mode |= SIO_PLAY;
|
|
||||||
format = output_stream_params->format;
|
|
||||||
rate = output_stream_params->rate;
|
|
||||||
}
|
|
||||||
if (s->mode == 0) {
|
|
||||||
DPR("sndio_stream_init(), neither playing nor recording\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
s->context = context;
|
|
||||||
s->hdl = WRAP(sio_open)(sndio_get_device(), s->mode, 1);
|
|
||||||
if (s->hdl == NULL) {
|
|
||||||
DPR("sndio_stream_init(), sio_open() failed\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
WRAP(sio_initpar)(&wpar);
|
|
||||||
wpar.sig = 1;
|
|
||||||
wpar.bits = 16;
|
|
||||||
switch (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:
|
|
||||||
DPR("sndio_stream_init() unsupported format\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
wpar.rate = rate;
|
|
||||||
if (s->mode & SIO_REC)
|
|
||||||
wpar.rchan = input_stream_params->channels;
|
|
||||||
if (s->mode & SIO_PLAY)
|
|
||||||
wpar.pchan = output_stream_params->channels;
|
|
||||||
wpar.appbufsz = latency_frames;
|
|
||||||
if (!WRAP(sio_setpar)(s->hdl, &wpar) || !WRAP(sio_getpar)(s->hdl, &rpar)) {
|
|
||||||
DPR("sndio_stream_init(), sio_setpar() failed\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
|
|
||||||
rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
|
|
||||||
((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
|
|
||||||
((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
|
|
||||||
DPR("sndio_stream_init() unsupported params\n");
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
WRAP(sio_onmove)(s->hdl, sndio_onmove, s);
|
|
||||||
s->active = 0;
|
|
||||||
s->nfr = rpar.round;
|
|
||||||
s->rbpf = rpar.bps * rpar.rchan;
|
|
||||||
s->pbpf = rpar.bps * rpar.pchan;
|
|
||||||
s->rchan = rpar.rchan;
|
|
||||||
s->pchan = rpar.pchan;
|
|
||||||
s->nblks = rpar.bufsz / rpar.round;
|
|
||||||
s->data_cb = data_callback;
|
|
||||||
s->state_cb = state_callback;
|
|
||||||
s->arg = user_ptr;
|
|
||||||
s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
|
||||||
s->hwpos = s->swpos = 0;
|
|
||||||
if (format == CUBEB_SAMPLE_FLOAT32LE) {
|
|
||||||
s->conv = 1;
|
|
||||||
bps = sizeof(float);
|
|
||||||
} else {
|
|
||||||
s->conv = 0;
|
|
||||||
bps = rpar.bps;
|
|
||||||
}
|
|
||||||
if (s->mode & SIO_PLAY) {
|
|
||||||
s->pbuf = malloc(bps * rpar.pchan * rpar.round);
|
|
||||||
if (s->pbuf == NULL)
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
if (s->mode & SIO_REC) {
|
|
||||||
s->rbuf = malloc(bps * rpar.rchan * rpar.round);
|
|
||||||
if (s->rbuf == NULL)
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
s->volume = 1.;
|
|
||||||
*stream = s;
|
|
||||||
DPR("sndio_stream_init() end, ok\n");
|
|
||||||
(void)context;
|
|
||||||
(void)stream_name;
|
|
||||||
return CUBEB_OK;
|
|
||||||
err:
|
|
||||||
if (s->hdl)
|
|
||||||
WRAP(sio_close)(s->hdl);
|
|
||||||
if (s->pbuf)
|
|
||||||
free(s->pbuf);
|
|
||||||
if (s->rbuf)
|
|
||||||
free(s->pbuf);
|
|
||||||
free(s);
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We've no device-independent prefered rate; any rate will work if
|
|
||||||
* sndiod is running. If it isn't, 48kHz is what is most likely to
|
|
||||||
* work as most (but not all) devices support it.
|
|
||||||
*/
|
|
||||||
*rate = 48000;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* We've no device-independent minimum latency.
|
|
||||||
*/
|
|
||||||
*latency_frames = 2048;
|
|
||||||
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sndio_stream_destroy(cubeb_stream *s)
|
|
||||||
{
|
|
||||||
DPR("sndio_stream_destroy()\n");
|
|
||||||
WRAP(sio_close)(s->hdl);
|
|
||||||
if (s->mode & SIO_PLAY)
|
|
||||||
free(s->pbuf);
|
|
||||||
if (s->mode & SIO_REC)
|
|
||||||
free(s->rbuf);
|
|
||||||
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() %" PRId64 "\n", s->hwpos);
|
|
||||||
*p = s->hwpos;
|
|
||||||
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);
|
|
||||||
if (volume < 0.)
|
|
||||||
volume = 0.;
|
|
||||||
else if (volume > 1.0)
|
|
||||||
volume = 1.;
|
|
||||||
s->volume = 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->swpos - stm->hwpos;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
|
|
||||||
cubeb_device_collection *collection)
|
|
||||||
{
|
|
||||||
static char dev[] = SIO_DEVANY;
|
|
||||||
cubeb_device_info *device;
|
|
||||||
|
|
||||||
device = malloc(sizeof(cubeb_device_info));
|
|
||||||
if (device == NULL)
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
|
|
||||||
device->devid = dev; /* passed to stream_init() */
|
|
||||||
device->device_id = dev; /* printable in UI */
|
|
||||||
device->friendly_name = dev; /* same, but friendly */
|
|
||||||
device->group_id = dev; /* actual device if full-duplex */
|
|
||||||
device->vendor_name = NULL; /* may be NULL */
|
|
||||||
device->type = type; /* Input/Output */
|
|
||||||
device->state = CUBEB_DEVICE_STATE_ENABLED;
|
|
||||||
device->preferred = CUBEB_DEVICE_PREF_ALL;
|
|
||||||
device->format = CUBEB_DEVICE_FMT_S16NE;
|
|
||||||
device->default_format = CUBEB_DEVICE_FMT_S16NE;
|
|
||||||
device->max_channels = 16;
|
|
||||||
device->default_rate = 48000;
|
|
||||||
device->min_rate = 4000;
|
|
||||||
device->max_rate = 192000;
|
|
||||||
device->latency_lo = 480;
|
|
||||||
device->latency_hi = 9600;
|
|
||||||
collection->device = device;
|
|
||||||
collection->count = 1;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sndio_device_collection_destroy(cubeb * context,
|
|
||||||
cubeb_device_collection * collection)
|
|
||||||
{
|
|
||||||
free(collection->device);
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct cubeb_ops const sndio_ops = {
|
|
||||||
.init = sndio_init,
|
|
||||||
.get_backend_id = sndio_get_backend_id,
|
|
||||||
.get_max_channel_count = sndio_get_max_channel_count,
|
|
||||||
.get_min_latency = sndio_get_min_latency,
|
|
||||||
.get_preferred_sample_rate = sndio_get_preferred_sample_rate,
|
|
||||||
.enumerate_devices = sndio_enumerate_devices,
|
|
||||||
.device_collection_destroy = sndio_device_collection_destroy,
|
|
||||||
.destroy = sndio_destroy,
|
|
||||||
.stream_init = sndio_stream_init,
|
|
||||||
.stream_destroy = sndio_stream_destroy,
|
|
||||||
.stream_start = sndio_stream_start,
|
|
||||||
.stream_stop = sndio_stream_stop,
|
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = sndio_stream_get_position,
|
|
||||||
.stream_get_latency = sndio_stream_get_latency,
|
|
||||||
.stream_set_volume = sndio_stream_set_volume,
|
|
||||||
.stream_get_current_device = NULL,
|
|
||||||
.stream_device_destroy = NULL,
|
|
||||||
.stream_register_device_changed_callback = NULL,
|
|
||||||
.register_device_collection_changed = NULL
|
|
||||||
};
|
|
155
thirdparty/cubeb/src/cubeb_strings.c
vendored
155
thirdparty/cubeb/src/cubeb_strings.c
vendored
|
@ -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 <assert.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
|
|
44
thirdparty/cubeb/src/cubeb_strings.h
vendored
44
thirdparty/cubeb/src/cubeb_strings.h
vendored
|
@ -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
|
|
730
thirdparty/cubeb/src/cubeb_sun.c
vendored
730
thirdparty/cubeb/src/cubeb_sun.c
vendored
|
@ -1,730 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2019-2020 Nia Alarie <nia@NetBSD.org>
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
#include <sys/audioio.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
|
|
||||||
/* Default to 4 + 1 for the default device. */
|
|
||||||
#ifndef SUN_DEVICE_COUNT
|
|
||||||
#define SUN_DEVICE_COUNT (5)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Supported well by most hardware. */
|
|
||||||
#ifndef SUN_PREFER_RATE
|
|
||||||
#define SUN_PREFER_RATE (48000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Standard acceptable minimum. */
|
|
||||||
#ifndef SUN_LATENCY_MS
|
|
||||||
#define SUN_LATENCY_MS (40)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SUN_DEFAULT_DEVICE
|
|
||||||
#define SUN_DEFAULT_DEVICE "/dev/audio"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SUN_BUFFER_FRAMES
|
|
||||||
#define SUN_BUFFER_FRAMES (32)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Supported on NetBSD regardless of hardware.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SUN_MAX_CHANNELS
|
|
||||||
# ifdef __NetBSD__
|
|
||||||
# define SUN_MAX_CHANNELS (12)
|
|
||||||
# else
|
|
||||||
# define SUN_MAX_CHANNELS (2)
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SUN_MIN_RATE
|
|
||||||
#define SUN_MIN_RATE (1000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SUN_MAX_RATE
|
|
||||||
#define SUN_MAX_RATE (192000)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static struct cubeb_ops const sun_ops;
|
|
||||||
|
|
||||||
struct cubeb {
|
|
||||||
struct cubeb_ops const * ops;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sun_stream {
|
|
||||||
char name[32];
|
|
||||||
int fd;
|
|
||||||
void * buf;
|
|
||||||
struct audio_info info;
|
|
||||||
unsigned frame_size; /* precision in bytes * channels */
|
|
||||||
bool floating;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cubeb_stream {
|
|
||||||
struct cubeb * context;
|
|
||||||
void * user_ptr;
|
|
||||||
pthread_t thread;
|
|
||||||
pthread_mutex_t mutex; /* protects running, volume, frames_written */
|
|
||||||
bool running;
|
|
||||||
float volume;
|
|
||||||
struct sun_stream play;
|
|
||||||
struct sun_stream record;
|
|
||||||
cubeb_data_callback data_cb;
|
|
||||||
cubeb_state_callback state_cb;
|
|
||||||
uint64_t frames_written;
|
|
||||||
uint64_t blocks_written;
|
|
||||||
};
|
|
||||||
|
|
||||||
int
|
|
||||||
sun_init(cubeb ** context, char const * context_name)
|
|
||||||
{
|
|
||||||
cubeb * c;
|
|
||||||
|
|
||||||
(void)context_name;
|
|
||||||
if ((c = calloc(1, sizeof(cubeb))) == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
c->ops = &sun_ops;
|
|
||||||
*context = c;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sun_destroy(cubeb * context)
|
|
||||||
{
|
|
||||||
free(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char const *
|
|
||||||
sun_get_backend_id(cubeb * context)
|
|
||||||
{
|
|
||||||
return "sun";
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
|
|
||||||
{
|
|
||||||
(void)context;
|
|
||||||
|
|
||||||
*rate = SUN_PREFER_RATE;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_get_max_channel_count(cubeb * context, uint32_t * max_channels)
|
|
||||||
{
|
|
||||||
(void)context;
|
|
||||||
|
|
||||||
*max_channels = SUN_MAX_CHANNELS;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_get_min_latency(cubeb * context, cubeb_stream_params params,
|
|
||||||
uint32_t * latency_frames)
|
|
||||||
{
|
|
||||||
(void)context;
|
|
||||||
|
|
||||||
*latency_frames = SUN_LATENCY_MS * params.rate / 1000;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_get_hwinfo(const char * device, struct audio_info * format,
|
|
||||||
int * props, struct audio_device * dev)
|
|
||||||
{
|
|
||||||
int fd = -1;
|
|
||||||
|
|
||||||
if ((fd = open(device, O_RDONLY)) == -1) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
#ifdef AUDIO_GETFORMAT
|
|
||||||
if (ioctl(fd, AUDIO_GETFORMAT, format) != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef AUDIO_GETPROPS
|
|
||||||
if (ioctl(fd, AUDIO_GETPROPS, props) != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (ioctl(fd, AUDIO_GETDEV, dev) != 0) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
close(fd);
|
|
||||||
return CUBEB_OK;
|
|
||||||
error:
|
|
||||||
if (fd != -1) {
|
|
||||||
close(fd);
|
|
||||||
}
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* XXX: PR kern/54264
|
|
||||||
*/
|
|
||||||
static int
|
|
||||||
sun_prinfo_verify_sanity(struct audio_prinfo * prinfo)
|
|
||||||
{
|
|
||||||
return prinfo->precision >= 8 && prinfo->precision <= 32 &&
|
|
||||||
prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
|
|
||||||
prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|
||||||
cubeb_device_collection * collection)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
cubeb_device_info device = {0};
|
|
||||||
char dev[16] = SUN_DEFAULT_DEVICE;
|
|
||||||
char dev_friendly[64];
|
|
||||||
struct audio_info hwfmt;
|
|
||||||
struct audio_device hwname;
|
|
||||||
struct audio_prinfo *prinfo = NULL;
|
|
||||||
int hwprops;
|
|
||||||
|
|
||||||
collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info));
|
|
||||||
if (collection->device == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
collection->count = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < SUN_DEVICE_COUNT; ++i) {
|
|
||||||
if (i > 0) {
|
|
||||||
(void)snprintf(dev, sizeof(dev), "/dev/audio%u", i - 1);
|
|
||||||
}
|
|
||||||
if (sun_get_hwinfo(dev, &hwfmt, &hwprops, &hwname) != CUBEB_OK) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
#ifdef AUDIO_GETPROPS
|
|
||||||
device.type = 0;
|
|
||||||
if ((hwprops & AUDIO_PROP_CAPTURE) != 0 &&
|
|
||||||
sun_prinfo_verify_sanity(&hwfmt.record)) {
|
|
||||||
/* the device supports recording, probably */
|
|
||||||
device.type |= CUBEB_DEVICE_TYPE_INPUT;
|
|
||||||
}
|
|
||||||
if ((hwprops & AUDIO_PROP_PLAYBACK) != 0 &&
|
|
||||||
sun_prinfo_verify_sanity(&hwfmt.play)) {
|
|
||||||
/* the device supports playback, probably */
|
|
||||||
device.type |= CUBEB_DEVICE_TYPE_OUTPUT;
|
|
||||||
}
|
|
||||||
switch (device.type) {
|
|
||||||
case 0:
|
|
||||||
/* device doesn't do input or output, aliens probably involved */
|
|
||||||
continue;
|
|
||||||
case CUBEB_DEVICE_TYPE_INPUT:
|
|
||||||
if ((type & CUBEB_DEVICE_TYPE_INPUT) == 0) {
|
|
||||||
/* this device is input only, not scanning for those, skip it */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CUBEB_DEVICE_TYPE_OUTPUT:
|
|
||||||
if ((type & CUBEB_DEVICE_TYPE_OUTPUT) == 0) {
|
|
||||||
/* this device is output only, not scanning for those, skip it */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if ((type & CUBEB_DEVICE_TYPE_INPUT) != 0) {
|
|
||||||
prinfo = &hwfmt.record;
|
|
||||||
}
|
|
||||||
if ((type & CUBEB_DEVICE_TYPE_OUTPUT) != 0) {
|
|
||||||
prinfo = &hwfmt.play;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (i > 0) {
|
|
||||||
(void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (%d)",
|
|
||||||
hwname.name, hwname.version, hwname.config, i - 1);
|
|
||||||
} else {
|
|
||||||
(void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (default)",
|
|
||||||
hwname.name, hwname.version, hwname.config);
|
|
||||||
}
|
|
||||||
device.devid = (void *)(uintptr_t)i;
|
|
||||||
device.device_id = strdup(dev);
|
|
||||||
device.friendly_name = strdup(dev_friendly);
|
|
||||||
device.group_id = strdup(dev);
|
|
||||||
device.vendor_name = strdup(hwname.name);
|
|
||||||
device.type = type;
|
|
||||||
device.state = CUBEB_DEVICE_STATE_ENABLED;
|
|
||||||
device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
|
||||||
#ifdef AUDIO_GETFORMAT
|
|
||||||
device.max_channels = prinfo->channels;
|
|
||||||
device.default_rate = prinfo->sample_rate;
|
|
||||||
#else
|
|
||||||
device.max_channels = 2;
|
|
||||||
device.default_rate = SUN_PREFER_RATE;
|
|
||||||
#endif
|
|
||||||
device.default_format = CUBEB_DEVICE_FMT_S16NE;
|
|
||||||
device.format = CUBEB_DEVICE_FMT_S16NE;
|
|
||||||
device.min_rate = SUN_MIN_RATE;
|
|
||||||
device.max_rate = SUN_MAX_RATE;
|
|
||||||
device.latency_lo = SUN_LATENCY_MS * SUN_MIN_RATE / 1000;
|
|
||||||
device.latency_hi = SUN_LATENCY_MS * SUN_MAX_RATE / 1000;
|
|
||||||
collection->device[collection->count++] = device;
|
|
||||||
}
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_device_collection_destroy(cubeb * context,
|
|
||||||
cubeb_device_collection * collection)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < collection->count; ++i) {
|
|
||||||
free((char *)collection->device[i].device_id);
|
|
||||||
free((char *)collection->device[i].friendly_name);
|
|
||||||
free((char *)collection->device[i].group_id);
|
|
||||||
free((char *)collection->device[i].vendor_name);
|
|
||||||
}
|
|
||||||
free(collection->device);
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
|
|
||||||
struct audio_info * info, struct audio_prinfo * prinfo)
|
|
||||||
{
|
|
||||||
prinfo->channels = params->channels;
|
|
||||||
prinfo->sample_rate = params->rate;
|
|
||||||
#ifdef AUDIO_ENCODING_SLINEAR_LE
|
|
||||||
switch (params->format) {
|
|
||||||
case CUBEB_SAMPLE_S16LE:
|
|
||||||
prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
|
|
||||||
prinfo->precision = 16;
|
|
||||||
break;
|
|
||||||
case CUBEB_SAMPLE_S16BE:
|
|
||||||
prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
|
|
||||||
prinfo->precision = 16;
|
|
||||||
break;
|
|
||||||
case CUBEB_SAMPLE_FLOAT32NE:
|
|
||||||
prinfo->encoding = AUDIO_ENCODING_SLINEAR;
|
|
||||||
prinfo->precision = 32;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG("Unsupported format");
|
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
switch (params->format) {
|
|
||||||
case CUBEB_SAMPLE_S16NE:
|
|
||||||
prinfo->encoding = AUDIO_ENCODING_LINEAR;
|
|
||||||
prinfo->precision = 16;
|
|
||||||
break;
|
|
||||||
case CUBEB_SAMPLE_FLOAT32NE:
|
|
||||||
prinfo->encoding = AUDIO_ENCODING_LINEAR;
|
|
||||||
prinfo->precision = 32;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG("Unsupported format");
|
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (ioctl(fd, AUDIO_SETINFO, info) == -1) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
if (ioctl(fd, AUDIO_GETINFO, info) == -1) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_stream_stop(cubeb_stream * s)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&s->mutex);
|
|
||||||
if (s->running) {
|
|
||||||
s->running = false;
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
pthread_join(s->thread, NULL);
|
|
||||||
} else {
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
}
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sun_stream_destroy(cubeb_stream * s)
|
|
||||||
{
|
|
||||||
pthread_mutex_destroy(&s->mutex);
|
|
||||||
sun_stream_stop(s);
|
|
||||||
if (s->play.fd != -1) {
|
|
||||||
close(s->play.fd);
|
|
||||||
}
|
|
||||||
if (s->record.fd != -1) {
|
|
||||||
close(s->record.fd);
|
|
||||||
}
|
|
||||||
free(s->play.buf);
|
|
||||||
free(s->record.buf);
|
|
||||||
free(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sun_float_to_linear32(void * buf, unsigned sample_count, float vol)
|
|
||||||
{
|
|
||||||
float * in = buf;
|
|
||||||
int32_t * out = buf;
|
|
||||||
int32_t * tail = out + sample_count;
|
|
||||||
|
|
||||||
while (out < tail) {
|
|
||||||
float f = *(in++) * vol;
|
|
||||||
if (f < -1.0)
|
|
||||||
f = -1.0;
|
|
||||||
else if (f > 1.0)
|
|
||||||
f = 1.0;
|
|
||||||
*(out++) = f * (float)INT32_MAX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sun_linear32_to_float(void * buf, unsigned sample_count)
|
|
||||||
{
|
|
||||||
int32_t * in = buf;
|
|
||||||
float * out = buf;
|
|
||||||
float * tail = out + sample_count;
|
|
||||||
|
|
||||||
while (out < tail) {
|
|
||||||
*(out++) = (1.0 / 0x80000000) * *(in++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sun_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
int32_t multiplier = vol * 0x8000;
|
|
||||||
|
|
||||||
for (i = 0; i < sample_count; ++i) {
|
|
||||||
buf[i] = (buf[i] * multiplier) >> 15;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
sun_io_routine(void * arg)
|
|
||||||
{
|
|
||||||
cubeb_stream *s = arg;
|
|
||||||
cubeb_state state = CUBEB_STATE_STARTED;
|
|
||||||
size_t to_read = 0;
|
|
||||||
long to_write = 0;
|
|
||||||
size_t write_ofs = 0;
|
|
||||||
size_t read_ofs = 0;
|
|
||||||
int drain = 0;
|
|
||||||
|
|
||||||
s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED);
|
|
||||||
while (state != CUBEB_STATE_ERROR) {
|
|
||||||
pthread_mutex_lock(&s->mutex);
|
|
||||||
if (!s->running) {
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
state = CUBEB_STATE_STOPPED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
if (s->record.fd != -1 && s->record.floating) {
|
|
||||||
sun_linear32_to_float(s->record.buf,
|
|
||||||
s->record.info.record.channels * SUN_BUFFER_FRAMES);
|
|
||||||
}
|
|
||||||
to_write = s->data_cb(s, s->user_ptr,
|
|
||||||
s->record.buf, s->play.buf, SUN_BUFFER_FRAMES);
|
|
||||||
if (to_write == CUBEB_ERROR) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (s->play.fd != -1) {
|
|
||||||
float vol;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&s->mutex);
|
|
||||||
vol = s->volume;
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
|
|
||||||
if (s->play.floating) {
|
|
||||||
sun_float_to_linear32(s->play.buf,
|
|
||||||
s->play.info.play.channels * to_write, vol);
|
|
||||||
} else {
|
|
||||||
sun_linear16_set_vol(s->play.buf,
|
|
||||||
s->play.info.play.channels * to_write, vol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (to_write < SUN_BUFFER_FRAMES) {
|
|
||||||
drain = 1;
|
|
||||||
}
|
|
||||||
to_write = s->play.fd != -1 ? to_write : 0;
|
|
||||||
to_read = s->record.fd != -1 ? SUN_BUFFER_FRAMES : 0;
|
|
||||||
write_ofs = 0;
|
|
||||||
read_ofs = 0;
|
|
||||||
while (to_write > 0 || to_read > 0) {
|
|
||||||
size_t bytes;
|
|
||||||
ssize_t n, frames;
|
|
||||||
|
|
||||||
if (to_write > 0) {
|
|
||||||
bytes = to_write * s->play.frame_size;
|
|
||||||
if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frames = n / s->play.frame_size;
|
|
||||||
pthread_mutex_lock(&s->mutex);
|
|
||||||
s->frames_written += frames;
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
to_write -= frames;
|
|
||||||
write_ofs += n;
|
|
||||||
}
|
|
||||||
if (to_read > 0) {
|
|
||||||
bytes = to_read * s->record.frame_size;
|
|
||||||
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
frames = n / s->record.frame_size;
|
|
||||||
to_read -= frames;
|
|
||||||
read_ofs += n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (drain && state != CUBEB_STATE_ERROR) {
|
|
||||||
state = CUBEB_STATE_DRAINED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s->state_cb(s, s->user_ptr, state);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_stream_init(cubeb * context,
|
|
||||||
cubeb_stream ** stream,
|
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
|
||||||
cubeb_devid output_device,
|
|
||||||
cubeb_stream_params * output_stream_params,
|
|
||||||
unsigned latency_frames,
|
|
||||||
cubeb_data_callback data_callback,
|
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr)
|
|
||||||
{
|
|
||||||
int ret = CUBEB_OK;
|
|
||||||
cubeb_stream *s = NULL;
|
|
||||||
|
|
||||||
(void)stream_name;
|
|
||||||
(void)latency_frames;
|
|
||||||
if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) {
|
|
||||||
ret = CUBEB_ERROR;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
s->record.fd = -1;
|
|
||||||
s->play.fd = -1;
|
|
||||||
if (input_device != 0) {
|
|
||||||
snprintf(s->record.name, sizeof(s->record.name),
|
|
||||||
"/dev/audio%zu", (uintptr_t)input_device - 1);
|
|
||||||
} else {
|
|
||||||
snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE);
|
|
||||||
}
|
|
||||||
if (output_device != 0) {
|
|
||||||
snprintf(s->play.name, sizeof(s->play.name),
|
|
||||||
"/dev/audio%zu", (uintptr_t)output_device - 1);
|
|
||||||
} else {
|
|
||||||
snprintf(s->play.name, sizeof(s->play.name), "%s", SUN_DEFAULT_DEVICE);
|
|
||||||
}
|
|
||||||
if (input_stream_params != NULL) {
|
|
||||||
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
|
||||||
LOG("Loopback not supported");
|
|
||||||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (s->record.fd == -1) {
|
|
||||||
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
|
|
||||||
LOG("Audio device could not be opened as read-only");
|
|
||||||
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AUDIO_INITINFO(&s->record.info);
|
|
||||||
#ifdef AUMODE_RECORD
|
|
||||||
s->record.info.mode = AUMODE_RECORD;
|
|
||||||
#endif
|
|
||||||
if ((ret = sun_copy_params(s->record.fd, s, input_stream_params,
|
|
||||||
&s->record.info, &s->record.info.record)) != CUBEB_OK) {
|
|
||||||
LOG("Setting record params failed");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
|
||||||
}
|
|
||||||
if (output_stream_params != NULL) {
|
|
||||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
|
||||||
LOG("Loopback not supported");
|
|
||||||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (s->play.fd == -1) {
|
|
||||||
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
|
|
||||||
LOG("Audio device could not be opened as write-only");
|
|
||||||
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
AUDIO_INITINFO(&s->play.info);
|
|
||||||
#ifdef AUMODE_PLAY
|
|
||||||
s->play.info.mode = AUMODE_PLAY;
|
|
||||||
#endif
|
|
||||||
if ((ret = sun_copy_params(s->play.fd, s, output_stream_params,
|
|
||||||
&s->play.info, &s->play.info.play)) != CUBEB_OK) {
|
|
||||||
LOG("Setting play params failed");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
|
||||||
}
|
|
||||||
s->context = context;
|
|
||||||
s->volume = 1.0;
|
|
||||||
s->state_cb = state_callback;
|
|
||||||
s->data_cb = data_callback;
|
|
||||||
s->user_ptr = user_ptr;
|
|
||||||
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
|
|
||||||
LOG("Failed to create mutex");
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
s->play.frame_size = s->play.info.play.channels *
|
|
||||||
(s->play.info.play.precision / 8);
|
|
||||||
if (s->play.fd != -1 &&
|
|
||||||
(s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) {
|
|
||||||
ret = CUBEB_ERROR;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
s->record.frame_size = s->record.info.record.channels *
|
|
||||||
(s->record.info.record.precision / 8);
|
|
||||||
if (s->record.fd != -1 &&
|
|
||||||
(s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == NULL) {
|
|
||||||
ret = CUBEB_ERROR;
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
*stream = s;
|
|
||||||
return CUBEB_OK;
|
|
||||||
error:
|
|
||||||
if (s != NULL) {
|
|
||||||
sun_stream_destroy(s);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_stream_start(cubeb_stream * s)
|
|
||||||
{
|
|
||||||
s->running = true;
|
|
||||||
if (pthread_create(&s->thread, NULL, sun_io_routine, s) != 0) {
|
|
||||||
LOG("Couldn't create thread");
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_stream_get_position(cubeb_stream * s, uint64_t * position)
|
|
||||||
{
|
|
||||||
#ifdef AUDIO_GETOOFFS
|
|
||||||
struct audio_offset offset;
|
|
||||||
|
|
||||||
if (ioctl(s->play.fd, AUDIO_GETOOFFS, &offset) == -1) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
s->blocks_written += offset.deltablks;
|
|
||||||
*position = (s->blocks_written * s->play.info.blocksize) / s->play.frame_size;
|
|
||||||
return CUBEB_OK;
|
|
||||||
#else
|
|
||||||
pthread_mutex_lock(&s->mutex);
|
|
||||||
*position = s->frames_written;
|
|
||||||
pthread_mutex_unlock(&s->mutex);
|
|
||||||
return CUBEB_OK;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_stream_get_latency(cubeb_stream * s, uint32_t * latency)
|
|
||||||
{
|
|
||||||
#ifdef AUDIO_GETBUFINFO
|
|
||||||
struct audio_info info;
|
|
||||||
|
|
||||||
if (ioctl(s->play.fd, AUDIO_GETBUFINFO, &info) == -1) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
*latency = (info.play.seek + info.blocksize) / s->play.frame_size;
|
|
||||||
return CUBEB_OK;
|
|
||||||
#else
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
params.rate = stream->play.info.play.sample_rate;
|
|
||||||
|
|
||||||
return sun_get_min_latency(NULL, params, latency);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_stream_set_volume(cubeb_stream * stream, float volume)
|
|
||||||
{
|
|
||||||
pthread_mutex_lock(&stream->mutex);
|
|
||||||
stream->volume = volume;
|
|
||||||
pthread_mutex_unlock(&stream->mutex);
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
|
|
||||||
{
|
|
||||||
*device = calloc(1, sizeof(cubeb_device));
|
|
||||||
if (*device == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
(*device)->input_name = stream->record.fd != -1 ?
|
|
||||||
strdup(stream->record.name) : NULL;
|
|
||||||
(*device)->output_name = stream->play.fd != -1 ?
|
|
||||||
strdup(stream->play.name) : NULL;
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
|
||||||
sun_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 struct cubeb_ops const sun_ops = {
|
|
||||||
.init = sun_init,
|
|
||||||
.get_backend_id = sun_get_backend_id,
|
|
||||||
.get_max_channel_count = sun_get_max_channel_count,
|
|
||||||
.get_min_latency = sun_get_min_latency,
|
|
||||||
.get_preferred_sample_rate = sun_get_preferred_sample_rate,
|
|
||||||
.enumerate_devices = sun_enumerate_devices,
|
|
||||||
.device_collection_destroy = sun_device_collection_destroy,
|
|
||||||
.destroy = sun_destroy,
|
|
||||||
.stream_init = sun_stream_init,
|
|
||||||
.stream_destroy = sun_stream_destroy,
|
|
||||||
.stream_start = sun_stream_start,
|
|
||||||
.stream_stop = sun_stream_stop,
|
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = sun_stream_get_position,
|
|
||||||
.stream_get_latency = sun_stream_get_latency,
|
|
||||||
.stream_get_input_latency = NULL,
|
|
||||||
.stream_set_volume = sun_stream_set_volume,
|
|
||||||
.stream_get_current_device = sun_get_current_device,
|
|
||||||
.stream_device_destroy = sun_stream_device_destroy,
|
|
||||||
.stream_register_device_changed_callback = NULL,
|
|
||||||
.register_device_collection_changed = NULL
|
|
||||||
};
|
|
24
thirdparty/cubeb/src/cubeb_utils.cpp
vendored
24
thirdparty/cubeb/src/cubeb_utils.cpp
vendored
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2018 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "cubeb_utils.h"
|
|
||||||
|
|
||||||
size_t cubeb_sample_size(cubeb_sample_format format)
|
|
||||||
{
|
|
||||||
switch (format) {
|
|
||||||
case CUBEB_SAMPLE_S16LE:
|
|
||||||
case CUBEB_SAMPLE_S16BE:
|
|
||||||
return sizeof(int16_t);
|
|
||||||
case CUBEB_SAMPLE_FLOAT32LE:
|
|
||||||
case CUBEB_SAMPLE_FLOAT32BE:
|
|
||||||
return sizeof(float);
|
|
||||||
default:
|
|
||||||
// should never happen as all cases are handled above.
|
|
||||||
assert(false);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
343
thirdparty/cubeb/src/cubeb_utils.h
vendored
343
thirdparty/cubeb/src/cubeb_utils.h
vendored
|
@ -1,343 +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 <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <mutex>
|
|
||||||
#include <type_traits>
|
|
||||||
#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<typename T>
|
|
||||||
void PodCopy(T * destination, const T * source, size_t count)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivial<T>::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<typename T>
|
|
||||||
void PodMove(T * destination, const T * source, size_t count)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivial<T>::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<typename T>
|
|
||||||
void PodZero(T * destination, size_t count)
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
|
||||||
assert(destination);
|
|
||||||
memset(destination, 0, count * sizeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<typename T, typename Trait>
|
|
||||||
void Copy(T * destination, const T * source, size_t count, Trait)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
destination[i] = source[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
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<typename T>
|
|
||||||
void Copy(T * destination, const T * source, size_t count)
|
|
||||||
{
|
|
||||||
assert(destination && source);
|
|
||||||
Copy(destination, source, count, typename std::is_trivial<T>::type());
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
template<typename T, typename Trait>
|
|
||||||
void ConstructDefault(T * destination, size_t count, Trait)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < count; i++) {
|
|
||||||
destination[i] = T();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
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<typename T>
|
|
||||||
void ConstructDefault(T * destination, size_t count)
|
|
||||||
{
|
|
||||||
assert(destination);
|
|
||||||
ConstructDefault(destination, count,
|
|
||||||
typename std::is_arithmetic<T>::type());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
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 <typename T>
|
|
||||||
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<T *>(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<T> ar;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
size_t cubeb_sample_size(cubeb_sample_format format);
|
|
||||||
}
|
|
||||||
|
|
||||||
using auto_lock = std::lock_guard<owned_critical_section>;
|
|
||||||
#endif // __cplusplus
|
|
||||||
|
|
||||||
#endif /* CUBEB_UTILS */
|
|
89
thirdparty/cubeb/src/cubeb_utils_unix.h
vendored
89
thirdparty/cubeb/src/cubeb_utils_unix.h
vendored
|
@ -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 <pthread.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
/* 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 */
|
|
71
thirdparty/cubeb/src/cubeb_utils_win.h
vendored
71
thirdparty/cubeb/src/cubeb_utils_win.h
vendored
|
@ -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 <windows.h>
|
|
||||||
#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 */
|
|
3038
thirdparty/cubeb/src/cubeb_wasapi.cpp
vendored
3038
thirdparty/cubeb/src/cubeb_wasapi.cpp
vendored
File diff suppressed because it is too large
Load Diff
1068
thirdparty/cubeb/src/cubeb_winmm.c
vendored
1068
thirdparty/cubeb/src/cubeb_winmm.c
vendored
File diff suppressed because it is too large
Load Diff
235
thirdparty/cubeb/src/speex/arch.h
vendored
235
thirdparty/cubeb/src/speex/arch.h
vendored
|
@ -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
|
|
110
thirdparty/cubeb/src/speex/fixed_generic.h
vendored
110
thirdparty/cubeb/src/speex/fixed_generic.h
vendored
|
@ -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
|
|
1240
thirdparty/cubeb/src/speex/resample.c
vendored
1240
thirdparty/cubeb/src/speex/resample.c
vendored
File diff suppressed because it is too large
Load Diff
201
thirdparty/cubeb/src/speex/resample_neon.h
vendored
201
thirdparty/cubeb/src/speex/resample_neon.h
vendored
|
@ -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 <arm_neon.h>
|
|
||||||
|
|
||||||
#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
|
|
128
thirdparty/cubeb/src/speex/resample_sse.h
vendored
128
thirdparty/cubeb/src/speex/resample_sse.h
vendored
|
@ -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 <xmmintrin.h>
|
|
||||||
|
|
||||||
#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<len;i+=8)
|
|
||||||
{
|
|
||||||
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));
|
|
||||||
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));
|
|
||||||
}
|
|
||||||
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
|
|
||||||
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
|
|
||||||
_mm_store_ss(&ret, sum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
|
|
||||||
static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
|
|
||||||
int i;
|
|
||||||
float ret;
|
|
||||||
__m128 sum = _mm_setzero_ps();
|
|
||||||
__m128 f = _mm_loadu_ps(frac);
|
|
||||||
for(i=0;i<len;i+=2)
|
|
||||||
{
|
|
||||||
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)));
|
|
||||||
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)));
|
|
||||||
}
|
|
||||||
sum = _mm_mul_ps(f, sum);
|
|
||||||
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
|
|
||||||
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
|
|
||||||
_mm_store_ss(&ret, sum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _USE_SSE2
|
|
||||||
#include <emmintrin.h>
|
|
||||||
#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<len;i+=8)
|
|
||||||
{
|
|
||||||
t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i));
|
|
||||||
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
|
|
||||||
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
|
||||||
|
|
||||||
t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4));
|
|
||||||
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
|
|
||||||
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
|
||||||
}
|
|
||||||
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
|
|
||||||
_mm_store_sd(&ret, sum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
|
|
||||||
static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
|
|
||||||
int i;
|
|
||||||
double ret;
|
|
||||||
__m128d sum;
|
|
||||||
__m128d sum1 = _mm_setzero_pd();
|
|
||||||
__m128d sum2 = _mm_setzero_pd();
|
|
||||||
__m128 f = _mm_loadu_ps(frac);
|
|
||||||
__m128d f1 = _mm_cvtps_pd(f);
|
|
||||||
__m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f));
|
|
||||||
__m128 t;
|
|
||||||
for(i=0;i<len;i+=2)
|
|
||||||
{
|
|
||||||
t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample));
|
|
||||||
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
|
|
||||||
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
|
||||||
|
|
||||||
t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample));
|
|
||||||
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
|
|
||||||
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
|
|
||||||
}
|
|
||||||
sum1 = _mm_mul_pd(f1, sum1);
|
|
||||||
sum2 = _mm_mul_pd(f2, sum2);
|
|
||||||
sum = _mm_add_pd(sum1, sum2);
|
|
||||||
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
|
|
||||||
_mm_store_sd(&ret, sum);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
10
thirdparty/cubeb/src/speex/speex_config_types.h
vendored
10
thirdparty/cubeb/src/speex/speex_config_types.h
vendored
|
@ -1,10 +0,0 @@
|
||||||
#ifndef __SPEEX_TYPES_H__
|
|
||||||
#define __SPEEX_TYPES_H__
|
|
||||||
|
|
||||||
/* these are filled in by configure */
|
|
||||||
typedef int16_t spx_int16_t;
|
|
||||||
typedef uint16_t spx_uint16_t;
|
|
||||||
typedef int32_t spx_int32_t;
|
|
||||||
typedef uint32_t spx_uint32_t;
|
|
||||||
|
|
||||||
#endif
|
|
343
thirdparty/cubeb/src/speex/speex_resampler.h
vendored
343
thirdparty/cubeb/src/speex/speex_resampler.h
vendored
|
@ -1,343 +0,0 @@
|
||||||
/* Copyright (C) 2007 Jean-Marc Valin
|
|
||||||
|
|
||||||
File: speex_resampler.h
|
|
||||||
Resampling code
|
|
||||||
|
|
||||||
The design goals of this code are:
|
|
||||||
- Very fast algorithm
|
|
||||||
- Low memory requirement
|
|
||||||
- Good *perceptual* quality (and not best SNR)
|
|
||||||
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef SPEEX_RESAMPLER_H
|
|
||||||
#define SPEEX_RESAMPLER_H
|
|
||||||
|
|
||||||
#ifdef OUTSIDE_SPEEX
|
|
||||||
|
|
||||||
/********* WARNING: MENTAL SANITY ENDS HERE *************/
|
|
||||||
|
|
||||||
/* If the resampler is defined outside of Speex, we change the symbol names so that
|
|
||||||
there won't be any clash if linking with Speex later on. */
|
|
||||||
|
|
||||||
/* #define RANDOM_PREFIX your software name here */
|
|
||||||
#ifndef RANDOM_PREFIX
|
|
||||||
#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define CAT_PREFIX2(a,b) a ## b
|
|
||||||
#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
|
|
||||||
|
|
||||||
#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
|
|
||||||
#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
|
|
||||||
#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
|
|
||||||
#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
|
|
||||||
#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
|
|
||||||
#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
|
|
||||||
#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
|
|
||||||
#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
|
|
||||||
#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
|
|
||||||
#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
|
|
||||||
#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
|
|
||||||
#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
|
|
||||||
#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
|
|
||||||
#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
|
|
||||||
#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
|
|
||||||
#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
|
|
||||||
#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
|
|
||||||
#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
|
|
||||||
#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
|
|
||||||
#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
|
|
||||||
#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
|
|
||||||
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
|
|
||||||
|
|
||||||
#define spx_int16_t short
|
|
||||||
#define spx_int32_t int
|
|
||||||
#define spx_uint16_t unsigned short
|
|
||||||
#define spx_uint32_t unsigned int
|
|
||||||
|
|
||||||
#define speex_assert(cond)
|
|
||||||
|
|
||||||
#else /* OUTSIDE_SPEEX */
|
|
||||||
|
|
||||||
#include "speexdsp_types.h"
|
|
||||||
|
|
||||||
#endif /* OUTSIDE_SPEEX */
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SPEEX_RESAMPLER_QUALITY_MAX 10
|
|
||||||
#define SPEEX_RESAMPLER_QUALITY_MIN 0
|
|
||||||
#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
|
|
||||||
#define SPEEX_RESAMPLER_QUALITY_VOIP 3
|
|
||||||
#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
|
|
||||||
|
|
||||||
enum {
|
|
||||||
RESAMPLER_ERR_SUCCESS = 0,
|
|
||||||
RESAMPLER_ERR_ALLOC_FAILED = 1,
|
|
||||||
RESAMPLER_ERR_BAD_STATE = 2,
|
|
||||||
RESAMPLER_ERR_INVALID_ARG = 3,
|
|
||||||
RESAMPLER_ERR_PTR_OVERLAP = 4,
|
|
||||||
RESAMPLER_ERR_OVERFLOW = 5,
|
|
||||||
|
|
||||||
RESAMPLER_ERR_MAX_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SpeexResamplerState_;
|
|
||||||
typedef struct SpeexResamplerState_ SpeexResamplerState;
|
|
||||||
|
|
||||||
/** Create a new resampler with integer input and output rates.
|
|
||||||
* @param nb_channels Number of channels to be processed
|
|
||||||
* @param in_rate Input sampling rate (integer number of Hz).
|
|
||||||
* @param out_rate Output sampling rate (integer number of Hz).
|
|
||||||
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
|
|
||||||
* and 10 has very high quality.
|
|
||||||
* @return Newly created resampler state
|
|
||||||
* @retval NULL Error: not enough memory
|
|
||||||
*/
|
|
||||||
SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
|
|
||||||
spx_uint32_t in_rate,
|
|
||||||
spx_uint32_t out_rate,
|
|
||||||
int quality,
|
|
||||||
int *err);
|
|
||||||
|
|
||||||
/** Create a new resampler with fractional input/output rates. The sampling
|
|
||||||
* rate ratio is an arbitrary rational number with both the numerator and
|
|
||||||
* denominator being 32-bit integers.
|
|
||||||
* @param nb_channels Number of channels to be processed
|
|
||||||
* @param ratio_num Numerator of the sampling rate ratio
|
|
||||||
* @param ratio_den Denominator of the sampling rate ratio
|
|
||||||
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
|
|
||||||
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
|
|
||||||
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
|
|
||||||
* and 10 has very high quality.
|
|
||||||
* @return Newly created resampler state
|
|
||||||
* @retval NULL Error: not enough memory
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
/** Destroy a resampler state.
|
|
||||||
* @param st Resampler state
|
|
||||||
*/
|
|
||||||
void speex_resampler_destroy(SpeexResamplerState *st);
|
|
||||||
|
|
||||||
/** Resample a float array. The input and output buffers must *not* overlap.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param channel_index Index of the channel to process for the multi-channel
|
|
||||||
* base (0 otherwise)
|
|
||||||
* @param in Input buffer
|
|
||||||
* @param in_len Number of input samples in the input buffer. Returns the
|
|
||||||
* number of samples processed
|
|
||||||
* @param out Output buffer
|
|
||||||
* @param out_len Size of the output buffer. Returns the number of samples written
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
/** Resample an int array. The input and output buffers must *not* overlap.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param channel_index Index of the channel to process for the multi-channel
|
|
||||||
* base (0 otherwise)
|
|
||||||
* @param in Input buffer
|
|
||||||
* @param in_len Number of input samples in the input buffer. Returns the number
|
|
||||||
* of samples processed
|
|
||||||
* @param out Output buffer
|
|
||||||
* @param out_len Size of the output buffer. Returns the number of samples written
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
/** Resample an interleaved float array. The input and output buffers must *not* overlap.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param in Input buffer
|
|
||||||
* @param in_len Number of input samples in the input buffer. Returns the number
|
|
||||||
* of samples processed. This is all per-channel.
|
|
||||||
* @param out Output buffer
|
|
||||||
* @param out_len Size of the output buffer. Returns the number of samples written.
|
|
||||||
* This is all per-channel.
|
|
||||||
*/
|
|
||||||
int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
|
|
||||||
const float *in,
|
|
||||||
spx_uint32_t *in_len,
|
|
||||||
float *out,
|
|
||||||
spx_uint32_t *out_len);
|
|
||||||
|
|
||||||
/** Resample an interleaved int array. The input and output buffers must *not* overlap.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param in Input buffer
|
|
||||||
* @param in_len Number of input samples in the input buffer. Returns the number
|
|
||||||
* of samples processed. This is all per-channel.
|
|
||||||
* @param out Output buffer
|
|
||||||
* @param out_len Size of the output buffer. Returns the number of samples written.
|
|
||||||
* This is all per-channel.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
/** Set (change) the input/output sampling rates (integer value).
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param in_rate Input sampling rate (integer number of Hz).
|
|
||||||
* @param out_rate Output sampling rate (integer number of Hz).
|
|
||||||
*/
|
|
||||||
int speex_resampler_set_rate(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t in_rate,
|
|
||||||
spx_uint32_t out_rate);
|
|
||||||
|
|
||||||
/** Get the current input/output sampling rates (integer value).
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param in_rate Input sampling rate (integer number of Hz) copied.
|
|
||||||
* @param out_rate Output sampling rate (integer number of Hz) copied.
|
|
||||||
*/
|
|
||||||
void speex_resampler_get_rate(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t *in_rate,
|
|
||||||
spx_uint32_t *out_rate);
|
|
||||||
|
|
||||||
/** Set (change) the input/output sampling rates and resampling ratio
|
|
||||||
* (fractional values in Hz supported).
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param ratio_num Numerator of the sampling rate ratio
|
|
||||||
* @param ratio_den Denominator of the sampling rate ratio
|
|
||||||
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
|
|
||||||
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
|
|
||||||
/** Get the current resampling ratio. This will be reduced to the least
|
|
||||||
* common denominator.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param ratio_num Numerator of the sampling rate ratio copied
|
|
||||||
* @param ratio_den Denominator of the sampling rate ratio copied
|
|
||||||
*/
|
|
||||||
void speex_resampler_get_ratio(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t *ratio_num,
|
|
||||||
spx_uint32_t *ratio_den);
|
|
||||||
|
|
||||||
/** Set (change) the conversion quality.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param quality Resampling quality between 0 and 10, where 0 has poor
|
|
||||||
* quality and 10 has very high quality.
|
|
||||||
*/
|
|
||||||
int speex_resampler_set_quality(SpeexResamplerState *st,
|
|
||||||
int quality);
|
|
||||||
|
|
||||||
/** Get the conversion quality.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param quality Resampling quality between 0 and 10, where 0 has poor
|
|
||||||
* quality and 10 has very high quality.
|
|
||||||
*/
|
|
||||||
void speex_resampler_get_quality(SpeexResamplerState *st,
|
|
||||||
int *quality);
|
|
||||||
|
|
||||||
/** Set (change) the input stride.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param stride Input stride
|
|
||||||
*/
|
|
||||||
void speex_resampler_set_input_stride(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t stride);
|
|
||||||
|
|
||||||
/** Get the input stride.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param stride Input stride copied
|
|
||||||
*/
|
|
||||||
void speex_resampler_get_input_stride(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t *stride);
|
|
||||||
|
|
||||||
/** Set (change) the output stride.
|
|
||||||
* @param st Resampler state
|
|
||||||
* @param stride Output stride
|
|
||||||
*/
|
|
||||||
void speex_resampler_set_output_stride(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t stride);
|
|
||||||
|
|
||||||
/** Get the output stride.
|
|
||||||
* @param st Resampler state copied
|
|
||||||
* @param stride Output stride
|
|
||||||
*/
|
|
||||||
void speex_resampler_get_output_stride(SpeexResamplerState *st,
|
|
||||||
spx_uint32_t *stride);
|
|
||||||
|
|
||||||
/** Get the latency introduced by the resampler measured in input samples.
|
|
||||||
* @param st Resampler state
|
|
||||||
*/
|
|
||||||
int speex_resampler_get_input_latency(SpeexResamplerState *st);
|
|
||||||
|
|
||||||
/** Get the latency introduced by the resampler measured in output samples.
|
|
||||||
* @param st Resampler state
|
|
||||||
*/
|
|
||||||
int speex_resampler_get_output_latency(SpeexResamplerState *st);
|
|
||||||
|
|
||||||
/** Make sure that the first samples to go out of the resamplers don't have
|
|
||||||
* leading zeros. This is only useful before starting to use a newly created
|
|
||||||
* resampler. It is recommended to use that when resampling an audio file, as
|
|
||||||
* it will generate a file with the same length. For real-time processing,
|
|
||||||
* it is probably easier not to use this call (so that the output duration
|
|
||||||
* is the same for the first frame).
|
|
||||||
* @param st Resampler state
|
|
||||||
*/
|
|
||||||
int speex_resampler_skip_zeros(SpeexResamplerState *st);
|
|
||||||
|
|
||||||
/** Reset a resampler so a new (unrelated) stream can be processed.
|
|
||||||
* @param st Resampler state
|
|
||||||
*/
|
|
||||||
int speex_resampler_reset_mem(SpeexResamplerState *st);
|
|
||||||
|
|
||||||
/** Returns the English meaning for an error code
|
|
||||||
* @param err Error code
|
|
||||||
* @return English string
|
|
||||||
*/
|
|
||||||
const char *speex_resampler_strerror(int err);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
115
thirdparty/cubeb/src/speex/stack_alloc.h
vendored
115
thirdparty/cubeb/src/speex/stack_alloc.h
vendored
|
@ -1,115 +0,0 @@
|
||||||
/* Copyright (C) 2002 Jean-Marc Valin */
|
|
||||||
/**
|
|
||||||
@file stack_alloc.h
|
|
||||||
@brief Temporary memory allocation on stack
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
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 STACK_ALLOC_H
|
|
||||||
#define STACK_ALLOC_H
|
|
||||||
|
|
||||||
#ifdef USE_ALLOCA
|
|
||||||
# ifdef WIN32
|
|
||||||
# include <malloc.h>
|
|
||||||
# else
|
|
||||||
# ifdef HAVE_ALLOCA_H
|
|
||||||
# include <alloca.h>
|
|
||||||
# else
|
|
||||||
# include <stdlib.h>
|
|
||||||
# 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 <valgrind/memcheck.h>
|
|
||||||
|
|
||||||
#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
|
|
13
thirdparty/cubeb/test/README.md
vendored
13
thirdparty/cubeb/test/README.md
vendored
|
@ -1,13 +0,0 @@
|
||||||
Notes on writing tests.
|
|
||||||
|
|
||||||
The googletest submodule is currently at 1.6 rather than the latest, and should
|
|
||||||
only be updated to track the version used in Gecko to make test compatibility
|
|
||||||
easier.
|
|
||||||
|
|
||||||
Always #include "gtest/gtest.h" before *anything* else.
|
|
||||||
|
|
||||||
All tests should be part of the "cubeb" test case, e.g. TEST(cubeb, my_test).
|
|
||||||
|
|
||||||
Tests are built stand-alone in cubeb, but built as a single unit in Gecko, so
|
|
||||||
you must use unique names for globally visible items in each test, e.g. rather
|
|
||||||
than state_cb use state_cb_my_test.
|
|
145
thirdparty/cubeb/test/common.h
vendored
145
thirdparty/cubeb/test/common.h
vendored
|
@ -1,145 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2013 Sebastien Alaiwan
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
#if !defined(TEST_COMMON)
|
|
||||||
#define TEST_COMMON
|
|
||||||
|
|
||||||
#if defined( _WIN32)
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <objbase.h>
|
|
||||||
#include <windows.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <cstdarg>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb_mixer.h"
|
|
||||||
|
|
||||||
template<typename T, size_t N>
|
|
||||||
constexpr size_t
|
|
||||||
ARRAY_LENGTH(T(&)[N])
|
|
||||||
{
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
|
|
||||||
void delay(unsigned int ms)
|
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
Sleep(ms);
|
|
||||||
#else
|
|
||||||
sleep(ms / 1000);
|
|
||||||
usleep(ms % 1000 * 1000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(M_PI)
|
|
||||||
#define M_PI 3.14159265358979323846
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char const * name;
|
|
||||||
unsigned int const channels;
|
|
||||||
uint32_t const layout;
|
|
||||||
} layout_info;
|
|
||||||
|
|
||||||
int has_available_input_device(cubeb * ctx)
|
|
||||||
{
|
|
||||||
cubeb_device_collection devices;
|
|
||||||
int input_device_available = 0;
|
|
||||||
int r;
|
|
||||||
/* Bail out early if the host does not have input devices. */
|
|
||||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &devices);
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "error enumerating devices.");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (devices.count == 0) {
|
|
||||||
fprintf(stderr, "no input device available, skipping test.\n");
|
|
||||||
cubeb_device_collection_destroy(ctx, &devices);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < devices.count; i++) {
|
|
||||||
input_device_available |= (devices.device[i].state ==
|
|
||||||
CUBEB_DEVICE_STATE_ENABLED);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!input_device_available) {
|
|
||||||
fprintf(stderr, "there are input devices, but they are not "
|
|
||||||
"available, skipping\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_device_collection_destroy(ctx, &devices);
|
|
||||||
return !!input_device_available;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_log(const char * msg, ...)
|
|
||||||
{
|
|
||||||
va_list args;
|
|
||||||
va_start(args, msg);
|
|
||||||
vprintf(msg, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initialize cubeb with backend override.
|
|
||||||
* Create call cubeb_init passing value for CUBEB_BACKEND env var as
|
|
||||||
* override. */
|
|
||||||
int common_init(cubeb ** ctx, char const * ctx_name)
|
|
||||||
{
|
|
||||||
#ifdef ENABLE_NORMAL_LOG
|
|
||||||
if (cubeb_set_log_callback(CUBEB_LOG_NORMAL, print_log) != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Set normal log callback failed\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef ENABLE_VERBOSE_LOG
|
|
||||||
if (cubeb_set_log_callback(CUBEB_LOG_VERBOSE, print_log) != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Set verbose log callback failed\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int r;
|
|
||||||
char const * backend;
|
|
||||||
char const * ctx_backend;
|
|
||||||
|
|
||||||
backend = getenv("CUBEB_BACKEND");
|
|
||||||
r = cubeb_init(ctx, ctx_name, backend);
|
|
||||||
if (r == CUBEB_OK && backend) {
|
|
||||||
ctx_backend = cubeb_get_backend_id(*ctx);
|
|
||||||
if (strcmp(backend, ctx_backend) != 0) {
|
|
||||||
fprintf(stderr, "Requested backend `%s', got `%s'\n",
|
|
||||||
backend, ctx_backend);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined( _WIN32)
|
|
||||||
class TestEnvironment : public ::testing::Environment {
|
|
||||||
public:
|
|
||||||
void SetUp() override {
|
|
||||||
hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TearDown() override {
|
|
||||||
if (SUCCEEDED(hr)) {
|
|
||||||
CoUninitialize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
HRESULT hr;
|
|
||||||
};
|
|
||||||
|
|
||||||
::testing::Environment* const foo_env = ::testing::AddGlobalTestEnvironment(new TestEnvironment);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* TEST_COMMON */
|
|
244
thirdparty/cubeb/test/test_audio.cpp
vendored
244
thirdparty/cubeb/test/test_audio.cpp
vendored
|
@ -1,244 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2013 Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function exhaustive test. Plays a series of tones in different
|
|
||||||
* conditions. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <string.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
#define MAX_NUM_CHANNELS 32
|
|
||||||
|
|
||||||
#if !defined(M_PI)
|
|
||||||
#define M_PI 3.14159265358979323846
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VOLUME 0.2
|
|
||||||
|
|
||||||
float get_frequency(int channel_index)
|
|
||||||
{
|
|
||||||
return 220.0f * (channel_index+1);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T> T ConvertSample(double input);
|
|
||||||
template<> float ConvertSample(double input) { return input; }
|
|
||||||
template<> short ConvertSample(double input) { return short(input * 32767.0f); }
|
|
||||||
|
|
||||||
/* store the phase of the generated waveform */
|
|
||||||
struct synth_state {
|
|
||||||
synth_state(int num_channels_, float sample_rate_)
|
|
||||||
: num_channels(num_channels_),
|
|
||||||
sample_rate(sample_rate_)
|
|
||||||
{
|
|
||||||
for(int i=0;i < MAX_NUM_CHANNELS;++i)
|
|
||||||
phase[i] = 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void run(T* audiobuffer, long nframes)
|
|
||||||
{
|
|
||||||
for(int c=0;c < num_channels;++c) {
|
|
||||||
float freq = get_frequency(c);
|
|
||||||
float phase_inc = 2.0 * M_PI * freq / sample_rate;
|
|
||||||
for(long n=0;n < nframes;++n) {
|
|
||||||
audiobuffer[n*num_channels+c] = ConvertSample<T>(sin(phase[c]) * VOLUME);
|
|
||||||
phase[c] += phase_inc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
int num_channels;
|
|
||||||
float phase[MAX_NUM_CHANNELS];
|
|
||||||
float sample_rate;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
long data_cb(cubeb_stream * /*stream*/, void * user, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
synth_state *synth = (synth_state *)user;
|
|
||||||
synth->run((T*)outputbuffer, nframes);
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Our android backends don't support float, only int16. */
|
|
||||||
int supports_float32(string backend_id)
|
|
||||||
{
|
|
||||||
return backend_id != "opensl"
|
|
||||||
&& backend_id != "audiotrack";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Some backends don't have code to deal with more than mono or stereo. */
|
|
||||||
int supports_channel_count(string backend_id, int nchannels)
|
|
||||||
{
|
|
||||||
return nchannels <= 2 ||
|
|
||||||
(backend_id != "opensl" && backend_id != "audiotrack");
|
|
||||||
}
|
|
||||||
|
|
||||||
int run_test(int num_channels, int sampling_rate, int is_float)
|
|
||||||
{
|
|
||||||
int r = CUBEB_OK;
|
|
||||||
|
|
||||||
cubeb *ctx = NULL;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb audio test: channels");
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Error initializing cubeb library\n");
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
const char * backend_id = cubeb_get_backend_id(ctx);
|
|
||||||
|
|
||||||
if ((is_float && !supports_float32(backend_id)) ||
|
|
||||||
!supports_channel_count(backend_id, num_channels)) {
|
|
||||||
/* don't treat this as a test failure. */
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Testing %d channel(s), %d Hz, %s (%s)\n", num_channels, sampling_rate, is_float ? "float" : "short", cubeb_get_backend_id(ctx));
|
|
||||||
|
|
||||||
cubeb_stream_params params;
|
|
||||||
params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
|
|
||||||
params.rate = sampling_rate;
|
|
||||||
params.channels = num_channels;
|
|
||||||
params.layout = CUBEB_LAYOUT_UNDEFINED;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
synth_state synth(params.channels, params.rate);
|
|
||||||
|
|
||||||
cubeb_stream *stream = NULL;
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, ¶ms,
|
|
||||||
4096, is_float ? &data_cb<float> : &data_cb<short>, state_cb_audio, &synth);
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(200);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
int run_volume_test(int is_float)
|
|
||||||
{
|
|
||||||
int r = CUBEB_OK;
|
|
||||||
|
|
||||||
cubeb *ctx = NULL;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb audio test");
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Error initializing cubeb library\n");
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
const char * backend_id = cubeb_get_backend_id(ctx);
|
|
||||||
|
|
||||||
if ((is_float && !supports_float32(backend_id))) {
|
|
||||||
/* don't treat this as a test failure. */
|
|
||||||
return CUBEB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_stream_params params;
|
|
||||||
params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16NE;
|
|
||||||
params.rate = 44100;
|
|
||||||
params.channels = 2;
|
|
||||||
params.layout = CUBEB_LAYOUT_STEREO;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
synth_state synth(params.channels, params.rate);
|
|
||||||
|
|
||||||
cubeb_stream *stream = NULL;
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test tone", NULL, NULL, NULL, ¶ms,
|
|
||||||
4096, is_float ? &data_cb<float> : &data_cb<short>,
|
|
||||||
state_cb_audio, &synth);
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Error initializing cubeb stream: %d\n", r);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
fprintf(stderr, "Testing: volume\n");
|
|
||||||
for(int i=0;i <= 4; ++i)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Volume: %d%%\n", i*25);
|
|
||||||
|
|
||||||
cubeb_stream_set_volume(stream, i/4.0f);
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(400);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
delay(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, run_volume_test_short)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(run_volume_test(0), CUBEB_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, run_volume_test_float)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(run_volume_test(1), CUBEB_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, run_channel_rate_test)
|
|
||||||
{
|
|
||||||
unsigned int channel_values[] = {
|
|
||||||
1,
|
|
||||||
2,
|
|
||||||
3,
|
|
||||||
4,
|
|
||||||
6,
|
|
||||||
};
|
|
||||||
|
|
||||||
int freq_values[] = {
|
|
||||||
16000,
|
|
||||||
24000,
|
|
||||||
44100,
|
|
||||||
48000,
|
|
||||||
};
|
|
||||||
|
|
||||||
for(auto channels : channel_values) {
|
|
||||||
for(auto freq : freq_values) {
|
|
||||||
ASSERT_TRUE(channels < MAX_NUM_CHANNELS);
|
|
||||||
fprintf(stderr, "--------------------------\n");
|
|
||||||
ASSERT_EQ(run_test(channels, freq, 0), CUBEB_OK);
|
|
||||||
ASSERT_EQ(run_test(channels, freq, 1), CUBEB_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
207
thirdparty/cubeb/test/test_callback_ret.cpp
vendored
207
thirdparty/cubeb/test/test_callback_ret.cpp
vendored
|
@ -1,207 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright <EFBFBD> 2017 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function test. Test that different return values from user
|
|
||||||
specified callbacks are handled correctly. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
|
||||||
#include <string>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
const uint32_t SAMPLE_FREQUENCY = 48000;
|
|
||||||
const cubeb_sample_format SAMPLE_FORMAT = CUBEB_SAMPLE_S16NE;
|
|
||||||
|
|
||||||
enum test_direction {
|
|
||||||
INPUT_ONLY,
|
|
||||||
OUTPUT_ONLY,
|
|
||||||
DUPLEX
|
|
||||||
};
|
|
||||||
|
|
||||||
// Structure which is used by data callbacks to track the total callbacks
|
|
||||||
// executed vs the number of callbacks expected.
|
|
||||||
struct user_state_callback_ret {
|
|
||||||
std::atomic<int> cb_count{ 0 };
|
|
||||||
std::atomic<int> expected_cb_count{ 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
// Data callback that always returns 0
|
|
||||||
long data_cb_ret_zero(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
user_state_callback_ret * u = (user_state_callback_ret *) user;
|
|
||||||
// If this is the first time the callback has been called set our expected
|
|
||||||
// callback count
|
|
||||||
if (u->cb_count == 0) {
|
|
||||||
u->expected_cb_count = 1;
|
|
||||||
}
|
|
||||||
u->cb_count++;
|
|
||||||
if (nframes < 1) {
|
|
||||||
// This shouldn't happen
|
|
||||||
EXPECT_TRUE(false) << "nframes should not be 0 in data callback!";
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data callback that always returns nframes - 1
|
|
||||||
long data_cb_ret_nframes_minus_one(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
user_state_callback_ret * u = (user_state_callback_ret *)user;
|
|
||||||
// If this is the first time the callback has been called set our expected
|
|
||||||
// callback count
|
|
||||||
if (u->cb_count == 0) {
|
|
||||||
u->expected_cb_count = 1;
|
|
||||||
}
|
|
||||||
u->cb_count++;
|
|
||||||
if (nframes < 1) {
|
|
||||||
// This shouldn't happen
|
|
||||||
EXPECT_TRUE(false) << "nframes should not be 0 in data callback!";
|
|
||||||
}
|
|
||||||
if (outputbuffer != NULL) {
|
|
||||||
// If we have an output buffer insert silence
|
|
||||||
short * ob = (short *) outputbuffer;
|
|
||||||
for (long i = 0; i < nframes - 1; i++) {
|
|
||||||
ob[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nframes - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Data callback that always returns nframes
|
|
||||||
long data_cb_ret_nframes(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
user_state_callback_ret * u = (user_state_callback_ret *)user;
|
|
||||||
u->cb_count++;
|
|
||||||
// Every callback returns nframes, so every callback is expected
|
|
||||||
u->expected_cb_count++;
|
|
||||||
if (nframes < 1) {
|
|
||||||
// This shouldn't happen
|
|
||||||
EXPECT_TRUE(false) << "nframes should not be 0 in data callback!";
|
|
||||||
}
|
|
||||||
if (outputbuffer != NULL) {
|
|
||||||
// If we have an output buffer insert silence
|
|
||||||
short * ob = (short *) outputbuffer;
|
|
||||||
for (long i = 0; i < nframes; i++) {
|
|
||||||
ob[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_ret(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
if (stream == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
fprintf(stderr, "stream started\n"); break;
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
fprintf(stderr, "stream drained\n"); break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "unknown stream state %d\n", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_test_callback(test_direction direction,
|
|
||||||
cubeb_data_callback data_cb,
|
|
||||||
const std::string & test_desc) {
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
user_state_callback_ret user_state;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb callback return value example");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
if ((direction == INPUT_ONLY || direction == DUPLEX) &&
|
|
||||||
!has_available_input_device(ctx)) {
|
|
||||||
/* This test needs an available input device, skip it if this host does not
|
|
||||||
* have one. */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup all params, but only pass them later as required by direction
|
|
||||||
input_params.format = SAMPLE_FORMAT;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = 1;
|
|
||||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
output_params = input_params;
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &input_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
switch (direction)
|
|
||||||
{
|
|
||||||
case INPUT_ONLY:
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret input",
|
|
||||||
NULL, &input_params, NULL, NULL,
|
|
||||||
latency_frames, data_cb, state_cb_ret, &user_state);
|
|
||||||
break;
|
|
||||||
case OUTPUT_ONLY:
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret output",
|
|
||||||
NULL, NULL, NULL, &output_params,
|
|
||||||
latency_frames, data_cb, state_cb_ret, &user_state);
|
|
||||||
break;
|
|
||||||
case DUPLEX:
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb callback ret duplex",
|
|
||||||
NULL, &input_params, NULL, &output_params,
|
|
||||||
latency_frames, data_cb, state_cb_ret, &user_state);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ASSERT_TRUE(false) << "Unrecognized test direction!";
|
|
||||||
}
|
|
||||||
EXPECT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(100);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
|
|
||||||
ASSERT_EQ(user_state.expected_cb_count, user_state.cb_count) <<
|
|
||||||
"Callback called unexpected number of times for " << test_desc << "!";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, test_input_callback)
|
|
||||||
{
|
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_zero, "input only, return 0");
|
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_nframes_minus_one, "input only, return nframes - 1");
|
|
||||||
run_test_callback(INPUT_ONLY, data_cb_ret_nframes, "input only, return nframes");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, test_output_callback)
|
|
||||||
{
|
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_zero, "output only, return 0");
|
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes_minus_one, "output only, return nframes - 1");
|
|
||||||
run_test_callback(OUTPUT_ONLY, data_cb_ret_nframes, "output only, return nframes");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, test_duplex_callback)
|
|
||||||
{
|
|
||||||
run_test_callback(DUPLEX, data_cb_ret_zero, "duplex, return 0");
|
|
||||||
run_test_callback(DUPLEX, data_cb_ret_nframes_minus_one, "duplex, return nframes - 1");
|
|
||||||
run_test_callback(DUPLEX, data_cb_ret_nframes, "duplex, return nframes");
|
|
||||||
}
|
|
260
thirdparty/cubeb/test/test_deadlock.cpp
vendored
260
thirdparty/cubeb/test/test_deadlock.cpp
vendored
|
@ -1,260 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2017 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Purpose
|
|
||||||
* =============================================================================
|
|
||||||
* In CoreAudio, the data callback will holds a mutex shared with AudioUnit
|
|
||||||
* (mutex_AU). Thus, if the callback request another mutex M held by the another
|
|
||||||
* function, without releasing mutex_AU, then it will cause a deadlock when the
|
|
||||||
* another function, which holds the mutex M, request to use AudioUnit.
|
|
||||||
*
|
|
||||||
* The following figure illustrates the deadlock in bug 1337805:
|
|
||||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1337805
|
|
||||||
* (The detail analysis can be found on bug 1350511:
|
|
||||||
* https://bugzilla.mozilla.org/show_bug.cgi?id=1350511)
|
|
||||||
*
|
|
||||||
* holds
|
|
||||||
* data_callback <---------- mutext_AudioUnit(mutex_AU)
|
|
||||||
* | ^
|
|
||||||
* | |
|
|
||||||
* | request | request
|
|
||||||
* | |
|
|
||||||
* v holds |
|
|
||||||
* mutex_cubeb ------------> get_channel_layout
|
|
||||||
*
|
|
||||||
* In this example, the "audiounit_get_channel_layout" in f4edfb8:
|
|
||||||
* https://github.com/kinetiknz/cubeb/blob/f4edfb8eea920887713325e44773f3a2d959860c/src/cubeb_audiounit.cpp#L2725
|
|
||||||
* requests the mutex_AU to create an AudioUnit, when it holds a mutex for cubeb
|
|
||||||
* context. Meanwhile, the data callback who holds the mutex_AU requests the
|
|
||||||
* mutex for cubeb context. As a result, it causes a deadlock.
|
|
||||||
*
|
|
||||||
* The problem is solve by pull 236: https://github.com/kinetiknz/cubeb/pull/236
|
|
||||||
* We store the latest channel layout and return it when there is an active
|
|
||||||
* AudioUnit, otherwise, we will create an AudioUnit to get it.
|
|
||||||
*
|
|
||||||
* Although the problem is solved, to prevent it happens again, we add the test
|
|
||||||
* here in case someone without such knowledge misuses the AudioUnit in
|
|
||||||
* get_channel_layout. Moreover, it's a good way to record the known issues
|
|
||||||
* to warn other developers.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h" // for layout_infos
|
|
||||||
#include "cubeb/cubeb.h" // for cubeb utils
|
|
||||||
#include "cubeb_utils.h" // for owned_critical_section, auto_lock
|
|
||||||
#include <iostream> // for fprintf
|
|
||||||
#include <pthread.h> // for pthread
|
|
||||||
#include <signal.h> // for signal
|
|
||||||
#include <stdexcept> // for std::logic_error
|
|
||||||
#include <string> // for std::string
|
|
||||||
#include <unistd.h> // for sleep, usleep
|
|
||||||
#include <atomic> // for std::atomic
|
|
||||||
|
|
||||||
// The signal alias for calling our thread killer.
|
|
||||||
#define CALL_THREAD_KILLER SIGUSR1
|
|
||||||
|
|
||||||
// This indicator will become true when our pending task thread is killed by
|
|
||||||
// ourselves.
|
|
||||||
bool killed = false;
|
|
||||||
|
|
||||||
// This indicator will become true when the assigned task is done.
|
|
||||||
std::atomic<bool> task_done{ false };
|
|
||||||
|
|
||||||
// Indicating the data callback is fired or not.
|
|
||||||
bool called = false;
|
|
||||||
|
|
||||||
// Toggle to true when running data callback. Before data callback gets
|
|
||||||
// the mutex for cubeb context, it toggles back to false.
|
|
||||||
// The task to get channel layout should be executed when this is true.
|
|
||||||
std::atomic<bool> callbacking_before_getting_context{ false };
|
|
||||||
|
|
||||||
owned_critical_section context_mutex;
|
|
||||||
cubeb * context = nullptr;
|
|
||||||
|
|
||||||
cubeb * get_cubeb_context_unlocked()
|
|
||||||
{
|
|
||||||
if (context) {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
int r = CUBEB_OK;
|
|
||||||
r = common_init(&context, "Cubeb deadlock test");
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
context = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb * get_cubeb_context()
|
|
||||||
{
|
|
||||||
auto_lock lock(context_mutex);
|
|
||||||
return get_cubeb_context_unlocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_audio(cubeb_stream * /*stream*/, void * /*user*/, cubeb_state /*state*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fired by coreaudio's rendering mechanism. It holds a mutex shared with the
|
|
||||||
// current used AudioUnit.
|
|
||||||
template<typename T>
|
|
||||||
long data_cb(cubeb_stream * /*stream*/, void * /*user*/,
|
|
||||||
const void * /*inputbuffer*/, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
called = true;
|
|
||||||
|
|
||||||
uint64_t tid; // Current thread id.
|
|
||||||
pthread_threadid_np(NULL, &tid);
|
|
||||||
fprintf(stderr, "Audio output is on thread %llu\n", tid);
|
|
||||||
|
|
||||||
if (!task_done) {
|
|
||||||
callbacking_before_getting_context = true;
|
|
||||||
fprintf(stderr, "[%llu] time to switch thread\n", tid);
|
|
||||||
// Force to switch threads by sleeping 10 ms. Notice that anything over
|
|
||||||
// 10ms would create a glitch. It's intended here for test, so the delay
|
|
||||||
// is ok.
|
|
||||||
usleep(10000);
|
|
||||||
callbacking_before_getting_context = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "[%llu] try getting backend id ...\n", tid);
|
|
||||||
|
|
||||||
// Try requesting mutex for context by get_cubeb_context()
|
|
||||||
// when holding a mutex for AudioUnit.
|
|
||||||
char const * backend_id = cubeb_get_backend_id(get_cubeb_context());
|
|
||||||
fprintf(stderr, "[%llu] callback on %s\n", tid, backend_id);
|
|
||||||
|
|
||||||
// Mute the output (or get deaf)
|
|
||||||
memset(outputbuffer, 0, nframes * 2 * sizeof(float));
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called by wait_to_get_layout, which is run out of main thread.
|
|
||||||
void get_preferred_channel_layout()
|
|
||||||
{
|
|
||||||
auto_lock lock(context_mutex);
|
|
||||||
cubeb * context = get_cubeb_context_unlocked();
|
|
||||||
ASSERT_TRUE(!!context);
|
|
||||||
|
|
||||||
// We will cause a deadlock if cubeb_get_preferred_channel_layout requests
|
|
||||||
// mutex for AudioUnit when it holds mutex for context.
|
|
||||||
cubeb_channel_layout layout;
|
|
||||||
int r = cubeb_get_preferred_channel_layout(context, &layout);
|
|
||||||
ASSERT_EQ(r == CUBEB_OK, layout != CUBEB_LAYOUT_UNDEFINED);
|
|
||||||
fprintf(stderr, "layout is %s\n", layout_infos[layout].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void * wait_to_get_layout(void *)
|
|
||||||
{
|
|
||||||
uint64_t tid; // Current thread id.
|
|
||||||
pthread_threadid_np(NULL, &tid);
|
|
||||||
|
|
||||||
while(!callbacking_before_getting_context) {
|
|
||||||
fprintf(stderr, "[%llu] waiting for data callback ...\n", tid);
|
|
||||||
usleep(1000); // Force to switch threads by sleeping 1 ms.
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "[%llu] try getting channel layout ...\n", tid);
|
|
||||||
get_preferred_channel_layout(); // Deadlock checkpoint.
|
|
||||||
task_done = true;
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void * watchdog(void * s)
|
|
||||||
{
|
|
||||||
uint64_t tid; // Current thread id.
|
|
||||||
pthread_threadid_np(NULL, &tid);
|
|
||||||
|
|
||||||
pthread_t subject = *((pthread_t *) s);
|
|
||||||
uint64_t stid; // task thread id.
|
|
||||||
pthread_threadid_np(subject, &stid);
|
|
||||||
|
|
||||||
unsigned int sec = 2;
|
|
||||||
fprintf(stderr, "[%llu] sleep %d seconds before checking task for thread %llu\n", tid, sec, stid);
|
|
||||||
sleep(sec); // Force to switch threads.
|
|
||||||
|
|
||||||
fprintf(stderr, "[%llu] check task for thread %llu now\n", tid, stid);
|
|
||||||
if (!task_done) {
|
|
||||||
fprintf(stderr, "[%llu] kill the task thread %llu\n", tid, stid);
|
|
||||||
pthread_kill(subject, CALL_THREAD_KILLER);
|
|
||||||
pthread_detach(subject);
|
|
||||||
// pthread_kill doesn't release the mutex held by the killed thread,
|
|
||||||
// so we need to unlock it manually.
|
|
||||||
context_mutex.unlock();
|
|
||||||
}
|
|
||||||
fprintf(stderr, "[%llu] the assigned task for thread %llu is %sdone\n", tid, stid, (task_done) ? "" : "not ");
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void thread_killer(int signal)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(signal, CALL_THREAD_KILLER);
|
|
||||||
fprintf(stderr, "task thread is killed!\n");
|
|
||||||
killed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, run_deadlock_test)
|
|
||||||
{
|
|
||||||
#if !defined(__APPLE__)
|
|
||||||
FAIL() << "Deadlock test is only for OSX now";
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cubeb * ctx = get_cubeb_context();
|
|
||||||
ASSERT_TRUE(!!ctx);
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_params params;
|
|
||||||
params.format = CUBEB_SAMPLE_FLOAT32NE;
|
|
||||||
params.rate = 44100;
|
|
||||||
params.channels = 2;
|
|
||||||
params.layout = CUBEB_LAYOUT_STEREO;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
cubeb_stream * stream = NULL;
|
|
||||||
int r = cubeb_stream_init(ctx, &stream, "test deadlock", NULL, NULL, NULL,
|
|
||||||
¶ms, 512, &data_cb<float>, state_cb_audio, NULL);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
// Install signal handler.
|
|
||||||
signal(CALL_THREAD_KILLER, thread_killer);
|
|
||||||
|
|
||||||
pthread_t subject, detector;
|
|
||||||
pthread_create(&subject, NULL, wait_to_get_layout, NULL);
|
|
||||||
pthread_create(&detector, NULL, watchdog, (void *) &subject);
|
|
||||||
|
|
||||||
uint64_t stid, dtid;
|
|
||||||
pthread_threadid_np(subject, &stid);
|
|
||||||
pthread_threadid_np(detector, &dtid);
|
|
||||||
fprintf(stderr, "task thread %llu, monitor thread %llu are created\n", stid, dtid);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
|
|
||||||
pthread_join(subject, NULL);
|
|
||||||
pthread_join(detector, NULL);
|
|
||||||
|
|
||||||
ASSERT_TRUE(called);
|
|
||||||
|
|
||||||
fprintf(stderr, "\n%sDeadlock detected!\n", (called && !task_done.load()) ? "" : "No ");
|
|
||||||
|
|
||||||
// Check the task is killed by ourselves if deadlock happends.
|
|
||||||
// Otherwise, thread_killer should not be triggered.
|
|
||||||
ASSERT_NE(task_done.load(), killed);
|
|
||||||
|
|
||||||
ASSERT_TRUE(task_done.load());
|
|
||||||
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
}
|
|
|
@ -1,109 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2018 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function test. Check behaviors of registering device changed
|
|
||||||
* callbacks for the streams. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <memory>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#define SAMPLE_FREQUENCY 48000
|
|
||||||
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
|
|
||||||
#define INPUT_CHANNELS 1
|
|
||||||
#define INPUT_LAYOUT CUBEB_LAYOUT_MONO
|
|
||||||
#define OUTPUT_CHANNELS 2
|
|
||||||
#define OUTPUT_LAYOUT CUBEB_LAYOUT_STEREO
|
|
||||||
|
|
||||||
long data_callback(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_callback(cubeb_stream * stream, void * user, cubeb_state state)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void device_changed_callback(void * user)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "device changed callback\n");
|
|
||||||
ASSERT_TRUE(false) << "Error: device changed callback"
|
|
||||||
" called without changing devices";
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_registering_null_callback_twice(cubeb_stream * stream)
|
|
||||||
{
|
|
||||||
int r = cubeb_stream_register_device_changed_callback(stream, nullptr);
|
|
||||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error registering null device changed callback";
|
|
||||||
|
|
||||||
r = cubeb_stream_register_device_changed_callback(stream, nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error registering null device changed callback again";
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_registering_and_unregistering_callback(cubeb_stream * stream)
|
|
||||||
{
|
|
||||||
int r = cubeb_stream_register_device_changed_callback(stream, device_changed_callback);
|
|
||||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error registering device changed callback";
|
|
||||||
|
|
||||||
r = cubeb_stream_register_device_changed_callback(stream, nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error unregistering device changed callback";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, device_changed_callbacks)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r = CUBEB_OK;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb duplex example with device change");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
/* typical user-case: mono input, stereo output, low latency. */
|
|
||||||
input_params.format = STREAM_FORMAT;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = INPUT_CHANNELS;
|
|
||||||
input_params.layout = INPUT_LAYOUT;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
output_params.format = STREAM_FORMAT;
|
|
||||||
output_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
output_params.channels = OUTPUT_CHANNELS;
|
|
||||||
output_params.layout = OUTPUT_LAYOUT;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
|
||||||
NULL, &input_params, NULL, &output_params,
|
|
||||||
latency_frames, data_callback, state_callback, nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
test_registering_null_callback_twice(stream);
|
|
||||||
|
|
||||||
test_registering_and_unregistering_callback(stream);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
}
|
|
255
thirdparty/cubeb/test/test_devices.cpp
vendored
255
thirdparty/cubeb/test/test_devices.cpp
vendored
|
@ -1,255 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2015 Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb enumerate device test/example.
|
|
||||||
* Prints out a list of devices enumerated. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <memory>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
// noop, unused
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
// noop, unused
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_device_info(cubeb_device_info * info, FILE * f)
|
|
||||||
{
|
|
||||||
char devfmts[64] = "";
|
|
||||||
const char * devtype, * devstate, * devdeffmt;
|
|
||||||
|
|
||||||
switch (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 (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 (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 (info->format & CUBEB_DEVICE_FMT_S16LE)
|
|
||||||
strcat(devfmts, " S16LE");
|
|
||||||
if (info->format & CUBEB_DEVICE_FMT_S16BE)
|
|
||||||
strcat(devfmts, " S16BE");
|
|
||||||
if (info->format & CUBEB_DEVICE_FMT_F32LE)
|
|
||||||
strcat(devfmts, " F32LE");
|
|
||||||
if (info->format & CUBEB_DEVICE_FMT_F32BE)
|
|
||||||
strcat(devfmts, " F32BE");
|
|
||||||
|
|
||||||
fprintf(f,
|
|
||||||
"dev: \"%s\"%s\n"
|
|
||||||
"\tName: \"%s\"\n"
|
|
||||||
"\tGroup: \"%s\"\n"
|
|
||||||
"\tVendor: \"%s\"\n"
|
|
||||||
"\tType: %s\n"
|
|
||||||
"\tState: %s\n"
|
|
||||||
"\tCh: %u\n"
|
|
||||||
"\tFormat: %s (0x%x) (default: %s)\n"
|
|
||||||
"\tRate: %u - %u (default: %u)\n"
|
|
||||||
"\tLatency: lo %u frames, hi %u frames\n",
|
|
||||||
info->device_id, info->preferred ? " (PREFERRED)" : "",
|
|
||||||
info->friendly_name, info->group_id, info->vendor_name,
|
|
||||||
devtype, devstate, info->max_channels,
|
|
||||||
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
|
|
||||||
(unsigned int)info->format, devdeffmt,
|
|
||||||
info->min_rate, info->max_rate, info->default_rate,
|
|
||||||
info->latency_lo, info->latency_hi);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
print_device_collection(cubeb_device_collection * collection, FILE * f)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < collection->count; i++)
|
|
||||||
print_device_info(&collection->device[i], f);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, destroy_default_collection)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx = NULL;
|
|
||||||
cubeb_device_collection collection{ nullptr, 0 };
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb audio test");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
ASSERT_EQ(collection.device, nullptr);
|
|
||||||
ASSERT_EQ(collection.count, (size_t) 0);
|
|
||||||
|
|
||||||
r = cubeb_device_collection_destroy(ctx, &collection);
|
|
||||||
if (r != CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_EQ(collection.device, nullptr);
|
|
||||||
ASSERT_EQ(collection.count, (size_t) 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, enumerate_devices)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx = NULL;
|
|
||||||
cubeb_device_collection collection;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb audio test");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
fprintf(stdout, "Enumerating input devices for backend %s\n",
|
|
||||||
cubeb_get_backend_id(ctx));
|
|
||||||
|
|
||||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_INPUT, &collection);
|
|
||||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
fprintf(stderr, "Device enumeration not supported"
|
|
||||||
" for this backend, skipping this test.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
|
||||||
|
|
||||||
fprintf(stdout, "Found %zu input devices\n", collection.count);
|
|
||||||
print_device_collection(&collection, stdout);
|
|
||||||
cubeb_device_collection_destroy(ctx, &collection);
|
|
||||||
|
|
||||||
fprintf(stdout, "Enumerating output devices for backend %s\n",
|
|
||||||
cubeb_get_backend_id(ctx));
|
|
||||||
|
|
||||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
|
||||||
|
|
||||||
fprintf(stdout, "Found %zu output devices\n", collection.count);
|
|
||||||
print_device_collection(&collection, stdout);
|
|
||||||
cubeb_device_collection_destroy(ctx, &collection);
|
|
||||||
|
|
||||||
uint32_t count_before_creating_duplex_stream;
|
|
||||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
|
||||||
count_before_creating_duplex_stream = collection.count;
|
|
||||||
cubeb_device_collection_destroy(ctx, &collection);
|
|
||||||
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
|
|
||||||
input_params.format = output_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
|
||||||
input_params.rate = output_params.rate = 48000;
|
|
||||||
input_params.channels = output_params.channels = 1;
|
|
||||||
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
|
||||||
NULL, &input_params, NULL, &output_params,
|
|
||||||
1024, data_cb_duplex, state_cb_duplex, nullptr);
|
|
||||||
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
|
||||||
ASSERT_EQ(count_before_creating_duplex_stream, collection.count);
|
|
||||||
cubeb_device_collection_destroy(ctx, &collection);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, stream_get_current_device)
|
|
||||||
{
|
|
||||||
cubeb * ctx = NULL;
|
|
||||||
int r = common_init(&ctx, "Cubeb audio test");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
fprintf(stdout, "Getting current devices for backend %s\n",
|
|
||||||
cubeb_get_backend_id(ctx));
|
|
||||||
|
|
||||||
cubeb_stream * stream = NULL;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
|
|
||||||
input_params.format = output_params.format = CUBEB_SAMPLE_FLOAT32NE;
|
|
||||||
input_params.rate = output_params.rate = 48000;
|
|
||||||
input_params.channels = output_params.channels = 1;
|
|
||||||
input_params.layout = output_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
|
||||||
NULL, &input_params, NULL, &output_params,
|
|
||||||
1024, data_cb_duplex, state_cb_duplex, nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_device * device;
|
|
||||||
r = cubeb_stream_get_current_device(stream, &device);
|
|
||||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
fprintf(stderr, "Getting current device is not supported"
|
|
||||||
" for this backend, skipping this test.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error getting current devices";
|
|
||||||
|
|
||||||
fprintf(stdout, "Current output device: %s\n", device->output_name);
|
|
||||||
fprintf(stdout, "Current input device: %s\n", device->input_name);
|
|
||||||
|
|
||||||
r = cubeb_stream_device_destroy(stream, device);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error destroying current devices";
|
|
||||||
}
|
|
181
thirdparty/cubeb/test/test_duplex.cpp
vendored
181
thirdparty/cubeb/test/test_duplex.cpp
vendored
|
@ -1,181 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2016 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function test. Loops input back to output and check audio
|
|
||||||
* is flowing. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <memory>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#define SAMPLE_FREQUENCY 48000
|
|
||||||
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
|
|
||||||
#define INPUT_CHANNELS 1
|
|
||||||
#define INPUT_LAYOUT CUBEB_LAYOUT_MONO
|
|
||||||
#define OUTPUT_CHANNELS 2
|
|
||||||
#define OUTPUT_LAYOUT CUBEB_LAYOUT_STEREO
|
|
||||||
|
|
||||||
struct user_state_duplex
|
|
||||||
{
|
|
||||||
std::atomic<int> invalid_audio_value{ 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
long data_cb_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
user_state_duplex * u = reinterpret_cast<user_state_duplex*>(user);
|
|
||||||
float *ib = (float *)inputbuffer;
|
|
||||||
float *ob = (float *)outputbuffer;
|
|
||||||
|
|
||||||
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop back: upmix the single input channel to the two output channels,
|
|
||||||
// checking if there is noise in the process.
|
|
||||||
long output_index = 0;
|
|
||||||
for (long i = 0; i < nframes; i++) {
|
|
||||||
if (ib[i] <= -1.0 || ib[i] >= 1.0) {
|
|
||||||
u->invalid_audio_value = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ob[output_index] = ob[output_index + 1] = ib[i];
|
|
||||||
output_index += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_duplex(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
if (stream == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
fprintf(stderr, "stream started\n"); break;
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
fprintf(stderr, "stream drained\n"); break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "unknown stream state %d\n", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, duplex)
|
|
||||||
{
|
|
||||||
cubeb *ctx;
|
|
||||||
cubeb_stream *stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
user_state_duplex stream_state;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb duplex example");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
/* This test needs an available input device, skip it if this host does not
|
|
||||||
* have one. */
|
|
||||||
if (!has_available_input_device(ctx)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* typical user-case: mono input, stereo output, low latency. */
|
|
||||||
input_params.format = STREAM_FORMAT;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = INPUT_CHANNELS;
|
|
||||||
input_params.layout = INPUT_LAYOUT;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
output_params.format = STREAM_FORMAT;
|
|
||||||
output_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
output_params.channels = OUTPUT_CHANNELS;
|
|
||||||
output_params.layout = OUTPUT_LAYOUT;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
|
||||||
NULL, &input_params, NULL, &output_params,
|
|
||||||
latency_frames, data_cb_duplex, state_cb_duplex, &stream_state);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(500);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
|
|
||||||
ASSERT_FALSE(stream_state.invalid_audio_value.load());
|
|
||||||
}
|
|
||||||
|
|
||||||
void device_collection_changed_callback(cubeb * context, void * user)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "collection changed callback\n");
|
|
||||||
ASSERT_TRUE(false) << "Error: device collection changed callback"
|
|
||||||
" called when opening a stream";
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, duplex_collection_change)
|
|
||||||
{
|
|
||||||
cubeb *ctx;
|
|
||||||
cubeb_stream *stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb duplex example with collection change");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
r = cubeb_register_device_collection_changed(ctx,
|
|
||||||
static_cast<cubeb_device_type>(CUBEB_DEVICE_TYPE_INPUT),
|
|
||||||
device_collection_changed_callback,
|
|
||||||
nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
/* typical user-case: mono input, stereo output, low latency. */
|
|
||||||
input_params.format = STREAM_FORMAT;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = INPUT_CHANNELS;
|
|
||||||
input_params.layout = INPUT_LAYOUT;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
output_params.format = STREAM_FORMAT;
|
|
||||||
output_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
output_params.channels = OUTPUT_CHANNELS;
|
|
||||||
output_params.layout = OUTPUT_LAYOUT;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb duplex",
|
|
||||||
NULL, &input_params, NULL, &output_params,
|
|
||||||
latency_frames, data_cb_duplex, state_cb_duplex, nullptr);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
}
|
|
47
thirdparty/cubeb/test/test_latency.cpp
vendored
47
thirdparty/cubeb/test/test_latency.cpp
vendored
|
@ -1,47 +0,0 @@
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <memory>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
TEST(cubeb, latency)
|
|
||||||
{
|
|
||||||
cubeb * ctx = NULL;
|
|
||||||
int r;
|
|
||||||
uint32_t max_channels;
|
|
||||||
uint32_t preferred_rate;
|
|
||||||
uint32_t latency_frames;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb audio test");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
r = cubeb_get_max_channel_count(ctx, &max_channels);
|
|
||||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
|
||||||
if (r == CUBEB_OK) {
|
|
||||||
ASSERT_GT(max_channels, 0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = cubeb_get_preferred_sample_rate(ctx, &preferred_rate);
|
|
||||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
|
||||||
if (r == CUBEB_OK) {
|
|
||||||
ASSERT_GT(preferred_rate, 0u);
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_stream_params params = {
|
|
||||||
CUBEB_SAMPLE_FLOAT32NE,
|
|
||||||
preferred_rate,
|
|
||||||
max_channels,
|
|
||||||
CUBEB_LAYOUT_UNDEFINED,
|
|
||||||
CUBEB_STREAM_PREF_NONE
|
|
||||||
};
|
|
||||||
r = cubeb_get_min_latency(ctx, ¶ms, &latency_frames);
|
|
||||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
|
||||||
if (r == CUBEB_OK) {
|
|
||||||
ASSERT_GT(latency_frames, 0u);
|
|
||||||
}
|
|
||||||
}
|
|
578
thirdparty/cubeb/test/test_loopback.cpp
vendored
578
thirdparty/cubeb/test/test_loopback.cpp
vendored
|
@ -1,578 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2017 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function test. Requests a loopback device and checks that
|
|
||||||
output is being looped back to input. NOTE: Usage of output devices while
|
|
||||||
performing this test will cause flakey results! */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
const uint32_t SAMPLE_FREQUENCY = 48000;
|
|
||||||
const uint32_t TONE_FREQUENCY = 440;
|
|
||||||
const double OUTPUT_AMPLITUDE = 0.25;
|
|
||||||
const int32_t NUM_FRAMES_TO_OUTPUT = SAMPLE_FREQUENCY / 20; /* play ~50ms of samples */
|
|
||||||
|
|
||||||
template<typename T> T ConvertSampleToOutput(double input);
|
|
||||||
template<> float ConvertSampleToOutput(double input) { return float(input); }
|
|
||||||
template<> short ConvertSampleToOutput(double input) { return short(input * 32767.0f); }
|
|
||||||
|
|
||||||
template<typename T> double ConvertSampleFromOutput(T sample);
|
|
||||||
template<> double ConvertSampleFromOutput(float sample) { return double(sample); }
|
|
||||||
template<> double ConvertSampleFromOutput(short sample) { return double(sample / 32767.0); }
|
|
||||||
|
|
||||||
/* Simple cross correlation to help find phase shift. Not a performant impl */
|
|
||||||
std::vector<double> cross_correlate(std::vector<double> & f,
|
|
||||||
std::vector<double> & g,
|
|
||||||
size_t signal_length)
|
|
||||||
{
|
|
||||||
/* the length we sweep our window through to find the cross correlation */
|
|
||||||
size_t sweep_length = f.size() - signal_length + 1;
|
|
||||||
std::vector<double> correlation;
|
|
||||||
correlation.reserve(sweep_length);
|
|
||||||
for (size_t i = 0; i < sweep_length; i++) {
|
|
||||||
double accumulator = 0.0;
|
|
||||||
for (size_t j = 0; j < signal_length; j++) {
|
|
||||||
accumulator += f.at(j) * g.at(i + j);
|
|
||||||
}
|
|
||||||
correlation.push_back(accumulator);
|
|
||||||
}
|
|
||||||
return correlation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* best effort discovery of phase shift between output and (looped) input*/
|
|
||||||
size_t find_phase(std::vector<double> & output_frames,
|
|
||||||
std::vector<double> & input_frames,
|
|
||||||
size_t signal_length)
|
|
||||||
{
|
|
||||||
std::vector<double> correlation = cross_correlate(output_frames, input_frames, signal_length);
|
|
||||||
size_t phase = 0;
|
|
||||||
double max_correlation = correlation.at(0);
|
|
||||||
for (size_t i = 1; i < correlation.size(); i++) {
|
|
||||||
if (correlation.at(i) > max_correlation) {
|
|
||||||
max_correlation = correlation.at(i);
|
|
||||||
phase = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<double> normalize_frames(std::vector<double> & frames) {
|
|
||||||
double max = abs(*std::max_element(frames.begin(), frames.end(),
|
|
||||||
[](double a, double b) { return abs(a) < abs(b); }));
|
|
||||||
std::vector<double> normalized_frames;
|
|
||||||
normalized_frames.reserve(frames.size());
|
|
||||||
for (const double frame : frames) {
|
|
||||||
normalized_frames.push_back(frame / max);
|
|
||||||
}
|
|
||||||
return normalized_frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* heuristic comparison of aligned output and input signals, gets flaky if TONE_FREQUENCY is too high */
|
|
||||||
void compare_signals(std::vector<double> & output_frames,
|
|
||||||
std::vector<double> & input_frames)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(output_frames.size(), input_frames.size()) << "#Output frames != #input frames";
|
|
||||||
size_t num_frames = output_frames.size();
|
|
||||||
std::vector<double> normalized_output_frames = normalize_frames(output_frames);
|
|
||||||
std::vector<double> normalized_input_frames = normalize_frames(input_frames);
|
|
||||||
|
|
||||||
/* calculate mean absolute errors */
|
|
||||||
/* mean absolute errors between output and input */
|
|
||||||
double io_mas = 0.0;
|
|
||||||
/* mean absolute errors between output and silence */
|
|
||||||
double output_silence_mas = 0.0;
|
|
||||||
/* mean absolute errors between input and silence */
|
|
||||||
double input_silence_mas = 0.0;
|
|
||||||
for (size_t i = 0; i < num_frames; i++) {
|
|
||||||
io_mas += abs(normalized_output_frames.at(i) - normalized_input_frames.at(i));
|
|
||||||
output_silence_mas += abs(normalized_output_frames.at(i));
|
|
||||||
input_silence_mas += abs(normalized_input_frames.at(i));
|
|
||||||
}
|
|
||||||
io_mas /= num_frames;
|
|
||||||
output_silence_mas /= num_frames;
|
|
||||||
input_silence_mas /= num_frames;
|
|
||||||
|
|
||||||
ASSERT_LT(io_mas, output_silence_mas)
|
|
||||||
<< "Error between output and input should be less than output and silence!";
|
|
||||||
ASSERT_LT(io_mas, input_silence_mas)
|
|
||||||
<< "Error between output and input should be less than output and silence!";
|
|
||||||
|
|
||||||
/* make sure extrema are in (roughly) correct location */
|
|
||||||
/* number of maxima + minama expected in the frames*/
|
|
||||||
const long NUM_EXTREMA = 2 * TONE_FREQUENCY * NUM_FRAMES_TO_OUTPUT / SAMPLE_FREQUENCY;
|
|
||||||
/* expected index of first maxima */
|
|
||||||
const long FIRST_MAXIMUM_INDEX = SAMPLE_FREQUENCY / TONE_FREQUENCY / 4;
|
|
||||||
/* Threshold we expect all maxima and minima to be above or below. Ideally
|
|
||||||
the extrema would be 1 or -1, but particularly at the start of loopback
|
|
||||||
the values seen can be significantly lower. */
|
|
||||||
const double THRESHOLD = 0.5;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < NUM_EXTREMA; i++) {
|
|
||||||
bool is_maximum = i % 2 == 0;
|
|
||||||
/* expected offset to current extreme: i * stide between extrema */
|
|
||||||
size_t offset = i * SAMPLE_FREQUENCY / TONE_FREQUENCY / 2;
|
|
||||||
if (is_maximum) {
|
|
||||||
ASSERT_GT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
|
|
||||||
<< "Output frames have unexpected missing maximum!";
|
|
||||||
ASSERT_GT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), THRESHOLD)
|
|
||||||
<< "Input frames have unexpected missing maximum!";
|
|
||||||
} else {
|
|
||||||
ASSERT_LT(normalized_output_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
|
|
||||||
<< "Output frames have unexpected missing minimum!";
|
|
||||||
ASSERT_LT(normalized_input_frames.at(FIRST_MAXIMUM_INDEX + offset), -THRESHOLD)
|
|
||||||
<< "Input frames have unexpected missing minimum!";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct user_state_loopback {
|
|
||||||
std::mutex user_state_mutex;
|
|
||||||
long position = 0;
|
|
||||||
/* track output */
|
|
||||||
std::vector<double> output_frames;
|
|
||||||
/* track input */
|
|
||||||
std::vector<double> input_frames;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
long data_cb_loop_duplex(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
struct user_state_loopback * u = (struct user_state_loopback *) user;
|
|
||||||
T * ib = (T *) inputbuffer;
|
|
||||||
T * ob = (T *) outputbuffer;
|
|
||||||
|
|
||||||
if (stream == NULL || inputbuffer == NULL || outputbuffer == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(u->user_state_mutex);
|
|
||||||
/* generate our test tone on the fly */
|
|
||||||
for (int i = 0; i < nframes; i++) {
|
|
||||||
double tone = 0.0;
|
|
||||||
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
|
|
||||||
/* generate sine wave */
|
|
||||||
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
|
|
||||||
tone *= OUTPUT_AMPLITUDE;
|
|
||||||
}
|
|
||||||
ob[i] = ConvertSampleToOutput<T>(tone);
|
|
||||||
u->output_frames.push_back(tone);
|
|
||||||
/* store any looped back output, may be silence */
|
|
||||||
u->input_frames.push_back(ConvertSampleFromOutput(ib[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
u->position += nframes;
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
long data_cb_loop_input_only(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
struct user_state_loopback * u = (struct user_state_loopback *) user;
|
|
||||||
T * ib = (T *) inputbuffer;
|
|
||||||
|
|
||||||
if (outputbuffer != NULL) {
|
|
||||||
// Can't assert as it needs to return, so expect to fail instead
|
|
||||||
EXPECT_EQ(outputbuffer, (void *) NULL) << "outputbuffer should be null in input only callback";
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (stream == NULL || inputbuffer == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(u->user_state_mutex);
|
|
||||||
for (int i = 0; i < nframes; i++) {
|
|
||||||
u->input_frames.push_back(ConvertSampleFromOutput(ib[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
long data_cb_playback(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
struct user_state_loopback * u = (struct user_state_loopback *) user;
|
|
||||||
T * ob = (T *) outputbuffer;
|
|
||||||
|
|
||||||
if (stream == NULL || outputbuffer == NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(u->user_state_mutex);
|
|
||||||
/* generate our test tone on the fly */
|
|
||||||
for (int i = 0; i < nframes; i++) {
|
|
||||||
double tone = 0.0;
|
|
||||||
if (u->position + i < NUM_FRAMES_TO_OUTPUT) {
|
|
||||||
/* generate sine wave */
|
|
||||||
tone = sin(2 * M_PI*(i + u->position) * TONE_FREQUENCY / SAMPLE_FREQUENCY);
|
|
||||||
tone *= OUTPUT_AMPLITUDE;
|
|
||||||
}
|
|
||||||
ob[i] = ConvertSampleToOutput<T>(tone);
|
|
||||||
u->output_frames.push_back(tone);
|
|
||||||
}
|
|
||||||
|
|
||||||
u->position += nframes;
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_loop(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
if (stream == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
fprintf(stderr, "stream started\n"); break;
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
fprintf(stderr, "stream drained\n"); break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "unknown stream state %d\n", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_loopback_duplex_test(bool is_float)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb loopback example: duplex stream");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = 1;
|
|
||||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
|
||||||
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
output_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
output_params.channels = 1;
|
|
||||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
|
||||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
/* setup a duplex stream with loopback */
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb loopback",
|
|
||||||
NULL, &input_params, NULL, &output_params, latency_frames,
|
|
||||||
is_float ? data_cb_loop_duplex<float> : data_cb_loop_duplex<short>,
|
|
||||||
state_cb_loop, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(300);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
|
|
||||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
|
||||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
|
||||||
std::vector<double> & output_frames = user_data->output_frames;
|
|
||||||
std::vector<double> & input_frames = user_data->input_frames;
|
|
||||||
ASSERT_EQ(output_frames.size(), input_frames.size())
|
|
||||||
<< "#Output frames != #input frames";
|
|
||||||
|
|
||||||
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
|
|
||||||
|
|
||||||
/* extract vectors of just the relevant signal from output and input */
|
|
||||||
auto output_frames_signal_start = output_frames.begin();
|
|
||||||
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
|
|
||||||
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
|
|
||||||
auto input_frames_signal_start = input_frames.begin() + phase;
|
|
||||||
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
|
|
||||||
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
|
|
||||||
|
|
||||||
compare_signals(trimmed_output_frames, trimmed_input_frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, loopback_duplex)
|
|
||||||
{
|
|
||||||
run_loopback_duplex_test(true);
|
|
||||||
run_loopback_duplex_test(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_loopback_separate_streams_test(bool is_float)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * input_stream;
|
|
||||||
cubeb_stream * output_stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb loopback example: separate streams");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = 1;
|
|
||||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
|
||||||
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
output_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
output_params.channels = 1;
|
|
||||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
|
||||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
/* setup an input stream with loopback */
|
|
||||||
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
|
|
||||||
NULL, &input_params, NULL, NULL, latency_frames,
|
|
||||||
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
|
|
||||||
state_cb_loop, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
/* setup an output stream */
|
|
||||||
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
|
|
||||||
NULL, NULL, NULL, &output_params, latency_frames,
|
|
||||||
is_float ? data_cb_playback<float> : data_cb_playback<short>,
|
|
||||||
state_cb_loop, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(input_stream);
|
|
||||||
cubeb_stream_start(output_stream);
|
|
||||||
delay(300);
|
|
||||||
cubeb_stream_stop(output_stream);
|
|
||||||
cubeb_stream_stop(input_stream);
|
|
||||||
|
|
||||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
|
||||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
|
||||||
std::vector<double> & output_frames = user_data->output_frames;
|
|
||||||
std::vector<double> & input_frames = user_data->input_frames;
|
|
||||||
ASSERT_LE(output_frames.size(), input_frames.size())
|
|
||||||
<< "#Output frames should be less or equal to #input frames";
|
|
||||||
|
|
||||||
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
|
|
||||||
|
|
||||||
/* extract vectors of just the relevant signal from output and input */
|
|
||||||
auto output_frames_signal_start = output_frames.begin();
|
|
||||||
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
|
|
||||||
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
|
|
||||||
auto input_frames_signal_start = input_frames.begin() + phase;
|
|
||||||
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
|
|
||||||
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
|
|
||||||
|
|
||||||
compare_signals(trimmed_output_frames, trimmed_input_frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, loopback_separate_streams)
|
|
||||||
{
|
|
||||||
run_loopback_separate_streams_test(true);
|
|
||||||
run_loopback_separate_streams_test(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_loopback_silence_test(bool is_float)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * input_stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
int r;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb loopback example: record silence");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = 1;
|
|
||||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
|
||||||
|
|
||||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
|
||||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &input_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
/* setup an input stream with loopback */
|
|
||||||
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
|
|
||||||
NULL, &input_params, NULL, NULL, latency_frames,
|
|
||||||
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
|
|
||||||
state_cb_loop, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(input_stream);
|
|
||||||
delay(300);
|
|
||||||
cubeb_stream_stop(input_stream);
|
|
||||||
|
|
||||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
|
||||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
|
||||||
std::vector<double> & input_frames = user_data->input_frames;
|
|
||||||
|
|
||||||
/* expect to have at least ~50ms of frames */
|
|
||||||
ASSERT_GE(input_frames.size(), SAMPLE_FREQUENCY / 20);
|
|
||||||
double EPISILON = 0.0001;
|
|
||||||
/* frames should be 0.0, but use epsilon to avoid possible issues with impls
|
|
||||||
that may use ~0.0 silence values. */
|
|
||||||
for (double frame : input_frames) {
|
|
||||||
ASSERT_LT(abs(frame), EPISILON);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, loopback_silence)
|
|
||||||
{
|
|
||||||
run_loopback_silence_test(true);
|
|
||||||
run_loopback_silence_test(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_loopback_device_selection_test(bool is_float)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_device_collection collection;
|
|
||||||
cubeb_stream * input_stream;
|
|
||||||
cubeb_stream * output_stream;
|
|
||||||
cubeb_stream_params input_params;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb loopback example: device selection, separate streams");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
r = cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection);
|
|
||||||
if (r == CUBEB_ERROR_NOT_SUPPORTED) {
|
|
||||||
fprintf(stderr, "Device enumeration not supported"
|
|
||||||
" for this backend, skipping this test.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error enumerating devices " << r;
|
|
||||||
/* get first preferred output device id */
|
|
||||||
std::string device_id;
|
|
||||||
for (size_t i = 0; i < collection.count; i++) {
|
|
||||||
if (collection.device[i].preferred) {
|
|
||||||
device_id = collection.device[i].device_id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cubeb_device_collection_destroy(ctx, &collection);
|
|
||||||
if (device_id.empty()) {
|
|
||||||
fprintf(stderr, "Could not find preferred device, aborting test.\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
input_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
input_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
input_params.channels = 1;
|
|
||||||
input_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
input_params.prefs = CUBEB_STREAM_PREF_LOOPBACK;
|
|
||||||
output_params.format = is_float ? CUBEB_SAMPLE_FLOAT32NE : CUBEB_SAMPLE_S16LE;
|
|
||||||
output_params.rate = SAMPLE_FREQUENCY;
|
|
||||||
output_params.channels = 1;
|
|
||||||
output_params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
std::unique_ptr<user_state_loopback> user_data(new user_state_loopback());
|
|
||||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Could not get minimal latency";
|
|
||||||
|
|
||||||
/* setup an input stream with loopback */
|
|
||||||
r = cubeb_stream_init(ctx, &input_stream, "Cubeb loopback input only",
|
|
||||||
device_id.c_str(), &input_params, NULL, NULL, latency_frames,
|
|
||||||
is_float ? data_cb_loop_input_only<float> : data_cb_loop_input_only<short>,
|
|
||||||
state_cb_loop, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_input_stream_at_exit(input_stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
/* setup an output stream */
|
|
||||||
r = cubeb_stream_init(ctx, &output_stream, "Cubeb loopback output only",
|
|
||||||
NULL, NULL, device_id.c_str(), &output_params, latency_frames,
|
|
||||||
is_float ? data_cb_playback<float> : data_cb_playback<short>,
|
|
||||||
state_cb_loop, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_output_stream_at_exit(output_stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(input_stream);
|
|
||||||
cubeb_stream_start(output_stream);
|
|
||||||
delay(300);
|
|
||||||
cubeb_stream_stop(output_stream);
|
|
||||||
cubeb_stream_stop(input_stream);
|
|
||||||
|
|
||||||
/* access after stop should not happen, but lock just in case and to appease sanitization tools */
|
|
||||||
std::lock_guard<std::mutex> lock(user_data->user_state_mutex);
|
|
||||||
std::vector<double> & output_frames = user_data->output_frames;
|
|
||||||
std::vector<double> & input_frames = user_data->input_frames;
|
|
||||||
ASSERT_LE(output_frames.size(), input_frames.size())
|
|
||||||
<< "#Output frames should be less or equal to #input frames";
|
|
||||||
|
|
||||||
size_t phase = find_phase(user_data->output_frames, user_data->input_frames, NUM_FRAMES_TO_OUTPUT);
|
|
||||||
|
|
||||||
/* extract vectors of just the relevant signal from output and input */
|
|
||||||
auto output_frames_signal_start = output_frames.begin();
|
|
||||||
auto output_frames_signal_end = output_frames.begin() + NUM_FRAMES_TO_OUTPUT;
|
|
||||||
std::vector<double> trimmed_output_frames(output_frames_signal_start, output_frames_signal_end);
|
|
||||||
auto input_frames_signal_start = input_frames.begin() + phase;
|
|
||||||
auto input_frames_signal_end = input_frames.begin() + phase + NUM_FRAMES_TO_OUTPUT;
|
|
||||||
std::vector<double> trimmed_input_frames(input_frames_signal_start, input_frames_signal_end);
|
|
||||||
|
|
||||||
compare_signals(trimmed_output_frames, trimmed_input_frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, loopback_device_selection)
|
|
||||||
{
|
|
||||||
run_loopback_device_selection_test(true);
|
|
||||||
run_loopback_device_selection_test(false);
|
|
||||||
}
|
|
92
thirdparty/cubeb/test/test_overload_callback.cpp
vendored
92
thirdparty/cubeb/test/test_overload_callback.cpp
vendored
|
@ -1,92 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2017 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#define SAMPLE_FREQUENCY 48000
|
|
||||||
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
|
|
||||||
|
|
||||||
std::atomic<bool> load_callback{ false };
|
|
||||||
|
|
||||||
long data_cb(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
if (load_callback) {
|
|
||||||
fprintf(stderr, "Sleeping...\n");
|
|
||||||
delay(100000);
|
|
||||||
fprintf(stderr, "Sleeping done\n");
|
|
||||||
}
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
ASSERT_TRUE(!!stream);
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
fprintf(stderr, "stream started\n"); break;
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
FAIL() << "this test is not supposed to drain"; break;
|
|
||||||
case CUBEB_STATE_ERROR:
|
|
||||||
fprintf(stderr, "stream error\n"); break;
|
|
||||||
default:
|
|
||||||
FAIL() << "this test is not supposed to have a weird state"; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, overload_callback)
|
|
||||||
{
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params output_params;
|
|
||||||
int r;
|
|
||||||
uint32_t latency_frames = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb callback overload");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
output_params.format = STREAM_FORMAT;
|
|
||||||
output_params.rate = 48000;
|
|
||||||
output_params.channels = 2;
|
|
||||||
output_params.layout = CUBEB_LAYOUT_STEREO;
|
|
||||||
output_params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, &output_params, &latency_frames);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb",
|
|
||||||
NULL, NULL, NULL, &output_params,
|
|
||||||
latency_frames, data_cb, state_cb, NULL);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(500);
|
|
||||||
// This causes the callback to sleep for a large number of seconds.
|
|
||||||
load_callback = true;
|
|
||||||
delay(500);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
}
|
|
116
thirdparty/cubeb/test/test_record.cpp
vendored
116
thirdparty/cubeb/test/test_record.cpp
vendored
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2016 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function test. Record the mic and check there is sound. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <memory>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#define SAMPLE_FREQUENCY 48000
|
|
||||||
#define STREAM_FORMAT CUBEB_SAMPLE_FLOAT32LE
|
|
||||||
|
|
||||||
struct user_state_record
|
|
||||||
{
|
|
||||||
std::atomic<int> invalid_audio_value{ 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
long data_cb_record(cubeb_stream * stream, void * user, const void * inputbuffer, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
user_state_record * u = reinterpret_cast<user_state_record*>(user);
|
|
||||||
float *b = (float *)inputbuffer;
|
|
||||||
|
|
||||||
if (stream == NULL || inputbuffer == NULL || outputbuffer != NULL) {
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (long i = 0; i < nframes; i++) {
|
|
||||||
if (b[i] <= -1.0 || b[i] >= 1.0) {
|
|
||||||
u->invalid_audio_value = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_record(cubeb_stream * stream, void * /*user*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
if (stream == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
fprintf(stderr, "stream started\n"); break;
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
fprintf(stderr, "stream drained\n"); break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "unknown stream state %d\n", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, record)
|
|
||||||
{
|
|
||||||
if (cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr /*print_log*/) != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Set log callback failed\n");
|
|
||||||
}
|
|
||||||
cubeb *ctx;
|
|
||||||
cubeb_stream *stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
int r;
|
|
||||||
user_state_record stream_state;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb record example");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
/* This test needs an available input device, skip it if this host does not
|
|
||||||
* have one. */
|
|
||||||
if (!has_available_input_device(ctx)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = SAMPLE_FREQUENCY;
|
|
||||||
params.channels = 1;
|
|
||||||
params.layout = CUBEB_LAYOUT_UNDEFINED;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb record (mono)", NULL, ¶ms, NULL, nullptr,
|
|
||||||
4096, data_cb_record, state_cb_record, &stream_state);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(500);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
// user callback does not arrive in Linux, silence the error
|
|
||||||
fprintf(stderr, "Check is disabled in Linux\n");
|
|
||||||
#else
|
|
||||||
ASSERT_FALSE(stream_state.invalid_audio_value.load());
|
|
||||||
#endif
|
|
||||||
}
|
|
1063
thirdparty/cubeb/test/test_resampler.cpp
vendored
1063
thirdparty/cubeb/test/test_resampler.cpp
vendored
File diff suppressed because it is too large
Load Diff
73
thirdparty/cubeb/test/test_ring_array.cpp
vendored
73
thirdparty/cubeb/test/test_ring_array.cpp
vendored
|
@ -1,73 +0,0 @@
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#ifdef __APPLE__
|
|
||||||
#include <string.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <CoreAudio/CoreAudioTypes.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb_ring_array.h"
|
|
||||||
|
|
||||||
TEST(cubeb, ring_array)
|
|
||||||
{
|
|
||||||
ring_array ra;
|
|
||||||
|
|
||||||
ASSERT_EQ(ring_array_init(&ra, 0, 0, 1, 1), CUBEB_ERROR_INVALID_PARAMETER);
|
|
||||||
ASSERT_EQ(ring_array_init(&ra, 1, 0, 0, 1), CUBEB_ERROR_INVALID_PARAMETER);
|
|
||||||
|
|
||||||
unsigned int capacity = 8;
|
|
||||||
ring_array_init(&ra, capacity, sizeof(int), 1, 1);
|
|
||||||
int verify_data[capacity] ;// {1,2,3,4,5,6,7,8};
|
|
||||||
AudioBuffer * p_data = NULL;
|
|
||||||
|
|
||||||
for (unsigned int i = 0; i < capacity; ++i) {
|
|
||||||
verify_data[i] = i; // in case capacity change value
|
|
||||||
*(int*)ra.buffer_array[i].mData = i;
|
|
||||||
ASSERT_EQ(ra.buffer_array[i].mDataByteSize, sizeof(int));
|
|
||||||
ASSERT_EQ(ra.buffer_array[i].mNumberChannels, 1u);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get store buffers*/
|
|
||||||
for (unsigned int i = 0; i < capacity; ++i) {
|
|
||||||
p_data = ring_array_get_free_buffer(&ra);
|
|
||||||
ASSERT_NE(p_data, nullptr);
|
|
||||||
ASSERT_EQ(*(int*)p_data->mData, verify_data[i]);
|
|
||||||
}
|
|
||||||
/*Now array is full extra store should give NULL*/
|
|
||||||
ASSERT_EQ(ring_array_get_free_buffer(&ra), nullptr);
|
|
||||||
/* Get fetch buffers*/
|
|
||||||
for (unsigned int i = 0; i < capacity; ++i) {
|
|
||||||
p_data = ring_array_get_data_buffer(&ra);
|
|
||||||
ASSERT_NE(p_data, nullptr);
|
|
||||||
ASSERT_EQ(*(int*)p_data->mData, verify_data[i]);
|
|
||||||
}
|
|
||||||
/*Now array is empty extra fetch should give NULL*/
|
|
||||||
ASSERT_EQ(ring_array_get_data_buffer(&ra), nullptr);
|
|
||||||
|
|
||||||
p_data = NULL;
|
|
||||||
/* Repeated store fetch should can go for ever*/
|
|
||||||
for (unsigned int i = 0; i < 2*capacity; ++i) {
|
|
||||||
p_data = ring_array_get_free_buffer(&ra);
|
|
||||||
ASSERT_NE(p_data, nullptr);
|
|
||||||
ASSERT_EQ(ring_array_get_data_buffer(&ra), p_data);
|
|
||||||
}
|
|
||||||
|
|
||||||
p_data = NULL;
|
|
||||||
/* Verify/modify buffer data*/
|
|
||||||
for (unsigned int i = 0; i < capacity; ++i) {
|
|
||||||
p_data = ring_array_get_free_buffer(&ra);
|
|
||||||
ASSERT_NE(p_data, nullptr);
|
|
||||||
ASSERT_EQ(*((int*)p_data->mData), verify_data[i]);
|
|
||||||
(*((int*)p_data->mData))++; // Modify data
|
|
||||||
}
|
|
||||||
for (unsigned int i = 0; i < capacity; ++i) {
|
|
||||||
p_data = ring_array_get_data_buffer(&ra);
|
|
||||||
ASSERT_NE(p_data, nullptr);
|
|
||||||
ASSERT_EQ(*((int*)p_data->mData), verify_data[i]+1); // Verify modified data
|
|
||||||
}
|
|
||||||
|
|
||||||
ring_array_destroy(&ra);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
TEST(cubeb, DISABLED_ring_array)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
227
thirdparty/cubeb/test/test_ring_buffer.cpp
vendored
227
thirdparty/cubeb/test/test_ring_buffer.cpp
vendored
|
@ -1,227 +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 "gtest/gtest.h"
|
|
||||||
#include "cubeb_ringbuffer.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
/* Generate a monotonically increasing sequence of numbers. */
|
|
||||||
template<typename T>
|
|
||||||
class sequence_generator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sequence_generator(size_t channels)
|
|
||||||
: channels(channels)
|
|
||||||
{ }
|
|
||||||
void get(T * elements, size_t frames)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < frames; i++) {
|
|
||||||
for (size_t c = 0; c < channels; c++) {
|
|
||||||
elements[i * channels + c] = static_cast<T>(index_);
|
|
||||||
}
|
|
||||||
index_++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void rewind(size_t frames)
|
|
||||||
{
|
|
||||||
index_ -= frames;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
size_t index_ = 0;
|
|
||||||
size_t channels = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Checks that a sequence is monotonically increasing. */
|
|
||||||
template<typename T>
|
|
||||||
class sequence_verifier
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
sequence_verifier(size_t channels)
|
|
||||||
: channels(channels)
|
|
||||||
{ }
|
|
||||||
void check(T * elements, size_t frames)
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < frames; i++) {
|
|
||||||
for (size_t c = 0; c < channels; c++) {
|
|
||||||
if (elements[i * channels + c] != static_cast<T>(index_)) {
|
|
||||||
std::cerr << "Element " << i << " is different. Expected "
|
|
||||||
<< static_cast<T>(index_) << ", got " << elements[i]
|
|
||||||
<< ". (channel count: " << channels << ")." << std::endl;
|
|
||||||
ASSERT_TRUE(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index_++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
size_t index_ = 0;
|
|
||||||
size_t channels = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void test_ring(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_frames)
|
|
||||||
{
|
|
||||||
std::unique_ptr<T[]> seq(new T[capacity_frames * channels]);
|
|
||||||
sequence_generator<T> gen(channels);
|
|
||||||
sequence_verifier<T> checker(channels);
|
|
||||||
|
|
||||||
int iterations = 1002;
|
|
||||||
|
|
||||||
const int block_size = 128;
|
|
||||||
|
|
||||||
while(iterations--) {
|
|
||||||
gen.get(seq.get(), block_size);
|
|
||||||
int rv = buf.enqueue(seq.get(), block_size);
|
|
||||||
ASSERT_EQ(rv, block_size);
|
|
||||||
PodZero(seq.get(), block_size);
|
|
||||||
rv = buf.dequeue(seq.get(), block_size);
|
|
||||||
ASSERT_EQ(rv, block_size);
|
|
||||||
checker.check(seq.get(), block_size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void test_ring_multi(lock_free_audio_ring_buffer<T>& buf, int channels, int capacity_frames)
|
|
||||||
{
|
|
||||||
sequence_verifier<T> checker(channels);
|
|
||||||
std::unique_ptr<T[]> out_buffer(new T[capacity_frames * channels]);
|
|
||||||
|
|
||||||
const int block_size = 128;
|
|
||||||
|
|
||||||
std::thread t([=, &buf] {
|
|
||||||
int iterations = 1002;
|
|
||||||
std::unique_ptr<T[]> in_buffer(new T[capacity_frames * channels]);
|
|
||||||
sequence_generator<T> gen(channels);
|
|
||||||
|
|
||||||
while(iterations--) {
|
|
||||||
std::this_thread::yield();
|
|
||||||
gen.get(in_buffer.get(), block_size);
|
|
||||||
int rv = buf.enqueue(in_buffer.get(), block_size);
|
|
||||||
ASSERT_TRUE(rv <= block_size);
|
|
||||||
if (rv != block_size) {
|
|
||||||
gen.rewind(block_size - rv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
int remaining = 1002;
|
|
||||||
|
|
||||||
while(remaining--) {
|
|
||||||
std::this_thread::yield();
|
|
||||||
int rv = buf.dequeue(out_buffer.get(), block_size);
|
|
||||||
ASSERT_TRUE(rv <= block_size);
|
|
||||||
checker.check(out_buffer.get(), rv);
|
|
||||||
}
|
|
||||||
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void basic_api_test(T& ring)
|
|
||||||
{
|
|
||||||
ASSERT_EQ(ring.capacity(), 128);
|
|
||||||
|
|
||||||
ASSERT_EQ(ring.available_read(), 0);
|
|
||||||
ASSERT_EQ(ring.available_write(), 128);
|
|
||||||
|
|
||||||
int rv = ring.enqueue_default(63);
|
|
||||||
|
|
||||||
ASSERT_TRUE(rv == 63);
|
|
||||||
ASSERT_EQ(ring.available_read(), 63);
|
|
||||||
ASSERT_EQ(ring.available_write(), 65);
|
|
||||||
|
|
||||||
rv = ring.enqueue_default(65);
|
|
||||||
|
|
||||||
ASSERT_EQ(rv, 65);
|
|
||||||
ASSERT_EQ(ring.available_read(), 128);
|
|
||||||
ASSERT_EQ(ring.available_write(), 0);
|
|
||||||
|
|
||||||
rv = ring.dequeue(nullptr, 63);
|
|
||||||
|
|
||||||
ASSERT_EQ(ring.available_read(), 65);
|
|
||||||
ASSERT_EQ(ring.available_write(), 63);
|
|
||||||
|
|
||||||
rv = ring.dequeue(nullptr, 65);
|
|
||||||
|
|
||||||
ASSERT_EQ(ring.available_read(), 0);
|
|
||||||
ASSERT_EQ(ring.available_write(), 128);
|
|
||||||
}
|
|
||||||
|
|
||||||
void test_reset_api() {
|
|
||||||
const size_t ring_buffer_size = 128;
|
|
||||||
const size_t enqueue_size = ring_buffer_size / 2;
|
|
||||||
|
|
||||||
lock_free_queue<float> ring(ring_buffer_size);
|
|
||||||
std::thread t([=, &ring] {
|
|
||||||
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
|
|
||||||
ring.enqueue(in_buffer.get(), enqueue_size);
|
|
||||||
});
|
|
||||||
|
|
||||||
t.join();
|
|
||||||
|
|
||||||
ring.reset_thread_ids();
|
|
||||||
|
|
||||||
// Enqueue with a different thread. We have reset the thread ID
|
|
||||||
// in the ring buffer, this should work.
|
|
||||||
std::thread t2([=, &ring] {
|
|
||||||
std::unique_ptr<float[]> in_buffer(new float[enqueue_size]);
|
|
||||||
ring.enqueue(in_buffer.get(), enqueue_size);
|
|
||||||
});
|
|
||||||
|
|
||||||
t2.join();
|
|
||||||
|
|
||||||
ASSERT_TRUE(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, ring_buffer)
|
|
||||||
{
|
|
||||||
/* Basic API test. */
|
|
||||||
const int min_channels = 1;
|
|
||||||
const int max_channels = 10;
|
|
||||||
const int min_capacity = 199;
|
|
||||||
const int max_capacity = 1277;
|
|
||||||
const int capacity_increment = 27;
|
|
||||||
|
|
||||||
lock_free_queue<float> q1(128);
|
|
||||||
basic_api_test(q1);
|
|
||||||
lock_free_queue<short> q2(128);
|
|
||||||
basic_api_test(q2);
|
|
||||||
|
|
||||||
for (size_t channels = min_channels; channels < max_channels; channels++) {
|
|
||||||
lock_free_audio_ring_buffer<float> q3(channels, 128);
|
|
||||||
basic_api_test(q3);
|
|
||||||
lock_free_audio_ring_buffer<short> q4(channels, 128);
|
|
||||||
basic_api_test(q4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Single thread testing. */
|
|
||||||
/* Test mono to 9.1 */
|
|
||||||
for (size_t channels = min_channels; channels < max_channels; channels++) {
|
|
||||||
/* Use non power-of-two numbers to catch edge-cases. */
|
|
||||||
for (size_t capacity_frames = min_capacity;
|
|
||||||
capacity_frames < max_capacity; capacity_frames+=capacity_increment) {
|
|
||||||
lock_free_audio_ring_buffer<float> ring(channels, capacity_frames);
|
|
||||||
test_ring(ring, channels, capacity_frames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Multi thread testing */
|
|
||||||
for (size_t channels = min_channels; channels < max_channels; channels++) {
|
|
||||||
/* Use non power-of-two numbers to catch edge-cases. */
|
|
||||||
for (size_t capacity_frames = min_capacity;
|
|
||||||
capacity_frames < max_capacity; capacity_frames+=capacity_increment) {
|
|
||||||
lock_free_audio_ring_buffer<short> ring(channels, capacity_frames);
|
|
||||||
test_ring_multi(ring, channels, capacity_frames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
test_reset_api();
|
|
||||||
}
|
|
689
thirdparty/cubeb/test/test_sanity.cpp
vendored
689
thirdparty/cubeb/test/test_sanity.cpp
vendored
|
@ -1,689 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2011 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include <atomic>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
#define STREAM_RATE 44100
|
|
||||||
#define STREAM_LATENCY 100 * STREAM_RATE / 1000
|
|
||||||
#define STREAM_CHANNELS 1
|
|
||||||
#define STREAM_LAYOUT CUBEB_LAYOUT_MONO
|
|
||||||
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
|
|
||||||
|
|
||||||
int is_windows_7()
|
|
||||||
{
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
fprintf(stderr, "Warning: this test was built with MinGW.\n"
|
|
||||||
"MinGW does not contain necessary version checking infrastructure. Claiming to be Windows 7, even if we're not.\n");
|
|
||||||
return 1;
|
|
||||||
#endif
|
|
||||||
#if (defined(_WIN32) || defined(__WIN32__)) && ( !defined(__MINGW32__))
|
|
||||||
OSVERSIONINFOEX osvi;
|
|
||||||
DWORDLONG condition_mask = 0;
|
|
||||||
|
|
||||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
|
||||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
|
||||||
|
|
||||||
// NT 6.1 is Windows 7
|
|
||||||
osvi.dwMajorVersion = 6;
|
|
||||||
osvi.dwMinorVersion = 1;
|
|
||||||
|
|
||||||
VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_EQUAL);
|
|
||||||
VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
|
||||||
|
|
||||||
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, condition_mask);
|
|
||||||
#else
|
|
||||||
return 0;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dummy;
|
|
||||||
static std::atomic<uint64_t> total_frames_written;
|
|
||||||
static int delay_callback;
|
|
||||||
|
|
||||||
static long
|
|
||||||
test_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
|
|
||||||
assert(outputbuffer);
|
|
||||||
memset(outputbuffer, 0, nframes * sizeof(short));
|
|
||||||
|
|
||||||
total_frames_written += nframes;
|
|
||||||
if (delay_callback) {
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
test_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state /*state*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, init_destroy_context)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
char const* backend_id;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
backend_id = cubeb_get_backend_id(ctx);
|
|
||||||
ASSERT_TRUE(backend_id);
|
|
||||||
|
|
||||||
fprintf(stderr, "Backend: %s\n", backend_id);
|
|
||||||
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, init_destroy_multiple_contexts)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
int r;
|
|
||||||
cubeb * ctx[4];
|
|
||||||
int order[4] = {2, 0, 3, 1};
|
|
||||||
ASSERT_EQ(ARRAY_LENGTH(ctx), ARRAY_LENGTH(order));
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
|
|
||||||
r = common_init(&ctx[i], NULL);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx[i], nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* destroy in a different order */
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
|
|
||||||
cubeb_destroy(ctx[order[i]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, context_variables)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
uint32_t value;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_context_variables");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_get_min_latency(ctx, ¶ms, &value);
|
|
||||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
|
||||||
if (r == CUBEB_OK) {
|
|
||||||
ASSERT_TRUE(value > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = cubeb_get_preferred_sample_rate(ctx, &value);
|
|
||||||
ASSERT_TRUE(r == CUBEB_OK || r == CUBEB_ERROR_NOT_SUPPORTED);
|
|
||||||
if (r == CUBEB_OK) {
|
|
||||||
ASSERT_TRUE(value > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, init_destroy_stream)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream, nullptr);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, init_destroy_multiple_streams)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream[8];
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
|
||||||
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream[i], nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
|
||||||
cubeb_stream_destroy(stream[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, configure_stream)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = 2;
|
|
||||||
params.layout = CUBEB_LAYOUT_STEREO;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream, nullptr);
|
|
||||||
|
|
||||||
r = cubeb_stream_set_volume(stream, 1.0f);
|
|
||||||
ASSERT_TRUE(r == 0 || r == CUBEB_ERROR_NOT_SUPPORTED);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, configure_stream_undefined_layout)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = 2;
|
|
||||||
params.layout = CUBEB_LAYOUT_UNDEFINED;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream, nullptr);
|
|
||||||
|
|
||||||
r = cubeb_stream_start(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
delay(100);
|
|
||||||
|
|
||||||
r = cubeb_stream_stop(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
test_init_start_stop_destroy_multiple_streams(int early, int delay_ms)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream[8];
|
|
||||||
cubeb_stream_params params;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
|
||||||
r = cubeb_stream_init(ctx, &stream[i], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream[i], nullptr);
|
|
||||||
if (early) {
|
|
||||||
r = cubeb_stream_start(stream[i]);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!early) {
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
|
||||||
r = cubeb_stream_start(stream[i]);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (delay_ms) {
|
|
||||||
delay(delay_ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!early) {
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
|
||||||
r = cubeb_stream_stop(stream[i]);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(stream); ++i) {
|
|
||||||
if (early) {
|
|
||||||
r = cubeb_stream_stop(stream[i]);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
}
|
|
||||||
cubeb_stream_destroy(stream[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, init_start_stop_destroy_multiple_streams)
|
|
||||||
{
|
|
||||||
/* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and
|
|
||||||
* calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is
|
|
||||||
* the HRESULT value for "Cannot create a file when that file already exists",
|
|
||||||
* and is not documented as a possible return value for this call. Hence, we
|
|
||||||
* try to limit the number of streams we create in this test. */
|
|
||||||
if (!is_windows_7()) {
|
|
||||||
delay_callback = 0;
|
|
||||||
test_init_start_stop_destroy_multiple_streams(0, 0);
|
|
||||||
test_init_start_stop_destroy_multiple_streams(1, 0);
|
|
||||||
test_init_start_stop_destroy_multiple_streams(0, 150);
|
|
||||||
test_init_start_stop_destroy_multiple_streams(1, 150);
|
|
||||||
delay_callback = 1;
|
|
||||||
test_init_start_stop_destroy_multiple_streams(0, 0);
|
|
||||||
test_init_start_stop_destroy_multiple_streams(1, 0);
|
|
||||||
test_init_start_stop_destroy_multiple_streams(0, 150);
|
|
||||||
test_init_start_stop_destroy_multiple_streams(1, 150);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, init_destroy_multiple_contexts_and_streams)
|
|
||||||
{
|
|
||||||
size_t i, j;
|
|
||||||
int r;
|
|
||||||
cubeb * ctx[2];
|
|
||||||
cubeb_stream * stream[8];
|
|
||||||
cubeb_stream_params params;
|
|
||||||
size_t streams_per_ctx = ARRAY_LENGTH(stream) / ARRAY_LENGTH(ctx);
|
|
||||||
ASSERT_EQ(ARRAY_LENGTH(ctx) * streams_per_ctx, ARRAY_LENGTH(stream));
|
|
||||||
|
|
||||||
/* Sometimes, when using WASAPI on windows 7 (vista and 8 are okay), and
|
|
||||||
* calling Activate a lot on an AudioClient, 0x800700b7 is returned. This is
|
|
||||||
* the HRESULT value for "Cannot create a file when that file already exists",
|
|
||||||
* and is not documented as a possible return value for this call. Hence, we
|
|
||||||
* try to limit the number of streams we create in this test. */
|
|
||||||
if (is_windows_7())
|
|
||||||
return;
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
|
|
||||||
r = common_init(&ctx[i], "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx[i], nullptr);
|
|
||||||
|
|
||||||
for (j = 0; j < streams_per_ctx; ++j) {
|
|
||||||
r = cubeb_stream_init(ctx[i], &stream[i * streams_per_ctx + j], "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream[i * streams_per_ctx + j], nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_LENGTH(ctx); ++i) {
|
|
||||||
for (j = 0; j < streams_per_ctx; ++j) {
|
|
||||||
cubeb_stream_destroy(stream[i * streams_per_ctx + j]);
|
|
||||||
}
|
|
||||||
cubeb_destroy(ctx[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, basic_stream_operations)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
uint64_t position;
|
|
||||||
uint32_t latency;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream, nullptr);
|
|
||||||
|
|
||||||
/* position and latency before stream has started */
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_EQ(position, 0u);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_latency(stream, &latency);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
r = cubeb_stream_start(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
/* position and latency after while stream running */
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_latency(stream, &latency);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
r = cubeb_stream_stop(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
/* position and latency after stream has stopped */
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_latency(stream, &latency);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, stream_position)
|
|
||||||
{
|
|
||||||
size_t i;
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
uint64_t position, last_position;
|
|
||||||
|
|
||||||
total_frames_written = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_data_callback, test_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream, nullptr);
|
|
||||||
|
|
||||||
/* stream position should not advance before starting playback */
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_EQ(position, 0u);
|
|
||||||
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_EQ(position, 0u);
|
|
||||||
|
|
||||||
/* stream position should advance during playback */
|
|
||||||
r = cubeb_stream_start(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
/* XXX let start happen */
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
/* stream should have prefilled */
|
|
||||||
ASSERT_TRUE(total_frames_written.load() > 0);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
last_position = position;
|
|
||||||
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_GE(position, last_position);
|
|
||||||
last_position = position;
|
|
||||||
|
|
||||||
/* stream position should not exceed total frames written */
|
|
||||||
for (i = 0; i < 5; ++i) {
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_GE(position, last_position);
|
|
||||||
ASSERT_LE(position, total_frames_written.load());
|
|
||||||
last_position = position;
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* test that the position is valid even when starting and
|
|
||||||
* stopping the stream. */
|
|
||||||
for (i = 0; i < 5; ++i) {
|
|
||||||
r = cubeb_stream_stop(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_TRUE(last_position < position);
|
|
||||||
last_position = position;
|
|
||||||
delay(500);
|
|
||||||
r = cubeb_stream_start(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_NE(last_position, 0u);
|
|
||||||
|
|
||||||
/* stream position should not advance after stopping playback */
|
|
||||||
r = cubeb_stream_stop(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
/* XXX allow stream to settle */
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
last_position = position;
|
|
||||||
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_EQ(position, last_position);
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::atomic<int> do_drain;
|
|
||||||
static std::atomic<int> got_drain;
|
|
||||||
|
|
||||||
static long
|
|
||||||
test_drain_data_callback(cubeb_stream * stm, void * user_ptr, const void * /*inputbuffer*/, void * outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
EXPECT_TRUE(stm && user_ptr == &dummy && outputbuffer && nframes > 0);
|
|
||||||
assert(outputbuffer);
|
|
||||||
if (do_drain == 1) {
|
|
||||||
do_drain = 2;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
/* once drain has started, callback must never be called again */
|
|
||||||
EXPECT_TRUE(do_drain != 2);
|
|
||||||
memset(outputbuffer, 0, nframes * sizeof(short));
|
|
||||||
total_frames_written += nframes;
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
test_drain_state_callback(cubeb_stream * /*stm*/, void * /*user_ptr*/, cubeb_state state)
|
|
||||||
{
|
|
||||||
if (state == CUBEB_STATE_DRAINED) {
|
|
||||||
ASSERT_TRUE(!got_drain);
|
|
||||||
got_drain = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, drain)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_stream * stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
uint64_t position;
|
|
||||||
|
|
||||||
delay_callback = 0;
|
|
||||||
total_frames_written = 0;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = STREAM_RATE;
|
|
||||||
params.channels = STREAM_CHANNELS;
|
|
||||||
params.layout = STREAM_LAYOUT;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "test", NULL, NULL, NULL, ¶ms, STREAM_LATENCY,
|
|
||||||
test_drain_data_callback, test_drain_state_callback, &dummy);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(stream, nullptr);
|
|
||||||
|
|
||||||
r = cubeb_stream_start(stream);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
delay(500);
|
|
||||||
|
|
||||||
do_drain = 1;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
if (got_drain) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
ASSERT_LE(position, total_frames_written.load());
|
|
||||||
}
|
|
||||||
delay(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = cubeb_stream_get_position(stream, &position);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_TRUE(got_drain);
|
|
||||||
|
|
||||||
// Really, we should be able to rely on position reaching our final written frame, but
|
|
||||||
// for now let's make sure it doesn't continue beyond that point.
|
|
||||||
//ASSERT_LE(position, total_frames_written.load());
|
|
||||||
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
|
|
||||||
got_drain = 0;
|
|
||||||
do_drain = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, DISABLED_eos_during_prefill)
|
|
||||||
{
|
|
||||||
// This test needs to be implemented.
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, DISABLED_stream_destroy_pending_drain)
|
|
||||||
{
|
|
||||||
// This test needs to be implemented.
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, stable_devid)
|
|
||||||
{
|
|
||||||
/* Test that the devid field of cubeb_device_info is stable
|
|
||||||
* (ie. compares equal) over two invocations of
|
|
||||||
* cubeb_enumerate_devices(). */
|
|
||||||
|
|
||||||
int r;
|
|
||||||
cubeb * ctx;
|
|
||||||
cubeb_device_collection first;
|
|
||||||
cubeb_device_collection second;
|
|
||||||
cubeb_device_type all_devices =
|
|
||||||
(cubeb_device_type) (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
|
|
||||||
size_t n;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "test_sanity");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
ASSERT_NE(ctx, nullptr);
|
|
||||||
|
|
||||||
r = cubeb_enumerate_devices(ctx, all_devices, &first);
|
|
||||||
if (r == CUBEB_ERROR_NOT_SUPPORTED)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
r = cubeb_enumerate_devices(ctx, all_devices, &second);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
|
|
||||||
ASSERT_EQ(first.count, second.count);
|
|
||||||
for (n = 0; n < first.count; n++) {
|
|
||||||
ASSERT_EQ(first.device[n].devid, second.device[n].devid);
|
|
||||||
}
|
|
||||||
|
|
||||||
r = cubeb_device_collection_destroy(ctx, &first);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
r = cubeb_device_collection_destroy(ctx, &second);
|
|
||||||
ASSERT_EQ(r, CUBEB_OK);
|
|
||||||
cubeb_destroy(ctx);
|
|
||||||
}
|
|
121
thirdparty/cubeb/test/test_tone.cpp
vendored
121
thirdparty/cubeb/test/test_tone.cpp
vendored
|
@ -1,121 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright © 2011 Mozilla Foundation
|
|
||||||
*
|
|
||||||
* This program is made available under an ISC-style license. See the
|
|
||||||
* accompanying file LICENSE for details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* libcubeb api/function test. Plays a simple tone. */
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#if !defined(_XOPEN_SOURCE)
|
|
||||||
#define _XOPEN_SOURCE 600
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <memory>
|
|
||||||
#include <limits.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
//#define ENABLE_NORMAL_LOG
|
|
||||||
//#define ENABLE_VERBOSE_LOG
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
|
|
||||||
#define SAMPLE_FREQUENCY 48000
|
|
||||||
#define STREAM_FORMAT CUBEB_SAMPLE_S16LE
|
|
||||||
|
|
||||||
/* store the phase of the generated waveform */
|
|
||||||
struct cb_user_data {
|
|
||||||
std::atomic<long> position;
|
|
||||||
};
|
|
||||||
|
|
||||||
long data_cb_tone(cubeb_stream *stream, void *user, const void* /*inputbuffer*/, void *outputbuffer, long nframes)
|
|
||||||
{
|
|
||||||
struct cb_user_data *u = (struct cb_user_data *)user;
|
|
||||||
short *b = (short *)outputbuffer;
|
|
||||||
float t1, t2;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
if (stream == NULL || u == NULL)
|
|
||||||
return CUBEB_ERROR;
|
|
||||||
|
|
||||||
/* generate our test tone on the fly */
|
|
||||||
for (i = 0; i < nframes; i++) {
|
|
||||||
/* North American dial tone */
|
|
||||||
t1 = sin(2*M_PI*(i + u->position)*350/SAMPLE_FREQUENCY);
|
|
||||||
t2 = sin(2*M_PI*(i + u->position)*440/SAMPLE_FREQUENCY);
|
|
||||||
b[i] = (SHRT_MAX / 2) * t1;
|
|
||||||
b[i] += (SHRT_MAX / 2) * t2;
|
|
||||||
/* European dial tone */
|
|
||||||
/*
|
|
||||||
t1 = sin(2*M_PI*(i + u->position)*425/SAMPLE_FREQUENCY);
|
|
||||||
b[i] = SHRT_MAX * t1;
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
/* remember our phase to avoid clicking on buffer transitions */
|
|
||||||
/* we'll still click if position overflows */
|
|
||||||
u->position += nframes;
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void state_cb_tone(cubeb_stream *stream, void *user, cubeb_state state)
|
|
||||||
{
|
|
||||||
struct cb_user_data *u = (struct cb_user_data *)user;
|
|
||||||
|
|
||||||
if (stream == NULL || u == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
fprintf(stderr, "stream started\n"); break;
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
fprintf(stderr, "stream stopped\n"); break;
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
fprintf(stderr, "stream drained\n"); break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "unknown stream state %d\n", state);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(cubeb, tone)
|
|
||||||
{
|
|
||||||
cubeb *ctx;
|
|
||||||
cubeb_stream *stream;
|
|
||||||
cubeb_stream_params params;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
r = common_init(&ctx, "Cubeb tone example");
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb library";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb, decltype(&cubeb_destroy)>
|
|
||||||
cleanup_cubeb_at_exit(ctx, cubeb_destroy);
|
|
||||||
|
|
||||||
params.format = STREAM_FORMAT;
|
|
||||||
params.rate = SAMPLE_FREQUENCY;
|
|
||||||
params.channels = 1;
|
|
||||||
params.layout = CUBEB_LAYOUT_MONO;
|
|
||||||
params.prefs = CUBEB_STREAM_PREF_NONE;
|
|
||||||
|
|
||||||
std::unique_ptr<cb_user_data> user_data(new cb_user_data());
|
|
||||||
ASSERT_TRUE(!!user_data) << "Error allocating user data";
|
|
||||||
|
|
||||||
user_data->position = 0;
|
|
||||||
|
|
||||||
r = cubeb_stream_init(ctx, &stream, "Cubeb tone (mono)", NULL, NULL, NULL, ¶ms,
|
|
||||||
4096, data_cb_tone, state_cb_tone, user_data.get());
|
|
||||||
ASSERT_EQ(r, CUBEB_OK) << "Error initializing cubeb stream";
|
|
||||||
|
|
||||||
std::unique_ptr<cubeb_stream, decltype(&cubeb_stream_destroy)>
|
|
||||||
cleanup_stream_at_exit(stream, cubeb_stream_destroy);
|
|
||||||
|
|
||||||
cubeb_stream_start(stream);
|
|
||||||
delay(500);
|
|
||||||
cubeb_stream_stop(stream);
|
|
||||||
|
|
||||||
ASSERT_TRUE(user_data->position.load());
|
|
||||||
}
|
|
72
thirdparty/cubeb/test/test_utils.cpp
vendored
72
thirdparty/cubeb/test/test_utils.cpp
vendored
|
@ -1,72 +0,0 @@
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include "cubeb_utils.h"
|
|
||||||
|
|
||||||
TEST(cubeb, auto_array)
|
|
||||||
{
|
|
||||||
auto_array<uint32_t> array;
|
|
||||||
auto_array<uint32_t> array2(10);
|
|
||||||
uint32_t a[10];
|
|
||||||
|
|
||||||
ASSERT_EQ(array2.length(), 0u);
|
|
||||||
ASSERT_EQ(array2.capacity(), 10u);
|
|
||||||
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < 10; i++) {
|
|
||||||
a[i] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_EQ(array.capacity(), 0u);
|
|
||||||
ASSERT_EQ(array.length(), 0u);
|
|
||||||
|
|
||||||
array.push(a, 10);
|
|
||||||
|
|
||||||
ASSERT_TRUE(!array.reserve(9));
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < 10; i++) {
|
|
||||||
ASSERT_EQ(array.data()[i], i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_EQ(array.capacity(), 10u);
|
|
||||||
ASSERT_EQ(array.length(), 10u);
|
|
||||||
|
|
||||||
uint32_t b[10];
|
|
||||||
|
|
||||||
array.pop(b, 5);
|
|
||||||
|
|
||||||
ASSERT_EQ(array.capacity(), 10u);
|
|
||||||
ASSERT_EQ(array.length(), 5u);
|
|
||||||
for (uint32_t i = 0; i < 5; i++) {
|
|
||||||
ASSERT_EQ(b[i], i);
|
|
||||||
ASSERT_EQ(array.data()[i], 5 + i);
|
|
||||||
}
|
|
||||||
uint32_t* bb = b + 5;
|
|
||||||
array.pop(bb, 5);
|
|
||||||
|
|
||||||
ASSERT_EQ(array.capacity(), 10u);
|
|
||||||
ASSERT_EQ(array.length(), 0u);
|
|
||||||
for (uint32_t i = 0; i < 5; i++) {
|
|
||||||
ASSERT_EQ(bb[i], 5 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_TRUE(!array.pop(nullptr, 1));
|
|
||||||
|
|
||||||
array.push(a, 10);
|
|
||||||
array.push(a, 10);
|
|
||||||
|
|
||||||
for (uint32_t j = 0; j < 2; j++) {
|
|
||||||
for (uint32_t i = 0; i < 10; i++) {
|
|
||||||
ASSERT_EQ(array.data()[10 * j + i], i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ASSERT_EQ(array.length(), 20u);
|
|
||||||
ASSERT_EQ(array.capacity(), 20u);
|
|
||||||
array.pop(nullptr, 5);
|
|
||||||
|
|
||||||
for (uint32_t i = 0; i < 5; i++) {
|
|
||||||
ASSERT_EQ(array.data()[i], 5 + i);
|
|
||||||
}
|
|
||||||
|
|
||||||
ASSERT_EQ(array.length(), 15u);
|
|
||||||
ASSERT_EQ(array.capacity(), 20u);
|
|
||||||
}
|
|
||||||
|
|
558
thirdparty/cubeb/tools/cubeb-test.cpp
vendored
558
thirdparty/cubeb/tools/cubeb-test.cpp
vendored
|
@ -1,558 +0,0 @@
|
||||||
#define __STDC_FORMAT_MACROS
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include <atomic>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstring>
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <iostream>
|
|
||||||
#ifdef _WIN32
|
|
||||||
#include <objbase.h> // Used by CoInitialize()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef M_PI
|
|
||||||
#define M_PI 3.14159263
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Default values if none specified
|
|
||||||
#define DEFAULT_RATE 44100
|
|
||||||
#define DEFAULT_OUTPUT_CHANNELS 2
|
|
||||||
#define DEFAULT_INPUT_CHANNELS 1
|
|
||||||
|
|
||||||
static const char* state_to_string(cubeb_state state) {
|
|
||||||
switch (state) {
|
|
||||||
case CUBEB_STATE_STARTED:
|
|
||||||
return "CUBEB_STATE_STARTED";
|
|
||||||
case CUBEB_STATE_STOPPED:
|
|
||||||
return "CUBEB_STATE_STOPPED";
|
|
||||||
case CUBEB_STATE_DRAINED:
|
|
||||||
return "CUBEB_STATE_DRAINED";
|
|
||||||
case CUBEB_STATE_ERROR:
|
|
||||||
return "CUBEB_STATE_ERROR";
|
|
||||||
default:
|
|
||||||
return "Undefined state";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_log(const char* msg, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, msg);
|
|
||||||
vprintf(msg, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
class cubeb_client final {
|
|
||||||
public:
|
|
||||||
cubeb_client() {}
|
|
||||||
~cubeb_client() {}
|
|
||||||
|
|
||||||
bool init(char const * backend_name = nullptr);
|
|
||||||
bool init_stream();
|
|
||||||
bool start_stream();
|
|
||||||
bool stop_stream();
|
|
||||||
bool destroy_stream() const;
|
|
||||||
bool destroy();
|
|
||||||
bool activate_log(cubeb_log_level log_level) const;
|
|
||||||
void set_latency_testing(bool on);
|
|
||||||
void set_latency_frames(uint32_t latency_frames);
|
|
||||||
uint64_t get_stream_position() const;
|
|
||||||
uint32_t get_stream_latency() const;
|
|
||||||
uint32_t get_max_channel_count() const;
|
|
||||||
|
|
||||||
long user_data_cb(cubeb_stream* stm, void* user, const void* input_buffer,
|
|
||||||
void* output_buffer, long nframes);
|
|
||||||
|
|
||||||
void user_state_cb(cubeb_stream* stm, void* user, cubeb_state state);
|
|
||||||
|
|
||||||
bool register_device_collection_changed(cubeb_device_type devtype) const;
|
|
||||||
bool unregister_device_collection_changed(cubeb_device_type devtype) const;
|
|
||||||
|
|
||||||
cubeb_stream_params output_params = {};
|
|
||||||
cubeb_stream_params input_params = {};
|
|
||||||
|
|
||||||
void force_drain() { _force_drain = true; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
bool has_input() { return input_params.rate != 0; }
|
|
||||||
bool has_output() { return output_params.rate != 0; }
|
|
||||||
|
|
||||||
cubeb* context = nullptr;
|
|
||||||
|
|
||||||
cubeb_stream* stream = nullptr;
|
|
||||||
cubeb_devid output_device = nullptr;
|
|
||||||
cubeb_devid input_device = nullptr;
|
|
||||||
|
|
||||||
/* Accessed only from client and audio thread. */
|
|
||||||
std::atomic<uint32_t> _rate = {0};
|
|
||||||
std::atomic<uint32_t> _channels = {0};
|
|
||||||
std::atomic<bool> _latency_testing = {false};
|
|
||||||
std::atomic<uint32_t> _latency_frames = {0}; // if !0, override. Else, use min.
|
|
||||||
std::atomic<bool> _force_drain = {false};
|
|
||||||
|
|
||||||
|
|
||||||
/* Accessed only from audio thread. */
|
|
||||||
uint32_t _total_frames = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool cubeb_client::init(char const * backend_name) {
|
|
||||||
int rv = cubeb_init(&context, "Cubeb Test Application", backend_name);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not init cubeb\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "Init cubeb backend: %s\n", cubeb_get_backend_id(context));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static long user_data_cb_s(cubeb_stream* stm, void* user,
|
|
||||||
const void* input_buffer, void* output_buffer,
|
|
||||||
long nframes) {
|
|
||||||
assert(user);
|
|
||||||
return static_cast<cubeb_client*>(user)->user_data_cb(stm, user, input_buffer,
|
|
||||||
output_buffer, nframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void user_state_cb_s(cubeb_stream* stm, void* user, cubeb_state state) {
|
|
||||||
assert(user);
|
|
||||||
return static_cast<cubeb_client*>(user)->user_state_cb(stm, user, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
void input_device_changed_callback_s(cubeb* context, void* user) {
|
|
||||||
fprintf(stderr, "input_device_changed_callback_s\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void output_device_changed_callback_s(cubeb* context, void* user) {
|
|
||||||
fprintf(stderr, "output_device_changed_callback_s\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void io_device_changed_callback_s(cubeb* context, void* user) {
|
|
||||||
fprintf(stderr, "io_device_changed_callback\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::init_stream() {
|
|
||||||
assert(has_input() || has_output());
|
|
||||||
|
|
||||||
_rate = has_output() ? output_params.rate : input_params.rate;
|
|
||||||
_channels = has_output() ? output_params.channels : input_params.channels;
|
|
||||||
|
|
||||||
cubeb_stream_params params;
|
|
||||||
params.rate = _rate;
|
|
||||||
params.channels = 2;
|
|
||||||
params.format = CUBEB_SAMPLE_FLOAT32NE;
|
|
||||||
|
|
||||||
uint32_t latency = 0;
|
|
||||||
int rv = cubeb_get_min_latency(context, ¶ms, &latency);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not get min latency.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_latency_frames) {
|
|
||||||
latency = _latency_frames.load();
|
|
||||||
printf("Opening a stream with a forced latency of %d frames\n", latency);
|
|
||||||
}
|
|
||||||
|
|
||||||
rv =
|
|
||||||
cubeb_stream_init(context, &stream, "Stream", input_device,
|
|
||||||
has_input() ? &input_params : nullptr, output_device,
|
|
||||||
has_output() ? &output_params : nullptr, latency, user_data_cb_s, user_state_cb_s, this);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not open the stream\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::start_stream() {
|
|
||||||
_force_drain = false;
|
|
||||||
int rv = cubeb_stream_start(stream);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not start the stream\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::stop_stream() {
|
|
||||||
_force_drain = false;
|
|
||||||
int rv = cubeb_stream_stop(stream);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not stop the stream\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t cubeb_client::get_stream_position() const {
|
|
||||||
uint64_t pos = 0;
|
|
||||||
int rv = cubeb_stream_get_position(stream, &pos);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not get the position of the stream\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t cubeb_client::get_stream_latency() const {
|
|
||||||
uint32_t latency = 0;
|
|
||||||
int rv = cubeb_stream_get_latency(stream, &latency);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not get the latency of the stream\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return latency;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t cubeb_client::get_max_channel_count() const {
|
|
||||||
uint32_t channels = 0;
|
|
||||||
int rv = cubeb_get_max_channel_count(context, &channels);
|
|
||||||
if (rv != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Could not get max channel count\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::destroy_stream() const {
|
|
||||||
cubeb_stream_destroy(stream);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::destroy() {
|
|
||||||
cubeb_destroy(context);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::activate_log(cubeb_log_level log_level) const {
|
|
||||||
cubeb_log_callback log_callback = nullptr;
|
|
||||||
if (log_level != CUBEB_LOG_DISABLED) {
|
|
||||||
log_callback = print_log;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cubeb_set_log_callback(log_level, log_callback) != CUBEB_OK) {
|
|
||||||
fprintf(stderr, "Set log callback failed\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cubeb_client::set_latency_testing(bool on) {
|
|
||||||
_latency_testing = on;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cubeb_client::set_latency_frames(uint32_t latency_frames) {
|
|
||||||
_latency_frames = latency_frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void fill_with_sine_tone(float* buf, uint32_t num_of_frames,
|
|
||||||
uint32_t num_of_channels, uint32_t frame_rate,
|
|
||||||
uint32_t position) {
|
|
||||||
for (uint32_t i = 0; i < num_of_frames; ++i) {
|
|
||||||
for (uint32_t c = 0; c < num_of_channels; ++c) {
|
|
||||||
buf[i * num_of_channels + c] =
|
|
||||||
0.2 * sin(2 * M_PI * (i + position) * 350 / frame_rate);
|
|
||||||
buf[i * num_of_channels + c] +=
|
|
||||||
0.2 * sin(2 * M_PI * (i + position) * 440 / frame_rate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long cubeb_client::user_data_cb(cubeb_stream* stm, void* user,
|
|
||||||
const void* input_buffer, void* output_buffer,
|
|
||||||
long nframes) {
|
|
||||||
if (input_buffer && output_buffer) {
|
|
||||||
const float* in = static_cast<const float*>(input_buffer);
|
|
||||||
float* out = static_cast<float*>(output_buffer);
|
|
||||||
if (_latency_testing) {
|
|
||||||
for (int32_t i = 0; i < nframes; i++) {
|
|
||||||
// Impulses every second, mixed with the input signal fed back at half
|
|
||||||
// gain, to measure the input-to-output latency via feedback.
|
|
||||||
uint32_t clock = ((_total_frames + i) % _rate);
|
|
||||||
if (!clock) {
|
|
||||||
for (uint32_t j = 0; j < _channels; j++) {
|
|
||||||
out[i * _channels + j] = 1.0 + in[i] * 0.5;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (uint32_t j = 0; j < _channels; j++) {
|
|
||||||
out[i * _channels + j] = 0.0 + in[i] * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int32_t i = 0; i < nframes; i++) {
|
|
||||||
for (uint32_t j = 0; j < _channels; j++) {
|
|
||||||
out[i * _channels + j] = in[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (output_buffer && !input_buffer) {
|
|
||||||
fill_with_sine_tone(static_cast<float*>(output_buffer), nframes, _channels,
|
|
||||||
_rate, _total_frames);
|
|
||||||
}
|
|
||||||
|
|
||||||
_total_frames += nframes;
|
|
||||||
|
|
||||||
if (_force_drain) {
|
|
||||||
return nframes - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nframes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void cubeb_client::user_state_cb(cubeb_stream* stm, void* user,
|
|
||||||
cubeb_state state) {
|
|
||||||
fprintf(stderr, "state is %s\n", state_to_string(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::register_device_collection_changed(
|
|
||||||
cubeb_device_type devtype) const {
|
|
||||||
cubeb_device_collection_changed_callback callback = nullptr;
|
|
||||||
if (devtype == static_cast<cubeb_device_type>(CUBEB_DEVICE_TYPE_INPUT |
|
|
||||||
CUBEB_DEVICE_TYPE_OUTPUT)) {
|
|
||||||
callback = io_device_changed_callback_s;
|
|
||||||
} else if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
|
|
||||||
callback = output_device_changed_callback_s;
|
|
||||||
} else if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
|
|
||||||
callback = input_device_changed_callback_s;
|
|
||||||
}
|
|
||||||
int r = cubeb_register_device_collection_changed(
|
|
||||||
context, devtype, callback, nullptr);
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool cubeb_client::unregister_device_collection_changed(
|
|
||||||
cubeb_device_type devtype) const {
|
|
||||||
int r = cubeb_register_device_collection_changed(
|
|
||||||
context, devtype, nullptr, nullptr);
|
|
||||||
if (r != CUBEB_OK) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum play_mode {
|
|
||||||
RECORD,
|
|
||||||
PLAYBACK,
|
|
||||||
DUPLEX,
|
|
||||||
LATENCY_TESTING,
|
|
||||||
COLLECTION_CHANGE,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct operation_data {
|
|
||||||
play_mode pm;
|
|
||||||
uint32_t rate;
|
|
||||||
cubeb_device_type collection_device_type;
|
|
||||||
};
|
|
||||||
|
|
||||||
void print_help() {
|
|
||||||
const char * msg =
|
|
||||||
"0: change log level to disabled\n"
|
|
||||||
"1: change log level to normal\n"
|
|
||||||
"2: change log level to verbose\n"
|
|
||||||
"c: get max number of channels\n"
|
|
||||||
"p: start a initialized stream\n"
|
|
||||||
"s: stop a started stream\n"
|
|
||||||
"d: destroy stream\n"
|
|
||||||
"e: force stream to drain\n"
|
|
||||||
"f: get stream position (client thread)\n"
|
|
||||||
"i: change device type to input\n"
|
|
||||||
"o: change device type to output\n"
|
|
||||||
"a: change device type to input and output\n"
|
|
||||||
"k: change device type to unknown\n"
|
|
||||||
"r: register device collection changed callback for the current device type\n"
|
|
||||||
"u: unregister device collection changed callback for the current device type\n"
|
|
||||||
"q: quit\n"
|
|
||||||
"h: print this message\n";
|
|
||||||
fprintf(stderr, "%s\n", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool choose_action(cubeb_client& cl, operation_data * op, int c) {
|
|
||||||
// Consume "enter" and "space"
|
|
||||||
while (c == 10 || c == 32) {
|
|
||||||
c = getchar();
|
|
||||||
}
|
|
||||||
if (c == EOF) {
|
|
||||||
c = 'q';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c == 'q') {
|
|
||||||
if (op->pm == PLAYBACK || op->pm == RECORD || op->pm == DUPLEX || op->pm == LATENCY_TESTING) {
|
|
||||||
bool res = cl.stop_stream();
|
|
||||||
if (!res) {
|
|
||||||
fprintf(stderr, "stop_stream failed\n");
|
|
||||||
}
|
|
||||||
res = cl.destroy_stream();
|
|
||||||
if (!res) {
|
|
||||||
fprintf(stderr, "destroy_stream failed\n");
|
|
||||||
}
|
|
||||||
} else if (op->pm == COLLECTION_CHANGE) {
|
|
||||||
bool res = cl.unregister_device_collection_changed(op->collection_device_type);
|
|
||||||
if (!res) {
|
|
||||||
fprintf(stderr, "unregister_device_collection_changed failed\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false; // exit the loop
|
|
||||||
} else if (c == 'h') {
|
|
||||||
print_help();
|
|
||||||
} else if (c == '0') {
|
|
||||||
cl.activate_log(CUBEB_LOG_DISABLED);
|
|
||||||
fprintf(stderr, "Log level changed to DISABLED\n");
|
|
||||||
} else if (c == '1') {
|
|
||||||
cl.activate_log(CUBEB_LOG_DISABLED);
|
|
||||||
cl.activate_log(CUBEB_LOG_NORMAL);
|
|
||||||
fprintf(stderr, "Log level changed to NORMAL\n");
|
|
||||||
} else if (c == '2') {
|
|
||||||
cl.activate_log(CUBEB_LOG_DISABLED);
|
|
||||||
cl.activate_log(CUBEB_LOG_VERBOSE);
|
|
||||||
fprintf(stderr, "Log level changed to VERBOSE\n");
|
|
||||||
} else if (c == 'p') {
|
|
||||||
bool res = cl.start_stream();
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "start_stream succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "start_stream failed\n");
|
|
||||||
}
|
|
||||||
} else if (c == 's') {
|
|
||||||
bool res = cl.stop_stream();
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "stop_stream succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "stop_stream failed\n");
|
|
||||||
}
|
|
||||||
} else if (c == 'd') {
|
|
||||||
bool res = cl.destroy_stream();
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "destroy_stream succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "destroy_stream failed\n");
|
|
||||||
}
|
|
||||||
} else if (c == 'e') {
|
|
||||||
cl.force_drain();
|
|
||||||
} else if (c == 'c') {
|
|
||||||
uint32_t channel_count = cl.get_max_channel_count();
|
|
||||||
fprintf(stderr, "max channel count (default output device): %u\n", channel_count);
|
|
||||||
} else if (c == 'f') {
|
|
||||||
uint64_t pos = cl.get_stream_position();
|
|
||||||
uint64_t latency = cl.get_stream_latency();
|
|
||||||
fprintf(stderr, "stream position %" PRIu64 " (latency %" PRIu64 ")\n", pos, latency);
|
|
||||||
} else if (c == 'i') {
|
|
||||||
op->collection_device_type = CUBEB_DEVICE_TYPE_INPUT;
|
|
||||||
fprintf(stderr, "collection device type changed to INPUT\n");
|
|
||||||
} else if (c == 'o') {
|
|
||||||
op->collection_device_type = CUBEB_DEVICE_TYPE_OUTPUT;
|
|
||||||
fprintf(stderr, "collection device type changed to OUTPUT\n");
|
|
||||||
} else if (c == 'a') {
|
|
||||||
op->collection_device_type = static_cast<cubeb_device_type>(CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT);
|
|
||||||
fprintf(stderr, "collection device type changed to INPUT | OUTPUT\n");
|
|
||||||
} else if (c == 'k') {
|
|
||||||
op->collection_device_type = CUBEB_DEVICE_TYPE_UNKNOWN;
|
|
||||||
fprintf(stderr, "collection device type changed to UNKNOWN\n");
|
|
||||||
} else if (c == 'r') {
|
|
||||||
bool res = cl.register_device_collection_changed(op->collection_device_type);
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "register_device_collection_changed succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "register_device_collection_changed failed\n");
|
|
||||||
}
|
|
||||||
} else if (c == 'u') {
|
|
||||||
bool res = cl.unregister_device_collection_changed(op->collection_device_type);
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "unregister_device_collection_changed succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "unregister_device_collection_changed failed\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Error: '%c' is not a valid entry\n", c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true; // Loop up
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
#ifdef _WIN32
|
|
||||||
CoInitialize(nullptr);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
operation_data op;
|
|
||||||
op.pm = PLAYBACK;
|
|
||||||
if (argc > 1) {
|
|
||||||
if ('r' == argv[1][0]) {
|
|
||||||
op.pm = RECORD;
|
|
||||||
} else if ('p' == argv[1][0]) {
|
|
||||||
op.pm = PLAYBACK;
|
|
||||||
} else if ('d' == argv[1][0]) {
|
|
||||||
op.pm = DUPLEX;
|
|
||||||
} else if ('l' == argv[1][0]) {
|
|
||||||
op.pm = LATENCY_TESTING;
|
|
||||||
} else if ('c' == argv[1][0]) {
|
|
||||||
op.pm = COLLECTION_CHANGE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
op.rate = DEFAULT_RATE;
|
|
||||||
uint32_t latency_override = 0;
|
|
||||||
if (op.pm == LATENCY_TESTING && argc > 2) {
|
|
||||||
latency_override = strtoul(argv[2], NULL, 10);
|
|
||||||
printf("LATENCY_TESTING %d\n", latency_override);
|
|
||||||
} else if (argc > 2) {
|
|
||||||
op.rate = strtoul(argv[2], NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool res = false;
|
|
||||||
cubeb_client cl;
|
|
||||||
cl.activate_log(CUBEB_LOG_DISABLED);
|
|
||||||
fprintf(stderr, "Log level is DISABLED\n");
|
|
||||||
cl.init(/* default backend */);
|
|
||||||
|
|
||||||
op.collection_device_type = CUBEB_DEVICE_TYPE_UNKNOWN;
|
|
||||||
fprintf(stderr, "collection device type is UNKNOWN\n");
|
|
||||||
if (op.pm == COLLECTION_CHANGE) {
|
|
||||||
op.collection_device_type = CUBEB_DEVICE_TYPE_OUTPUT;
|
|
||||||
fprintf(stderr, "collection device type changed to OUTPUT\n");
|
|
||||||
res = cl.register_device_collection_changed(op.collection_device_type);
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "register_device_collection_changed succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "register_device_collection_changed failed\n");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (op.pm == PLAYBACK || op.pm == DUPLEX || op.pm == LATENCY_TESTING) {
|
|
||||||
cl.output_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_OUTPUT_CHANNELS,
|
|
||||||
CUBEB_LAYOUT_STEREO, CUBEB_STREAM_PREF_NONE};
|
|
||||||
}
|
|
||||||
if (op.pm == RECORD || op.pm == DUPLEX || op.pm == LATENCY_TESTING) {
|
|
||||||
cl.input_params = {CUBEB_SAMPLE_FLOAT32NE, op.rate, DEFAULT_INPUT_CHANNELS, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE};
|
|
||||||
}
|
|
||||||
if (op.pm == LATENCY_TESTING) {
|
|
||||||
cl.set_latency_testing(true);
|
|
||||||
if (latency_override) {
|
|
||||||
cl.set_latency_frames(latency_override);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = cl.init_stream();
|
|
||||||
if (!res) {
|
|
||||||
fprintf(stderr, "stream_init failed\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "stream_init succeed\n");
|
|
||||||
|
|
||||||
res = cl.start_stream();
|
|
||||||
if (res) {
|
|
||||||
fprintf(stderr, "stream_start succeed\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "stream_init failed\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// User input
|
|
||||||
do {
|
|
||||||
fprintf(stderr, "press `q` to abort or `h` for help\n");
|
|
||||||
} while (choose_action(cl, &op, getchar()));
|
|
||||||
|
|
||||||
cl.destroy();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user