dep/cubeb: Update to dc511c6
This commit is contained in:
parent
06ecc50797
commit
8f45bf7f27
|
@ -1,17 +1,47 @@
|
||||||
# TODO
|
# TODO
|
||||||
# - backend selection via command line, rather than simply detecting headers.
|
# - backend selection via command line, rather than simply detecting headers.
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
|
||||||
project(cubeb
|
project(cubeb
|
||||||
VERSION 0.0.0)
|
VERSION 0.0.0)
|
||||||
|
|
||||||
if(POLICY CMP0063)
|
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||||
cmake_policy(SET CMP0063 NEW)
|
option(BUILD_RUST_LIBS "Build rust backends" OFF)
|
||||||
|
option(BUNDLE_SPEEX "Bundle the speex library" OFF)
|
||||||
|
option(LAZY_LOAD_LIBS "Lazily load shared libraries" ON)
|
||||||
|
option(USE_SANITIZERS "Use sanitizers" ON)
|
||||||
|
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
|
||||||
|
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 99)
|
set(CMAKE_C_STANDARD 99)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
if(USE_SANITIZERS)
|
||||||
|
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()
|
||||||
|
else()
|
||||||
|
macro(add_sanitizers UNUSED)
|
||||||
|
endmacro()
|
||||||
|
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.
|
# On OS/2, visibility attribute is not supported.
|
||||||
if(NOT OS2)
|
if(NOT OS2)
|
||||||
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
set(CMAKE_C_VISIBILITY_PRESET hidden)
|
||||||
|
@ -35,26 +65,169 @@ add_library(cubeb
|
||||||
src/cubeb_log.cpp
|
src/cubeb_log.cpp
|
||||||
src/cubeb_strings.c
|
src/cubeb_strings.c
|
||||||
src/cubeb_utils.cpp
|
src/cubeb_utils.cpp
|
||||||
$<TARGET_OBJECTS:speex>)
|
)
|
||||||
target_include_directories(cubeb
|
target_include_directories(cubeb
|
||||||
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
|
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>
|
||||||
|
)
|
||||||
|
set_target_properties(cubeb PROPERTIES
|
||||||
|
VERSION ${cubeb_VERSION}
|
||||||
|
SOVERSION ${cubeb_VERSION_MAJOR}
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(cubeb PRIVATE src)
|
add_sanitizers(cubeb)
|
||||||
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_library(speex OBJECT
|
include(GenerateExportHeader)
|
||||||
src/speex/resample.c)
|
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>
|
||||||
|
)
|
||||||
|
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
|
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME} TYPE INCLUDE)
|
||||||
|
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(
|
||||||
|
FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS cubeb EXPORT "${PROJECT_NAME}Targets")
|
||||||
|
install(
|
||||||
|
EXPORT "${PROJECT_NAME}Targets"
|
||||||
|
NAMESPACE "${PROJECT_NAME}::"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
|
||||||
|
)
|
||||||
|
|
||||||
|
if(NOT BUNDLE_SPEEX)
|
||||||
|
find_package(PkgConfig)
|
||||||
|
if(PKG_CONFIG_FOUND)
|
||||||
|
pkg_check_modules(speexdsp IMPORTED_TARGET speexdsp)
|
||||||
|
if(speexdsp_FOUND)
|
||||||
|
add_library(speex ALIAS PkgConfig::speexdsp)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET speex)
|
||||||
|
add_library(speex OBJECT subprojects/speex/resample.c)
|
||||||
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
|
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
|
||||||
target_compile_definitions(speex PRIVATE OUTSIDE_SPEEX)
|
target_include_directories(speex INTERFACE subprojects)
|
||||||
target_compile_definitions(speex PRIVATE FLOATING_POINT)
|
target_compile_definitions(speex PUBLIC
|
||||||
target_compile_definitions(speex PRIVATE EXPORT=)
|
OUTSIDE_SPEEX
|
||||||
target_compile_definitions(speex PRIVATE RANDOM_PREFIX=speex)
|
FLOATING_POINT
|
||||||
|
EXPORT=
|
||||||
|
RANDOM_PREFIX=speex
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# $<BUILD_INTERFACE:> required because of https://gitlab.kitware.com/cmake/cmake/-/issues/15415
|
||||||
|
target_link_libraries(cubeb PRIVATE $<BUILD_INTERFACE:speex>)
|
||||||
|
|
||||||
include(CheckIncludeFiles)
|
include(CheckIncludeFiles)
|
||||||
|
|
||||||
|
# Threads needed by cubeb_log, _pulse, _alsa, _jack, _sndio, _oss and _sun
|
||||||
|
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||||
|
find_package(Threads)
|
||||||
|
target_link_libraries(cubeb PRIVATE Threads::Threads)
|
||||||
|
|
||||||
|
if(LAZY_LOAD_LIBS)
|
||||||
|
check_include_files(pulse/pulseaudio.h USE_PULSE)
|
||||||
|
check_include_files(alsa/asoundlib.h USE_ALSA)
|
||||||
|
check_include_files(jack/jack.h USE_JACK)
|
||||||
|
check_include_files(sndio.h USE_SNDIO)
|
||||||
|
check_include_files(aaudio/AAudio.h USE_AAUDIO)
|
||||||
|
|
||||||
|
if(USE_PULSE OR USE_ALSA OR USE_JACK OR USE_SNDIO OR USE_AAUDIO)
|
||||||
|
target_link_libraries(cubeb PRIVATE ${CMAKE_DL_LIBS})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
else()
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
pkg_check_modules(libpulse IMPORTED_TARGET libpulse)
|
||||||
|
if(libpulse_FOUND)
|
||||||
|
set(USE_PULSE ON)
|
||||||
|
target_compile_definitions(cubeb PRIVATE DISABLE_LIBPULSE_DLOPEN)
|
||||||
|
target_link_libraries(cubeb PRIVATE PkgConfig::libpulse)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
pkg_check_modules(alsa IMPORTED_TARGET alsa)
|
||||||
|
if(alsa_FOUND)
|
||||||
|
set(USE_ALSA ON)
|
||||||
|
target_compile_definitions(cubeb PRIVATE DISABLE_LIBASOUND_DLOPEN)
|
||||||
|
target_link_libraries(cubeb PRIVATE PkgConfig::alsa)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
pkg_check_modules(jack IMPORTED_TARGET jack)
|
||||||
|
if(jack_FOUND)
|
||||||
|
set(USE_JACK ON)
|
||||||
|
target_compile_definitions(cubeb PRIVATE DISABLE_LIBJACK_DLOPEN)
|
||||||
|
target_link_libraries(cubeb PRIVATE PkgConfig::jack)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
check_include_files(sndio.h USE_SNDIO)
|
||||||
|
if(USE_SNDIO)
|
||||||
|
target_compile_definitions(cubeb PRIVATE DISABLE_LIBSNDIO_DLOPEN)
|
||||||
|
target_link_libraries(cubeb PRIVATE sndio)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
check_include_files(aaudio/AAudio.h USE_AAUDIO)
|
||||||
|
if(USE_AAUDIO)
|
||||||
|
target_compile_definitions(cubeb PRIVATE DISABLE_LIBAAUDIO_DLOPEN)
|
||||||
|
target_link_libraries(cubeb PRIVATE aaudio)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_PULSE)
|
||||||
|
target_sources(cubeb PRIVATE src/cubeb_pulse.c)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_PULSE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_ALSA)
|
||||||
|
target_sources(cubeb PRIVATE src/cubeb_alsa.c)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_ALSA)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_JACK)
|
||||||
|
target_sources(cubeb PRIVATE src/cubeb_jack.cpp)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_JACK)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_SNDIO)
|
||||||
|
target_sources(cubeb PRIVATE src/cubeb_sndio.c)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_AAUDIO)
|
||||||
|
target_sources(cubeb PRIVATE src/cubeb_aaudio.cpp)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_AAUDIO)
|
||||||
|
|
||||||
|
# set this definition to enable low latency mode. Possibly bad for battery
|
||||||
|
target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_LATENCY)
|
||||||
|
|
||||||
|
# set this definition to enable power saving mode. Possibly resulting
|
||||||
|
# in high latency
|
||||||
|
# target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_POWER_SAVING)
|
||||||
|
|
||||||
|
# set this mode to make the backend use an exclusive stream.
|
||||||
|
# will decrease latency.
|
||||||
|
# target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_EXCLUSIVE_STREAM)
|
||||||
|
endif()
|
||||||
|
|
||||||
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
|
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
|
||||||
if(USE_AUDIOUNIT)
|
if(USE_AUDIOUNIT)
|
||||||
target_sources(cubeb PRIVATE
|
target_sources(cubeb PRIVATE
|
||||||
|
@ -64,36 +237,12 @@ if(USE_AUDIOUNIT)
|
||||||
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
|
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
|
||||||
endif()
|
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)
|
check_include_files(audioclient.h USE_WASAPI)
|
||||||
if(USE_WASAPI)
|
if(USE_WASAPI)
|
||||||
target_sources(cubeb PRIVATE
|
target_sources(cubeb PRIVATE
|
||||||
src/cubeb_wasapi.cpp)
|
src/cubeb_wasapi.cpp)
|
||||||
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
|
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
|
||||||
target_link_libraries(cubeb PRIVATE avrt ole32)
|
target_link_libraries(cubeb PRIVATE avrt ole32 ksuser)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_files("windows.h;mmsystem.h" USE_WINMM)
|
check_include_files("windows.h;mmsystem.h" USE_WINMM)
|
||||||
|
@ -118,31 +267,23 @@ if(HAVE_SYS_SOUNDCARD_H)
|
||||||
try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests"
|
try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests"
|
||||||
${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c)
|
${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c)
|
||||||
if(USE_OSS)
|
if(USE_OSS)
|
||||||
|
# strlcpy is not available on BSD systems that use glibc,
|
||||||
|
# like Debian kfreebsd, so try using libbsd if available
|
||||||
|
include(CheckSymbolExists)
|
||||||
|
check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
|
||||||
|
if(NOT HAVE_STRLCPY)
|
||||||
|
pkg_check_modules(libbsd-overlay IMPORTED_TARGET libbsd-overlay)
|
||||||
|
if(libbsd-overlay_FOUND)
|
||||||
|
target_link_libraries(cubeb PRIVATE PkgConfig::libbsd-overlay)
|
||||||
|
set(HAVE_STRLCPY true)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if (HAVE_STRLCPY)
|
||||||
target_sources(cubeb PRIVATE
|
target_sources(cubeb PRIVATE
|
||||||
src/cubeb_oss.c)
|
src/cubeb_oss.c)
|
||||||
target_compile_definitions(cubeb PRIVATE USE_OSS)
|
target_compile_definitions(cubeb PRIVATE USE_OSS)
|
||||||
target_link_libraries(cubeb PRIVATE pthread)
|
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_files(aaudio/AAudio.h USE_AAUDIO)
|
|
||||||
if(USE_AAUDIO)
|
|
||||||
target_sources(cubeb PRIVATE
|
|
||||||
src/cubeb_aaudio.cpp)
|
|
||||||
target_compile_definitions(cubeb PRIVATE USE_AAUDIO)
|
|
||||||
|
|
||||||
# set this definition to enable low latency mode. Possibly bad for battery
|
|
||||||
target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_LATENCY)
|
|
||||||
|
|
||||||
# set this definition to enable power saving mode. Possibly resulting
|
|
||||||
# in high latency
|
|
||||||
# target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_POWER_SAVING)
|
|
||||||
|
|
||||||
# set this mode to make the backend use an exclusive stream.
|
|
||||||
# will decrease latency.
|
|
||||||
# target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_EXCLUSIVE_STREAM)
|
|
||||||
|
|
||||||
target_link_libraries(cubeb PRIVATE ${CMAKE_DL_LIBS})
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_files(android/log.h USE_AUDIOTRACK)
|
check_include_files(android/log.h USE_AUDIOTRACK)
|
||||||
|
@ -153,20 +294,11 @@ if(USE_AUDIOTRACK)
|
||||||
target_link_libraries(cubeb PRIVATE log)
|
target_link_libraries(cubeb PRIVATE log)
|
||||||
endif()
|
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)
|
check_include_files(sys/audioio.h USE_SUN)
|
||||||
if(USE_SUN)
|
if(USE_SUN)
|
||||||
target_sources(cubeb PRIVATE
|
target_sources(cubeb PRIVATE
|
||||||
src/cubeb_sun.c)
|
src/cubeb_sun.c)
|
||||||
target_compile_definitions(cubeb PRIVATE USE_SUN)
|
target_compile_definitions(cubeb PRIVATE USE_SUN)
|
||||||
target_link_libraries(cubeb PRIVATE pthread)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
check_include_files(kai.h USE_KAI)
|
check_include_files(kai.h USE_KAI)
|
||||||
|
@ -177,3 +309,61 @@ if(USE_KAI)
|
||||||
target_link_libraries(cubeb PRIVATE kai)
|
target_link_libraries(cubeb PRIVATE kai)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(USE_PULSE AND USE_PULSE_RUST)
|
||||||
|
include(ExternalProject)
|
||||||
|
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
|
||||||
|
ExternalProject_Add(
|
||||||
|
cubeb_pulse_rs
|
||||||
|
DOWNLOAD_COMMAND ""
|
||||||
|
CONFIGURE_COMMAND ""
|
||||||
|
BUILD_COMMAND cargo build COMMAND cargo build --release
|
||||||
|
BUILD_ALWAYS ON
|
||||||
|
BINARY_DIR "${PROJECT_SOURCE_DIR}/src/cubeb-pulse-rs"
|
||||||
|
INSTALL_COMMAND ""
|
||||||
|
LOG_BUILD ON)
|
||||||
|
add_dependencies(cubeb cubeb_pulse_rs)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_PULSE_RUST)
|
||||||
|
target_link_libraries(cubeb PRIVATE
|
||||||
|
debug "${PROJECT_SOURCE_DIR}/src/cubeb-pulse-rs/target/debug/libcubeb_pulse.a"
|
||||||
|
optimized "${PROJECT_SOURCE_DIR}/src/cubeb-pulse-rs/target/release/libcubeb_pulse.a" pulse)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(USE_AUDIOUNIT AND USE_AUDIOUNIT_RUST)
|
||||||
|
include(ExternalProject)
|
||||||
|
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
|
||||||
|
ExternalProject_Add(
|
||||||
|
cubeb_coreaudio_rs
|
||||||
|
DOWNLOAD_COMMAND ""
|
||||||
|
CONFIGURE_COMMAND ""
|
||||||
|
BUILD_COMMAND cargo build COMMAND cargo build --release
|
||||||
|
BUILD_ALWAYS ON
|
||||||
|
BINARY_DIR "${PROJECT_SOURCE_DIR}/src/cubeb-coreaudio-rs"
|
||||||
|
INSTALL_COMMAND ""
|
||||||
|
LOG_BUILD ON)
|
||||||
|
add_dependencies(cubeb cubeb_coreaudio_rs)
|
||||||
|
target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT_RUST)
|
||||||
|
target_link_libraries(cubeb PRIVATE
|
||||||
|
debug "${PROJECT_SOURCE_DIR}/src/cubeb-coreaudio-rs/target/debug/libcubeb_coreaudio.a"
|
||||||
|
optimized "${PROJECT_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()
|
||||||
|
|
||||||
|
add_custom_target(clang-format-check
|
||||||
|
find
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
-type f (-name "*.cpp" -o -name "*.c" -o -name "*.h")
|
||||||
|
-not -path "*/subprojects/speex/*"
|
||||||
|
-print0
|
||||||
|
| xargs -0 clang-format -Werror -n
|
||||||
|
COMMENT "Check formatting with clang-format"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
# Build instructions for libcubeb
|
# Build instructions for libcubeb
|
||||||
|
|
||||||
You must have CMake v3.1 or later installed.
|
You must have CMake v3.14 or later installed.
|
||||||
|
|
||||||
1. `git clone --recursive https://github.com/kinetiknz/cubeb.git`
|
1. `git clone --recursive https://github.com/mozilla/cubeb.git`
|
||||||
2. `mkdir cubeb-build`
|
2. `cd cubeb`
|
||||||
3. `cd cubeb-build`
|
3. `cmake -B ./build .`
|
||||||
3. `cmake ../cubeb`
|
4. `cmake --build ./build`
|
||||||
4. `cmake --build .`
|
5. `cd build && ctest`
|
||||||
5. `ctest`
|
|
||||||
|
|
||||||
# Windows build notes
|
# Windows build notes
|
||||||
|
|
||||||
|
@ -41,6 +40,6 @@ To build with MinGW-w64, install the following items:
|
||||||
- Download and install MinGW-w64 with Win32 threads.
|
- Download and install MinGW-w64 with Win32 threads.
|
||||||
- Download and install CMake.
|
- Download and install CMake.
|
||||||
- Run MinGW-w64 Terminal from the Start Menu.
|
- Run MinGW-w64 Terminal from the Start Menu.
|
||||||
- Follow the build steps at the top of this file, but at step 3 run:
|
- Follow the build steps at the top of this file, but at step 4 run:
|
||||||
`cmake -G "MinGW Makefiles" ..`
|
`cmake -G "MinGW Makefiles" ../cubeb`
|
||||||
- Continue the build steps at the top of this file.
|
- Continue the build steps at the top of this file.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
[![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb)
|
[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml)
|
||||||
[![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 INSTALL.md for build instructions.
|
||||||
|
|
||||||
|
See [Backend Support](https://github.com/mozilla/cubeb/wiki/Backend-Support) in the wiki for the support level of each backend.
|
||||||
|
|
||||||
Licensed under an ISC-style license. See LICENSE for details.
|
Licensed under an ISC-style license. See LICENSE for details.
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
TODO:
|
|
||||||
- directsound: incomplete and somewhat broken
|
|
||||||
- osx: understand why AudioQueueGetCurrentTime can return negative mSampleTime
|
|
||||||
- test (and fix) sub-prefill size data playback
|
|
||||||
- report stream delay instead of position; leave position calculation to user
|
|
||||||
- capture support
|
|
||||||
- capture and output enumeration and configuration
|
|
||||||
- also expose default hardware config to allow decisions on speaker layout
|
|
||||||
- prefill occurs at different times in each backend:
|
|
||||||
- pulse prefills async off worker thread after init
|
|
||||||
- coreaudio prefills during init
|
|
||||||
- alsa prefills async after start
|
|
||||||
- expose configured prefill size; may differ from requested latency
|
|
||||||
- solved by exposing stream delay
|
|
||||||
- xruns may occur in user callback but also in audio hardware
|
|
||||||
may need to expose details of hardware xruns to user api
|
|
||||||
- document thread safety
|
|
||||||
- document which calls may block, and when effects take effect
|
|
||||||
- document what's permissible inside callbacks
|
|
||||||
- implement basic channel mapping for surround
|
|
||||||
- vorbis has documented mapping based on channel count (if mapping type ==
|
|
||||||
0) -- http://xiph.org/vorbis/doc/Vorbis_I_spec.html#x1-800004.3.9
|
|
||||||
1 -> M
|
|
||||||
2 -> L, R
|
|
||||||
3 -> L, C, R
|
|
||||||
4 -> L, R, RL, RR
|
|
||||||
5 -> L, C, R, RL, RR
|
|
||||||
6 -> L, C, R, RL, RR, LFE
|
|
||||||
7 -> L, C, R, SL, SR, RC, LFE
|
|
||||||
8 -> L, C, R, SL, SR, RL, RR, LFE
|
|
||||||
>8 -> application defined
|
|
||||||
- wave files with channel count only
|
|
||||||
3 -> L, R, C
|
|
||||||
4 -> L, R, RL, RR
|
|
||||||
5 -> L, R, C, RL, RR
|
|
||||||
6 -> L, R, C, LFE, RL, RR
|
|
||||||
7 -> L, R, C, LFE, RC, SL, SR
|
|
||||||
8 -> L, R, C, LFE, RL, RR, SL, SR
|
|
||||||
- wave files with WAVE_FORMAT_EXTENSIBLE have explicitly mappings, can
|
|
||||||
extract these
|
|
||||||
- implement configurable channel mapping
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
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)
|
||||||
|
|
|
@ -51,5 +51,11 @@
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
|
<ItemDefinitionGroup>
|
||||||
|
<Lib>
|
||||||
|
<AdditionalDependencies>ksuser.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Lib>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
|
||||||
<Import Project="..\msvc\vsprops\Targets.props" />
|
<Import Project="..\msvc\vsprops\Targets.props" />
|
||||||
</Project>
|
</Project>
|
|
@ -7,9 +7,9 @@
|
||||||
#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382)
|
#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382)
|
||||||
#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
|
#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
|
||||||
|
|
||||||
|
#include "cubeb_export.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "cubeb_export.h"
|
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
@ -122,8 +122,10 @@ extern "C" {
|
||||||
/** @file
|
/** @file
|
||||||
The <tt>libcubeb</tt> C API. */
|
The <tt>libcubeb</tt> C API. */
|
||||||
|
|
||||||
typedef struct cubeb cubeb; /**< Opaque handle referencing the application state. */
|
typedef struct cubeb
|
||||||
typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */
|
cubeb; /**< Opaque handle referencing the application state. */
|
||||||
|
typedef struct cubeb_stream
|
||||||
|
cubeb_stream; /**< Opaque handle referencing the stream state. */
|
||||||
|
|
||||||
/** Sample format enumeration. */
|
/** Sample format enumeration. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -155,8 +157,10 @@ typedef void const * cubeb_devid;
|
||||||
/** Level (verbosity) of logging for a particular cubeb context. */
|
/** Level (verbosity) of logging for a particular cubeb context. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
|
CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
|
||||||
CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
|
CUBEB_LOG_NORMAL =
|
||||||
CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
|
1, /**< Logging lifetime operation (creation/destruction). */
|
||||||
|
CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance
|
||||||
|
implications. */
|
||||||
} cubeb_log_level;
|
} cubeb_log_level;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -223,29 +227,31 @@ enum {
|
||||||
/** Miscellaneous stream preferences. */
|
/** Miscellaneous stream preferences. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
|
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
|
||||||
CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
|
CUBEB_STREAM_PREF_LOOPBACK =
|
||||||
|
0x01, /**< Request a loopback stream. Should be
|
||||||
specified on the input params and an
|
specified on the input params and an
|
||||||
output device to loopback from should
|
output device to loopback from should
|
||||||
be passed in place of an input device. */
|
be passed in place of an input device. */
|
||||||
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
|
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
|
||||||
default device on OS
|
default device on OS
|
||||||
changes. */
|
changes. */
|
||||||
CUBEB_STREAM_PREF_VOICE = 0x04, /**< This stream is going to transport voice data.
|
CUBEB_STREAM_PREF_VOICE =
|
||||||
|
0x04, /**< This stream is going to transport voice data.
|
||||||
Depending on the backend and platform, this can
|
Depending on the backend and platform, this can
|
||||||
change the audio input or output devices
|
change the audio input or output devices
|
||||||
selected, as well as the quality of the stream,
|
selected, as well as the quality of the stream,
|
||||||
for example to accomodate bluetooth SCO modes on
|
for example to accomodate bluetooth SCO modes on
|
||||||
bluetooth devices. */
|
bluetooth devices. */
|
||||||
CUBEB_STREAM_PREF_RAW = 0x08, /**< Windows only. Bypass all signal processing
|
CUBEB_STREAM_PREF_RAW =
|
||||||
|
0x08, /**< Windows only. Bypass all signal processing
|
||||||
except for always on APO, driver and hardware. */
|
except for always on APO, driver and hardware. */
|
||||||
CUBEB_STREAM_PREF_PERSIST = 0x10, /**< Request that the volume and mute settings
|
CUBEB_STREAM_PREF_PERSIST = 0x10, /**< Request that the volume and mute
|
||||||
should persist across restarts of the stream
|
settings should persist across restarts
|
||||||
and/or application. May not be honored for
|
of the stream and/or application. This is
|
||||||
all backends and platforms. */
|
obsolete and ignored by all backends. */
|
||||||
|
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT = 0x20 /**< Don't automatically try to
|
||||||
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT = 0x20 /**< Don't automatically try to connect
|
connect ports. Only affects
|
||||||
ports. Only affects the jack
|
the jack backend. */
|
||||||
backend. */
|
|
||||||
} cubeb_stream_prefs;
|
} cubeb_stream_prefs;
|
||||||
|
|
||||||
/** Stream format initialization parameters. */
|
/** Stream format initialization parameters. */
|
||||||
|
@ -254,7 +260,9 @@ typedef struct {
|
||||||
#cubeb_sample_format. */
|
#cubeb_sample_format. */
|
||||||
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
|
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
|
||||||
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
|
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_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_prefs prefs; /**< Requested preferences. */
|
||||||
} cubeb_stream_params;
|
} cubeb_stream_params;
|
||||||
|
|
||||||
|
@ -276,10 +284,13 @@ typedef enum {
|
||||||
enum {
|
enum {
|
||||||
CUBEB_OK = 0, /**< Success. */
|
CUBEB_OK = 0, /**< Success. */
|
||||||
CUBEB_ERROR = -1, /**< Unclassified error. */
|
CUBEB_ERROR = -1, /**< Unclassified error. */
|
||||||
CUBEB_ERROR_INVALID_FORMAT = -2, /**< Unsupported #cubeb_stream_params requested. */
|
CUBEB_ERROR_INVALID_FORMAT =
|
||||||
|
-2, /**< Unsupported #cubeb_stream_params requested. */
|
||||||
CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
|
CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
|
||||||
CUBEB_ERROR_NOT_SUPPORTED = -4, /**< Optional function not implemented in current backend. */
|
CUBEB_ERROR_NOT_SUPPORTED =
|
||||||
CUBEB_ERROR_DEVICE_UNAVAILABLE = -5 /**< Device specified by #cubeb_devid not available. */
|
-4, /**< Optional function not implemented in current backend. */
|
||||||
|
CUBEB_ERROR_DEVICE_UNAVAILABLE =
|
||||||
|
-5 /**< Device specified by #cubeb_devid not available. */
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -295,8 +306,10 @@ typedef enum {
|
||||||
* The state of a device.
|
* The state of a device.
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system level. */
|
CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system
|
||||||
CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is plugged into it. */
|
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_ENABLED /**< The device is enabled. */
|
||||||
} cubeb_device_state;
|
} cubeb_device_state;
|
||||||
|
|
||||||
|
@ -313,7 +326,8 @@ typedef enum {
|
||||||
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
|
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
|
||||||
/** 16-bit integers, native endianess, when on a Big Endian environment. */
|
/** 16-bit integers, native endianess, when on a Big Endian environment. */
|
||||||
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
|
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
|
||||||
/** 32-bit floating points, native endianess, when on a Big Endian environment. */
|
/** 32-bit floating points, native endianess, when on a Big Endian environment.
|
||||||
|
*/
|
||||||
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
|
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
|
||||||
#else
|
#else
|
||||||
/** 16-bit integers, native endianess, when on a Little Endian environment. */
|
/** 16-bit integers, native endianess, when on a Little Endian environment. */
|
||||||
|
@ -323,11 +337,14 @@ typedef enum {
|
||||||
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
|
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
|
||||||
#endif
|
#endif
|
||||||
/** All the 16-bit integers types. */
|
/** All the 16-bit integers types. */
|
||||||
#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
|
#define CUBEB_DEVICE_FMT_S16_MASK \
|
||||||
|
(CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
|
||||||
/** All the 32-bit floating points types. */
|
/** All the 32-bit floating points types. */
|
||||||
#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
|
#define CUBEB_DEVICE_FMT_F32_MASK \
|
||||||
|
(CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
|
||||||
/** All the device formats types. */
|
/** All the device formats types. */
|
||||||
#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
|
#define CUBEB_DEVICE_FMT_ALL \
|
||||||
|
(CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
|
||||||
|
|
||||||
/** Channel type for a `cubeb_stream`. Depending on the backend and platform
|
/** Channel type for a `cubeb_stream`. Depending on the backend and platform
|
||||||
* used, this can control inter-stream interruption, ducking, and volume
|
* used, this can control inter-stream interruption, ducking, and volume
|
||||||
|
@ -348,9 +365,13 @@ typedef enum {
|
||||||
* `cubeb_device_collection_destroy`. */
|
* `cubeb_device_collection_destroy`. */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
cubeb_devid devid; /**< Device identifier handle. */
|
cubeb_devid devid; /**< Device identifier handle. */
|
||||||
char const * device_id; /**< Device identifier which might be presented in a UI. */
|
char const *
|
||||||
char const * friendly_name; /**< Friendly device name which might be presented in a UI. */
|
device_id; /**< Device identifier 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 * 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. */
|
char const * vendor_name; /**< Optional vendor name, may be NULL. */
|
||||||
|
|
||||||
cubeb_device_type type; /**< Type of device (Input/Output). */
|
cubeb_device_type type; /**< Type of device (Input/Output). */
|
||||||
|
@ -358,7 +379,8 @@ typedef struct {
|
||||||
cubeb_device_pref preferred; /**< Preferred device. */
|
cubeb_device_pref preferred; /**< Preferred device. */
|
||||||
|
|
||||||
cubeb_device_fmt format; /**< Sample format supported. */
|
cubeb_device_fmt format; /**< Sample format supported. */
|
||||||
cubeb_device_fmt default_format; /**< The default sample format for this device. */
|
cubeb_device_fmt
|
||||||
|
default_format; /**< The default sample format for this device. */
|
||||||
uint32_t max_channels; /**< Channels. */
|
uint32_t max_channels; /**< Channels. */
|
||||||
uint32_t default_rate; /**< Default/Preferred sample rate. */
|
uint32_t default_rate; /**< Default/Preferred sample rate. */
|
||||||
uint32_t max_rate; /**< Maximum sample rate supported. */
|
uint32_t max_rate; /**< Maximum sample rate supported. */
|
||||||
|
@ -398,18 +420,15 @@ typedef struct {
|
||||||
being stopped.
|
being stopped.
|
||||||
@retval CUBEB_ERROR on error, in which case the data callback will stop
|
@retval CUBEB_ERROR on error, in which case the data callback will stop
|
||||||
and the stream will enter a shutdown state. */
|
and the stream will enter a shutdown state. */
|
||||||
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
|
typedef long (*cubeb_data_callback)(cubeb_stream * stream, void * user_ptr,
|
||||||
void * user_ptr,
|
|
||||||
void const * input_buffer,
|
void const * input_buffer,
|
||||||
void * output_buffer,
|
void * output_buffer, long nframes);
|
||||||
long nframes);
|
|
||||||
|
|
||||||
/** User supplied state callback.
|
/** User supplied state callback.
|
||||||
@param stream The stream for this this callback fired.
|
@param stream The stream for this this callback fired.
|
||||||
@param user_ptr The pointer passed to cubeb_stream_init.
|
@param user_ptr The pointer passed to cubeb_stream_init.
|
||||||
@param state The new state of the stream. */
|
@param state The new state of the stream. */
|
||||||
typedef void (* cubeb_state_callback)(cubeb_stream * stream,
|
typedef void (*cubeb_state_callback)(cubeb_stream * stream, void * user_ptr,
|
||||||
void * user_ptr,
|
|
||||||
cubeb_state state);
|
cubeb_state state);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -420,7 +439,8 @@ typedef void (* cubeb_device_changed_callback)(void * user_ptr);
|
||||||
/**
|
/**
|
||||||
* User supplied callback called when the underlying device collection changed.
|
* User supplied callback called when the underlying device collection changed.
|
||||||
* @param context A pointer to the cubeb context.
|
* @param context A pointer to the cubeb context.
|
||||||
* @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */
|
* @param user_ptr The pointer passed to
|
||||||
|
* cubeb_register_device_collection_changed. */
|
||||||
typedef void (*cubeb_device_collection_changed_callback)(cubeb * context,
|
typedef void (*cubeb_device_collection_changed_callback)(cubeb * context,
|
||||||
void * user_ptr);
|
void * user_ptr);
|
||||||
|
|
||||||
|
@ -445,13 +465,15 @@ typedef void (* cubeb_log_callback)(char const * fmt, ...);
|
||||||
@retval CUBEB_OK in case of success.
|
@retval CUBEB_OK in case of success.
|
||||||
@retval CUBEB_ERROR in case of error, for example because the host
|
@retval CUBEB_ERROR in case of error, for example because the host
|
||||||
has no audio hardware. */
|
has no audio hardware. */
|
||||||
CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name,
|
CUBEB_EXPORT int
|
||||||
|
cubeb_init(cubeb ** context, char const * context_name,
|
||||||
char const * backend_name);
|
char const * backend_name);
|
||||||
|
|
||||||
/** Get a read-only string identifying this context's current backend.
|
/** Get a read-only string identifying this context's current backend.
|
||||||
@param context A pointer to the cubeb context.
|
@param context A pointer to the cubeb context.
|
||||||
@retval Read-only string identifying current backend. */
|
@retval Read-only string identifying current backend. */
|
||||||
CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
|
CUBEB_EXPORT char const *
|
||||||
|
cubeb_get_backend_id(cubeb * context);
|
||||||
|
|
||||||
/** Get the maximum possible number of channels.
|
/** Get the maximum possible number of channels.
|
||||||
@param context A pointer to the cubeb context.
|
@param context A pointer to the cubeb context.
|
||||||
|
@ -460,7 +482,8 @@ CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER
|
@retval CUBEB_ERROR_INVALID_PARAMETER
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED
|
@retval CUBEB_ERROR_NOT_SUPPORTED
|
||||||
@retval CUBEB_ERROR */
|
@retval CUBEB_ERROR */
|
||||||
CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
|
||||||
|
|
||||||
/** Get the minimal latency value, in frames, that is guaranteed to work
|
/** Get the minimal latency value, in frames, that is guaranteed to work
|
||||||
when creating a stream for the specified sample rate. This is platform,
|
when creating a stream for the specified sample rate. This is platform,
|
||||||
|
@ -473,8 +496,8 @@ CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_cha
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER
|
@retval CUBEB_ERROR_INVALID_PARAMETER
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
|
CUBEB_EXPORT int
|
||||||
cubeb_stream_params * params,
|
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params,
|
||||||
uint32_t * latency_frames);
|
uint32_t * latency_frames);
|
||||||
|
|
||||||
/** Get the preferred sample rate for this backend: this is hardware and
|
/** Get the preferred sample rate for this backend: this is hardware and
|
||||||
|
@ -484,12 +507,14 @@ CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER
|
@retval CUBEB_ERROR_INVALID_PARAMETER
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
|
||||||
|
|
||||||
/** Destroy an application context. This must be called after all stream have
|
/** Destroy an application context. This must be called after all stream have
|
||||||
* been destroyed.
|
* been destroyed.
|
||||||
@param context A pointer to the cubeb context.*/
|
@param context A pointer to the cubeb context.*/
|
||||||
CUBEB_EXPORT void cubeb_destroy(cubeb * context);
|
CUBEB_EXPORT void
|
||||||
|
cubeb_destroy(cubeb * context);
|
||||||
|
|
||||||
/** Initialize a stream associated with the supplied application context.
|
/** Initialize a stream associated with the supplied application context.
|
||||||
@param context A pointer to the cubeb context.
|
@param context A pointer to the cubeb context.
|
||||||
|
@ -497,17 +522,17 @@ CUBEB_EXPORT void cubeb_destroy(cubeb * context);
|
||||||
cubeb stream.
|
cubeb stream.
|
||||||
@param stream_name A name for this stream.
|
@param stream_name A name for this stream.
|
||||||
@param input_device Device for the input side of the stream. If NULL the
|
@param input_device Device for the input side of the stream. If NULL the
|
||||||
default input device is used. Passing a valid cubeb_devid
|
default input device is used. Passing a valid
|
||||||
means the stream only ever uses that device. Passing a NULL
|
cubeb_devid means the stream only ever uses that device. Passing a NULL
|
||||||
cubeb_devid allows the stream to follow that device type's
|
cubeb_devid allows the stream to follow that device
|
||||||
OS default.
|
type's OS default.
|
||||||
@param input_stream_params Parameters for the input side of the stream, or
|
@param input_stream_params Parameters for the input side of the stream, or
|
||||||
NULL if this stream is output only.
|
NULL if this stream is output only.
|
||||||
@param output_device Device for the output side of the stream. If NULL the
|
@param output_device Device for the output side of the stream. If NULL the
|
||||||
default output device is used. Passing a valid cubeb_devid
|
default output device is used. Passing a valid
|
||||||
means the stream only ever uses that device. Passing a NULL
|
cubeb_devid means the stream only ever uses that device. Passing a NULL
|
||||||
cubeb_devid allows the stream to follow that device type's
|
cubeb_devid allows the stream to follow that device
|
||||||
OS default.
|
type's OS default.
|
||||||
@param output_stream_params Parameters for the output side of the stream, or
|
@param output_stream_params Parameters for the output side of the stream, or
|
||||||
NULL if this stream is input only. When input
|
NULL if this stream is input only. When input
|
||||||
and output stream parameters are supplied, their
|
and output stream parameters are supplied, their
|
||||||
|
@ -523,49 +548,42 @@ CUBEB_EXPORT void cubeb_destroy(cubeb * context);
|
||||||
@retval CUBEB_ERROR
|
@retval CUBEB_ERROR
|
||||||
@retval CUBEB_ERROR_INVALID_FORMAT
|
@retval CUBEB_ERROR_INVALID_FORMAT
|
||||||
@retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
|
@retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
|
||||||
CUBEB_EXPORT int cubeb_stream_init(cubeb * context,
|
CUBEB_EXPORT int
|
||||||
cubeb_stream ** stream,
|
cubeb_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
char const * stream_name,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
uint32_t latency_frames,
|
uint32_t latency_frames, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_state_callback state_callback, void * user_ptr);
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr);
|
|
||||||
|
|
||||||
/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
|
/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
|
||||||
stream.
|
stream.
|
||||||
@param stream The stream to destroy. */
|
@param stream The stream to destroy. */
|
||||||
CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream);
|
CUBEB_EXPORT void
|
||||||
|
cubeb_stream_destroy(cubeb_stream * stream);
|
||||||
|
|
||||||
/** Start playback.
|
/** Start playback.
|
||||||
@param stream
|
@param stream
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR */
|
@retval CUBEB_ERROR */
|
||||||
CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_start(cubeb_stream * stream);
|
||||||
|
|
||||||
/** Stop playback.
|
/** Stop playback.
|
||||||
@param stream
|
@param stream
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR */
|
@retval CUBEB_ERROR */
|
||||||
CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
|
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.
|
/** Get the current stream playback position.
|
||||||
@param stream
|
@param stream
|
||||||
@param position Playback position in frames.
|
@param position Playback position in frames.
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR */
|
@retval CUBEB_ERROR */
|
||||||
CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
||||||
|
|
||||||
/** Get the latency for this stream, in frames. This is the number of frames
|
/** 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
|
between the time cubeb acquires the data in the callback and the listener
|
||||||
|
@ -575,7 +593,8 @@ CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * pos
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED
|
@retval CUBEB_ERROR_NOT_SUPPORTED
|
||||||
@retval CUBEB_ERROR */
|
@retval CUBEB_ERROR */
|
||||||
CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
|
||||||
|
|
||||||
/** Get the input latency for this stream, in frames. This is the number of
|
/** 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
|
frames between the time the audio input devices records the data, and they
|
||||||
|
@ -586,7 +605,8 @@ CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * late
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED
|
@retval CUBEB_ERROR_NOT_SUPPORTED
|
||||||
@retval CUBEB_ERROR */
|
@retval CUBEB_ERROR */
|
||||||
CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency);
|
||||||
/** Set the volume for a stream.
|
/** Set the volume for a stream.
|
||||||
@param stream the stream for which to adjust the volume.
|
@param stream the stream for which to adjust the volume.
|
||||||
@param volume a float between 0.0 (muted) and 1.0 (maximum volume)
|
@param volume a float between 0.0 (muted) and 1.0 (maximum volume)
|
||||||
|
@ -594,7 +614,8 @@ CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or
|
@retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or
|
||||||
stream is an invalid pointer
|
stream is an invalid pointer
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_set_volume(cubeb_stream * stream, float volume);
|
||||||
|
|
||||||
/** Change a stream's name.
|
/** Change a stream's name.
|
||||||
@param stream the stream for which to set the name.
|
@param stream the stream for which to set the name.
|
||||||
|
@ -602,7 +623,8 @@ CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if any pointer is invalid
|
@retval CUBEB_ERROR_INVALID_PARAMETER if any pointer is invalid
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name);
|
||||||
|
|
||||||
/** Get the current output device for this stream.
|
/** Get the current output device for this stream.
|
||||||
@param stm the stream for which to query the current output device
|
@param stm the stream for which to query the current output device
|
||||||
|
@ -611,7 +633,8 @@ CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * strea
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
|
@retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
|
||||||
invalid pointers
|
invalid pointers
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_get_current_device(cubeb_stream * stm,
|
||||||
cubeb_device ** const device);
|
cubeb_device ** const device);
|
||||||
|
|
||||||
/** Destroy a cubeb_device structure.
|
/** Destroy a cubeb_device structure.
|
||||||
|
@ -620,8 +643,8 @@ CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
|
||||||
@retval CUBEB_OK in case of success
|
@retval CUBEB_OK in case of success
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
|
@retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
|
CUBEB_EXPORT int
|
||||||
cubeb_device * devices);
|
cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * devices);
|
||||||
|
|
||||||
/** Set a callback to be notified when the output device changes.
|
/** Set a callback to be notified when the output device changes.
|
||||||
@param stream the stream for which to set the callback.
|
@param stream the stream for which to set the callback.
|
||||||
|
@ -631,23 +654,28 @@ CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
|
@retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
|
||||||
device_changed_callback are invalid pointers.
|
device_changed_callback are invalid pointers.
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
CUBEB_EXPORT int
|
||||||
|
cubeb_stream_register_device_changed_callback(
|
||||||
|
cubeb_stream * stream,
|
||||||
cubeb_device_changed_callback device_changed_callback);
|
cubeb_device_changed_callback device_changed_callback);
|
||||||
|
|
||||||
/** Return the user data pointer registered with the stream with cubeb_stream_init.
|
/** Return the user data pointer registered with the stream with
|
||||||
|
cubeb_stream_init.
|
||||||
@param stream the stream for which to retrieve user data pointer.
|
@param stream the stream for which to retrieve user data pointer.
|
||||||
@retval user data pointer */
|
@retval user data pointer */
|
||||||
CUBEB_EXPORT void * cubeb_stream_user_ptr(cubeb_stream * stream);
|
CUBEB_EXPORT void *
|
||||||
|
cubeb_stream_user_ptr(cubeb_stream * stream);
|
||||||
|
|
||||||
/** Returns enumerated devices.
|
/** Returns enumerated devices.
|
||||||
@param context
|
@param context
|
||||||
@param devtype device type to include
|
@param devtype device type to include
|
||||||
@param collection output collection. Must be destroyed with cubeb_device_collection_destroy
|
@param collection output collection. Must be destroyed with
|
||||||
|
cubeb_device_collection_destroy
|
||||||
@retval CUBEB_OK in case of success
|
@retval CUBEB_OK in case of success
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
|
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
|
CUBEB_EXPORT int
|
||||||
cubeb_device_type devtype,
|
cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
|
||||||
cubeb_device_collection * collection);
|
cubeb_device_collection * collection);
|
||||||
|
|
||||||
/** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
|
/** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
|
||||||
|
@ -655,7 +683,8 @@ CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
|
||||||
@param collection collection to destroy
|
@param collection collection to destroy
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
|
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
|
||||||
CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
|
CUBEB_EXPORT int
|
||||||
|
cubeb_device_collection_destroy(cubeb * context,
|
||||||
cubeb_device_collection * collection);
|
cubeb_device_collection * collection);
|
||||||
|
|
||||||
/** Registers a callback which is called when the system detects
|
/** Registers a callback which is called when the system detects
|
||||||
|
@ -664,17 +693,18 @@ CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
|
||||||
@param devtype device type to include. Different callbacks and user pointers
|
@param devtype device type to include. Different callbacks and user pointers
|
||||||
can be registered for each devtype. The hybrid devtype
|
can be registered for each devtype. The hybrid devtype
|
||||||
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
|
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
|
||||||
and will register the provided callback and user pointer in both sides.
|
and will register the provided callback and user pointer in both
|
||||||
|
sides.
|
||||||
@param callback a function called whenever the system device list changes.
|
@param callback a function called whenever the system device list changes.
|
||||||
Passing NULL allow to unregister a function. You have to unregister
|
Passing NULL allow to unregister a function. You have to unregister
|
||||||
first before you register a new callback.
|
first before you register a new callback.
|
||||||
@param user_ptr pointer to user specified data which will be present in
|
@param user_ptr pointer to user specified data which will be present in
|
||||||
subsequent callbacks.
|
subsequent callbacks.
|
||||||
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
@retval CUBEB_ERROR_NOT_SUPPORTED */
|
||||||
CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
|
CUBEB_EXPORT int
|
||||||
cubeb_device_type devtype,
|
cubeb_register_device_collection_changed(
|
||||||
cubeb_device_collection_changed_callback callback,
|
cubeb * context, cubeb_device_type devtype,
|
||||||
void * user_ptr);
|
cubeb_device_collection_changed_callback callback, void * user_ptr);
|
||||||
|
|
||||||
/** Set a callback to be called with a message.
|
/** Set a callback to be called with a message.
|
||||||
@param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
|
@param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
|
||||||
|
@ -684,7 +714,8 @@ CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
|
||||||
@retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
|
@retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
|
||||||
invalid pointers, or if level is not
|
invalid pointers, or if level is not
|
||||||
in cubeb_log_level. */
|
in cubeb_log_level. */
|
||||||
CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
|
CUBEB_EXPORT int
|
||||||
|
cubeb_set_log_callback(cubeb_log_level log_level,
|
||||||
cubeb_log_callback log_callback);
|
cubeb_log_callback log_callback);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
|
|
|
@ -22,12 +22,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
|
* From
|
||||||
|
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
|
||||||
*/
|
*/
|
||||||
typedef int32_t status_t;
|
typedef int32_t status_t;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h
|
* From
|
||||||
|
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h
|
||||||
*/
|
*/
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
|
@ -52,7 +54,8 @@ enum event_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
|
* From
|
||||||
|
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
|
||||||
* and
|
* and
|
||||||
* https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h
|
* https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h
|
||||||
*/
|
*/
|
||||||
|
@ -63,14 +66,16 @@ enum {
|
||||||
AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1,
|
AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1,
|
||||||
AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2,
|
AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2,
|
||||||
AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS,
|
AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS,
|
||||||
AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS)
|
AUDIO_CHANNEL_OUT_STEREO_ICS =
|
||||||
|
(AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS)
|
||||||
} AudioTrack_ChannelMapping_ICS;
|
} AudioTrack_ChannelMapping_ICS;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy = 0x4,
|
AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy = 0x4,
|
||||||
AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy = 0x8,
|
AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy = 0x8,
|
||||||
AUDIO_CHANNEL_OUT_MONO_Legacy = AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy,
|
AUDIO_CHANNEL_OUT_MONO_Legacy = AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy,
|
||||||
AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy)
|
AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy |
|
||||||
|
AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy)
|
||||||
} AudioTrack_ChannelMapping_Legacy;
|
} AudioTrack_ChannelMapping_Legacy;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -78,4 +83,3 @@ typedef enum {
|
||||||
AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1,
|
AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1,
|
||||||
AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT),
|
AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT),
|
||||||
} AudioTrack_SampleType;
|
} AudioTrack_SampleType;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#ifndef _CUBEB_OUTPUT_LATENCY_H_
|
#ifndef _CUBEB_OUTPUT_LATENCY_H_
|
||||||
#define _CUBEB_OUTPUT_LATENCY_H_
|
#define _CUBEB_OUTPUT_LATENCY_H_
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include "cubeb_media_library.h"
|
|
||||||
#include "../cubeb-jni.h"
|
#include "../cubeb-jni.h"
|
||||||
|
#include "cubeb_media_library.h"
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
struct output_latency_function {
|
struct output_latency_function {
|
||||||
media_lib * from_lib;
|
media_lib * from_lib;
|
||||||
|
|
|
@ -17,10 +17,12 @@ cubeb_load_media_library()
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the latency, in ms, from AudioFlinger. First, try the most recent signature.
|
// Get the latency, in ms, from AudioFlinger. First, try the most recent
|
||||||
// status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
|
// signature. status_t AudioSystem::getOutputLatency(uint32_t* latency,
|
||||||
ml.get_output_latency =
|
// audio_stream_type_t streamType)
|
||||||
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
|
ml.get_output_latency = dlsym(
|
||||||
|
ml.libmedia,
|
||||||
|
"_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
|
||||||
if (!ml.get_output_latency) {
|
if (!ml.get_output_latency) {
|
||||||
// In case of failure, try the signature from legacy version.
|
// In case of failure, try the signature from legacy version.
|
||||||
// status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
|
// status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
|
||||||
|
|
|
@ -29,7 +29,8 @@
|
||||||
|
|
||||||
/** Audio recording preset */
|
/** Audio recording preset */
|
||||||
/** Audio recording preset key */
|
/** Audio recording preset key */
|
||||||
#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset")
|
#define SL_ANDROID_KEY_RECORDING_PRESET \
|
||||||
|
((const SLchar *)"androidRecordingPreset")
|
||||||
/** Audio recording preset values */
|
/** Audio recording preset values */
|
||||||
/** preset "none" cannot be set, it is used to indicate the current settings
|
/** preset "none" cannot be set, it is used to indicate the current settings
|
||||||
* do not match any of the presets. */
|
* do not match any of the presets. */
|
||||||
|
@ -46,7 +47,6 @@
|
||||||
/** uses the main microphone unprocessed */
|
/** uses the main microphone unprocessed */
|
||||||
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32)0x00000005)
|
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32)0x00000005)
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* Android AudioPlayer configuration */
|
/* Android AudioPlayer configuration */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
@ -69,7 +69,6 @@
|
||||||
/* same as android.media.AudioManager.STREAM_NOTIFICATION */
|
/* same as android.media.AudioManager.STREAM_NOTIFICATION */
|
||||||
#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32)0x00000005)
|
#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32)0x00000005)
|
||||||
|
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* Android AudioPlayer and AudioRecorder configuration */
|
/* Android AudioPlayer and AudioRecorder configuration */
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
|
@ -85,15 +84,18 @@
|
||||||
* granted or not.
|
* granted or not.
|
||||||
*/
|
*/
|
||||||
/** Audio Performance mode key */
|
/** Audio Performance mode key */
|
||||||
#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
|
#define SL_ANDROID_KEY_PERFORMANCE_MODE \
|
||||||
|
((const SLchar *)"androidPerformanceMode")
|
||||||
|
|
||||||
/** Audio performance values */
|
/** Audio performance values */
|
||||||
/* No specific performance requirement. Allows HW and SW pre/post processing. */
|
/* No specific performance requirement. Allows HW and SW pre/post
|
||||||
|
* processing. */
|
||||||
#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32)0x00000000)
|
#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32)0x00000000)
|
||||||
/* Priority given to latency. No HW or software pre/post processing.
|
/* Priority given to latency. No HW or software pre/post processing.
|
||||||
* This is the default if no performance mode is specified. */
|
* This is the default if no performance mode is specified. */
|
||||||
#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32)0x00000001)
|
#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32)0x00000001)
|
||||||
/* Priority given to latency while still allowing HW pre and post processing. */
|
/* Priority given to latency while still allowing HW pre and post
|
||||||
|
* processing. */
|
||||||
#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32)0x00000002)
|
#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32)0x00000002)
|
||||||
/* Priority given to power saving if latency is not a concern.
|
/* Priority given to power saving if latency is not a concern.
|
||||||
* Allows HW and SW pre/post processing. */
|
* Allows HW and SW pre/post processing. */
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
|
#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
|
||||||
|
|
||||||
#include "cubeb/cubeb.h"
|
#include "cubeb/cubeb.h"
|
||||||
#include "cubeb_log.h"
|
|
||||||
#include "cubeb_assert.h"
|
#include "cubeb_assert.h"
|
||||||
|
#include "cubeb_log.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -37,8 +37,7 @@ struct cubeb_ops {
|
||||||
int (*init)(cubeb ** context, char const * context_name);
|
int (*init)(cubeb ** context, char const * context_name);
|
||||||
char const * (*get_backend_id)(cubeb * context);
|
char const * (*get_backend_id)(cubeb * context);
|
||||||
int (*get_max_channel_count)(cubeb * context, uint32_t * max_channels);
|
int (*get_max_channel_count)(cubeb * context, uint32_t * max_channels);
|
||||||
int (* get_min_latency)(cubeb * context,
|
int (*get_min_latency)(cubeb * context, cubeb_stream_params params,
|
||||||
cubeb_stream_params params,
|
|
||||||
uint32_t * latency_ms);
|
uint32_t * latency_ms);
|
||||||
int (*get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
|
int (*get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
|
||||||
int (*enumerate_devices)(cubeb * context, cubeb_device_type type,
|
int (*enumerate_devices)(cubeb * context, cubeb_device_type type,
|
||||||
|
@ -46,21 +45,16 @@ struct cubeb_ops {
|
||||||
int (*device_collection_destroy)(cubeb * context,
|
int (*device_collection_destroy)(cubeb * context,
|
||||||
cubeb_device_collection * collection);
|
cubeb_device_collection * collection);
|
||||||
void (*destroy)(cubeb * context);
|
void (*destroy)(cubeb * context);
|
||||||
int (* stream_init)(cubeb * context,
|
int (*stream_init)(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_stream ** stream,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency,
|
unsigned int latency, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_state_callback state_callback, void * user_ptr);
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr);
|
|
||||||
void (*stream_destroy)(cubeb_stream * stream);
|
void (*stream_destroy)(cubeb_stream * stream);
|
||||||
int (*stream_start)(cubeb_stream * stream);
|
int (*stream_start)(cubeb_stream * stream);
|
||||||
int (*stream_stop)(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_position)(cubeb_stream * stream, uint64_t * position);
|
||||||
int (*stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
int (*stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||||
int (*stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency);
|
int (*stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency);
|
||||||
|
@ -68,14 +62,13 @@ struct cubeb_ops {
|
||||||
int (*stream_set_name)(cubeb_stream * stream, char const * stream_name);
|
int (*stream_set_name)(cubeb_stream * stream, char const * stream_name);
|
||||||
int (*stream_get_current_device)(cubeb_stream * stream,
|
int (*stream_get_current_device)(cubeb_stream * stream,
|
||||||
cubeb_device ** const device);
|
cubeb_device ** const device);
|
||||||
int (* stream_device_destroy)(cubeb_stream * stream,
|
int (*stream_device_destroy)(cubeb_stream * stream, cubeb_device * device);
|
||||||
cubeb_device * device);
|
int (*stream_register_device_changed_callback)(
|
||||||
int (* stream_register_device_changed_callback)(cubeb_stream * stream,
|
cubeb_stream * stream,
|
||||||
cubeb_device_changed_callback device_changed_callback);
|
cubeb_device_changed_callback device_changed_callback);
|
||||||
int (* register_device_collection_changed)(cubeb * context,
|
int (*register_device_collection_changed)(
|
||||||
cubeb_device_type devtype,
|
cubeb * context, cubeb_device_type devtype,
|
||||||
cubeb_device_collection_changed_callback callback,
|
cubeb_device_collection_changed_callback callback, void * user_ptr);
|
||||||
void * user_ptr);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
|
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
/* clang-format off */
|
||||||
#include "jni.h"
|
#include "jni.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include "cubeb-jni-instances.h"
|
#include "cubeb-jni-instances.h"
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
#define AUDIO_STREAM_TYPE_MUSIC 3
|
#define AUDIO_STREAM_TYPE_MUSIC 3
|
||||||
|
|
||||||
|
@ -10,8 +12,7 @@ struct cubeb_jni {
|
||||||
jmethodID s_get_output_latency_id = nullptr;
|
jmethodID s_get_output_latency_id = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C"
|
extern "C" cubeb_jni *
|
||||||
cubeb_jni *
|
|
||||||
cubeb_jni_init()
|
cubeb_jni_init()
|
||||||
{
|
{
|
||||||
jobject ctx_obj = cubeb_jni_get_context_instance();
|
jobject ctx_obj = cubeb_jni_get_context_instance();
|
||||||
|
@ -23,18 +24,28 @@ cubeb_jni_init()
|
||||||
cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
|
cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
|
||||||
assert(cubeb_jni_ptr);
|
assert(cubeb_jni_ptr);
|
||||||
|
|
||||||
// Find the audio manager object and make it global to call it from another method
|
// Find the audio manager object and make it global to call it from another
|
||||||
|
// method
|
||||||
jclass context_class = jni_env->FindClass("android/content/Context");
|
jclass context_class = jni_env->FindClass("android/content/Context");
|
||||||
jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
|
jfieldID audio_service_field = jni_env->GetStaticFieldID(
|
||||||
jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field);
|
context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
|
||||||
jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
|
jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class,
|
||||||
jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
|
audio_service_field);
|
||||||
cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
|
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
|
// Make the audio manager class a global reference in order to preserve method
|
||||||
|
// id
|
||||||
jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager");
|
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_audio_manager_class =
|
||||||
cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I");
|
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(ctx_obj);
|
||||||
jni_env->DeleteLocalRef(context_class);
|
jni_env->DeleteLocalRef(context_class);
|
||||||
|
@ -45,16 +56,19 @@ cubeb_jni_init()
|
||||||
return cubeb_jni_ptr;
|
return cubeb_jni_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C" int
|
||||||
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
|
cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
|
||||||
{
|
{
|
||||||
assert(cubeb_jni_ptr);
|
assert(cubeb_jni_ptr);
|
||||||
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
|
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
|
||||||
return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC
|
return jni_env->CallIntMethod(
|
||||||
|
cubeb_jni_ptr->s_audio_manager_obj,
|
||||||
|
cubeb_jni_ptr->s_get_output_latency_id,
|
||||||
|
AUDIO_STREAM_TYPE_MUSIC); // param: AudioManager.STREAM_MUSIC
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C" void
|
||||||
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
|
cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
|
||||||
{
|
{
|
||||||
assert(cubeb_jni_ptr);
|
assert(cubeb_jni_ptr);
|
||||||
|
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
|
|
||||||
typedef struct cubeb_jni cubeb_jni;
|
typedef struct cubeb_jni cubeb_jni;
|
||||||
|
|
||||||
cubeb_jni * cubeb_jni_init();
|
cubeb_jni *
|
||||||
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
|
cubeb_jni_init();
|
||||||
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
|
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_
|
#endif // _CUBEB_JNI_H_
|
||||||
|
|
|
@ -10,19 +10,14 @@
|
||||||
#include <SLES/OpenSLES.h>
|
#include <SLES/OpenSLES.h>
|
||||||
|
|
||||||
static SLresult
|
static SLresult
|
||||||
cubeb_get_sles_engine(SLObjectItf * pEngine,
|
cubeb_get_sles_engine(SLObjectItf * pEngine, SLuint32 numOptions,
|
||||||
SLuint32 numOptions,
|
|
||||||
const SLEngineOption * pEngineOptions,
|
const SLEngineOption * pEngineOptions,
|
||||||
SLuint32 numInterfaces,
|
SLuint32 numInterfaces,
|
||||||
const SLInterfaceID * pInterfaceIds,
|
const SLInterfaceID * pInterfaceIds,
|
||||||
const SLboolean * pInterfaceRequired)
|
const SLboolean * pInterfaceRequired)
|
||||||
{
|
{
|
||||||
return slCreateEngine(pEngine,
|
return slCreateEngine(pEngine, numOptions, pEngineOptions, numInterfaces,
|
||||||
numOptions,
|
pInterfaceIds, pInterfaceRequired);
|
||||||
pEngineOptions,
|
|
||||||
numInterfaces,
|
|
||||||
pInterfaceIds,
|
|
||||||
pInterfaceRequired);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
#include "cubeb-internal.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
|
|
||||||
#define NELEMS(x) ((int)(sizeof(x) / sizeof(x[0])))
|
#define NELEMS(x) ((int)(sizeof(x) / sizeof(x[0])))
|
||||||
|
|
||||||
|
@ -28,49 +28,64 @@ struct cubeb_stream {
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(USE_PULSE)
|
#if defined(USE_PULSE)
|
||||||
int pulse_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
pulse_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_PULSE_RUST)
|
#if defined(USE_PULSE_RUST)
|
||||||
int pulse_rust_init(cubeb ** contet, char const * context_name);
|
int
|
||||||
|
pulse_rust_init(cubeb ** contet, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_JACK)
|
#if defined(USE_JACK)
|
||||||
int jack_init (cubeb ** context, char const * context_name);
|
int
|
||||||
|
jack_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_ALSA)
|
#if defined(USE_ALSA)
|
||||||
int alsa_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
alsa_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_AUDIOUNIT)
|
#if defined(USE_AUDIOUNIT)
|
||||||
int audiounit_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
audiounit_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_AUDIOUNIT_RUST)
|
#if defined(USE_AUDIOUNIT_RUST)
|
||||||
int audiounit_rust_init(cubeb ** contet, char const * context_name);
|
int
|
||||||
|
audiounit_rust_init(cubeb ** contet, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_WINMM)
|
#if defined(USE_WINMM)
|
||||||
int winmm_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
winmm_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_WASAPI)
|
#if defined(USE_WASAPI)
|
||||||
int wasapi_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
wasapi_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_SNDIO)
|
#if defined(USE_SNDIO)
|
||||||
int sndio_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
sndio_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_SUN)
|
#if defined(USE_SUN)
|
||||||
int sun_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
sun_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_OPENSL)
|
#if defined(USE_OPENSL)
|
||||||
int opensl_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
opensl_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_OSS)
|
#if defined(USE_OSS)
|
||||||
int oss_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
oss_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_AAUDIO)
|
#if defined(USE_AAUDIO)
|
||||||
int aaudio_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
aaudio_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_AUDIOTRACK)
|
#if defined(USE_AUDIOTRACK)
|
||||||
int audiotrack_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
audiotrack_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
#if defined(USE_KAI)
|
#if defined(USE_KAI)
|
||||||
int kai_init(cubeb ** context, char const * context_name);
|
int
|
||||||
|
kai_init(cubeb ** context, char const * context_name);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -79,14 +94,18 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
|
||||||
{
|
{
|
||||||
XASSERT(input_stream_params || output_stream_params);
|
XASSERT(input_stream_params || output_stream_params);
|
||||||
if (output_stream_params) {
|
if (output_stream_params) {
|
||||||
if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
|
if (output_stream_params->rate < 1000 ||
|
||||||
output_stream_params->channels < 1 || output_stream_params->channels > UINT8_MAX) {
|
output_stream_params->rate > 192000 ||
|
||||||
|
output_stream_params->channels < 1 ||
|
||||||
|
output_stream_params->channels > UINT8_MAX) {
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (input_stream_params) {
|
if (input_stream_params) {
|
||||||
if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
|
if (input_stream_params->rate < 1000 ||
|
||||||
input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) {
|
input_stream_params->rate > 192000 ||
|
||||||
|
input_stream_params->channels < 1 ||
|
||||||
|
input_stream_params->channels > UINT8_MAX) {
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,8 +118,8 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cubeb_stream_params * params = input_stream_params ?
|
cubeb_stream_params * params =
|
||||||
input_stream_params : output_stream_params;
|
input_stream_params ? input_stream_params : output_stream_params;
|
||||||
|
|
||||||
switch (params->format) {
|
switch (params->format) {
|
||||||
case CUBEB_SAMPLE_S16LE:
|
case CUBEB_SAMPLE_S16LE:
|
||||||
|
@ -123,7 +142,8 @@ validate_latency(int latency)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cubeb_init(cubeb ** context, char const * context_name, char const * backend_name)
|
cubeb_init(cubeb ** context, char const * context_name,
|
||||||
|
char const * backend_name)
|
||||||
{
|
{
|
||||||
int (*init_oneshot)(cubeb **, char const *) = NULL;
|
int (*init_oneshot)(cubeb **, char const *) = NULL;
|
||||||
|
|
||||||
|
@ -295,7 +315,8 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms)
|
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params,
|
||||||
|
uint32_t * latency_ms)
|
||||||
{
|
{
|
||||||
if (!context || !params || !latency_ms) {
|
if (!context || !params || !latency_ms) {
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
|
@ -333,15 +354,13 @@ cubeb_destroy(cubeb * context)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
cubeb_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_devid input_device,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency,
|
unsigned int latency, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
@ -349,24 +368,20 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
|
if ((r = validate_stream_params(input_stream_params, output_stream_params)) !=
|
||||||
|
CUBEB_OK ||
|
||||||
(r = validate_latency(latency)) != CUBEB_OK) {
|
(r = validate_latency(latency)) != CUBEB_OK) {
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = context->ops->stream_init(context, stream, stream_name,
|
r = context->ops->stream_init(context, stream, stream_name, input_device,
|
||||||
input_device,
|
input_stream_params, output_device,
|
||||||
input_stream_params,
|
output_stream_params, latency, data_callback,
|
||||||
output_device,
|
state_callback, user_ptr);
|
||||||
output_stream_params,
|
|
||||||
latency,
|
|
||||||
data_callback,
|
|
||||||
state_callback,
|
|
||||||
user_ptr);
|
|
||||||
|
|
||||||
if (r == CUBEB_ERROR_INVALID_FORMAT) {
|
if (r == CUBEB_ERROR_INVALID_FORMAT) {
|
||||||
LOG("Invalid format, %p %p %d %d",
|
LOG("Invalid format, %p %p %d %d", output_stream_params,
|
||||||
output_stream_params, input_stream_params,
|
input_stream_params,
|
||||||
output_stream_params && output_stream_params->format,
|
output_stream_params && output_stream_params->format,
|
||||||
input_stream_params && input_stream_params->format);
|
input_stream_params && input_stream_params->format);
|
||||||
}
|
}
|
||||||
|
@ -404,20 +419,6 @@ cubeb_stream_stop(cubeb_stream * stream)
|
||||||
return stream->context->ops->stream_stop(stream);
|
return stream->context->ops->stream_stop(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
|
||||||
cubeb_stream_reset_default_device(cubeb_stream * stream)
|
|
||||||
{
|
|
||||||
if (!stream) {
|
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stream->context->ops->stream_reset_default_device) {
|
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stream->context->ops->stream_reset_default_device(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
|
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
|
||||||
{
|
{
|
||||||
|
@ -484,7 +485,8 @@ cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name)
|
||||||
return stream->context->ops->stream_set_name(stream, stream_name);
|
return stream->context->ops->stream_set_name(stream, stream_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_stream_get_current_device(cubeb_stream * stream,
|
int
|
||||||
|
cubeb_stream_get_current_device(cubeb_stream * stream,
|
||||||
cubeb_device ** const device)
|
cubeb_device ** const device)
|
||||||
{
|
{
|
||||||
if (!stream || !device) {
|
if (!stream || !device) {
|
||||||
|
@ -498,8 +500,8 @@ int cubeb_stream_get_current_device(cubeb_stream * stream,
|
||||||
return stream->context->ops->stream_get_current_device(stream, device);
|
return stream->context->ops->stream_get_current_device(stream, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_stream_device_destroy(cubeb_stream * stream,
|
int
|
||||||
cubeb_device * device)
|
cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
|
||||||
{
|
{
|
||||||
if (!stream || !device) {
|
if (!stream || !device) {
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
|
@ -512,7 +514,9 @@ int cubeb_stream_device_destroy(cubeb_stream * stream,
|
||||||
return stream->context->ops->stream_device_destroy(stream, device);
|
return stream->context->ops->stream_device_destroy(stream, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
int
|
||||||
|
cubeb_stream_register_device_changed_callback(
|
||||||
|
cubeb_stream * stream,
|
||||||
cubeb_device_changed_callback device_changed_callback)
|
cubeb_device_changed_callback device_changed_callback)
|
||||||
{
|
{
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
|
@ -523,10 +527,12 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
|
return stream->context->ops->stream_register_device_changed_callback(
|
||||||
|
stream, device_changed_callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void * cubeb_stream_user_ptr(cubeb_stream * stream)
|
void *
|
||||||
|
cubeb_stream_user_ptr(cubeb_stream * stream)
|
||||||
{
|
{
|
||||||
if (!stream) {
|
if (!stream) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -535,8 +541,8 @@ void * cubeb_stream_user_ptr(cubeb_stream * stream)
|
||||||
return stream->user_ptr;
|
return stream->user_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static
|
static void
|
||||||
void log_device(cubeb_device_info * device_info)
|
log_device(cubeb_device_info * device_info)
|
||||||
{
|
{
|
||||||
char devfmts[128] = "";
|
char devfmts[128] = "";
|
||||||
const char *devtype, *devstate, *devdeffmt;
|
const char *devtype, *devstate, *devdeffmt;
|
||||||
|
@ -611,19 +617,16 @@ void log_device(cubeb_device_info * device_info)
|
||||||
"\tRate:\t[%u, %u] (default: %u)\n"
|
"\tRate:\t[%u, %u] (default: %u)\n"
|
||||||
"\tLatency: lo %u frames, hi %u frames",
|
"\tLatency: lo %u frames, hi %u frames",
|
||||||
device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
|
device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
|
||||||
device_info->friendly_name,
|
device_info->friendly_name, device_info->group_id,
|
||||||
device_info->group_id,
|
device_info->vendor_name, devtype, devstate, device_info->max_channels,
|
||||||
device_info->vendor_name,
|
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
|
||||||
devtype,
|
(unsigned int)device_info->format, devdeffmt, device_info->min_rate,
|
||||||
devstate,
|
device_info->max_rate, device_info->default_rate, device_info->latency_lo,
|
||||||
device_info->max_channels,
|
device_info->latency_hi);
|
||||||
(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,
|
int
|
||||||
cubeb_device_type devtype,
|
cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
|
||||||
cubeb_device_collection * collection)
|
cubeb_device_collection * collection)
|
||||||
{
|
{
|
||||||
int rv;
|
int rv;
|
||||||
|
@ -645,7 +648,8 @@ int cubeb_enumerate_devices(cubeb * context,
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_device_collection_destroy(cubeb * context,
|
int
|
||||||
|
cubeb_device_collection_destroy(cubeb * context,
|
||||||
cubeb_device_collection * collection)
|
cubeb_device_collection * collection)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
@ -668,22 +672,25 @@ int cubeb_device_collection_destroy(cubeb * context,
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_register_device_collection_changed(cubeb * context,
|
int
|
||||||
cubeb_device_type devtype,
|
cubeb_register_device_collection_changed(
|
||||||
cubeb_device_collection_changed_callback callback,
|
cubeb * context, cubeb_device_type devtype,
|
||||||
void * user_ptr)
|
cubeb_device_collection_changed_callback callback, void * user_ptr)
|
||||||
{
|
{
|
||||||
if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
|
if (context == NULL ||
|
||||||
|
(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
|
|
||||||
if (!context->ops->register_device_collection_changed) {
|
if (!context->ops->register_device_collection_changed) {
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
|
return context->ops->register_device_collection_changed(context, devtype,
|
||||||
|
callback, user_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_set_log_callback(cubeb_log_level log_level,
|
int
|
||||||
|
cubeb_set_log_callback(cubeb_log_level log_level,
|
||||||
cubeb_log_callback log_callback)
|
cubeb_log_callback log_callback)
|
||||||
{
|
{
|
||||||
if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
|
if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
|
||||||
|
@ -712,4 +719,3 @@ int cubeb_set_log_callback(cubeb_log_level log_level,
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
#ifdef DISABLE_LIBAAUDIO_DLOPEN
|
#ifdef DISABLE_LIBAAUDIO_DLOPEN
|
||||||
#define WRAP(x) x
|
#define WRAP(x) x
|
||||||
#else
|
#else
|
||||||
#define WRAP(x) cubeb_##x
|
#define WRAP(x) (*cubeb_##x)
|
||||||
#define LIBAAUDIO_API_VISIT(X) \
|
#define LIBAAUDIO_API_VISIT(X) \
|
||||||
X(AAudio_convertResultToText) \
|
X(AAudio_convertResultToText) \
|
||||||
X(AAudio_convertStreamStateToText) \
|
X(AAudio_convertStreamStateToText) \
|
||||||
|
@ -78,6 +78,7 @@
|
||||||
// X(AAudioStream_getContentType) \
|
// X(AAudioStream_getContentType) \
|
||||||
// X(AAudioStream_getInputPreset) \
|
// X(AAudioStream_getInputPreset) \
|
||||||
// X(AAudioStream_getSessionId) \
|
// X(AAudioStream_getSessionId) \
|
||||||
|
// END: not needed or added later on
|
||||||
|
|
||||||
#define MAKE_TYPEDEF(x) static decltype(x) * cubeb_##x;
|
#define MAKE_TYPEDEF(x) static decltype(x) * cubeb_##x;
|
||||||
LIBAAUDIO_API_VISIT(MAKE_TYPEDEF)
|
LIBAAUDIO_API_VISIT(MAKE_TYPEDEF)
|
||||||
|
@ -934,7 +935,8 @@ aaudio_stream_init_impl(cubeb_stream * stm, cubeb_devid input_device,
|
||||||
stm->resampler = cubeb_resampler_create(
|
stm->resampler = cubeb_resampler_create(
|
||||||
stm, input_stream_params ? &in_params : NULL,
|
stm, input_stream_params ? &in_params : NULL,
|
||||||
output_stream_params ? &out_params : NULL, target_sample_rate,
|
output_stream_params ? &out_params : NULL, target_sample_rate,
|
||||||
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT);
|
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT,
|
||||||
|
CUBEB_RESAMPLER_RECLOCK_NONE);
|
||||||
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
LOG("Failed to create resampler");
|
LOG("Failed to create resampler");
|
||||||
|
@ -997,8 +999,10 @@ aaudio_stream_init(cubeb * ctx, cubeb_stream ** stream,
|
||||||
stm->user_ptr = user_ptr;
|
stm->user_ptr = user_ptr;
|
||||||
stm->data_callback = data_callback;
|
stm->data_callback = data_callback;
|
||||||
stm->state_callback = state_callback;
|
stm->state_callback = state_callback;
|
||||||
stm->voice_input = input_stream_params && !!(input_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
|
stm->voice_input = input_stream_params &&
|
||||||
stm->voice_output = output_stream_params && !!(output_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
|
!!(input_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
|
||||||
|
stm->voice_output = output_stream_params &&
|
||||||
|
!!(output_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
|
||||||
stm->previous_clock = 0;
|
stm->previous_clock = 0;
|
||||||
|
|
||||||
LOG("cubeb stream prefs: voice_input: %s voice_output: %s",
|
LOG("cubeb stream prefs: voice_input: %s voice_output: %s",
|
||||||
|
@ -1450,7 +1454,6 @@ const static struct cubeb_ops aaudio_ops = {
|
||||||
/*.stream_destroy =*/aaudio_stream_destroy,
|
/*.stream_destroy =*/aaudio_stream_destroy,
|
||||||
/*.stream_start =*/aaudio_stream_start,
|
/*.stream_start =*/aaudio_stream_start,
|
||||||
/*.stream_stop =*/aaudio_stream_stop,
|
/*.stream_stop =*/aaudio_stream_stop,
|
||||||
/*.stream_reset_default_device =*/NULL,
|
|
||||||
/*.stream_get_position =*/aaudio_stream_get_position,
|
/*.stream_get_position =*/aaudio_stream_get_position,
|
||||||
/*.stream_get_latency =*/aaudio_stream_get_latency,
|
/*.stream_get_latency =*/aaudio_stream_get_latency,
|
||||||
/*.stream_get_input_latency =*/aaudio_stream_get_input_latency,
|
/*.stream_get_input_latency =*/aaudio_stream_get_input_latency,
|
||||||
|
@ -1474,7 +1477,7 @@ aaudio_init(cubeb ** context, char const * /* context_name */)
|
||||||
|
|
||||||
#define LOAD(x) \
|
#define LOAD(x) \
|
||||||
{ \
|
{ \
|
||||||
WRAP(x) = (decltype(WRAP(x)))(dlsym(libaaudio, #x)); \
|
cubeb_##x = (decltype(x) *)(dlsym(libaaudio, #x)); \
|
||||||
if (!WRAP(x)) { \
|
if (!WRAP(x)) { \
|
||||||
LOG("AAudio: Failed to load %s", #x); \
|
LOG("AAudio: Failed to load %s", #x); \
|
||||||
dlclose(libaaudio); \
|
dlclose(libaaudio); \
|
||||||
|
|
|
@ -8,21 +8,21 @@
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#define _BSD_SOURCE
|
#define _BSD_SOURCE
|
||||||
#define _XOPEN_SOURCE 500
|
#define _XOPEN_SOURCE 500
|
||||||
#include <pthread.h>
|
#include "cubeb-internal.h"
|
||||||
#include <sys/time.h>
|
#include "cubeb/cubeb.h"
|
||||||
|
#include <alsa/asoundlib.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <alsa/asoundlib.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
|
|
||||||
#ifdef DISABLE_LIBASOUND_DLOPEN
|
#ifdef DISABLE_LIBASOUND_DLOPEN
|
||||||
#define WRAP(x) x
|
#define WRAP(x) x
|
||||||
#else
|
#else
|
||||||
#define WRAP(x) cubeb_##x
|
#define WRAP(x) (*cubeb_##x)
|
||||||
#define LIBASOUND_API_VISIT(X) \
|
#define LIBASOUND_API_VISIT(X) \
|
||||||
X(snd_config) \
|
X(snd_config) \
|
||||||
X(snd_config_add) \
|
X(snd_config_add) \
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
X(snd_pcm_set_params) \
|
X(snd_pcm_set_params) \
|
||||||
X(snd_pcm_start) \
|
X(snd_pcm_start) \
|
||||||
X(snd_pcm_state) \
|
X(snd_pcm_state) \
|
||||||
X(snd_pcm_writei) \
|
X(snd_pcm_writei)
|
||||||
|
|
||||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
||||||
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
|
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
|
||||||
|
@ -101,7 +101,8 @@ struct cubeb {
|
||||||
|
|
||||||
int shutdown;
|
int shutdown;
|
||||||
|
|
||||||
/* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */
|
/* Control pipe for forcing poll to wake and rebuild fds or recalculate the
|
||||||
|
* timeout. */
|
||||||
int control_fd_read;
|
int control_fd_read;
|
||||||
int control_fd_write;
|
int control_fd_write;
|
||||||
|
|
||||||
|
@ -116,13 +117,7 @@ struct cubeb {
|
||||||
int is_pa;
|
int is_pa;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum stream_state {
|
enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };
|
||||||
INACTIVE,
|
|
||||||
RUNNING,
|
|
||||||
DRAINING,
|
|
||||||
PROCESSING,
|
|
||||||
ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cubeb_stream {
|
struct cubeb_stream {
|
||||||
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
||||||
|
@ -146,7 +141,8 @@ struct cubeb_stream {
|
||||||
enum stream_state state;
|
enum stream_state state;
|
||||||
|
|
||||||
struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
|
struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
|
||||||
struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
|
struct pollfd *
|
||||||
|
fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
|
||||||
nfds_t nfds;
|
nfds_t nfds;
|
||||||
|
|
||||||
struct timeval drain_timeout;
|
struct timeval drain_timeout;
|
||||||
|
@ -294,8 +290,10 @@ set_timeout(struct timeval * timeout, unsigned int ms)
|
||||||
static void
|
static void
|
||||||
stream_buffer_decrement(cubeb_stream * stm, long count)
|
stream_buffer_decrement(cubeb_stream * stm, long count)
|
||||||
{
|
{
|
||||||
char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
|
char * bufremains =
|
||||||
memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
|
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
|
||||||
|
memmove(stm->buffer, bufremains,
|
||||||
|
WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
|
||||||
stm->bufframes -= count;
|
stm->bufframes -= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +325,8 @@ alsa_process_stream(cubeb_stream * stm)
|
||||||
/* Call _poll_descriptors_revents() even if we don't use it
|
/* Call _poll_descriptors_revents() even if we don't use it
|
||||||
to let underlying plugins clear null events. Otherwise poll()
|
to let underlying plugins clear null events. Otherwise poll()
|
||||||
may wake up again and again, producing unnecessary CPU usage. */
|
may wake up again and again, producing unnecessary CPU usage. */
|
||||||
WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
|
WRAP(snd_pcm_poll_descriptors_revents)
|
||||||
|
(stm->pcm, stm->fds, stm->nfds, &revents);
|
||||||
|
|
||||||
avail = WRAP(snd_pcm_avail_update)(stm->pcm);
|
avail = WRAP(snd_pcm_avail_update)(stm->pcm);
|
||||||
|
|
||||||
|
@ -337,7 +336,8 @@ alsa_process_stream(cubeb_stream * stm)
|
||||||
return RUNNING;
|
return RUNNING;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. */
|
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
|
||||||
|
*/
|
||||||
if ((unsigned int)avail > stm->buffer_size) {
|
if ((unsigned int)avail > stm->buffer_size) {
|
||||||
avail = stm->buffer_size;
|
avail = stm->buffer_size;
|
||||||
}
|
}
|
||||||
|
@ -366,18 +366,24 @@ alsa_process_stream(cubeb_stream * stm)
|
||||||
|
|
||||||
/* Capture: Pass read frames to callback function */
|
/* Capture: Pass read frames to callback function */
|
||||||
if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
|
if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
|
||||||
(!stm->other_stream || stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
|
(!stm->other_stream ||
|
||||||
|
stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
|
||||||
snd_pcm_sframes_t wrote = stm->bufframes;
|
snd_pcm_sframes_t wrote = stm->bufframes;
|
||||||
struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
|
struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
|
||||||
void * other_buffer = stm->other_stream ? stm->other_stream->buffer + stm->other_stream->bufframes : NULL;
|
void * other_buffer = stm->other_stream ? stm->other_stream->buffer +
|
||||||
|
stm->other_stream->bufframes
|
||||||
|
: NULL;
|
||||||
|
|
||||||
/* Correct write size to the other stream available space */
|
/* Correct write size to the other stream available space */
|
||||||
if (stm->other_stream && wrote > (snd_pcm_sframes_t) (stm->other_stream->buffer_size - stm->other_stream->bufframes)) {
|
if (stm->other_stream &&
|
||||||
|
wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size -
|
||||||
|
stm->other_stream->bufframes)) {
|
||||||
wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
|
wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_mutex_unlock(&stm->mutex);
|
pthread_mutex_unlock(&stm->mutex);
|
||||||
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, other_buffer, wrote);
|
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer,
|
||||||
|
other_buffer, wrote);
|
||||||
pthread_mutex_lock(&stm->mutex);
|
pthread_mutex_lock(&stm->mutex);
|
||||||
|
|
||||||
if (wrote < 0) {
|
if (wrote < 0) {
|
||||||
|
@ -392,14 +398,17 @@ alsa_process_stream(cubeb_stream * stm)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Playback: Don't have enough data? Let's ask for more. */
|
/* Playback: Don't have enough data? Let's ask for more. */
|
||||||
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes &&
|
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
|
||||||
|
avail > (snd_pcm_sframes_t)stm->bufframes &&
|
||||||
(!stm->other_stream || stm->other_stream->bufframes > 0)) {
|
(!stm->other_stream || stm->other_stream->bufframes > 0)) {
|
||||||
long got = avail - stm->bufframes;
|
long got = avail - stm->bufframes;
|
||||||
void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
|
void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
|
||||||
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
char * buftail =
|
||||||
|
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
||||||
|
|
||||||
/* Correct read size to the other stream available frames */
|
/* Correct read size to the other stream available frames */
|
||||||
if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) {
|
if (stm->other_stream &&
|
||||||
|
got > (snd_pcm_sframes_t)stm->other_stream->bufframes) {
|
||||||
got = stm->other_stream->bufframes;
|
got = stm->other_stream->bufframes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,11 +428,13 @@ alsa_process_stream(cubeb_stream * stm)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Playback: Still don't have enough data? Add some silence. */
|
/* Playback: Still don't have enough data? Add some silence. */
|
||||||
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes) {
|
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
|
||||||
|
avail > (snd_pcm_sframes_t)stm->bufframes) {
|
||||||
long drain_frames = avail - stm->bufframes;
|
long drain_frames = avail - stm->bufframes;
|
||||||
double drain_time = (double)drain_frames / stm->params.rate;
|
double drain_time = (double)drain_frames / stm->params.rate;
|
||||||
|
|
||||||
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
char * buftail =
|
||||||
|
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
|
||||||
memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
|
memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
|
||||||
stm->bufframes = avail;
|
stm->bufframes = avail;
|
||||||
|
|
||||||
|
@ -467,8 +478,7 @@ alsa_process_stream(cubeb_stream * stm)
|
||||||
avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
|
avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
|
||||||
|
|
||||||
/* Capture pcm must be started after initial setup/recover */
|
/* Capture pcm must be started after initial setup/recover */
|
||||||
if (avail >= 0 &&
|
if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
||||||
stm->stream_type == SND_PCM_STREAM_CAPTURE &&
|
|
||||||
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
|
||||||
avail = WRAP(snd_pcm_start)(stm->pcm);
|
avail = WRAP(snd_pcm_start)(stm->pcm);
|
||||||
}
|
}
|
||||||
|
@ -533,7 +543,8 @@ alsa_run(cubeb * ctx)
|
||||||
stm = ctx->streams[i];
|
stm = ctx->streams[i];
|
||||||
/* We can't use snd_pcm_poll_descriptors_revents here because of
|
/* We can't use snd_pcm_poll_descriptors_revents here because of
|
||||||
https://github.com/kinetiknz/cubeb/issues/135. */
|
https://github.com/kinetiknz/cubeb/issues/135. */
|
||||||
if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
|
if (stm && stm->state == RUNNING && stm->fds &&
|
||||||
|
any_revents(stm->fds, stm->nfds)) {
|
||||||
alsa_set_stream_state(stm, PROCESSING);
|
alsa_set_stream_state(stm, PROCESSING);
|
||||||
pthread_mutex_unlock(&ctx->mutex);
|
pthread_mutex_unlock(&ctx->mutex);
|
||||||
state = alsa_process_stream(stm);
|
state = alsa_process_stream(stm);
|
||||||
|
@ -548,7 +559,8 @@ alsa_run(cubeb * ctx)
|
||||||
if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
|
if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
|
||||||
alsa_set_stream_state(stm, INACTIVE);
|
alsa_set_stream_state(stm, INACTIVE);
|
||||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
|
||||||
} else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
|
} else if (stm->state == RUNNING &&
|
||||||
|
ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
|
||||||
alsa_set_stream_state(stm, ERROR);
|
alsa_set_stream_state(stm, ERROR);
|
||||||
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
||||||
}
|
}
|
||||||
|
@ -593,7 +605,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
||||||
|
|
||||||
r = WRAP(snd_config_get_string)(slave_pcm, &string);
|
r = WRAP(snd_config_get_string)(slave_pcm, &string);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
|
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string,
|
||||||
|
&slave_def);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -633,7 +646,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
|
||||||
higher than requested latency, but the plugin does not update its (and
|
higher than requested latency, but the plugin does not update its (and
|
||||||
ALSA's) internal state to reflect that, leading to an immediate underrun
|
ALSA's) internal state to reflect that, leading to an immediate underrun
|
||||||
situation. Inspired by WINE's make_handle_underrun_config.
|
situation. Inspired by WINE's make_handle_underrun_config.
|
||||||
Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */
|
Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05
|
||||||
|
*/
|
||||||
static snd_config_t *
|
static snd_config_t *
|
||||||
init_local_config_with_workaround(char const * pcm_name)
|
init_local_config_with_workaround(char const * pcm_name)
|
||||||
{
|
{
|
||||||
|
@ -646,11 +660,11 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||||
|
|
||||||
lconf = NULL;
|
lconf = NULL;
|
||||||
|
|
||||||
if (*WRAP(snd_config) == NULL) {
|
if (WRAP(snd_config) == NULL) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
|
r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -675,12 +689,14 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If this PCM has a slave, walk the slave configurations until we reach the bottom. */
|
/* If this PCM has a slave, walk the slave configurations until we reach the
|
||||||
|
* bottom. */
|
||||||
while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
|
while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
|
||||||
pcm_node = node;
|
pcm_node = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
|
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio
|
||||||
|
* plugin. */
|
||||||
r = WRAP(snd_config_search)(pcm_node, "type", &node);
|
r = WRAP(snd_config_search)(pcm_node, "type", &node);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
break;
|
break;
|
||||||
|
@ -722,13 +738,15 @@ init_local_config_with_workaround(char const * pcm_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t stream, snd_config_t * local_config)
|
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
|
||||||
|
snd_pcm_stream_t stream, snd_config_t * local_config)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
pthread_mutex_lock(&cubeb_alsa_mutex);
|
pthread_mutex_lock(&cubeb_alsa_mutex);
|
||||||
if (local_config) {
|
if (local_config) {
|
||||||
r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
|
r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
|
||||||
|
local_config);
|
||||||
} else {
|
} else {
|
||||||
r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
|
||||||
}
|
}
|
||||||
|
@ -819,7 +837,8 @@ alsa_init(cubeb ** context, char const * context_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOAD(x) { \
|
#define LOAD(x) \
|
||||||
|
{ \
|
||||||
cubeb_##x = dlsym(libasound, #x); \
|
cubeb_##x = dlsym(libasound, #x); \
|
||||||
if (!cubeb_##x) { \
|
if (!cubeb_##x) { \
|
||||||
dlclose(libasound); \
|
dlclose(libasound); \
|
||||||
|
@ -876,7 +895,8 @@ alsa_init(cubeb ** context, char const * context_name)
|
||||||
|
|
||||||
/* Open a dummy PCM to force the configuration space to be evaluated so that
|
/* Open a dummy PCM to force the configuration space to be evaluated so that
|
||||||
init_local_config_with_workaround can find and modify the default node. */
|
init_local_config_with_workaround can find and modify the default node. */
|
||||||
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, NULL);
|
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
|
||||||
|
NULL);
|
||||||
if (r >= 0) {
|
if (r >= 0) {
|
||||||
alsa_locked_pcm_close(dummy);
|
alsa_locked_pcm_close(dummy);
|
||||||
}
|
}
|
||||||
|
@ -886,7 +906,8 @@ alsa_init(cubeb ** context, char const * context_name)
|
||||||
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
pthread_mutex_unlock(&cubeb_alsa_mutex);
|
||||||
if (ctx->local_config) {
|
if (ctx->local_config) {
|
||||||
ctx->is_pa = 1;
|
ctx->is_pa = 1;
|
||||||
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
|
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME,
|
||||||
|
SND_PCM_STREAM_PLAYBACK, ctx->local_config);
|
||||||
/* If we got a local_config, we found a PA PCM. If opening a PCM with that
|
/* If we got a local_config, we found a PA PCM. If opening a PCM with that
|
||||||
config fails with EINVAL, the PA PCM is too old for this workaround. */
|
config fails with EINVAL, the PA PCM is too old for this workaround. */
|
||||||
if (r == -EINVAL) {
|
if (r == -EINVAL) {
|
||||||
|
@ -944,17 +965,17 @@ alsa_destroy(cubeb * ctx)
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alsa_stream_destroy(cubeb_stream * stm);
|
static void
|
||||||
|
alsa_stream_destroy(cubeb_stream * stm);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream,
|
||||||
snd_pcm_stream_t stream_type,
|
char const * stream_name, snd_pcm_stream_t stream_type,
|
||||||
cubeb_devid deviceid,
|
cubeb_devid deviceid,
|
||||||
cubeb_stream_params * stream_params,
|
cubeb_stream_params * stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_data_callback data_callback,
|
||||||
cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
(void)stream_name;
|
(void)stream_name;
|
||||||
cubeb_stream * stm;
|
cubeb_stream * stm;
|
||||||
|
@ -962,7 +983,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||||
snd_pcm_format_t format;
|
snd_pcm_format_t format;
|
||||||
snd_pcm_uframes_t period_size;
|
snd_pcm_uframes_t period_size;
|
||||||
int latency_us = 0;
|
int latency_us = 0;
|
||||||
char const * pcm_name = deviceid ? (char const *) deviceid : CUBEB_ALSA_PCM_NAME;
|
char const * pcm_name =
|
||||||
|
deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;
|
||||||
|
|
||||||
assert(ctx && stream);
|
assert(ctx && stream);
|
||||||
|
|
||||||
|
@ -1018,7 +1040,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||||
r = pthread_cond_init(&stm->cond, NULL);
|
r = pthread_cond_init(&stm->cond, NULL);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
|
|
||||||
r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, ctx->local_config);
|
r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type,
|
||||||
|
ctx->local_config);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
alsa_stream_destroy(stm);
|
alsa_stream_destroy(stm);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -1048,9 +1071,11 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
|
||||||
r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
|
r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
|
|
||||||
/* Double internal buffer size to have enough space when waiting for the other side of duplex connection */
|
/* Double internal buffer size to have enough space when waiting for the other
|
||||||
|
* side of duplex connection */
|
||||||
stm->buffer_size *= 2;
|
stm->buffer_size *= 2;
|
||||||
stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
|
stm->buffer =
|
||||||
|
calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
|
||||||
assert(stm->buffer);
|
assert(stm->buffer);
|
||||||
|
|
||||||
stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
|
stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
|
||||||
|
@ -1077,22 +1102,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
int result = CUBEB_OK;
|
int result = CUBEB_OK;
|
||||||
cubeb_stream *instm = NULL, *outstm = NULL;
|
cubeb_stream *instm = NULL, *outstm = NULL;
|
||||||
|
|
||||||
if (result == CUBEB_OK && input_stream_params) {
|
if (result == CUBEB_OK && input_stream_params) {
|
||||||
result = alsa_stream_init_single(ctx, &instm, stream_name, SND_PCM_STREAM_CAPTURE,
|
result = alsa_stream_init_single(ctx, &instm, stream_name,
|
||||||
input_device, input_stream_params, latency_frames,
|
SND_PCM_STREAM_CAPTURE, input_device,
|
||||||
|
input_stream_params, latency_frames,
|
||||||
data_callback, state_callback, user_ptr);
|
data_callback, state_callback, user_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result == CUBEB_OK && output_stream_params) {
|
if (result == CUBEB_OK && output_stream_params) {
|
||||||
result = alsa_stream_init_single(ctx, &outstm, stream_name, SND_PCM_STREAM_PLAYBACK,
|
result = alsa_stream_init_single(ctx, &outstm, stream_name,
|
||||||
output_device, output_stream_params, latency_frames,
|
SND_PCM_STREAM_PLAYBACK, output_device,
|
||||||
|
output_stream_params, latency_frames,
|
||||||
data_callback, state_callback, user_ptr);
|
data_callback, state_callback, user_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1116,8 +1142,7 @@ alsa_stream_destroy(cubeb_stream * stm)
|
||||||
int r;
|
int r;
|
||||||
cubeb * ctx;
|
cubeb * ctx;
|
||||||
|
|
||||||
assert(stm && (stm->state == INACTIVE ||
|
assert(stm && (stm->state == INACTIVE || stm->state == ERROR ||
|
||||||
stm->state == ERROR ||
|
|
||||||
stm->state == DRAINING));
|
stm->state == DRAINING));
|
||||||
|
|
||||||
ctx = stm->context;
|
ctx = stm->context;
|
||||||
|
@ -1169,7 +1194,8 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||||
|
|
||||||
assert(ctx);
|
assert(ctx);
|
||||||
|
|
||||||
r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL, NULL, NULL);
|
r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, ¶ms, 100, NULL,
|
||||||
|
NULL, NULL);
|
||||||
if (r != CUBEB_OK) {
|
if (r != CUBEB_OK) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1192,7 +1218,8 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
||||||
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
int r, dir;
|
int r, dir;
|
||||||
snd_pcm_t * pcm;
|
snd_pcm_t * pcm;
|
||||||
|
@ -1202,7 +1229,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
||||||
|
|
||||||
/* get a pcm, disabling resampling, so we get a rate the
|
/* get a pcm, disabling resampling, so we get a rate the
|
||||||
* hardware/dmix/pulse/etc. supports. */
|
* hardware/dmix/pulse/etc. supports. */
|
||||||
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
|
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
|
||||||
|
SND_PCM_NO_AUTO_RESAMPLE);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1235,7 +1263,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||||
|
uint32_t * latency_frames)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
/* 40ms is found to be an acceptable minimum, even on a super low-end
|
/* 40ms is found to be an acceptable minimum, even on a super low-end
|
||||||
|
@ -1346,7 +1375,8 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||||
{
|
{
|
||||||
snd_pcm_sframes_t delay;
|
snd_pcm_sframes_t delay;
|
||||||
/* This function returns the delay in frames until a frame written using
|
/* This function returns the delay in frames until a frame written using
|
||||||
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
|
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways.
|
||||||
|
*/
|
||||||
if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
|
if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -1441,7 +1471,6 @@ static struct cubeb_ops const alsa_ops = {
|
||||||
.stream_destroy = alsa_stream_destroy,
|
.stream_destroy = alsa_stream_destroy,
|
||||||
.stream_start = alsa_stream_start,
|
.stream_start = alsa_stream_start,
|
||||||
.stream_stop = alsa_stream_stop,
|
.stream_stop = alsa_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = alsa_stream_get_position,
|
.stream_get_position = alsa_stream_get_position,
|
||||||
.stream_get_latency = alsa_stream_get_latency,
|
.stream_get_latency = alsa_stream_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
@ -1450,5 +1479,4 @@ static struct cubeb_ops const alsa_ops = {
|
||||||
.stream_get_current_device = NULL,
|
.stream_get_current_device = NULL,
|
||||||
.stream_device_destroy = NULL,
|
.stream_device_destroy = NULL,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = NULL
|
.register_device_collection_changed = NULL};
|
||||||
};
|
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct
|
typedef struct {
|
||||||
{
|
|
||||||
void ** buf;
|
void ** buf;
|
||||||
size_t num;
|
size_t num;
|
||||||
size_t writePos;
|
size_t writePos;
|
||||||
|
@ -25,7 +24,8 @@ typedef struct
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
} array_queue;
|
} array_queue;
|
||||||
|
|
||||||
array_queue * array_queue_create(size_t num)
|
array_queue *
|
||||||
|
array_queue_create(size_t num)
|
||||||
{
|
{
|
||||||
assert(num != 0);
|
assert(num != 0);
|
||||||
array_queue * new_queue = (array_queue *)calloc(1, sizeof(array_queue));
|
array_queue * new_queue = (array_queue *)calloc(1, sizeof(array_queue));
|
||||||
|
@ -39,7 +39,8 @@ array_queue * array_queue_create(size_t num)
|
||||||
return new_queue;
|
return new_queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void array_queue_destroy(array_queue * aq)
|
void
|
||||||
|
array_queue_destroy(array_queue * aq)
|
||||||
{
|
{
|
||||||
assert(aq);
|
assert(aq);
|
||||||
|
|
||||||
|
@ -48,14 +49,14 @@ void array_queue_destroy(array_queue * aq)
|
||||||
free(aq);
|
free(aq);
|
||||||
}
|
}
|
||||||
|
|
||||||
int array_queue_push(array_queue * aq, void * item)
|
int
|
||||||
|
array_queue_push(array_queue * aq, void * item)
|
||||||
{
|
{
|
||||||
assert(item);
|
assert(item);
|
||||||
|
|
||||||
pthread_mutex_lock(&aq->mutex);
|
pthread_mutex_lock(&aq->mutex);
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
if(aq->buf[aq->writePos % aq->num] == NULL)
|
if (aq->buf[aq->writePos % aq->num] == NULL) {
|
||||||
{
|
|
||||||
aq->buf[aq->writePos % aq->num] = item;
|
aq->buf[aq->writePos % aq->num] = item;
|
||||||
aq->writePos = (aq->writePos + 1) % aq->num;
|
aq->writePos = (aq->writePos + 1) % aq->num;
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -65,12 +66,12 @@ int array_queue_push(array_queue * aq, void * item)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void* array_queue_pop(array_queue * aq)
|
void *
|
||||||
|
array_queue_pop(array_queue * aq)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&aq->mutex);
|
pthread_mutex_lock(&aq->mutex);
|
||||||
void * value = aq->buf[aq->readPos % aq->num];
|
void * value = aq->buf[aq->readPos % aq->num];
|
||||||
if(value)
|
if (value) {
|
||||||
{
|
|
||||||
aq->buf[aq->readPos % aq->num] = NULL;
|
aq->buf[aq->readPos % aq->num] = NULL;
|
||||||
aq->readPos = (aq->readPos + 1) % aq->num;
|
aq->readPos = (aq->readPos + 1) % aq->num;
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,8 @@ void* array_queue_pop(array_queue * aq)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t array_queue_get_size(array_queue * aq)
|
size_t
|
||||||
|
array_queue_get_size(array_queue * aq)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&aq->mutex);
|
pthread_mutex_lock(&aq->mutex);
|
||||||
ssize_t r = aq->writePos - aq->readPos;
|
ssize_t r = aq->writePos - aq->readPos;
|
||||||
|
|
|
@ -16,7 +16,8 @@
|
||||||
* export a function or macro called XASSERT that aborts the program.
|
* export a function or macro called XASSERT that aborts the program.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define XASSERT(expr) do { \
|
#define XASSERT(expr) \
|
||||||
|
do { \
|
||||||
if (!(expr)) { \
|
if (!(expr)) { \
|
||||||
fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
|
fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
|
||||||
abort(); \
|
abort(); \
|
||||||
|
|
|
@ -8,20 +8,21 @@
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
#define NDEBUG
|
#define NDEBUG
|
||||||
#endif
|
#endif
|
||||||
|
#include <android/log.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
#include "android/audiotrack_definitions.h"
|
#include "android/audiotrack_definitions.h"
|
||||||
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
|
||||||
#ifndef ALOG
|
#ifndef ALOG
|
||||||
#if defined(DEBUG) || defined(FORCE_ALOG)
|
#if defined(DEBUG) || defined(FORCE_ALOG)
|
||||||
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args)
|
#define ALOG(args...) \
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb", ##args)
|
||||||
#else
|
#else
|
||||||
#define ALOG(args...)
|
#define ALOG(args...)
|
||||||
#endif
|
#endif
|
||||||
|
@ -46,17 +47,24 @@
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
static struct cubeb_ops const audiotrack_ops;
|
static struct cubeb_ops const audiotrack_ops;
|
||||||
void audiotrack_destroy(cubeb * context);
|
void
|
||||||
void audiotrack_stream_destroy(cubeb_stream * stream);
|
audiotrack_destroy(cubeb * context);
|
||||||
|
void
|
||||||
|
audiotrack_stream_destroy(cubeb_stream * stream);
|
||||||
|
|
||||||
struct AudioTrack {
|
struct AudioTrack {
|
||||||
/* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */
|
/* only available on ICS and later. The second int paramter is in fact of type
|
||||||
/* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate);
|
* 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
|
/* 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
|
* can get the minimum frame count with this signature, and we are
|
||||||
* running gingerbread. */
|
* running gingerbread. */
|
||||||
/* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate);
|
/* static */ status_t (*get_min_frame_count_gingerbread)(int * frame_count,
|
||||||
void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int);
|
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 * (*dtor)(void * instance);
|
||||||
void (*start)(void * instance);
|
void (*start)(void * instance);
|
||||||
void (*pause)(void * instance);
|
void (*pause)(void * instance);
|
||||||
|
@ -101,7 +109,8 @@ audiotrack_refill(int event, void* user, void* info)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount);
|
got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw,
|
||||||
|
b->frameCount);
|
||||||
|
|
||||||
stream->written += got;
|
stream->written += got;
|
||||||
|
|
||||||
|
@ -109,7 +118,8 @@ audiotrack_refill(int event, void* user, void* info)
|
||||||
stream->draining = 1;
|
stream->draining = 1;
|
||||||
/* set a marker so we are notified when the are done draining, that is,
|
/* set a marker so we are notified when the are done draining, that is,
|
||||||
* when every frame has been played by android. */
|
* when every frame has been played by android. */
|
||||||
stream->context->klass.set_marker_position(stream->instance, stream->written);
|
stream->context->klass.set_marker_position(stream->instance,
|
||||||
|
stream->written);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -125,7 +135,9 @@ audiotrack_refill(int event, void* user, void* info)
|
||||||
stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
|
stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
|
||||||
break;
|
break;
|
||||||
case EVENT_NEW_POS:
|
case EVENT_NEW_POS:
|
||||||
assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack.");
|
assert(
|
||||||
|
0 &&
|
||||||
|
"We don't support the setPositionUpdatePeriod feature of audiotrack.");
|
||||||
break;
|
break;
|
||||||
case EVENT_BUFFER_END:
|
case EVENT_BUFFER_END:
|
||||||
assert(0 && "Should not happen.");
|
assert(0 && "Should not happen.");
|
||||||
|
@ -142,14 +154,17 @@ audiotrack_version_is_gingerbread(cubeb * ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count)
|
audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params,
|
||||||
|
int * min_frame_count)
|
||||||
{
|
{
|
||||||
status_t status;
|
status_t status;
|
||||||
/* Recent Android have a getMinFrameCount method. */
|
/* Recent Android have a getMinFrameCount method. */
|
||||||
if (!audiotrack_version_is_gingerbread(ctx)) {
|
if (!audiotrack_version_is_gingerbread(ctx)) {
|
||||||
status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
|
status = ctx->klass.get_min_frame_count(
|
||||||
|
min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
|
||||||
} else {
|
} else {
|
||||||
status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
|
status = ctx->klass.get_min_frame_count_gingerbread(
|
||||||
|
min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
|
||||||
}
|
}
|
||||||
if (status != 0) {
|
if (status != 0) {
|
||||||
ALOG("error getting the min frame count");
|
ALOG("error getting the min frame count");
|
||||||
|
@ -182,33 +197,44 @@ audiotrack_init(cubeb ** context, char const * context_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Recent Android first, then Gingerbread. */
|
/* Recent Android first, then Gingerbread. */
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library);
|
DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii",
|
||||||
|
ctx->klass.ctor, ctx->library);
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library);
|
DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library);
|
||||||
|
|
||||||
DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library);
|
DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency,
|
||||||
DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library);
|
ctx->library);
|
||||||
|
DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check,
|
||||||
|
ctx->library);
|
||||||
|
|
||||||
DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library);
|
DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii",
|
||||||
|
ctx->klass.get_output_samplingrate, ctx->library);
|
||||||
|
|
||||||
/* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */
|
/* |getMinFrameCount| is available on gingerbread and ICS with different
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library);
|
* signatures. */
|
||||||
|
DLSYM_DLERROR(
|
||||||
|
"_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj",
|
||||||
|
ctx->klass.get_min_frame_count, ctx->library);
|
||||||
if (!ctx->klass.get_min_frame_count) {
|
if (!ctx->klass.get_min_frame_count) {
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library);
|
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij",
|
||||||
|
ctx->klass.get_min_frame_count_gingerbread, ctx->library);
|
||||||
}
|
}
|
||||||
|
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library);
|
DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start,
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library);
|
ctx->library);
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library);
|
DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause,
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library);
|
ctx->library);
|
||||||
DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, 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 */
|
/* check that we have a combination of symbol that makes sense */
|
||||||
c = &ctx->klass;
|
c = &ctx->klass;
|
||||||
if(!(c->ctor &&
|
if (!(c->ctor && c->dtor && c->latency && c->check &&
|
||||||
c->dtor && c->latency && c->check &&
|
|
||||||
/* at least one way to get the minimum frame count to request. */
|
/* at least one way to get the minimum frame count to request. */
|
||||||
(c->get_min_frame_count ||
|
(c->get_min_frame_count || c->get_min_frame_count_gingerbread) &&
|
||||||
c->get_min_frame_count_gingerbread) &&
|
|
||||||
c->start && c->pause && c->get_position && c->set_marker_position)) {
|
c->start && c->pause && c->get_position && c->set_marker_position)) {
|
||||||
ALOG("Could not find all the symbols we need.");
|
ALOG("Could not find all the symbols we need.");
|
||||||
audiotrack_destroy(ctx);
|
audiotrack_destroy(ctx);
|
||||||
|
@ -234,14 +260,16 @@ audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||||
assert(ctx && max_channels);
|
assert(ctx && max_channels);
|
||||||
|
|
||||||
/* The android mixer handles up to two channels, see
|
/* The android mixer handles up to two channels, see
|
||||||
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
|
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67
|
||||||
|
*/
|
||||||
*max_channels = 2;
|
*max_channels = 2;
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
|
audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||||
|
uint32_t * latency_ms)
|
||||||
{
|
{
|
||||||
/* We always use the lowest latency possible when using this backend (see
|
/* We always use the lowest latency possible when using this backend (see
|
||||||
* audiotrack_stream_init), so this value is not going to be used. */
|
* audiotrack_stream_init), so this value is not going to be used. */
|
||||||
|
@ -276,15 +304,13 @@ audiotrack_destroy(cubeb * context)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream,
|
||||||
cubeb_devid input_device,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency,
|
unsigned int latency, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
cubeb_stream * stm;
|
cubeb_stream * stm;
|
||||||
int32_t channels;
|
int32_t channels;
|
||||||
|
@ -303,7 +329,8 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) {
|
if (audiotrack_get_min_frame_count(ctx, output_stream_params,
|
||||||
|
(int *)&min_frame_count)) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,21 +344,25 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
|
||||||
stm->params = *output_stream_params;
|
stm->params = *output_stream_params;
|
||||||
|
|
||||||
stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1);
|
stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1);
|
||||||
(*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad;
|
(*(uint32_t *)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) =
|
||||||
|
0xbaadbaad;
|
||||||
assert(stm->instance && "cubeb: EOM");
|
assert(stm->instance && "cubeb: EOM");
|
||||||
|
|
||||||
/* gingerbread uses old channel layout enum */
|
/* gingerbread uses old channel layout enum */
|
||||||
if (audiotrack_version_is_gingerbread(ctx)) {
|
if (audiotrack_version_is_gingerbread(ctx)) {
|
||||||
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy;
|
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy
|
||||||
|
: AUDIO_CHANNEL_OUT_MONO_Legacy;
|
||||||
} else {
|
} else {
|
||||||
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS;
|
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS
|
||||||
|
: AUDIO_CHANNEL_OUT_MONO_ICS;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate,
|
ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate,
|
||||||
AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
|
AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
|
||||||
audiotrack_refill, stm, 0, 0);
|
audiotrack_refill, stm, 0, 0);
|
||||||
|
|
||||||
assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad);
|
assert((*(uint32_t *)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE -
|
||||||
|
4)) == 0xbaadbaad);
|
||||||
|
|
||||||
if (ctx->klass.check(stm->instance)) {
|
if (ctx->klass.check(stm->instance)) {
|
||||||
ALOG("stream not initialized properly.");
|
ALOG("stream not initialized properly.");
|
||||||
|
@ -430,7 +461,6 @@ static struct cubeb_ops const audiotrack_ops = {
|
||||||
.stream_destroy = audiotrack_stream_destroy,
|
.stream_destroy = audiotrack_stream_destroy,
|
||||||
.stream_start = audiotrack_stream_start,
|
.stream_start = audiotrack_stream_start,
|
||||||
.stream_stop = audiotrack_stream_stop,
|
.stream_stop = audiotrack_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = audiotrack_stream_get_position,
|
.stream_get_position = audiotrack_stream_get_position,
|
||||||
.stream_get_latency = audiotrack_stream_get_latency,
|
.stream_get_latency = audiotrack_stream_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
@ -439,5 +469,4 @@ static struct cubeb_ops const audiotrack_ops = {
|
||||||
.stream_get_current_device = NULL,
|
.stream_get_current_device = NULL,
|
||||||
.stream_device_destroy = NULL,
|
.stream_device_destroy = NULL,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = NULL
|
.register_device_collection_changed = NULL};
|
||||||
};
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,24 +8,28 @@
|
||||||
*/
|
*/
|
||||||
#define _DEFAULT_SOURCE
|
#define _DEFAULT_SOURCE
|
||||||
#define _BSD_SOURCE
|
#define _BSD_SOURCE
|
||||||
#if !defined(__FreeBSD__) && !defined(__APPLE__)
|
#ifndef __FreeBSD__
|
||||||
#define _POSIX_SOURCE
|
#define _POSIX_SOURCE
|
||||||
#endif
|
#endif
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
#include "cubeb_resampler.h"
|
#include "cubeb_resampler.h"
|
||||||
#include "cubeb_utils.h"
|
#include "cubeb_utils.h"
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <jack/jack.h>
|
#include <jack/jack.h>
|
||||||
#include <jack/statistics.h>
|
#include <jack/statistics.h>
|
||||||
|
|
||||||
|
#ifdef DISABLE_LIBJACK_DLOPEN
|
||||||
|
#define WRAP(x) x
|
||||||
|
#else
|
||||||
|
#define WRAP(x) (*api_##x)
|
||||||
#define JACK_API_VISIT(X) \
|
#define JACK_API_VISIT(X) \
|
||||||
X(jack_activate) \
|
X(jack_activate) \
|
||||||
X(jack_client_close) \
|
X(jack_client_close) \
|
||||||
|
@ -49,6 +53,8 @@
|
||||||
|
|
||||||
#define IMPORT_FUNC(x) static decltype(x) * api_##x;
|
#define IMPORT_FUNC(x) static decltype(x) * api_##x;
|
||||||
JACK_API_VISIT(IMPORT_FUNC);
|
JACK_API_VISIT(IMPORT_FUNC);
|
||||||
|
#undef IMPORT_FUNC
|
||||||
|
#endif
|
||||||
|
|
||||||
#define JACK_DEFAULT_IN "JACK capture"
|
#define JACK_DEFAULT_IN "JACK capture"
|
||||||
#define JACK_DEFAULT_OUT "JACK playback"
|
#define JACK_DEFAULT_OUT "JACK playback"
|
||||||
|
@ -64,6 +70,12 @@ enum devstream {
|
||||||
DUPLEX,
|
DUPLEX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum cbjack_connect_ports_options {
|
||||||
|
CBJACK_CP_OPTIONS_NONE = 0x0,
|
||||||
|
CBJACK_CP_OPTIONS_SKIP_OUTPUT = 0x1,
|
||||||
|
CBJACK_CP_OPTIONS_SKIP_INPUT = 0x2,
|
||||||
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
s16ne_to_float(float * dst, const int16_t * src, size_t n)
|
s16ne_to_float(float * dst, const int16_t * src, size_t n)
|
||||||
{
|
{
|
||||||
|
@ -75,46 +87,72 @@ static void
|
||||||
float_to_s16ne(int16_t * dst, float * src, size_t n)
|
float_to_s16ne(int16_t * dst, float * src, size_t n)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
if (*src > 1.f) *src = 1.f;
|
if (*src > 1.f)
|
||||||
if (*src < -1.f) *src = -1.f;
|
*src = 1.f;
|
||||||
|
if (*src < -1.f)
|
||||||
|
*src = -1.f;
|
||||||
*(dst++) = (int16_t)((int16_t)(*(src++) * 32767));
|
*(dst++) = (int16_t)((int16_t)(*(src++) * 32767));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
/*static*/ int
|
||||||
/*static*/ int jack_init (cubeb ** context, char const * context_name);
|
jack_init(cubeb ** context, char const * context_name);
|
||||||
}
|
}
|
||||||
static char const * cbjack_get_backend_id(cubeb * context);
|
static char const *
|
||||||
static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
|
cbjack_get_backend_id(cubeb * context);
|
||||||
static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames);
|
static int
|
||||||
static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
|
cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
|
||||||
static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
|
static int
|
||||||
static void cbjack_destroy(cubeb * context);
|
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||||
static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch);
|
uint32_t * latency_frames);
|
||||||
static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes);
|
static int
|
||||||
static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes);
|
cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
|
||||||
static int cbjack_stream_device_destroy(cubeb_stream * stream,
|
static int
|
||||||
cubeb_device * device);
|
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
|
||||||
static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
|
static void
|
||||||
static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
cbjack_destroy(cubeb * context);
|
||||||
|
static void
|
||||||
|
cbjack_interleave_capture(cubeb_stream * stream, float ** in,
|
||||||
|
jack_nframes_t nframes, bool format_mismatch);
|
||||||
|
static void
|
||||||
|
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream,
|
||||||
|
short ** bufs_in, float ** bufs_out,
|
||||||
|
jack_nframes_t nframes);
|
||||||
|
static void
|
||||||
|
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream,
|
||||||
|
float ** bufs_in, float ** bufs_out,
|
||||||
|
jack_nframes_t nframes);
|
||||||
|
static int
|
||||||
|
cbjack_stream_device_destroy(cubeb_stream * stream, cubeb_device * device);
|
||||||
|
static int
|
||||||
|
cbjack_stream_get_current_device(cubeb_stream * stm,
|
||||||
|
cubeb_device ** const device);
|
||||||
|
static int
|
||||||
|
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
cubeb_device_collection * collection);
|
cubeb_device_collection * collection);
|
||||||
static int cbjack_device_collection_destroy(cubeb * context,
|
static int
|
||||||
|
cbjack_device_collection_destroy(cubeb * context,
|
||||||
cubeb_device_collection * collection);
|
cubeb_device_collection * collection);
|
||||||
static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
static int
|
||||||
cubeb_devid input_device,
|
cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_data_callback data_callback,
|
||||||
cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr);
|
||||||
void * user_ptr);
|
static void
|
||||||
static void cbjack_stream_destroy(cubeb_stream * stream);
|
cbjack_stream_destroy(cubeb_stream * stream);
|
||||||
static int cbjack_stream_start(cubeb_stream * stream);
|
static int
|
||||||
static int cbjack_stream_stop(cubeb_stream * stream);
|
cbjack_stream_start(cubeb_stream * stream);
|
||||||
static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
static int
|
||||||
static int cbjack_stream_set_volume(cubeb_stream * stm, float volume);
|
cbjack_stream_stop(cubeb_stream * stream);
|
||||||
|
static int
|
||||||
|
cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
|
||||||
|
static int
|
||||||
|
cbjack_stream_set_volume(cubeb_stream * stm, float volume);
|
||||||
|
|
||||||
static struct cubeb_ops const cbjack_ops = {
|
static struct cubeb_ops const cbjack_ops = {
|
||||||
.init = jack_init,
|
.init = jack_init,
|
||||||
|
@ -129,7 +167,6 @@ static struct cubeb_ops const cbjack_ops = {
|
||||||
.stream_destroy = cbjack_stream_destroy,
|
.stream_destroy = cbjack_stream_destroy,
|
||||||
.stream_start = cbjack_stream_start,
|
.stream_start = cbjack_stream_start,
|
||||||
.stream_stop = cbjack_stream_stop,
|
.stream_stop = cbjack_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = cbjack_stream_get_position,
|
.stream_get_position = cbjack_stream_get_position,
|
||||||
.stream_get_latency = cbjack_get_latency,
|
.stream_get_latency = cbjack_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
@ -138,8 +175,7 @@ static struct cubeb_ops const cbjack_ops = {
|
||||||
.stream_get_current_device = cbjack_stream_get_current_device,
|
.stream_get_current_device = cbjack_stream_get_current_device,
|
||||||
.stream_device_destroy = cbjack_stream_device_destroy,
|
.stream_device_destroy = cbjack_stream_device_destroy,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = NULL
|
.register_device_collection_changed = NULL};
|
||||||
};
|
|
||||||
|
|
||||||
struct cubeb_stream {
|
struct cubeb_stream {
|
||||||
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
/* Note: Must match cubeb_stream layout in cubeb.c. */
|
||||||
|
@ -205,6 +241,7 @@ struct cubeb {
|
||||||
static int
|
static int
|
||||||
load_jack_lib(cubeb * context)
|
load_jack_lib(cubeb * context)
|
||||||
{
|
{
|
||||||
|
#ifndef DISABLE_LIBJACK_DLOPEN
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY);
|
context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY);
|
||||||
context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY);
|
context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY);
|
||||||
|
@ -235,45 +272,48 @@ load_jack_lib(cubeb * context)
|
||||||
|
|
||||||
JACK_API_VISIT(LOAD);
|
JACK_API_VISIT(LOAD);
|
||||||
#undef LOAD
|
#undef LOAD
|
||||||
|
#endif
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cbjack_connect_port_out (cubeb_stream * stream, const size_t out_port, const char * const phys_in_port)
|
cbjack_connect_port_out(cubeb_stream * stream, const size_t out_port,
|
||||||
|
const char * const phys_in_port)
|
||||||
{
|
{
|
||||||
const char *src_port = api_jack_port_name (stream->output_ports[out_port]);
|
const char * src_port = WRAP(jack_port_name)(stream->output_ports[out_port]);
|
||||||
|
|
||||||
api_jack_connect (stream->context->jack_client, src_port, phys_in_port);
|
WRAP(jack_connect)(stream->context->jack_client, src_port, phys_in_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cbjack_connect_port_in (cubeb_stream * stream, const char * const phys_out_port, size_t in_port)
|
cbjack_connect_port_in(cubeb_stream * stream, const char * const phys_out_port,
|
||||||
|
size_t in_port)
|
||||||
{
|
{
|
||||||
const char *src_port = api_jack_port_name (stream->input_ports[in_port]);
|
const char * src_port = WRAP(jack_port_name)(stream->input_ports[in_port]);
|
||||||
|
|
||||||
api_jack_connect (stream->context->jack_client, phys_out_port, src_port);
|
WRAP(jack_connect)(stream->context->jack_client, phys_out_port, src_port);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cbjack_connect_ports (cubeb_stream * stream)
|
cbjack_connect_ports(cubeb_stream * stream,
|
||||||
|
enum cbjack_connect_ports_options options)
|
||||||
{
|
{
|
||||||
int r = CUBEB_ERROR;
|
int r = CUBEB_ERROR;
|
||||||
const char ** phys_in_ports = api_jack_get_ports (stream->context->jack_client,
|
const char ** phys_in_ports =
|
||||||
NULL, NULL,
|
WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
|
||||||
JackPortIsInput
|
JackPortIsInput | JackPortIsPhysical);
|
||||||
| JackPortIsPhysical);
|
const char ** phys_out_ports =
|
||||||
const char ** phys_out_ports = api_jack_get_ports (stream->context->jack_client,
|
WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
|
||||||
NULL, NULL,
|
JackPortIsOutput | JackPortIsPhysical);
|
||||||
JackPortIsOutput
|
|
||||||
| JackPortIsPhysical);
|
|
||||||
|
|
||||||
if (phys_in_ports == NULL || *phys_in_ports == NULL) {
|
if (phys_in_ports == NULL || *phys_in_ports == NULL ||
|
||||||
|
options & CBJACK_CP_OPTIONS_SKIP_OUTPUT) {
|
||||||
goto skipplayback;
|
goto skipplayback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connect outputs to playback
|
// Connect outputs to playback
|
||||||
for (unsigned int c = 0; c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
|
for (unsigned int c = 0;
|
||||||
|
c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
|
||||||
cbjack_connect_port_out(stream, c, phys_in_ports[c]);
|
cbjack_connect_port_out(stream, c, phys_in_ports[c]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -285,20 +325,22 @@ cbjack_connect_ports (cubeb_stream * stream)
|
||||||
r = CUBEB_OK;
|
r = CUBEB_OK;
|
||||||
|
|
||||||
skipplayback:
|
skipplayback:
|
||||||
if (phys_out_ports == NULL || *phys_out_ports == NULL) {
|
if (phys_out_ports == NULL || *phys_out_ports == NULL ||
|
||||||
|
options & CBJACK_CP_OPTIONS_SKIP_INPUT) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
// Connect inputs to capture
|
// Connect inputs to capture
|
||||||
for (unsigned int c = 0; c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
|
for (unsigned int c = 0;
|
||||||
|
c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
|
||||||
cbjack_connect_port_in(stream, phys_out_ports[c], c);
|
cbjack_connect_port_in(stream, phys_out_ports[c], c);
|
||||||
}
|
}
|
||||||
r = CUBEB_OK;
|
r = CUBEB_OK;
|
||||||
end:
|
end:
|
||||||
if (phys_out_ports) {
|
if (phys_out_ports) {
|
||||||
api_jack_free(phys_out_ports);
|
WRAP(jack_free)(phys_out_ports);
|
||||||
}
|
}
|
||||||
if (phys_in_ports) {
|
if (phys_in_ports) {
|
||||||
api_jack_free(phys_in_ports);
|
WRAP(jack_free)(phys_in_ports);
|
||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -308,8 +350,9 @@ cbjack_xrun_callback(void * arg)
|
||||||
{
|
{
|
||||||
cubeb * ctx = (cubeb *)arg;
|
cubeb * ctx = (cubeb *)arg;
|
||||||
|
|
||||||
float delay = api_jack_get_xrun_delayed_usecs(ctx->jack_client);
|
float delay = WRAP(jack_get_xrun_delayed_usecs)(ctx->jack_client);
|
||||||
float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) / ctx->jack_buffer_size);
|
float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) /
|
||||||
|
ctx->jack_buffer_size);
|
||||||
|
|
||||||
ctx->jack_xruns += (unsigned int)fragments;
|
ctx->jack_xruns += (unsigned int)fragments;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -332,7 +375,8 @@ cbjack_graph_order_callback(void * arg)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
for (i = 0; i < (int)stm->out_params.channels; ++i) {
|
for (i = 0; i < (int)stm->out_params.channels; ++i) {
|
||||||
api_jack_port_get_latency_range(stm->output_ports[i], JackPlaybackLatency, &latency_range);
|
WRAP(jack_port_get_latency_range)
|
||||||
|
(stm->output_ports[i], JackPlaybackLatency, &latency_range);
|
||||||
port_latency = latency_range.max;
|
port_latency = latency_range.max;
|
||||||
if (port_latency > max_latency)
|
if (port_latency > max_latency)
|
||||||
max_latency = port_latency;
|
max_latency = port_latency;
|
||||||
|
@ -373,12 +417,14 @@ cbjack_process(jack_nframes_t nframes, void * arg)
|
||||||
if (stm->devs & OUT_ONLY) {
|
if (stm->devs & OUT_ONLY) {
|
||||||
// get jack output buffers
|
// get jack output buffers
|
||||||
for (i = 0; i < (int)stm->out_params.channels; i++)
|
for (i = 0; i < (int)stm->out_params.channels; i++)
|
||||||
bufs_out[i] = (float*)api_jack_port_get_buffer(stm->output_ports[i], nframes);
|
bufs_out[i] =
|
||||||
|
(float *)WRAP(jack_port_get_buffer)(stm->output_ports[i], nframes);
|
||||||
}
|
}
|
||||||
if (stm->devs & IN_ONLY) {
|
if (stm->devs & IN_ONLY) {
|
||||||
// get jack input buffers
|
// get jack input buffers
|
||||||
for (i = 0; i < (int)stm->in_params.channels; i++)
|
for (i = 0; i < (int)stm->in_params.channels; i++)
|
||||||
bufs_in[i] = (float*)api_jack_port_get_buffer(stm->input_ports[i], nframes);
|
bufs_in[i] =
|
||||||
|
(float *)WRAP(jack_port_get_buffer)(stm->input_ports[i], nframes);
|
||||||
}
|
}
|
||||||
if (stm->pause) {
|
if (stm->pause) {
|
||||||
// paused, play silence on output
|
// paused, play silence on output
|
||||||
|
@ -404,31 +450,38 @@ cbjack_process(jack_nframes_t nframes, void * arg)
|
||||||
// try to lock stream mutex
|
// try to lock stream mutex
|
||||||
if (pthread_mutex_trylock(&stm->mutex) == 0) {
|
if (pthread_mutex_trylock(&stm->mutex) == 0) {
|
||||||
|
|
||||||
int16_t *in_s16ne = stm->context->in_resampled_interleaved_buffer_s16ne;
|
int16_t * in_s16ne =
|
||||||
|
stm->context->in_resampled_interleaved_buffer_s16ne;
|
||||||
float * in_float = stm->context->in_resampled_interleaved_buffer_float;
|
float * in_float = stm->context->in_resampled_interleaved_buffer_float;
|
||||||
|
|
||||||
// unpaused, play audio
|
// unpaused, play audio
|
||||||
if (stm->devs == DUPLEX) {
|
if (stm->devs == DUPLEX) {
|
||||||
if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
|
if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
|
||||||
cbjack_interleave_capture(stm, bufs_in, nframes, true);
|
cbjack_interleave_capture(stm, bufs_in, nframes, true);
|
||||||
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out, nframes);
|
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out,
|
||||||
|
nframes);
|
||||||
} else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
} else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||||
cbjack_interleave_capture(stm, bufs_in, nframes, false);
|
cbjack_interleave_capture(stm, bufs_in, nframes, false);
|
||||||
cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out, nframes);
|
cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out,
|
||||||
|
nframes);
|
||||||
}
|
}
|
||||||
} else if (stm->devs == IN_ONLY) {
|
} else if (stm->devs == IN_ONLY) {
|
||||||
if (stm->in_params.format == CUBEB_SAMPLE_S16NE) {
|
if (stm->in_params.format == CUBEB_SAMPLE_S16NE) {
|
||||||
cbjack_interleave_capture(stm, bufs_in, nframes, true);
|
cbjack_interleave_capture(stm, bufs_in, nframes, true);
|
||||||
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr, nframes);
|
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr,
|
||||||
|
nframes);
|
||||||
} else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
} else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||||
cbjack_interleave_capture(stm, bufs_in, nframes, false);
|
cbjack_interleave_capture(stm, bufs_in, nframes, false);
|
||||||
cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr, nframes);
|
cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr,
|
||||||
|
nframes);
|
||||||
}
|
}
|
||||||
} else if (stm->devs == OUT_ONLY) {
|
} else if (stm->devs == OUT_ONLY) {
|
||||||
if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
|
if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
|
||||||
cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out, nframes);
|
cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out,
|
||||||
|
nframes);
|
||||||
} else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
} else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||||
cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, nframes);
|
cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out,
|
||||||
|
nframes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// unlock stream mutex
|
// unlock stream mutex
|
||||||
|
@ -461,7 +514,9 @@ cbjack_process(jack_nframes_t nframes, void * arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes)
|
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in,
|
||||||
|
float ** bufs_out,
|
||||||
|
jack_nframes_t nframes)
|
||||||
{
|
{
|
||||||
float * out_interleaved_buffer = nullptr;
|
float * out_interleaved_buffer = nullptr;
|
||||||
|
|
||||||
|
@ -472,20 +527,24 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl
|
||||||
long done_frames = 0;
|
long done_frames = 0;
|
||||||
long input_frames_count = (in != NULL) ? nframes : 0;
|
long input_frames_count = (in != NULL) ? nframes : 0;
|
||||||
|
|
||||||
done_frames = cubeb_resampler_fill(stream->resampler,
|
done_frames = cubeb_resampler_fill(
|
||||||
inptr,
|
stream->resampler, inptr, &input_frames_count,
|
||||||
&input_frames_count,
|
(bufs_out != NULL)
|
||||||
(bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_float : NULL,
|
? stream->context->out_resampled_interleaved_buffer_float
|
||||||
|
: NULL,
|
||||||
needed_frames);
|
needed_frames);
|
||||||
|
|
||||||
out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;
|
out_interleaved_buffer =
|
||||||
|
stream->context->out_resampled_interleaved_buffer_float;
|
||||||
|
|
||||||
if (outptr) {
|
if (outptr) {
|
||||||
// convert interleaved output buffers to contiguous buffers
|
// convert interleaved output buffers to contiguous buffers
|
||||||
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
|
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
|
||||||
float * buffer = bufs_out[c];
|
float * buffer = bufs_out[c];
|
||||||
for (long f = 0; f < done_frames; f++) {
|
for (long f = 0; f < done_frames; f++) {
|
||||||
buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
|
buffer[f] =
|
||||||
|
out_interleaved_buffer[(f * stream->out_params.channels) + c] *
|
||||||
|
stream->volume;
|
||||||
}
|
}
|
||||||
if (done_frames < needed_frames) {
|
if (done_frames < needed_frames) {
|
||||||
// draining
|
// draining
|
||||||
|
@ -519,7 +578,9 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes)
|
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in,
|
||||||
|
float ** bufs_out,
|
||||||
|
jack_nframes_t nframes)
|
||||||
{
|
{
|
||||||
float * out_interleaved_buffer = nullptr;
|
float * out_interleaved_buffer = nullptr;
|
||||||
|
|
||||||
|
@ -530,22 +591,28 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl
|
||||||
long done_frames = 0;
|
long done_frames = 0;
|
||||||
long input_frames_count = (in != NULL) ? nframes : 0;
|
long input_frames_count = (in != NULL) ? nframes : 0;
|
||||||
|
|
||||||
done_frames = cubeb_resampler_fill(stream->resampler,
|
done_frames = cubeb_resampler_fill(
|
||||||
inptr,
|
stream->resampler, inptr, &input_frames_count,
|
||||||
&input_frames_count,
|
(bufs_out != NULL)
|
||||||
(bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_s16ne : NULL,
|
? stream->context->out_resampled_interleaved_buffer_s16ne
|
||||||
|
: NULL,
|
||||||
needed_frames);
|
needed_frames);
|
||||||
|
|
||||||
s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, stream->context->out_resampled_interleaved_buffer_s16ne, done_frames * stream->out_params.channels);
|
s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float,
|
||||||
|
stream->context->out_resampled_interleaved_buffer_s16ne,
|
||||||
|
done_frames * stream->out_params.channels);
|
||||||
|
|
||||||
out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;
|
out_interleaved_buffer =
|
||||||
|
stream->context->out_resampled_interleaved_buffer_float;
|
||||||
|
|
||||||
if (outptr) {
|
if (outptr) {
|
||||||
// convert interleaved output buffers to contiguous buffers
|
// convert interleaved output buffers to contiguous buffers
|
||||||
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
|
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
|
||||||
float * buffer = bufs_out[c];
|
float * buffer = bufs_out[c];
|
||||||
for (long f = 0; f < done_frames; f++) {
|
for (long f = 0; f < done_frames; f++) {
|
||||||
buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
|
buffer[f] =
|
||||||
|
out_interleaved_buffer[(f * stream->out_params.channels) + c] *
|
||||||
|
stream->volume;
|
||||||
}
|
}
|
||||||
if (done_frames < needed_frames) {
|
if (done_frames < needed_frames) {
|
||||||
// draining
|
// draining
|
||||||
|
@ -579,20 +646,25 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch)
|
cbjack_interleave_capture(cubeb_stream * stream, float ** in,
|
||||||
|
jack_nframes_t nframes, bool format_mismatch)
|
||||||
{
|
{
|
||||||
float * in_buffer = stream->context->in_float_interleaved_buffer;
|
float * in_buffer = stream->context->in_float_interleaved_buffer;
|
||||||
|
|
||||||
for (unsigned int c = 0; c < stream->in_params.channels; c++) {
|
for (unsigned int c = 0; c < stream->in_params.channels; c++) {
|
||||||
for (long f = 0; f < nframes; f++) {
|
for (long f = 0; f < nframes; f++) {
|
||||||
in_buffer[(f * stream->in_params.channels) + c] = in[c][f] * stream->volume;
|
in_buffer[(f * stream->in_params.channels) + c] =
|
||||||
|
in[c][f] * stream->volume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (format_mismatch) {
|
if (format_mismatch) {
|
||||||
float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne, in_buffer, nframes * stream->in_params.channels);
|
float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne,
|
||||||
|
in_buffer, nframes * stream->in_params.channels);
|
||||||
} else {
|
} else {
|
||||||
memset(stream->context->in_resampled_interleaved_buffer_float, 0, (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
|
memset(stream->context->in_resampled_interleaved_buffer_float, 0,
|
||||||
memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer, (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
|
(FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
|
||||||
|
memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer,
|
||||||
|
(FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,8 +691,8 @@ jack_init (cubeb ** context, char const * context_name)
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
api_jack_set_error_function(silent_jack_error_callback);
|
WRAP(jack_set_error_function)(silent_jack_error_callback);
|
||||||
api_jack_set_info_function(silent_jack_error_callback);
|
WRAP(jack_set_info_function)(silent_jack_error_callback);
|
||||||
|
|
||||||
ctx->ops = &cbjack_ops;
|
ctx->ops = &cbjack_ops;
|
||||||
|
|
||||||
|
@ -633,9 +705,8 @@ jack_init (cubeb ** context, char const * context_name)
|
||||||
if (context_name)
|
if (context_name)
|
||||||
jack_client_name = context_name;
|
jack_client_name = context_name;
|
||||||
|
|
||||||
ctx->jack_client = api_jack_client_open(jack_client_name,
|
ctx->jack_client =
|
||||||
JackNoStartServer,
|
WRAP(jack_client_open)(jack_client_name, JackNoStartServer, NULL);
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (ctx->jack_client == NULL) {
|
if (ctx->jack_client == NULL) {
|
||||||
cbjack_destroy(ctx);
|
cbjack_destroy(ctx);
|
||||||
|
@ -644,16 +715,17 @@ jack_init (cubeb ** context, char const * context_name)
|
||||||
|
|
||||||
ctx->jack_xruns = 0;
|
ctx->jack_xruns = 0;
|
||||||
|
|
||||||
api_jack_set_process_callback (ctx->jack_client, cbjack_process, ctx);
|
WRAP(jack_set_process_callback)(ctx->jack_client, cbjack_process, ctx);
|
||||||
api_jack_set_xrun_callback (ctx->jack_client, cbjack_xrun_callback, ctx);
|
WRAP(jack_set_xrun_callback)(ctx->jack_client, cbjack_xrun_callback, ctx);
|
||||||
api_jack_set_graph_order_callback (ctx->jack_client, cbjack_graph_order_callback, ctx);
|
WRAP(jack_set_graph_order_callback)
|
||||||
|
(ctx->jack_client, cbjack_graph_order_callback, ctx);
|
||||||
|
|
||||||
if (api_jack_activate (ctx->jack_client)) {
|
if (WRAP(jack_activate)(ctx->jack_client)) {
|
||||||
cbjack_destroy(ctx);
|
cbjack_destroy(ctx);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->jack_sample_rate = api_jack_get_sample_rate(ctx->jack_client);
|
ctx->jack_sample_rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
|
||||||
ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate;
|
ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate;
|
||||||
|
|
||||||
ctx->active = true;
|
ctx->active = true;
|
||||||
|
@ -683,7 +755,8 @@ cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, uint32_t * latency_ms)
|
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/,
|
||||||
|
uint32_t * latency_ms)
|
||||||
{
|
{
|
||||||
*latency_ms = ctx->jack_latency;
|
*latency_ms = ctx->jack_latency;
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
|
@ -693,18 +766,17 @@ static int
|
||||||
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
||||||
{
|
{
|
||||||
if (!ctx->jack_client) {
|
if (!ctx->jack_client) {
|
||||||
jack_client_t * testclient = api_jack_client_open("test-samplerate",
|
jack_client_t * testclient =
|
||||||
JackNoStartServer,
|
WRAP(jack_client_open)("test-samplerate", JackNoStartServer, NULL);
|
||||||
NULL);
|
|
||||||
if (!testclient) {
|
if (!testclient) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
*rate = api_jack_get_sample_rate(testclient);
|
*rate = WRAP(jack_get_sample_rate)(testclient);
|
||||||
api_jack_client_close(testclient);
|
WRAP(jack_client_close)(testclient);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
*rate = api_jack_get_sample_rate(ctx->jack_client);
|
*rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
|
||||||
}
|
}
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
@ -715,7 +787,7 @@ cbjack_destroy(cubeb * context)
|
||||||
context->active = false;
|
context->active = false;
|
||||||
|
|
||||||
if (context->jack_client != NULL)
|
if (context->jack_client != NULL)
|
||||||
api_jack_client_close (context->jack_client);
|
WRAP(jack_client_close)(context->jack_client);
|
||||||
|
|
||||||
if (context->libjack)
|
if (context->libjack)
|
||||||
dlclose(context->libjack);
|
dlclose(context->libjack);
|
||||||
|
@ -738,30 +810,27 @@ context_alloc_stream(cubeb * context, char const * stream_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_devid input_device,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int /*latency_frames*/,
|
unsigned int /*latency_frames*/,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_data_callback data_callback,
|
||||||
cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
int stream_actual_rate = 0;
|
int stream_actual_rate = 0;
|
||||||
int jack_rate = api_jack_get_sample_rate(context->jack_client);
|
int jack_rate = WRAP(jack_get_sample_rate)(context->jack_client);
|
||||||
|
|
||||||
if (output_stream_params
|
if (output_stream_params &&
|
||||||
&& (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
|
(output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
|
||||||
output_stream_params->format != CUBEB_SAMPLE_S16NE)
|
output_stream_params->format != CUBEB_SAMPLE_S16NE)) {
|
||||||
) {
|
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_stream_params
|
if (input_stream_params &&
|
||||||
&& (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
|
(input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
|
||||||
input_stream_params->format != CUBEB_SAMPLE_S16NE)
|
input_stream_params->format != CUBEB_SAMPLE_S16NE)) {
|
||||||
) {
|
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -771,8 +840,10 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loopback is unsupported
|
// Loopback is unsupported
|
||||||
if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
|
if ((input_stream_params &&
|
||||||
(output_stream_params && (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
|
(input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
|
||||||
|
(output_stream_params &&
|
||||||
|
(output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -841,7 +912,7 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
||||||
stm->state_callback = state_callback;
|
stm->state_callback = state_callback;
|
||||||
stm->position = 0;
|
stm->position = 0;
|
||||||
stm->volume = 1.0f;
|
stm->volume = 1.0f;
|
||||||
context->jack_buffer_size = api_jack_get_buffer_size(context->jack_client);
|
context->jack_buffer_size = WRAP(jack_get_buffer_size)(context->jack_client);
|
||||||
context->fragment_size = context->jack_buffer_size;
|
context->fragment_size = context->jack_buffer_size;
|
||||||
|
|
||||||
if (stm->devs == NONE) {
|
if (stm->devs == NONE) {
|
||||||
|
@ -852,29 +923,20 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
||||||
stm->resampler = NULL;
|
stm->resampler = NULL;
|
||||||
|
|
||||||
if (stm->devs == DUPLEX) {
|
if (stm->devs == DUPLEX) {
|
||||||
stm->resampler = cubeb_resampler_create(stm,
|
stm->resampler = cubeb_resampler_create(
|
||||||
&stm->in_params,
|
stm, &stm->in_params, &stm->out_params, stream_actual_rate,
|
||||||
&stm->out_params,
|
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
||||||
stream_actual_rate,
|
CUBEB_RESAMPLER_RECLOCK_NONE);
|
||||||
stm->data_callback,
|
|
||||||
stm->user_ptr,
|
|
||||||
CUBEB_RESAMPLER_QUALITY_DESKTOP);
|
|
||||||
} else if (stm->devs == IN_ONLY) {
|
} else if (stm->devs == IN_ONLY) {
|
||||||
stm->resampler = cubeb_resampler_create(stm,
|
stm->resampler = cubeb_resampler_create(
|
||||||
&stm->in_params,
|
stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback,
|
||||||
nullptr,
|
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
||||||
stream_actual_rate,
|
CUBEB_RESAMPLER_RECLOCK_NONE);
|
||||||
stm->data_callback,
|
|
||||||
stm->user_ptr,
|
|
||||||
CUBEB_RESAMPLER_QUALITY_DESKTOP);
|
|
||||||
} else if (stm->devs == OUT_ONLY) {
|
} else if (stm->devs == OUT_ONLY) {
|
||||||
stm->resampler = cubeb_resampler_create(stm,
|
stm->resampler = cubeb_resampler_create(
|
||||||
nullptr,
|
stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback,
|
||||||
&stm->out_params,
|
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
|
||||||
stream_actual_rate,
|
CUBEB_RESAMPLER_RECLOCK_NONE);
|
||||||
stm->data_callback,
|
|
||||||
stm->user_ptr,
|
|
||||||
CUBEB_RESAMPLER_QUALITY_DESKTOP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
|
@ -887,11 +949,18 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
||||||
for (unsigned int c = 0; c < stm->out_params.channels; c++) {
|
for (unsigned int c = 0; c < stm->out_params.channels; c++) {
|
||||||
char portname[256];
|
char portname[256];
|
||||||
snprintf(portname, 255, "%s_out_%d", stm->stream_name, c);
|
snprintf(portname, 255, "%s_out_%d", stm->stream_name, c);
|
||||||
stm->output_ports[c] = api_jack_port_register(stm->context->jack_client,
|
stm->output_ports[c] = WRAP(jack_port_register)(
|
||||||
portname,
|
stm->context->jack_client, portname, JACK_DEFAULT_AUDIO_TYPE,
|
||||||
JACK_DEFAULT_AUDIO_TYPE,
|
JackPortIsOutput, 0);
|
||||||
JackPortIsOutput,
|
if (!(output_stream_params->prefs &
|
||||||
0);
|
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
|
||||||
|
if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_INPUT) !=
|
||||||
|
CUBEB_OK) {
|
||||||
|
pthread_mutex_unlock(&stm->mutex);
|
||||||
|
cbjack_stream_destroy(stm);
|
||||||
|
return CUBEB_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -899,21 +968,20 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
|
||||||
for (unsigned int c = 0; c < stm->in_params.channels; c++) {
|
for (unsigned int c = 0; c < stm->in_params.channels; c++) {
|
||||||
char portname[256];
|
char portname[256];
|
||||||
snprintf(portname, 255, "%s_in_%d", stm->stream_name, c);
|
snprintf(portname, 255, "%s_in_%d", stm->stream_name, c);
|
||||||
stm->input_ports[c] = api_jack_port_register(stm->context->jack_client,
|
stm->input_ports[c] =
|
||||||
portname,
|
WRAP(jack_port_register)(stm->context->jack_client, portname,
|
||||||
JACK_DEFAULT_AUDIO_TYPE,
|
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
|
||||||
JackPortIsInput,
|
if (!(input_stream_params->prefs &
|
||||||
0);
|
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
|
||||||
}
|
if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_OUTPUT) !=
|
||||||
}
|
CUBEB_OK) {
|
||||||
|
|
||||||
if (!input_stream_params->prefs & CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT) {
|
|
||||||
if (cbjack_connect_ports(stm) != CUBEB_OK) {
|
|
||||||
pthread_mutex_unlock(&stm->mutex);
|
pthread_mutex_unlock(&stm->mutex);
|
||||||
cbjack_stream_destroy(stm);
|
cbjack_stream_destroy(stm);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
*stream = stm;
|
*stream = stm;
|
||||||
|
|
||||||
|
@ -933,7 +1001,8 @@ cbjack_stream_destroy(cubeb_stream * stream)
|
||||||
if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) {
|
if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) {
|
||||||
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
|
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
|
||||||
if (stream->output_ports[c]) {
|
if (stream->output_ports[c]) {
|
||||||
api_jack_port_unregister (stream->context->jack_client, stream->output_ports[c]);
|
WRAP(jack_port_unregister)
|
||||||
|
(stream->context->jack_client, stream->output_ports[c]);
|
||||||
stream->output_ports[c] = NULL;
|
stream->output_ports[c] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -942,7 +1011,8 @@ cbjack_stream_destroy(cubeb_stream * stream)
|
||||||
if (stream->devs == DUPLEX || stream->devs == IN_ONLY) {
|
if (stream->devs == DUPLEX || stream->devs == IN_ONLY) {
|
||||||
for (unsigned int c = 0; c < stream->in_params.channels; c++) {
|
for (unsigned int c = 0; c < stream->in_params.channels; c++) {
|
||||||
if (stream->input_ports[c]) {
|
if (stream->input_ports[c]) {
|
||||||
api_jack_port_unregister (stream->context->jack_client, stream->input_ports[c]);
|
WRAP(jack_port_unregister)
|
||||||
|
(stream->context->jack_client, stream->input_ports[c]);
|
||||||
stream->input_ports[c] = NULL;
|
stream->input_ports[c] = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -987,7 +1057,8 @@ cbjack_stream_set_volume(cubeb_stream * stm, float volume)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
|
cbjack_stream_get_current_device(cubeb_stream * stm,
|
||||||
|
cubeb_device ** const device)
|
||||||
{
|
{
|
||||||
*device = (cubeb_device *)calloc(1, sizeof(cubeb_device));
|
*device = (cubeb_device *)calloc(1, sizeof(cubeb_device));
|
||||||
if (*device == NULL)
|
if (*device == NULL)
|
||||||
|
@ -1012,8 +1083,7 @@ cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const devic
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
|
cbjack_stream_device_destroy(cubeb_stream * /*stream*/, cubeb_device * device)
|
||||||
cubeb_device * device)
|
|
||||||
{
|
{
|
||||||
if (device->input_name)
|
if (device->input_name)
|
||||||
free(device->input_name);
|
free(device->input_name);
|
||||||
|
|
|
@ -4,15 +4,15 @@
|
||||||
* This program is made available under an ISC-style license. See the
|
* This program is made available under an ISC-style license. See the
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
#include <math.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <math.h>
|
|
||||||
#include <sys/fmutex.h>
|
#include <sys/fmutex.h>
|
||||||
|
|
||||||
#include <kai.h>
|
#include <kai.h>
|
||||||
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
|
||||||
/* We don't support more than 2 channels in KAI */
|
/* We don't support more than 2 channels in KAI */
|
||||||
#define MAX_CHANNELS 2
|
#define MAX_CHANNELS 2
|
||||||
|
@ -59,7 +59,8 @@ bytes_to_frames(long bytes, cubeb_stream_params params)
|
||||||
return bytes / 2 / params.channels; /* 2 bytes per frame */
|
return bytes / 2 / params.channels; /* 2 bytes per frame */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kai_destroy(cubeb * ctx);
|
static void
|
||||||
|
kai_destroy(cubeb * ctx);
|
||||||
|
|
||||||
/*static*/ int
|
/*static*/ int
|
||||||
kai_init(cubeb ** context, char const * context_name)
|
kai_init(cubeb ** context, char const * context_name)
|
||||||
|
@ -121,8 +122,7 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
|
||||||
float soft_volume;
|
float soft_volume;
|
||||||
int elements = len / sizeof(int16_t);
|
int elements = len / sizeof(int16_t);
|
||||||
|
|
||||||
p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE
|
p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE ? stm->float_buffer : buffer;
|
||||||
? stm->float_buffer : buffer;
|
|
||||||
|
|
||||||
wanted_frames = bytes_to_frames(len, stm->params);
|
wanted_frames = bytes_to_frames(len, stm->params);
|
||||||
frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames);
|
frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames);
|
||||||
|
@ -149,12 +149,12 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
|
||||||
return frames_to_bytes(frames, stm->params);
|
return frames_to_bytes(frames, stm->params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void kai_stream_destroy(cubeb_stream * stm);
|
static void
|
||||||
|
kai_stream_destroy(cubeb_stream * stm);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
kai_stream_init(cubeb * context, cubeb_stream ** stream,
|
kai_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
char const * stream_name,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
|
@ -328,8 +328,8 @@ kai_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||||
{
|
{
|
||||||
/* Out of buffers, one is being played, the others are being filled.
|
/* Out of buffers, one is being played, the others are being filled.
|
||||||
So there is as much latency as total buffers - 1. */
|
So there is as much latency as total buffers - 1. */
|
||||||
*latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params)
|
*latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params) *
|
||||||
* (stm->spec.ulNumBuffers - 1);
|
(stm->spec.ulNumBuffers - 1);
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
@ -358,7 +358,6 @@ static struct cubeb_ops const kai_ops = {
|
||||||
/*.stream_destroy =*/kai_stream_destroy,
|
/*.stream_destroy =*/kai_stream_destroy,
|
||||||
/*.stream_start =*/kai_stream_start,
|
/*.stream_start =*/kai_stream_start,
|
||||||
/*.stream_stop =*/kai_stream_stop,
|
/*.stream_stop =*/kai_stream_stop,
|
||||||
/*.stream_reset_default_device =*/ NULL,
|
|
||||||
/*.stream_get_position =*/kai_stream_get_position,
|
/*.stream_get_position =*/kai_stream_get_position,
|
||||||
/*.stream_get_latency = */ kai_stream_get_latency,
|
/*.stream_get_latency = */ kai_stream_get_latency,
|
||||||
/*.stream_get_input_latency = */ NULL,
|
/*.stream_get_input_latency = */ NULL,
|
||||||
|
@ -367,5 +366,4 @@ static struct cubeb_ops const kai_ops = {
|
||||||
/*.stream_get_current_device =*/NULL,
|
/*.stream_get_current_device =*/NULL,
|
||||||
/*.stream_device_destroy =*/NULL,
|
/*.stream_device_destroy =*/NULL,
|
||||||
/*.stream_register_device_changed_callback=*/NULL,
|
/*.stream_register_device_changed_callback=*/NULL,
|
||||||
/*.register_device_collection_changed=*/ NULL
|
/*.register_device_collection_changed=*/NULL};
|
||||||
};
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "cubeb_log.h"
|
#include "cubeb_log.h"
|
||||||
#include "cubeb_ringbuffer.h"
|
#include "cubeb_ringbuffer.h"
|
||||||
|
#include "cubeb_tracing.h"
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
@ -31,13 +32,9 @@ const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
|
||||||
* null-terminated.
|
* null-terminated.
|
||||||
* This class should not use system calls or other potentially blocking code.
|
* This class should not use system calls or other potentially blocking code.
|
||||||
*/
|
*/
|
||||||
class cubeb_log_message
|
class cubeb_log_message {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
cubeb_log_message()
|
cubeb_log_message() { *storage = '\0'; }
|
||||||
{
|
|
||||||
*storage = '\0';
|
|
||||||
}
|
|
||||||
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
|
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
|
||||||
{
|
{
|
||||||
size_t length = strlen(str);
|
size_t length = strlen(str);
|
||||||
|
@ -49,20 +46,19 @@ public:
|
||||||
PodCopy(storage, str, length);
|
PodCopy(storage, str, length);
|
||||||
storage[length] = '\0';
|
storage[length] = '\0';
|
||||||
}
|
}
|
||||||
char const * get() {
|
char const * get() { return storage; }
|
||||||
return storage;
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
|
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Lock-free asynchronous logger, made so that logging from a
|
/** Lock-free asynchronous logger, made so that logging from a
|
||||||
* real-time audio callback does not block the audio thread. */
|
* real-time audio callback does not block the audio thread. */
|
||||||
class cubeb_async_logger
|
class cubeb_async_logger {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
/* This is thread-safe since C++11 */
|
/* This is thread-safe since C++11 */
|
||||||
static cubeb_async_logger & get() {
|
static cubeb_async_logger & get()
|
||||||
|
{
|
||||||
static cubeb_async_logger instance;
|
static cubeb_async_logger instance;
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
@ -74,10 +70,11 @@ public:
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
std::thread([this]() {
|
std::thread([this]() {
|
||||||
|
CUBEB_REGISTER_THREAD("cubeb_log");
|
||||||
while (true) {
|
while (true) {
|
||||||
cubeb_log_message msg;
|
cubeb_log_message msg;
|
||||||
while (msg_queue.dequeue(&msg, 1)) {
|
while (msg_queue.dequeue(&msg, 1)) {
|
||||||
LOGV("%s", msg.get());
|
LOG_INTERNAL_NO_FORMAT(CUBEB_LOG_NORMAL, "%s", msg.get());
|
||||||
}
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
|
Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
|
||||||
|
@ -85,41 +82,34 @@ public:
|
||||||
timespec sleep_duration = sleep_for;
|
timespec sleep_duration = sleep_for;
|
||||||
timespec remainder;
|
timespec remainder;
|
||||||
do {
|
do {
|
||||||
if (nanosleep(&sleep_duration, &remainder) == 0 ||
|
if (nanosleep(&sleep_duration, &remainder) == 0 || errno != EINTR) {
|
||||||
errno != EINTR) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sleep_duration = remainder;
|
sleep_duration = remainder;
|
||||||
} while (remainder.tv_sec || remainder.tv_nsec);
|
} while (remainder.tv_sec || remainder.tv_nsec);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
CUBEB_UNREGISTER_THREAD();
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
// Tell the underlying queue the producer thread has changed, so it does not
|
// Tell the underlying queue the producer thread has changed, so it does not
|
||||||
// assert in debug. This should be called with the thread stopped.
|
// assert in debug. This should be called with the thread stopped.
|
||||||
void reset_producer_thread()
|
void reset_producer_thread() { msg_queue.reset_thread_ids(); }
|
||||||
{
|
|
||||||
msg_queue.reset_thread_ids();
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
const struct timespec sleep_for = {
|
const struct timespec sleep_for = {
|
||||||
CUBEB_LOG_BATCH_PRINT_INTERVAL_MS / 1000,
|
CUBEB_LOG_BATCH_PRINT_INTERVAL_MS / 1000,
|
||||||
(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000
|
(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS % 1000) * 1000 * 1000};
|
||||||
};
|
|
||||||
#endif
|
#endif
|
||||||
cubeb_async_logger()
|
cubeb_async_logger() : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH) { run(); }
|
||||||
: msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH)
|
|
||||||
{
|
|
||||||
run();
|
|
||||||
}
|
|
||||||
/** This is quite a big data structure, but is only instantiated if the
|
/** This is quite a big data structure, but is only instantiated if the
|
||||||
* asynchronous logger is used.*/
|
* asynchronous logger is used.*/
|
||||||
lock_free_queue<cubeb_log_message> msg_queue;
|
lock_free_queue<cubeb_log_message> msg_queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
void cubeb_async_log(char const * fmt, ...)
|
cubeb_async_log(char const * fmt, ...)
|
||||||
{
|
{
|
||||||
if (!g_cubeb_log_callback) {
|
if (!g_cubeb_log_callback) {
|
||||||
return;
|
return;
|
||||||
|
@ -135,7 +125,8 @@ void cubeb_async_log(char const * fmt, ...)
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cubeb_async_log_reset_threads()
|
void
|
||||||
|
cubeb_async_log_reset_threads(void)
|
||||||
{
|
{
|
||||||
if (!g_cubeb_log_callback) {
|
if (!g_cubeb_log_callback) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -19,18 +19,23 @@ extern "C" {
|
||||||
#if defined(__FILE_NAME__)
|
#if defined(__FILE_NAME__)
|
||||||
#define __FILENAME__ __FILE_NAME__
|
#define __FILENAME__ __FILE_NAME__
|
||||||
#else
|
#else
|
||||||
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
|
#define __FILENAME__ \
|
||||||
|
(__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \
|
||||||
|
: __FILE__)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#define PRINTF_FORMAT(fmt, args)
|
#define PRINTF_FORMAT(fmt, args)
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
#define __FILENAME__ \
|
||||||
|
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern cubeb_log_level g_cubeb_log_level;
|
extern cubeb_log_level g_cubeb_log_level;
|
||||||
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
|
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
|
||||||
void cubeb_async_log(const char * fmt, ...);
|
void
|
||||||
void cubeb_async_log_reset_threads();
|
cubeb_async_log(const char * fmt, ...);
|
||||||
|
void
|
||||||
|
cubeb_async_log_reset_threads(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -39,17 +44,31 @@ void cubeb_async_log_reset_threads();
|
||||||
#define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
|
#define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
|
||||||
#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
|
#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
#define LOG_INTERNAL(level, fmt, ...) do { \
|
#define LOG_INTERNAL_NO_FORMAT(level, fmt, ...) \
|
||||||
|
do { \
|
||||||
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
|
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
|
||||||
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, ##__VA_ARGS__); \
|
g_cubeb_log_callback(fmt, __VA_ARGS__); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
/* Asynchronous verbose logging, to log in real-time callbacks. */
|
#define LOG_INTERNAL(level, fmt, ...) \
|
||||||
/* Should not be used on android due to the use of global/static variables. */
|
|
||||||
#define ALOGV(fmt, ...) \
|
|
||||||
do { \
|
do { \
|
||||||
cubeb_async_log(fmt, ##__VA_ARGS__); \
|
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
|
||||||
|
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, \
|
||||||
|
##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define ALOG_INTERNAL(level, fmt, ...) \
|
||||||
|
do { \
|
||||||
|
if (level <= g_cubeb_log_level) { \
|
||||||
|
cubeb_async_log(fmt, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Asynchronous logging macros to log in real-time callbacks. */
|
||||||
|
/* Should not be used on android due to the use of global/static variables. */
|
||||||
|
#define ALOGV(msg, ...) ALOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
|
||||||
|
#define ALOG(msg, ...) ALOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
|
||||||
|
|
||||||
#endif // CUBEB_LOG
|
#endif // CUBEB_LOG
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
|
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
|
|
||||||
|
#include "cubeb_mixer.h"
|
||||||
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb_utils.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
@ -16,9 +19,6 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include "cubeb-internal.h"
|
|
||||||
#include "cubeb_mixer.h"
|
|
||||||
#include "cubeb_utils.h"
|
|
||||||
|
|
||||||
#ifndef FF_ARRAY_ELEMS
|
#ifndef FF_ARRAY_ELEMS
|
||||||
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
|
||||||
|
@ -66,14 +66,17 @@ cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
|
||||||
{
|
{
|
||||||
if (l == CUBEB_LAYOUT_UNDEFINED) {
|
if (l == CUBEB_LAYOUT_UNDEFINED) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 1: return CUBEB_LAYOUT_MONO;
|
case 1:
|
||||||
case 2: return CUBEB_LAYOUT_STEREO;
|
return CUBEB_LAYOUT_MONO;
|
||||||
|
case 2:
|
||||||
|
return CUBEB_LAYOUT_STEREO;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return l;
|
return l;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
|
unsigned int
|
||||||
|
cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
|
||||||
{
|
{
|
||||||
#if __GNUC__ || __clang__
|
#if __GNUC__ || __clang__
|
||||||
return __builtin_popcount(x);
|
return __builtin_popcount(x);
|
||||||
|
@ -87,16 +90,12 @@ unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MixerContext {
|
struct MixerContext {
|
||||||
MixerContext(cubeb_sample_format f,
|
MixerContext(cubeb_sample_format f, uint32_t in_channels,
|
||||||
uint32_t in_channels,
|
cubeb_channel_layout in, uint32_t out_channels,
|
||||||
cubeb_channel_layout in,
|
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out)
|
cubeb_channel_layout out)
|
||||||
: _format(f)
|
: _format(f), _in_ch_layout(cubeb_channel_layout_check(in, in_channels)),
|
||||||
, _in_ch_layout(cubeb_channel_layout_check(in, in_channels))
|
_out_ch_layout(cubeb_channel_layout_check(out, out_channels)),
|
||||||
, _out_ch_layout(cubeb_channel_layout_check(out, out_channels))
|
_in_ch_count(in_channels), _out_ch_count(out_channels)
|
||||||
, _in_ch_count(in_channels)
|
|
||||||
, _out_ch_count(out_channels)
|
|
||||||
{
|
{
|
||||||
if (in_channels != cubeb_channel_layout_nb_channels(in) ||
|
if (in_channels != cubeb_channel_layout_nb_channels(in) ||
|
||||||
out_channels != cubeb_channel_layout_nb_channels(out)) {
|
out_channels != cubeb_channel_layout_nb_channels(out)) {
|
||||||
|
@ -166,15 +165,21 @@ struct MixerContext {
|
||||||
const float _surround_mix_level = C_30DB; ///< surround mixing level
|
const float _surround_mix_level = C_30DB; ///< surround mixing level
|
||||||
const float _center_mix_level = C_30DB; ///< center mixing level
|
const float _center_mix_level = C_30DB; ///< center mixing level
|
||||||
const float _lfe_mix_level = 1; ///< LFE mixing level
|
const float _lfe_mix_level = 1; ///< LFE mixing level
|
||||||
double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< floating point rematrixing coefficients
|
double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {
|
||||||
float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< single precision floating point rematrixing coefficients
|
{0}}; ///< floating point rematrixing coefficients
|
||||||
int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< 17.15 fixed point rematrixing coefficients
|
float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {
|
||||||
uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }}; ///< Lists of input channels per output channel that have non zero rematrixing coefficients
|
{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 _clipping = false; ///< Set to true if clipping detection is required
|
||||||
bool _valid = false; ///< Set to true if context is valid.
|
bool _valid = false; ///< Set to true if context is valid.
|
||||||
};
|
};
|
||||||
|
|
||||||
int MixerContext::auto_matrix()
|
int
|
||||||
|
MixerContext::auto_matrix()
|
||||||
{
|
{
|
||||||
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}};
|
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}};
|
||||||
double maxcoef = 0;
|
double maxcoef = 0;
|
||||||
|
@ -239,8 +244,7 @@ int MixerContext::auto_matrix()
|
||||||
matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
|
matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
|
||||||
matrix[FRONT_RIGHT][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) {
|
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
|
||||||
matrix[FRONT_CENTER][BACK_CENTER] +=
|
matrix[FRONT_CENTER][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
|
||||||
_surround_mix_level * M_SQRT1_2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (unaccounted & CHANNEL_BACK_LEFT) {
|
if (unaccounted & CHANNEL_BACK_LEFT) {
|
||||||
|
@ -356,7 +360,8 @@ int MixerContext::auto_matrix()
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int MixerContext::init()
|
int
|
||||||
|
MixerContext::init()
|
||||||
{
|
{
|
||||||
int r = auto_matrix();
|
int r = auto_matrix();
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -400,20 +405,13 @@ int MixerContext::init()
|
||||||
|
|
||||||
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
|
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
|
||||||
void
|
void
|
||||||
sum2(TYPE_SAMPLE * out,
|
sum2(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in1,
|
||||||
uint32_t stride_out,
|
const TYPE_SAMPLE * in2, uint32_t stride_in, TYPE_COEFF coeff1,
|
||||||
const TYPE_SAMPLE * in1,
|
TYPE_COEFF coeff2, F && operand, uint32_t frames)
|
||||||
const TYPE_SAMPLE * in2,
|
|
||||||
uint32_t stride_in,
|
|
||||||
TYPE_COEFF coeff1,
|
|
||||||
TYPE_COEFF coeff2,
|
|
||||||
F&& operand,
|
|
||||||
uint32_t frames)
|
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same<TYPE_COEFF,
|
std::is_same<TYPE_COEFF, decltype(operand(coeff1))>::value,
|
||||||
typename std::result_of<F(TYPE_COEFF)>::type>::value,
|
"function must return the same type as used by coeff1 and coeff2");
|
||||||
"function must return the same type as used by matrix_coeff");
|
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
*out = operand(coeff1 * *in1 + coeff2 * *in2);
|
*out = operand(coeff1 * *in1 + coeff2 * *in2);
|
||||||
out += stride_out;
|
out += stride_out;
|
||||||
|
@ -424,18 +422,11 @@ sum2(TYPE_SAMPLE * out,
|
||||||
|
|
||||||
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
|
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
|
||||||
void
|
void
|
||||||
copy(TYPE_SAMPLE * out,
|
copy(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in,
|
||||||
uint32_t stride_out,
|
uint32_t stride_in, TYPE_COEFF coeff, F && operand, uint32_t frames)
|
||||||
const TYPE_SAMPLE * in,
|
|
||||||
uint32_t stride_in,
|
|
||||||
TYPE_COEFF coeff,
|
|
||||||
F&& operand,
|
|
||||||
uint32_t frames)
|
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(std::is_same<TYPE_COEFF, decltype(operand(coeff))>::value,
|
||||||
std::is_same<TYPE_COEFF,
|
"function must return the same type as used by 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++) {
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
*out = operand(coeff * *in);
|
*out = operand(coeff * *in);
|
||||||
out += stride_out;
|
out += stride_out;
|
||||||
|
@ -444,13 +435,12 @@ copy(TYPE_SAMPLE * out,
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
|
template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
|
||||||
static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
|
static int
|
||||||
const TYPE_COEFF (&matrix_coeff)[COLS][COLS],
|
rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
|
||||||
F&& aF, uint32_t frames)
|
const TYPE_COEFF (&matrix_coeff)[COLS][COLS], F && aF, uint32_t frames)
|
||||||
{
|
{
|
||||||
static_assert(
|
static_assert(
|
||||||
std::is_same<TYPE_COEFF,
|
std::is_same<TYPE_COEFF, decltype(aF(matrix_coeff[0][0]))>::value,
|
||||||
typename std::result_of<F(TYPE_COEFF)>::type>::value,
|
|
||||||
"function must return the same type as used by matrix_coeff");
|
"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++) {
|
for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
|
||||||
|
@ -463,32 +453,21 @@ static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
|
||||||
break;
|
break;
|
||||||
case 1: {
|
case 1: {
|
||||||
int in_i = s->_matrix_ch[out_i][1];
|
int in_i = s->_matrix_ch[out_i][1];
|
||||||
copy(out,
|
copy(out, s->_out_ch_count, aIn + in_i, s->_in_ch_count,
|
||||||
s->_out_ch_count,
|
matrix_coeff[out_i][in_i], aF, frames);
|
||||||
aIn + in_i,
|
|
||||||
s->_in_ch_count,
|
|
||||||
matrix_coeff[out_i][in_i],
|
|
||||||
aF,
|
|
||||||
frames);
|
|
||||||
} break;
|
} break;
|
||||||
case 2:
|
case 2:
|
||||||
sum2(out,
|
sum2(out, s->_out_ch_count, aIn + s->_matrix_ch[out_i][1],
|
||||||
s->_out_ch_count,
|
aIn + s->_matrix_ch[out_i][2], s->_in_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][1]],
|
||||||
matrix_coeff[out_i][s->_matrix_ch[out_i][2]],
|
matrix_coeff[out_i][s->_matrix_ch[out_i][2]], aF, frames);
|
||||||
aF,
|
|
||||||
frames);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
for (uint32_t i = 0; i < frames; i++) {
|
for (uint32_t i = 0; i < frames; i++) {
|
||||||
TYPE_COEFF v = 0;
|
TYPE_COEFF v = 0;
|
||||||
for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
|
for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
|
||||||
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
|
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
|
||||||
v +=
|
v += *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
|
||||||
*(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
|
|
||||||
}
|
}
|
||||||
out[i * s->_out_ch_count] = aF(v);
|
out[i * s->_out_ch_count] = aF(v);
|
||||||
}
|
}
|
||||||
|
@ -498,20 +477,16 @@ static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cubeb_mixer
|
struct cubeb_mixer {
|
||||||
{
|
cubeb_mixer(cubeb_sample_format format, uint32_t in_channels,
|
||||||
cubeb_mixer(cubeb_sample_format format,
|
cubeb_channel_layout in_layout, uint32_t out_channels,
|
||||||
uint32_t in_channels,
|
|
||||||
cubeb_channel_layout in_layout,
|
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out_layout)
|
cubeb_channel_layout out_layout)
|
||||||
: _context(format, in_channels, in_layout, out_channels, out_layout)
|
: _context(format, in_channels, in_layout, out_channels, out_layout)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void copy_and_trunc(size_t frames,
|
void copy_and_trunc(size_t frames, const T * input_buffer,
|
||||||
const T * input_buffer,
|
|
||||||
T * output_buffer) const
|
T * output_buffer) const
|
||||||
{
|
{
|
||||||
if (_context._in_ch_count <= _context._out_ch_count) {
|
if (_context._in_ch_count <= _context._out_ch_count) {
|
||||||
|
@ -545,11 +520,8 @@ struct cubeb_mixer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int mix(size_t frames,
|
int mix(size_t frames, const void * input_buffer, size_t input_buffer_size,
|
||||||
const void * input_buffer,
|
void * output_buffer, size_t output_buffer_size) const
|
||||||
size_t input_buffer_size,
|
|
||||||
void * output_buffer,
|
|
||||||
size_t output_buffer_size) const
|
|
||||||
{
|
{
|
||||||
if (frames <= 0 || _context._out_ch_count == 0) {
|
if (frames <= 0 || _context._out_ch_count == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -571,28 +543,22 @@ struct cubeb_mixer
|
||||||
// The channel layouts were invalid or unsupported, instead we will simply
|
// The channel layouts were invalid or unsupported, instead we will simply
|
||||||
// either drop the extra channels, or fill with silence the missing ones
|
// either drop the extra channels, or fill with silence the missing ones
|
||||||
if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
|
if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
|
||||||
copy_and_trunc(frames,
|
copy_and_trunc(frames, static_cast<const float *>(input_buffer),
|
||||||
static_cast<const float*>(input_buffer),
|
|
||||||
static_cast<float *>(output_buffer));
|
static_cast<float *>(output_buffer));
|
||||||
} else {
|
} else {
|
||||||
assert(_context._format == CUBEB_SAMPLE_S16NE);
|
assert(_context._format == CUBEB_SAMPLE_S16NE);
|
||||||
copy_and_trunc(frames,
|
copy_and_trunc(frames, static_cast<const int16_t *>(input_buffer),
|
||||||
static_cast<const int16_t*>(input_buffer),
|
|
||||||
reinterpret_cast<int16_t *>(output_buffer));
|
reinterpret_cast<int16_t *>(output_buffer));
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_context._format)
|
switch (_context._format) {
|
||||||
{
|
|
||||||
case CUBEB_SAMPLE_FLOAT32NE: {
|
case CUBEB_SAMPLE_FLOAT32NE: {
|
||||||
auto f = [](float x) { return x; };
|
auto f = [](float x) { return x; };
|
||||||
return rematrix(&_context,
|
return rematrix(&_context, static_cast<float *>(output_buffer),
|
||||||
static_cast<float*>(output_buffer),
|
|
||||||
static_cast<const float *>(input_buffer),
|
static_cast<const float *>(input_buffer),
|
||||||
_context._matrix_flt,
|
_context._matrix_flt, f, frames);
|
||||||
f,
|
|
||||||
frames);
|
|
||||||
}
|
}
|
||||||
case CUBEB_SAMPLE_S16NE:
|
case CUBEB_SAMPLE_S16NE:
|
||||||
if (_context._clipping) {
|
if (_context._clipping) {
|
||||||
|
@ -604,20 +570,14 @@ struct cubeb_mixer
|
||||||
}
|
}
|
||||||
return y;
|
return y;
|
||||||
};
|
};
|
||||||
return rematrix(&_context,
|
return rematrix(&_context, static_cast<int16_t *>(output_buffer),
|
||||||
static_cast<int16_t*>(output_buffer),
|
|
||||||
static_cast<const int16_t *>(input_buffer),
|
static_cast<const int16_t *>(input_buffer),
|
||||||
_context._matrix32,
|
_context._matrix32, f, frames);
|
||||||
f,
|
|
||||||
frames);
|
|
||||||
} else {
|
} else {
|
||||||
auto f = [](int x) { return (x + 16384) >> 15; };
|
auto f = [](int x) { return (x + 16384) >> 15; };
|
||||||
return rematrix(&_context,
|
return rematrix(&_context, static_cast<int16_t *>(output_buffer),
|
||||||
static_cast<int16_t*>(output_buffer),
|
|
||||||
static_cast<const int16_t *>(input_buffer),
|
static_cast<const int16_t *>(input_buffer),
|
||||||
_context._matrix32,
|
_context._matrix32, f, frames);
|
||||||
f,
|
|
||||||
frames);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -636,28 +596,26 @@ struct cubeb_mixer
|
||||||
MixerContext _context;
|
MixerContext _context;
|
||||||
};
|
};
|
||||||
|
|
||||||
cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format,
|
cubeb_mixer *
|
||||||
uint32_t in_channels,
|
cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels,
|
||||||
cubeb_channel_layout in_layout,
|
cubeb_channel_layout in_layout, uint32_t out_channels,
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out_layout)
|
cubeb_channel_layout out_layout)
|
||||||
{
|
{
|
||||||
return new cubeb_mixer(
|
return new cubeb_mixer(format, in_channels, in_layout, out_channels,
|
||||||
format, in_channels, in_layout, out_channels, out_layout);
|
out_layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cubeb_mixer_destroy(cubeb_mixer * mixer)
|
void
|
||||||
|
cubeb_mixer_destroy(cubeb_mixer * mixer)
|
||||||
{
|
{
|
||||||
delete mixer;
|
delete mixer;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cubeb_mixer_mix(cubeb_mixer * mixer,
|
int
|
||||||
size_t frames,
|
cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer,
|
||||||
const void * input_buffer,
|
size_t input_buffer_size, void * output_buffer,
|
||||||
size_t input_buffer_size,
|
|
||||||
void * output_buffer,
|
|
||||||
size_t output_buffer_size)
|
size_t output_buffer_size)
|
||||||
{
|
{
|
||||||
return mixer->mix(
|
return mixer->mix(frames, input_buffer, input_buffer_size, output_buffer,
|
||||||
frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size);
|
output_buffer_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,20 +15,19 @@ extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct cubeb_mixer cubeb_mixer;
|
typedef struct cubeb_mixer cubeb_mixer;
|
||||||
cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
|
cubeb_mixer *
|
||||||
uint32_t in_channels,
|
cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels,
|
||||||
cubeb_channel_layout in_layout,
|
cubeb_channel_layout in_layout, uint32_t out_channels,
|
||||||
uint32_t out_channels,
|
|
||||||
cubeb_channel_layout out_layout);
|
cubeb_channel_layout out_layout);
|
||||||
void cubeb_mixer_destroy(cubeb_mixer * mixer);
|
void
|
||||||
int cubeb_mixer_mix(cubeb_mixer * mixer,
|
cubeb_mixer_destroy(cubeb_mixer * mixer);
|
||||||
size_t frames,
|
int
|
||||||
const void * input_buffer,
|
cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer,
|
||||||
size_t input_buffer_size,
|
size_t input_buffer_size, void * output_buffer,
|
||||||
void * output_buffer,
|
|
||||||
size_t output_buffer_size);
|
size_t output_buffer_size);
|
||||||
|
|
||||||
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
|
unsigned int
|
||||||
|
cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,29 +5,29 @@
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
|
#include <SLES/OpenSLES.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <SLES/OpenSLES.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <sys/system_properties.h>
|
|
||||||
#include "android/sles_definitions.h"
|
#include "android/sles_definitions.h"
|
||||||
#include <SLES/OpenSLES_Android.h>
|
#include <SLES/OpenSLES_Android.h>
|
||||||
#include <android/log.h>
|
|
||||||
#include <android/api-level.h>
|
#include <android/api-level.h>
|
||||||
|
#include <android/log.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <sys/system_properties.h>
|
||||||
#endif
|
#endif
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
#include "cubeb_resampler.h"
|
|
||||||
#include "cubeb-sles.h"
|
|
||||||
#include "cubeb_array_queue.h"
|
|
||||||
#include "android/cubeb-output-latency.h"
|
#include "android/cubeb-output-latency.h"
|
||||||
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb-sles.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
#include "cubeb_android.h"
|
#include "cubeb_android.h"
|
||||||
|
#include "cubeb_array_queue.h"
|
||||||
|
#include "cubeb_resampler.h"
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
|
@ -35,22 +35,30 @@
|
||||||
#endif
|
#endif
|
||||||
//#define LOGGING_ENABLED
|
//#define LOGGING_ENABLED
|
||||||
#ifdef LOGGING_ENABLED
|
#ifdef LOGGING_ENABLED
|
||||||
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
|
#define LOG(args...) \
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL", ##args)
|
||||||
#else
|
#else
|
||||||
#define LOG(...)
|
#define LOG(...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//#define TIMESTAMP_ENABLED
|
//#define TIMESTAMP_ENABLED
|
||||||
#ifdef TIMESTAMP_ENABLED
|
#ifdef TIMESTAMP_ENABLED
|
||||||
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
#define FILENAME \
|
||||||
#define LOG_TS(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)" , ## args)
|
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
|
||||||
#define TIMESTAMP(msg) do { \
|
#define LOG_TS(args...) \
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)", \
|
||||||
|
##args)
|
||||||
|
#define TIMESTAMP(msg) \
|
||||||
|
do { \
|
||||||
struct timeval timestamp; \
|
struct timeval timestamp; \
|
||||||
int ts_ret = gettimeofday(×tamp, NULL); \
|
int ts_ret = gettimeofday(×tamp, NULL); \
|
||||||
if (ts_ret == 0) { \
|
if (ts_ret == 0) { \
|
||||||
LOG_TS("%lld: %s (%s %s:%d)", timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, __FUNCTION__, FILENAME, __LINE__);\
|
LOG_TS("%lld: %s (%s %s:%d)", \
|
||||||
|
timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, \
|
||||||
|
__FUNCTION__, FILENAME, __LINE__); \
|
||||||
} else { \
|
} else { \
|
||||||
LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, __LINE__);\
|
LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, \
|
||||||
|
__LINE__); \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#else
|
#else
|
||||||
|
@ -169,15 +177,18 @@ struct cubeb_stream {
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Forward declaration. */
|
/* Forward declaration. */
|
||||||
static int opensl_stop_player(cubeb_stream * stm);
|
static int
|
||||||
static int opensl_stop_recorder(cubeb_stream * stm);
|
opensl_stop_player(cubeb_stream * stm);
|
||||||
|
static int
|
||||||
|
opensl_stop_recorder(cubeb_stream * stm);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
opensl_get_draining(cubeb_stream * stm)
|
opensl_get_draining(cubeb_stream * stm)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int r = pthread_mutex_trylock(&stm->mutex);
|
int r = pthread_mutex_trylock(&stm->mutex);
|
||||||
assert((r == EDEADLK || r == EBUSY) && "get_draining: mutex should be locked but it's not.");
|
assert((r == EDEADLK || r == EBUSY) &&
|
||||||
|
"get_draining: mutex should be locked but it's not.");
|
||||||
#endif
|
#endif
|
||||||
return stm->draining;
|
return stm->draining;
|
||||||
}
|
}
|
||||||
|
@ -188,7 +199,8 @@ opensl_set_draining(cubeb_stream * stm, int value)
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int r = pthread_mutex_trylock(&stm->mutex);
|
int r = pthread_mutex_trylock(&stm->mutex);
|
||||||
LOG("set draining try r = %d", r);
|
LOG("set draining try r = %d", r);
|
||||||
assert((r == EDEADLK || r == EBUSY) && "set_draining: mutex should be locked but it's not.");
|
assert((r == EDEADLK || r == EBUSY) &&
|
||||||
|
"set_draining: mutex should be locked but it's not.");
|
||||||
#endif
|
#endif
|
||||||
assert(value == 0 || value == 1);
|
assert(value == 0 || value == 1);
|
||||||
stm->draining = value;
|
stm->draining = value;
|
||||||
|
@ -222,7 +234,8 @@ opensl_get_shutdown(cubeb_stream * stm)
|
||||||
{
|
{
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int r = pthread_mutex_trylock(&stm->mutex);
|
int r = pthread_mutex_trylock(&stm->mutex);
|
||||||
assert((r == EDEADLK || r == EBUSY) && "get_shutdown: mutex should be locked but it's not.");
|
assert((r == EDEADLK || r == EBUSY) &&
|
||||||
|
"get_shutdown: mutex should be locked but it's not.");
|
||||||
#endif
|
#endif
|
||||||
return stm->shutdown;
|
return stm->shutdown;
|
||||||
}
|
}
|
||||||
|
@ -233,7 +246,8 @@ opensl_set_shutdown(cubeb_stream * stm, uint32_t value)
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
int r = pthread_mutex_trylock(&stm->mutex);
|
int r = pthread_mutex_trylock(&stm->mutex);
|
||||||
LOG("set shutdown try r = %d", r);
|
LOG("set shutdown try r = %d", r);
|
||||||
assert((r == EDEADLK || r == EBUSY) && "set_shutdown: mutex should be locked but it's not.");
|
assert((r == EDEADLK || r == EBUSY) &&
|
||||||
|
"set_shutdown: mutex should be locked but it's not.");
|
||||||
#endif
|
#endif
|
||||||
assert(value == 0 || value == 1);
|
assert(value == 0 || value == 1);
|
||||||
stm->shutdown = value;
|
stm->shutdown = value;
|
||||||
|
@ -304,9 +318,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
r = pthread_mutex_unlock(&stm->mutex);
|
r = pthread_mutex_unlock(&stm->mutex);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
if (!draining && !shutdown) {
|
if (!draining && !shutdown) {
|
||||||
written = cubeb_resampler_fill(stm->resampler,
|
written = cubeb_resampler_fill(stm->resampler, NULL, NULL, buf,
|
||||||
NULL, NULL,
|
stm->queuebuf_len / stm->framesize);
|
||||||
buf, stm->queuebuf_len / stm->framesize);
|
|
||||||
LOG("bufferqueue_callback: resampler fill returned %ld frames", written);
|
LOG("bufferqueue_callback: resampler fill returned %ld frames", written);
|
||||||
if (written < 0 || written * stm->framesize > stm->queuebuf_len) {
|
if (written < 0 || written * stm->framesize > stm->queuebuf_len) {
|
||||||
r = pthread_mutex_lock(&stm->mutex);
|
r = pthread_mutex_lock(&stm->mutex);
|
||||||
|
@ -323,7 +336,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
// Keep sending silent data even in draining mode to prevent the audio
|
// Keep sending silent data even in draining mode to prevent the audio
|
||||||
// back-end from being stopped automatically by OpenSL/ES.
|
// back-end from being stopped automatically by OpenSL/ES.
|
||||||
assert(stm->queuebuf_len >= written * stm->framesize);
|
assert(stm->queuebuf_len >= written * stm->framesize);
|
||||||
memset(buf + written * stm->framesize, 0, stm->queuebuf_len - written * stm->framesize);
|
memset(buf + written * stm->framesize, 0,
|
||||||
|
stm->queuebuf_len - written * stm->framesize);
|
||||||
res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
|
res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
|
||||||
assert(res == SL_RESULT_SUCCESS);
|
assert(res == SL_RESULT_SUCCESS);
|
||||||
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
|
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
|
||||||
|
@ -338,7 +352,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
LOG("bufferqueue_callback draining");
|
LOG("bufferqueue_callback draining");
|
||||||
r = pthread_mutex_lock(&stm->mutex);
|
r = pthread_mutex_lock(&stm->mutex);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
|
int64_t written_duration =
|
||||||
|
INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
|
||||||
opensl_set_draining(stm, 1);
|
opensl_set_draining(stm, 1);
|
||||||
r = pthread_mutex_unlock(&stm->mutex);
|
r = pthread_mutex_unlock(&stm->mutex);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
|
@ -350,7 +365,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
} else {
|
} else {
|
||||||
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
|
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
|
||||||
// to make sure all the data has been processed.
|
// to make sure all the data has been processed.
|
||||||
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
|
(*stm->play)
|
||||||
|
->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -368,13 +384,15 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer)
|
||||||
// This is the first enqueue
|
// This is the first enqueue
|
||||||
current_index = 0;
|
current_index = 0;
|
||||||
} else {
|
} else {
|
||||||
// The current index hold the last filled buffer get it before advance index.
|
// The current index hold the last filled buffer get it before advance
|
||||||
|
// index.
|
||||||
last_buffer = stm->input_buffer_array[current_index];
|
last_buffer = stm->input_buffer_array[current_index];
|
||||||
// Advance to get next available buffer
|
// Advance to get next available buffer
|
||||||
current_index = (current_index + 1) % stm->input_array_capacity;
|
current_index = (current_index + 1) % stm->input_array_capacity;
|
||||||
}
|
}
|
||||||
// enqueue next empty buffer to be filled by the recorder
|
// enqueue next empty buffer to be filled by the recorder
|
||||||
SLresult res = (*stm->recorderBufferQueueItf)->Enqueue(stm->recorderBufferQueueItf,
|
SLresult res = (*stm->recorderBufferQueueItf)
|
||||||
|
->Enqueue(stm->recorderBufferQueueItf,
|
||||||
stm->input_buffer_array[current_index],
|
stm->input_buffer_array[current_index],
|
||||||
stm->input_buffer_length);
|
stm->input_buffer_length);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
|
@ -390,7 +408,8 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// input data callback
|
// input data callback
|
||||||
void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
|
void
|
||||||
|
recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
|
||||||
{
|
{
|
||||||
assert(context);
|
assert(context);
|
||||||
cubeb_stream * stm = context;
|
cubeb_stream * stm = context;
|
||||||
|
@ -420,11 +439,8 @@ void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
|
||||||
assert(input_buffer);
|
assert(input_buffer);
|
||||||
// Fill resampler with last input
|
// Fill resampler with last input
|
||||||
long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
|
long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
|
||||||
long got = cubeb_resampler_fill(stm->resampler,
|
long got = cubeb_resampler_fill(stm->resampler, input_buffer,
|
||||||
input_buffer,
|
&input_frame_count, NULL, 0);
|
||||||
&input_frame_count,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
// Error case
|
// Error case
|
||||||
if (got < 0 || got > input_frame_count) {
|
if (got < 0 || got > input_frame_count) {
|
||||||
r = pthread_mutex_lock(&stm->mutex);
|
r = pthread_mutex_lock(&stm->mutex);
|
||||||
|
@ -446,13 +462,16 @@ void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
|
||||||
opensl_set_draining(stm, 1);
|
opensl_set_draining(stm, 1);
|
||||||
r = pthread_mutex_unlock(&stm->mutex);
|
r = pthread_mutex_unlock(&stm->mutex);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
int64_t duration = INT64_C(1000) * stm->input_total_frames / stm->input_device_rate;
|
int64_t duration =
|
||||||
(*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration);
|
INT64_C(1000) * stm->input_total_frames / stm->input_device_rate;
|
||||||
|
(*stm->recorderItf)
|
||||||
|
->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
|
void
|
||||||
|
recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
|
||||||
{
|
{
|
||||||
assert(context);
|
assert(context);
|
||||||
cubeb_stream * stm = context;
|
cubeb_stream * stm = context;
|
||||||
|
@ -525,9 +544,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
memset(output_buffer, 0, stm->queuebuf_len);
|
memset(output_buffer, 0, stm->queuebuf_len);
|
||||||
|
|
||||||
// Enqueue data in player buffer queue
|
// Enqueue data in player buffer queue
|
||||||
res = (*stm->bufq)->Enqueue(stm->bufq,
|
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
|
||||||
output_buffer,
|
|
||||||
stm->queuebuf_len);
|
|
||||||
assert(res == SL_RESULT_SUCCESS);
|
assert(res == SL_RESULT_SUCCESS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -543,14 +560,12 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
|
|
||||||
long written = 0;
|
long written = 0;
|
||||||
// Trigger user callback through resampler
|
// Trigger user callback through resampler
|
||||||
written = cubeb_resampler_fill(stm->resampler,
|
written =
|
||||||
input_buffer,
|
cubeb_resampler_fill(stm->resampler, input_buffer, &input_frame_count,
|
||||||
&input_frame_count,
|
output_buffer, frames_needed);
|
||||||
output_buffer,
|
|
||||||
frames_needed);
|
|
||||||
|
|
||||||
LOG("Fill: written %ld, frames_needed %ld, input array size %zu",
|
LOG("Fill: written %ld, frames_needed %ld, input array size %zu", written,
|
||||||
written, frames_needed, array_queue_get_size(stm->input_queue));
|
frames_needed, array_queue_get_size(stm->input_queue));
|
||||||
|
|
||||||
if (written < 0 || written > frames_needed) {
|
if (written < 0 || written > frames_needed) {
|
||||||
// Error case
|
// Error case
|
||||||
|
@ -565,9 +580,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
memset(output_buffer, 0, stm->queuebuf_len);
|
memset(output_buffer, 0, stm->queuebuf_len);
|
||||||
|
|
||||||
// Enqueue data in player buffer queue
|
// Enqueue data in player buffer queue
|
||||||
res = (*stm->bufq)->Enqueue(stm->bufq,
|
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
|
||||||
output_buffer,
|
|
||||||
stm->queuebuf_len);
|
|
||||||
assert(res == SL_RESULT_SUCCESS);
|
assert(res == SL_RESULT_SUCCESS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -582,7 +595,8 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
if (written < frames_needed) {
|
if (written < frames_needed) {
|
||||||
r = pthread_mutex_lock(&stm->mutex);
|
r = pthread_mutex_lock(&stm->mutex);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
|
int64_t written_duration =
|
||||||
|
INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
|
||||||
opensl_set_draining(stm, 1);
|
opensl_set_draining(stm, 1);
|
||||||
r = pthread_mutex_unlock(&stm->mutex);
|
r = pthread_mutex_unlock(&stm->mutex);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
|
@ -598,14 +612,13 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
|
||||||
stm->queuebuf_len - written * stm->framesize);
|
stm->queuebuf_len - written * stm->framesize);
|
||||||
|
|
||||||
// Enqueue data in player buffer queue
|
// Enqueue data in player buffer queue
|
||||||
res = (*stm->bufq)->Enqueue(stm->bufq,
|
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
|
||||||
output_buffer,
|
|
||||||
stm->queuebuf_len);
|
|
||||||
assert(res == SL_RESULT_SUCCESS);
|
assert(res == SL_RESULT_SUCCESS);
|
||||||
TIMESTAMP("EXIT");
|
TIMESTAMP("EXIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void opensl_destroy(cubeb * ctx);
|
static void
|
||||||
|
opensl_destroy(cubeb * ctx);
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
|
#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
|
||||||
|
@ -619,8 +632,8 @@ wrap_system_property_get(const char* name, char* value)
|
||||||
LOG("Failed to open libc.so");
|
LOG("Failed to open libc.so");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
system_property_get* func = (system_property_get*)
|
system_property_get * func =
|
||||||
dlsym(libc, "__system_property_get");
|
(system_property_get *)dlsym(libc, "__system_property_get");
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
if (func) {
|
if (func) {
|
||||||
ret = func(name, value);
|
ret = func(name, value);
|
||||||
|
@ -660,7 +673,8 @@ opensl_init(cubeb ** context, char const * context_name)
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
int android_version = get_android_version();
|
int android_version = get_android_version();
|
||||||
if (android_version > 0 && android_version <= ANDROID_VERSION_GINGERBREAD_MR1) {
|
if (android_version > 0 &&
|
||||||
|
android_version <= ANDROID_VERSION_GINGERBREAD_MR1) {
|
||||||
// Don't even attempt to run on Gingerbread and lower
|
// Don't even attempt to run on Gingerbread and lower
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -679,35 +693,34 @@ opensl_init(cubeb ** context, char const * context_name)
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
|
typedef SLresult (*slCreateEngine_t)(
|
||||||
SLuint32,
|
SLObjectItf *, SLuint32, const SLEngineOption *, SLuint32,
|
||||||
const SLEngineOption *,
|
const SLInterfaceID *, const SLboolean *);
|
||||||
SLuint32,
|
|
||||||
const SLInterfaceID *,
|
|
||||||
const SLboolean *);
|
|
||||||
slCreateEngine_t f_slCreateEngine =
|
slCreateEngine_t f_slCreateEngine =
|
||||||
(slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
|
(slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
|
||||||
SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
|
SLInterfaceID SL_IID_ENGINE =
|
||||||
SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
|
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
|
||||||
|
SLInterfaceID SL_IID_OUTPUTMIX =
|
||||||
|
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
|
||||||
ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME");
|
ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME");
|
||||||
ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
|
ctx->SL_IID_BUFFERQUEUE =
|
||||||
|
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
|
ctx->SL_IID_ANDROIDCONFIGURATION =
|
||||||
ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
|
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
|
||||||
|
ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE =
|
||||||
|
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
|
||||||
#endif
|
#endif
|
||||||
ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
|
ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
|
||||||
ctx->SL_IID_RECORD = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_RECORD");
|
ctx->SL_IID_RECORD = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_RECORD");
|
||||||
|
|
||||||
if (!f_slCreateEngine ||
|
if (!f_slCreateEngine || !SL_IID_ENGINE || !SL_IID_OUTPUTMIX ||
|
||||||
!SL_IID_ENGINE ||
|
|
||||||
!SL_IID_OUTPUTMIX ||
|
|
||||||
!ctx->SL_IID_BUFFERQUEUE ||
|
!ctx->SL_IID_BUFFERQUEUE ||
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
!ctx->SL_IID_ANDROIDCONFIGURATION ||
|
!ctx->SL_IID_ANDROIDCONFIGURATION ||
|
||||||
!ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE ||
|
!ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE ||
|
||||||
#endif
|
#endif
|
||||||
!ctx->SL_IID_PLAY ||
|
!ctx->SL_IID_PLAY || !ctx->SL_IID_RECORD) {
|
||||||
!ctx->SL_IID_RECORD) {
|
|
||||||
opensl_destroy(ctx);
|
opensl_destroy(ctx);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -736,7 +749,8 @@ opensl_init(cubeb ** context, char const * context_name)
|
||||||
|
|
||||||
const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
|
const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
|
||||||
const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
|
const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
|
||||||
res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
|
res =
|
||||||
|
(*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
opensl_destroy(ctx);
|
opensl_destroy(ctx);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -748,9 +762,11 @@ opensl_init(cubeb ** context, char const * context_name)
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->p_output_latency_function = cubeb_output_latency_load_method(android_version);
|
ctx->p_output_latency_function =
|
||||||
|
cubeb_output_latency_load_method(android_version);
|
||||||
if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) {
|
if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) {
|
||||||
LOG("Warning: output latency is not available, cubeb_stream_get_position() is not supported");
|
LOG("Warning: output latency is not available, cubeb_stream_get_position() "
|
||||||
|
"is not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
*context = ctx;
|
*context = ctx;
|
||||||
|
@ -770,7 +786,8 @@ opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||||
{
|
{
|
||||||
assert(ctx && max_channels);
|
assert(ctx && max_channels);
|
||||||
/* The android mixer handles up to two channels, see
|
/* The android mixer handles up to two channels, see
|
||||||
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
|
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67
|
||||||
|
*/
|
||||||
*max_channels = 2;
|
*max_channels = 2;
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
|
@ -789,11 +806,13 @@ opensl_destroy(cubeb * ctx)
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void opensl_stream_destroy(cubeb_stream * stm);
|
static void
|
||||||
|
opensl_stream_destroy(cubeb_stream * stm);
|
||||||
|
|
||||||
#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
|
#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
|
||||||
static int
|
static int
|
||||||
opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params * params)
|
opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format,
|
||||||
|
cubeb_stream_params * params)
|
||||||
{
|
{
|
||||||
assert(format);
|
assert(format);
|
||||||
assert(params);
|
assert(params);
|
||||||
|
@ -802,9 +821,9 @@ opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params *
|
||||||
format->numChannels = params->channels;
|
format->numChannels = params->channels;
|
||||||
// sampleRate is in milliHertz
|
// sampleRate is in milliHertz
|
||||||
format->sampleRate = params->rate * 1000;
|
format->sampleRate = params->rate * 1000;
|
||||||
format->channelMask = params->channels == 1 ?
|
format->channelMask = params->channels == 1
|
||||||
SL_SPEAKER_FRONT_CENTER :
|
? SL_SPEAKER_FRONT_CENTER
|
||||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
: SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||||
|
|
||||||
switch (params->format) {
|
switch (params->format) {
|
||||||
case CUBEB_SAMPLE_S16LE:
|
case CUBEB_SAMPLE_S16LE:
|
||||||
|
@ -850,9 +869,9 @@ opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params)
|
||||||
format->samplesPerSec = params->rate * 1000;
|
format->samplesPerSec = params->rate * 1000;
|
||||||
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||||
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
|
||||||
format->channelMask = params->channels == 1 ?
|
format->channelMask = params->channels == 1
|
||||||
SL_SPEAKER_FRONT_CENTER :
|
? SL_SPEAKER_FRONT_CENTER
|
||||||
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
: SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||||
|
|
||||||
switch (params->format) {
|
switch (params->format) {
|
||||||
case CUBEB_SAMPLE_S16LE:
|
case CUBEB_SAMPLE_S16LE:
|
||||||
|
@ -900,19 +919,19 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
lDataSource.pLocator = &lDataLocatorIn;
|
lDataSource.pLocator = &lDataLocatorIn;
|
||||||
lDataSource.pFormat = NULL;
|
lDataSource.pFormat = NULL;
|
||||||
|
|
||||||
const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD,
|
const SLInterfaceID lSoundRecorderIIDs[] = {
|
||||||
|
stm->context->SL_IID_RECORD,
|
||||||
stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
stm->context->SL_IID_ANDROIDCONFIGURATION};
|
stm->context->SL_IID_ANDROIDCONFIGURATION};
|
||||||
|
|
||||||
const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
|
const SLboolean lSoundRecorderReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
|
||||||
|
SL_BOOLEAN_TRUE};
|
||||||
// create the audio recorder abstract object
|
// create the audio recorder abstract object
|
||||||
SLresult res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
|
SLresult res = (*stm->context->eng)
|
||||||
&stm->recorderObj,
|
->CreateAudioRecorder(
|
||||||
&lDataSource,
|
stm->context->eng, &stm->recorderObj, &lDataSource,
|
||||||
&lDataSink,
|
&lDataSink, NELEMS(lSoundRecorderIIDs),
|
||||||
NELEMS(lSoundRecorderIIDs),
|
lSoundRecorderIIDs, lSoundRecorderReqs);
|
||||||
lSoundRecorderIIDs,
|
|
||||||
lSoundRecorderReqs);
|
|
||||||
// Sample rate not supported. Try again with default sample rate!
|
// Sample rate not supported. Try again with default sample rate!
|
||||||
if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
|
if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
|
||||||
if (stm->output_enabled && stm->output_configured_rate != 0) {
|
if (stm->output_enabled && stm->output_configured_rate != 0) {
|
||||||
|
@ -925,13 +944,11 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
stm->input_device_rate = DEFAULT_SAMPLE_RATE;
|
stm->input_device_rate = DEFAULT_SAMPLE_RATE;
|
||||||
}
|
}
|
||||||
lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
|
lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
|
||||||
res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
|
res = (*stm->context->eng)
|
||||||
&stm->recorderObj,
|
->CreateAudioRecorder(stm->context->eng, &stm->recorderObj,
|
||||||
&lDataSource,
|
&lDataSource, &lDataSink,
|
||||||
&lDataSink,
|
|
||||||
NELEMS(lSoundRecorderIIDs),
|
NELEMS(lSoundRecorderIIDs),
|
||||||
lSoundRecorderIIDs,
|
lSoundRecorderIIDs, lSoundRecorderReqs);
|
||||||
lSoundRecorderReqs);
|
|
||||||
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to create recorder. Error code: %lu", res);
|
LOG("Failed to create recorder. Error code: %lu", res);
|
||||||
|
@ -939,7 +956,6 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
|
if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
|
||||||
SLAndroidConfigurationItf recorderConfig;
|
SLAndroidConfigurationItf recorderConfig;
|
||||||
res = (*stm->recorderObj)
|
res = (*stm->recorderObj)
|
||||||
|
@ -948,7 +964,8 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
&recorderConfig);
|
&recorderConfig);
|
||||||
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get the android configuration interface for recorder. Error "
|
LOG("Failed to get the android configuration interface for recorder. "
|
||||||
|
"Error "
|
||||||
"code: %lu",
|
"code: %lu",
|
||||||
res);
|
res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -956,16 +973,19 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
|
|
||||||
// Voice recognition is the lowest latency, according to the docs. Camcorder
|
// Voice recognition is the lowest latency, according to the docs. Camcorder
|
||||||
// uses a microphone that is in the same direction as the camera.
|
// uses a microphone that is in the same direction as the camera.
|
||||||
SLint32 streamType = stm->voice_input ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
|
SLint32 streamType = stm->voice_input
|
||||||
|
? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
|
||||||
: SL_ANDROID_RECORDING_PRESET_CAMCORDER;
|
: SL_ANDROID_RECORDING_PRESET_CAMCORDER;
|
||||||
|
|
||||||
res = (*recorderConfig)
|
res =
|
||||||
|
(*recorderConfig)
|
||||||
->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
|
->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
|
||||||
&streamType, sizeof(SLint32));
|
&streamType, sizeof(SLint32));
|
||||||
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to set the android configuration to VOICE for the recorder. "
|
LOG("Failed to set the android configuration to VOICE for the recorder. "
|
||||||
"Error code: %lu", res);
|
"Error code: %lu",
|
||||||
|
res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -976,15 +996,16 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
// get the record interface
|
// get the record interface
|
||||||
res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
|
res = (*stm->recorderObj)
|
||||||
stm->context->SL_IID_RECORD,
|
->GetInterface(stm->recorderObj, stm->context->SL_IID_RECORD,
|
||||||
&stm->recorderItf);
|
&stm->recorderItf);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get recorder interface. Error code: %lu", res);
|
LOG("Failed to get recorder interface. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = (*stm->recorderItf)->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm);
|
res = (*stm->recorderItf)
|
||||||
|
->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to register recorder marker callback. Error code: %lu", res);
|
LOG("Failed to register recorder marker callback. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -992,17 +1013,22 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
|
|
||||||
(*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)0);
|
(*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)0);
|
||||||
|
|
||||||
res = (*stm->recorderItf)->SetCallbackEventsMask(stm->recorderItf, (SLuint32)SL_RECORDEVENT_HEADATMARKER);
|
res = (*stm->recorderItf)
|
||||||
|
->SetCallbackEventsMask(stm->recorderItf,
|
||||||
|
(SLuint32)SL_RECORDEVENT_HEADATMARKER);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to set headatmarker event mask. Error code: %lu", res);
|
LOG("Failed to set headatmarker event mask. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
// get the simple android buffer queue interface
|
// get the simple android buffer queue interface
|
||||||
res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
|
res = (*stm->recorderObj)
|
||||||
|
->GetInterface(stm->recorderObj,
|
||||||
stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
|
||||||
&stm->recorderBufferQueueItf);
|
&stm->recorderBufferQueueItf);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get recorder (android) buffer queue interface. Error code: %lu", res);
|
LOG("Failed to get recorder (android) buffer queue interface. Error code: "
|
||||||
|
"%lu",
|
||||||
|
res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,11 +1038,11 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
// Register full duplex callback instead.
|
// Register full duplex callback instead.
|
||||||
rec_callback = recorder_fullduplex_callback;
|
rec_callback = recorder_fullduplex_callback;
|
||||||
}
|
}
|
||||||
res = (*stm->recorderBufferQueueItf)->RegisterCallback(stm->recorderBufferQueueItf,
|
res = (*stm->recorderBufferQueueItf)
|
||||||
rec_callback,
|
->RegisterCallback(stm->recorderBufferQueueItf, rec_callback, stm);
|
||||||
stm);
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to register recorder buffer queue callback. Error code: %lu", res);
|
LOG("Failed to register recorder buffer queue callback. Error code: %lu",
|
||||||
|
res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1028,10 +1054,12 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
stm->input_array_capacity = NBUFS;
|
stm->input_array_capacity = NBUFS;
|
||||||
if (stm->output_enabled) {
|
if (stm->output_enabled) {
|
||||||
// Full duplex, update capacity to hold 1 sec of data
|
// Full duplex, update capacity to hold 1 sec of data
|
||||||
stm->input_array_capacity = 1 * stm->input_device_rate / stm->input_buffer_length;
|
stm->input_array_capacity =
|
||||||
|
1 * stm->input_device_rate / stm->input_buffer_length;
|
||||||
}
|
}
|
||||||
// Allocate input array
|
// Allocate input array
|
||||||
stm->input_buffer_array = (void**)calloc(1, sizeof(void*)*stm->input_array_capacity);
|
stm->input_buffer_array =
|
||||||
|
(void **)calloc(1, sizeof(void *) * stm->input_array_capacity);
|
||||||
// Buffering has not started yet.
|
// Buffering has not started yet.
|
||||||
stm->input_buffer_index = -1;
|
stm->input_buffer_index = -1;
|
||||||
// Prepare input buffers
|
// Prepare input buffers
|
||||||
|
@ -1059,14 +1087,17 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params)
|
||||||
|
{
|
||||||
assert(stm);
|
assert(stm);
|
||||||
assert(params);
|
assert(params);
|
||||||
|
|
||||||
stm->user_output_rate = params->rate;
|
stm->user_output_rate = params->rate;
|
||||||
if(params->format == CUBEB_SAMPLE_S16NE || params->format == CUBEB_SAMPLE_S16BE) {
|
if (params->format == CUBEB_SAMPLE_S16NE ||
|
||||||
|
params->format == CUBEB_SAMPLE_S16BE) {
|
||||||
stm->framesize = params->channels * sizeof(int16_t);
|
stm->framesize = params->channels * sizeof(int16_t);
|
||||||
} else if(params->format == CUBEB_SAMPLE_FLOAT32NE || params->format == CUBEB_SAMPLE_FLOAT32BE) {
|
} else if (params->format == CUBEB_SAMPLE_FLOAT32NE ||
|
||||||
|
params->format == CUBEB_SAMPLE_FLOAT32BE) {
|
||||||
stm->framesize = params->channels * sizeof(float);
|
stm->framesize = params->channels * sizeof(float);
|
||||||
}
|
}
|
||||||
stm->lastPosition = -1;
|
stm->lastPosition = -1;
|
||||||
|
@ -1124,13 +1155,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
uint32_t preferred_sampling_rate = stm->user_output_rate;
|
uint32_t preferred_sampling_rate = stm->user_output_rate;
|
||||||
SLresult res = SL_RESULT_CONTENT_UNSUPPORTED;
|
SLresult res = SL_RESULT_CONTENT_UNSUPPORTED;
|
||||||
if (preferred_sampling_rate) {
|
if (preferred_sampling_rate) {
|
||||||
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
|
res = (*stm->context->eng)
|
||||||
&stm->playerObj,
|
->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source,
|
||||||
&source,
|
&sink, NELEMS(ids), ids, req);
|
||||||
&sink,
|
|
||||||
NELEMS(ids),
|
|
||||||
ids,
|
|
||||||
req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample rate not supported? Try again with primary sample rate!
|
// Sample rate not supported? Try again with primary sample rate!
|
||||||
|
@ -1138,13 +1165,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
|
preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
|
||||||
preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
|
preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
|
||||||
*format_sample_rate = preferred_sampling_rate * 1000;
|
*format_sample_rate = preferred_sampling_rate * 1000;
|
||||||
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
|
res = (*stm->context->eng)
|
||||||
&stm->playerObj,
|
->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source,
|
||||||
&source,
|
&sink, NELEMS(ids), ids, req);
|
||||||
&sink,
|
|
||||||
NELEMS(ids),
|
|
||||||
ids,
|
|
||||||
req);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
|
@ -1160,7 +1183,8 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
stm->queuebuf_capacity = NBUFS;
|
stm->queuebuf_capacity = NBUFS;
|
||||||
if (stm->output_enabled) {
|
if (stm->output_enabled) {
|
||||||
// Full duplex, update capacity to hold 1 sec of data
|
// Full duplex, update capacity to hold 1 sec of data
|
||||||
stm->queuebuf_capacity = 1 * stm->output_configured_rate / stm->queuebuf_len;
|
stm->queuebuf_capacity =
|
||||||
|
1 * stm->output_configured_rate / stm->queuebuf_len;
|
||||||
}
|
}
|
||||||
// Allocate input array
|
// Allocate input array
|
||||||
stm->queuebuf = (void **)calloc(1, sizeof(void *) * stm->queuebuf_capacity);
|
stm->queuebuf = (void **)calloc(1, sizeof(void *) * stm->queuebuf_capacity);
|
||||||
|
@ -1177,7 +1201,8 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
stm->context->SL_IID_ANDROIDCONFIGURATION,
|
stm->context->SL_IID_ANDROIDCONFIGURATION,
|
||||||
&playerConfig);
|
&playerConfig);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get Android configuration interface. Error code: %lu", res);
|
LOG("Failed to get Android configuration interface. Error code: %lu",
|
||||||
|
res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1185,10 +1210,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
if (stm->voice_output) {
|
if (stm->voice_output) {
|
||||||
streamType = SL_ANDROID_STREAM_VOICE;
|
streamType = SL_ANDROID_STREAM_VOICE;
|
||||||
}
|
}
|
||||||
res = (*playerConfig)->SetConfiguration(playerConfig,
|
res = (*playerConfig)
|
||||||
SL_ANDROID_KEY_STREAM_TYPE,
|
->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE,
|
||||||
&streamType,
|
&streamType, sizeof(streamType));
|
||||||
sizeof(streamType));
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to set Android configuration to %d Error code: %lu",
|
LOG("Failed to set Android configuration to %d Error code: %lu",
|
||||||
streamType, res);
|
streamType, res);
|
||||||
|
@ -1199,13 +1223,14 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
|
performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = (*playerConfig)->SetConfiguration(playerConfig,
|
res = (*playerConfig)
|
||||||
SL_ANDROID_KEY_PERFORMANCE_MODE,
|
->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE,
|
||||||
&performanceMode,
|
&performanceMode, sizeof(performanceMode));
|
||||||
sizeof(performanceMode));
|
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to set Android performance mode to %d Error code: %lu. This is"
|
LOG("Failed to set Android performance mode to %d Error code: %lu. This "
|
||||||
" not fatal", performanceMode, res);
|
"is"
|
||||||
|
" not fatal",
|
||||||
|
performanceMode, res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1229,10 +1254,10 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
SLuint32 paramSize = sizeof(SLuint32);
|
SLuint32 paramSize = sizeof(SLuint32);
|
||||||
// The reported latency is in milliseconds.
|
// The reported latency is in milliseconds.
|
||||||
if (playerConfig) {
|
if (playerConfig) {
|
||||||
res = (*playerConfig)->GetConfiguration(playerConfig,
|
res = (*playerConfig)
|
||||||
|
->GetConfiguration(playerConfig,
|
||||||
(const SLchar *)"androidGetAudioLatency",
|
(const SLchar *)"androidGetAudioLatency",
|
||||||
¶mSize,
|
¶mSize, &audioLatency);
|
||||||
&audioLatency);
|
|
||||||
if (res == SL_RESULT_SUCCESS) {
|
if (res == SL_RESULT_SUCCESS) {
|
||||||
LOG("Got playback latency using android configuration extension");
|
LOG("Got playback latency using android configuration extension");
|
||||||
stm->output_latency_ms = audioLatency;
|
stm->output_latency_ms = audioLatency;
|
||||||
|
@ -1241,11 +1266,12 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
// `playerConfig` is available, but the above failed, or `playerConfig` is not
|
// `playerConfig` is available, but the above failed, or `playerConfig` is not
|
||||||
// available. In both cases, we need to acquire the output latency by an other
|
// available. In both cases, we need to acquire the output latency by an other
|
||||||
// mean.
|
// mean.
|
||||||
if ((playerConfig && res != SL_RESULT_SUCCESS) ||
|
if ((playerConfig && res != SL_RESULT_SUCCESS) || !playerConfig) {
|
||||||
!playerConfig) {
|
if (cubeb_output_latency_method_is_loaded(
|
||||||
if (cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
|
stm->context->p_output_latency_function)) {
|
||||||
LOG("Got playback latency using JNI");
|
LOG("Got playback latency using JNI");
|
||||||
stm->output_latency_ms = cubeb_get_output_latency(stm->context->p_output_latency_function);
|
stm->output_latency_ms =
|
||||||
|
cubeb_get_output_latency(stm->context->p_output_latency_function);
|
||||||
} else {
|
} else {
|
||||||
LOG("No alternate latency querying method loaded, A/V sync will be off.");
|
LOG("No alternate latency querying method loaded, A/V sync will be off.");
|
||||||
stm->output_latency_ms = 0;
|
stm->output_latency_ms = 0;
|
||||||
|
@ -1254,24 +1280,24 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
|
|
||||||
LOG("Audio output latency: %dms", stm->output_latency_ms);
|
LOG("Audio output latency: %dms", stm->output_latency_ms);
|
||||||
|
|
||||||
res = (*stm->playerObj)->GetInterface(stm->playerObj,
|
res =
|
||||||
stm->context->SL_IID_PLAY,
|
(*stm->playerObj)
|
||||||
&stm->play);
|
->GetInterface(stm->playerObj, stm->context->SL_IID_PLAY, &stm->play);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get play interface. Error code: %lu", res);
|
LOG("Failed to get play interface. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = (*stm->playerObj)->GetInterface(stm->playerObj,
|
res = (*stm->playerObj)
|
||||||
stm->context->SL_IID_BUFFERQUEUE,
|
->GetInterface(stm->playerObj, stm->context->SL_IID_BUFFERQUEUE,
|
||||||
&stm->bufq);
|
&stm->bufq);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get bufferqueue interface. Error code: %lu", res);
|
LOG("Failed to get bufferqueue interface. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
res = (*stm->playerObj)->GetInterface(stm->playerObj,
|
res = (*stm->playerObj)
|
||||||
stm->context->SL_IID_VOLUME,
|
->GetInterface(stm->playerObj, stm->context->SL_IID_VOLUME,
|
||||||
&stm->volume);
|
&stm->volume);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to get volume interface. Error code: %lu", res);
|
LOG("Failed to get volume interface. Error code: %lu", res);
|
||||||
|
@ -1287,7 +1313,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
|
||||||
// Work around wilhelm/AudioTrack badness, bug 1221228
|
// Work around wilhelm/AudioTrack badness, bug 1221228
|
||||||
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0);
|
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0);
|
||||||
|
|
||||||
res = (*stm->play)->SetCallbackEventsMask(stm->play, (SLuint32)SL_PLAYEVENT_HEADATMARKER);
|
res = (*stm->play)
|
||||||
|
->SetCallbackEventsMask(stm->play,
|
||||||
|
(SLuint32)SL_PLAYEVENT_HEADATMARKER);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to set headatmarker event mask. Error code: %lu", res);
|
LOG("Failed to set headatmarker event mask. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -1325,37 +1353,37 @@ opensl_validate_stream_param(cubeb_stream_params * stream_params)
|
||||||
(stream_params->channels < 1 || stream_params->channels > 32))) {
|
(stream_params->channels < 1 || stream_params->channels > 32))) {
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
if ((stream_params &&
|
if ((stream_params && (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
|
||||||
(stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
|
|
||||||
LOG("Loopback is not supported");
|
LOG("Loopback is not supported");
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int has_pref_set(cubeb_stream_params* input_params,
|
int
|
||||||
cubeb_stream_params* output_params,
|
has_pref_set(cubeb_stream_params * input_params,
|
||||||
cubeb_stream_prefs pref)
|
cubeb_stream_params * output_params, cubeb_stream_prefs pref)
|
||||||
{
|
{
|
||||||
return (input_params && input_params->prefs & pref) ||
|
return (input_params && input_params->prefs & pref) ||
|
||||||
(output_params && output_params->prefs & pref);
|
(output_params && output_params->prefs & pref);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
|
opensl_stream_init(cubeb * ctx, cubeb_stream ** stream,
|
||||||
cubeb_devid input_device,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames,
|
||||||
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
|
cubeb_data_callback data_callback,
|
||||||
void * user_ptr)
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
{
|
{
|
||||||
cubeb_stream * stm;
|
cubeb_stream * stm;
|
||||||
|
|
||||||
assert(ctx);
|
assert(ctx);
|
||||||
if (input_device || output_device) {
|
if (input_device || output_device) {
|
||||||
LOG("Device selection is not supported in Android. The default will be used");
|
LOG("Device selection is not supported in Android. The default will be "
|
||||||
|
"used");
|
||||||
}
|
}
|
||||||
|
|
||||||
*stream = NULL;
|
*stream = NULL;
|
||||||
|
@ -1378,14 +1406,18 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
||||||
stm->data_callback = data_callback;
|
stm->data_callback = data_callback;
|
||||||
stm->state_callback = state_callback;
|
stm->state_callback = state_callback;
|
||||||
stm->user_ptr = user_ptr;
|
stm->user_ptr = user_ptr;
|
||||||
stm->buffer_size_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
|
stm->buffer_size_frames =
|
||||||
|
latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
|
||||||
stm->input_enabled = (input_stream_params) ? 1 : 0;
|
stm->input_enabled = (input_stream_params) ? 1 : 0;
|
||||||
stm->output_enabled = (output_stream_params) ? 1 : 0;
|
stm->output_enabled = (output_stream_params) ? 1 : 0;
|
||||||
stm->shutdown = 1;
|
stm->shutdown = 1;
|
||||||
stm->voice_input = has_pref_set(input_stream_params, NULL, CUBEB_STREAM_PREF_VOICE);
|
stm->voice_input =
|
||||||
stm->voice_output = has_pref_set(NULL, output_stream_params, CUBEB_STREAM_PREF_VOICE);
|
has_pref_set(input_stream_params, NULL, CUBEB_STREAM_PREF_VOICE);
|
||||||
|
stm->voice_output =
|
||||||
|
has_pref_set(NULL, output_stream_params, CUBEB_STREAM_PREF_VOICE);
|
||||||
|
|
||||||
LOG("cubeb stream prefs: voice_input: %s voice_output: %s", stm->voice_input ? "true" : "false",
|
LOG("cubeb stream prefs: voice_input: %s voice_output: %s",
|
||||||
|
stm->voice_input ? "true" : "false",
|
||||||
stm->voice_output ? "true" : "false");
|
stm->voice_output ? "true" : "false");
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
@ -1399,7 +1431,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
|
|
||||||
if (output_stream_params) {
|
if (output_stream_params) {
|
||||||
LOG("Playback params: Rate %d, channels %d, format %d, latency in frames %d.",
|
LOG("Playback params: Rate %d, channels %d, format %d, latency in frames "
|
||||||
|
"%d.",
|
||||||
output_stream_params->rate, output_stream_params->channels,
|
output_stream_params->rate, output_stream_params->channels,
|
||||||
output_stream_params->format, stm->buffer_size_frames);
|
output_stream_params->format, stm->buffer_size_frames);
|
||||||
r = opensl_configure_playback(stm, output_stream_params);
|
r = opensl_configure_playback(stm, output_stream_params);
|
||||||
|
@ -1410,7 +1443,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_stream_params) {
|
if (input_stream_params) {
|
||||||
LOG("Capture params: Rate %d, channels %d, format %d, latency in frames %d.",
|
LOG("Capture params: Rate %d, channels %d, format %d, latency in frames "
|
||||||
|
"%d.",
|
||||||
input_stream_params->rate, input_stream_params->channels,
|
input_stream_params->rate, input_stream_params->channels,
|
||||||
input_stream_params->format, stm->buffer_size_frames);
|
input_stream_params->format, stm->buffer_size_frames);
|
||||||
r = opensl_configure_capture(stm, input_stream_params);
|
r = opensl_configure_capture(stm, input_stream_params);
|
||||||
|
@ -1442,13 +1476,11 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
|
||||||
output_params.rate = stm->output_configured_rate;
|
output_params.rate = stm->output_configured_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
stm->resampler = cubeb_resampler_create(stm,
|
stm->resampler = cubeb_resampler_create(
|
||||||
input_stream_params ? &input_params : NULL,
|
stm, input_stream_params ? &input_params : NULL,
|
||||||
output_stream_params ? &output_params : NULL,
|
output_stream_params ? &output_params : NULL, target_sample_rate,
|
||||||
target_sample_rate,
|
data_callback, user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT,
|
||||||
data_callback,
|
CUBEB_RESAMPLER_RECLOCK_NONE);
|
||||||
user_ptr,
|
|
||||||
CUBEB_RESAMPLER_QUALITY_DEFAULT);
|
|
||||||
if (!stm->resampler) {
|
if (!stm->resampler) {
|
||||||
LOG("Failed to create resampler");
|
LOG("Failed to create resampler");
|
||||||
opensl_stream_destroy(stm);
|
opensl_stream_destroy(stm);
|
||||||
|
@ -1483,7 +1515,9 @@ opensl_start_recorder(cubeb_stream * stm)
|
||||||
SLuint32 recorderState;
|
SLuint32 recorderState;
|
||||||
(*stm->recorderObj)->GetState(stm->recorderObj, &recorderState);
|
(*stm->recorderObj)->GetState(stm->recorderObj, &recorderState);
|
||||||
if (recorderState == SL_OBJECT_STATE_REALIZED) {
|
if (recorderState == SL_OBJECT_STATE_REALIZED) {
|
||||||
SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING);
|
SLresult res =
|
||||||
|
(*stm->recorderItf)
|
||||||
|
->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to start recorder. Error code: %lu", res);
|
LOG("Failed to start recorder. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -1544,7 +1578,8 @@ opensl_stop_recorder(cubeb_stream * stm)
|
||||||
assert(stm->recorderObj);
|
assert(stm->recorderObj);
|
||||||
assert(stm->shutdown || stm->draining);
|
assert(stm->shutdown || stm->draining);
|
||||||
|
|
||||||
SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED);
|
SLresult res = (*stm->recorderItf)
|
||||||
|
->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to stop recorder. Error code: %lu", res);
|
LOG("Failed to stop recorder. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -1590,7 +1625,8 @@ opensl_destroy_recorder(cubeb_stream * stm)
|
||||||
assert(stm->recorderObj);
|
assert(stm->recorderObj);
|
||||||
|
|
||||||
if (stm->recorderBufferQueueItf) {
|
if (stm->recorderBufferQueueItf) {
|
||||||
SLresult res = (*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf);
|
SLresult res =
|
||||||
|
(*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf);
|
||||||
if (res != SL_RESULT_SUCCESS) {
|
if (res != SL_RESULT_SUCCESS) {
|
||||||
LOG("Failed to clear recorder buffer queue. Error code: %lu", res);
|
LOG("Failed to clear recorder buffer queue. Error code: %lu", res);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -1658,7 +1694,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||||
if (stm->lastPosition == msec) {
|
if (stm->lastPosition == msec) {
|
||||||
compensation_msec =
|
compensation_msec =
|
||||||
(t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000;
|
(t.tv_sec * 1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) /
|
||||||
|
1000000;
|
||||||
} else {
|
} else {
|
||||||
stm->lastPositionTimeStamp = t.tv_sec * 1000000000LL + t.tv_nsec;
|
stm->lastPositionTimeStamp = t.tv_sec * 1000000000LL + t.tv_nsec;
|
||||||
stm->lastPosition = msec;
|
stm->lastPosition = msec;
|
||||||
|
@ -1668,7 +1705,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||||
uint32_t output_latency = stm->output_latency_ms;
|
uint32_t output_latency = stm->output_latency_ms;
|
||||||
|
|
||||||
pthread_mutex_lock(&stm->mutex);
|
pthread_mutex_lock(&stm->mutex);
|
||||||
int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate;
|
int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate /
|
||||||
|
stm->output_configured_rate;
|
||||||
pthread_mutex_unlock(&stm->mutex);
|
pthread_mutex_unlock(&stm->mutex);
|
||||||
assert(maximum_position >= 0);
|
assert(maximum_position >= 0);
|
||||||
|
|
||||||
|
@ -1683,8 +1721,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||||
samplerate * (msec - output_latency + compensation_msec) / 1000;
|
samplerate * (msec - output_latency + compensation_msec) / 1000;
|
||||||
stm->lastCompensativePosition = msec + compensation_msec;
|
stm->lastCompensativePosition = msec + compensation_msec;
|
||||||
}
|
}
|
||||||
*position = unadjusted_position < maximum_position ?
|
*position = unadjusted_position < maximum_position ? unadjusted_position
|
||||||
unadjusted_position : maximum_position;
|
: maximum_position;
|
||||||
} else {
|
} else {
|
||||||
*position = 0;
|
*position = 0;
|
||||||
}
|
}
|
||||||
|
@ -1747,7 +1785,6 @@ static struct cubeb_ops const opensl_ops = {
|
||||||
.stream_destroy = opensl_stream_destroy,
|
.stream_destroy = opensl_stream_destroy,
|
||||||
.stream_start = opensl_stream_start,
|
.stream_start = opensl_stream_start,
|
||||||
.stream_stop = opensl_stream_stop,
|
.stream_stop = opensl_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = opensl_stream_get_position,
|
.stream_get_position = opensl_stream_get_position,
|
||||||
.stream_get_latency = opensl_stream_get_latency,
|
.stream_get_latency = opensl_stream_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
@ -1756,5 +1793,4 @@ static struct cubeb_ops const opensl_ops = {
|
||||||
.stream_get_current_device = NULL,
|
.stream_get_current_device = NULL,
|
||||||
.stream_device_destroy = NULL,
|
.stream_device_destroy = NULL,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = NULL
|
.register_device_collection_changed = NULL};
|
||||||
};
|
|
||||||
|
|
|
@ -10,25 +10,25 @@
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include "cubeb-internal.h"
|
||||||
#include <ctype.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <errno.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/soundcard.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 <poll.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
#include "cubeb/cubeb.h"
|
||||||
#include "cubeb_mixer.h"
|
#include "cubeb_mixer.h"
|
||||||
#include "cubeb_strings.h"
|
#include "cubeb_strings.h"
|
||||||
#include "cubeb-internal.h"
|
#include <assert.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <poll.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/soundcard.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
/* Supported well by most hardware. */
|
/* Supported well by most hardware. */
|
||||||
#ifndef OSS_PREFER_RATE
|
#ifndef OSS_PREFER_RATE
|
||||||
|
@ -96,6 +96,8 @@ struct oss_stream {
|
||||||
oss_devnode_t name;
|
oss_devnode_t name;
|
||||||
int fd;
|
int fd;
|
||||||
void * buf;
|
void * buf;
|
||||||
|
unsigned int bufframes;
|
||||||
|
unsigned int maxframes;
|
||||||
|
|
||||||
struct stream_info {
|
struct stream_info {
|
||||||
int channels;
|
int channels;
|
||||||
|
@ -126,9 +128,6 @@ struct cubeb_stream {
|
||||||
cubeb_data_callback data_cb;
|
cubeb_data_callback data_cb;
|
||||||
cubeb_state_callback state_cb;
|
cubeb_state_callback state_cb;
|
||||||
uint64_t frames_written /* (m) */;
|
uint64_t frames_written /* (m) */;
|
||||||
unsigned int nfr; /* Number of frames allocated */
|
|
||||||
unsigned int nfrags;
|
|
||||||
unsigned int bufframes;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static char const *
|
static char const *
|
||||||
|
@ -142,7 +141,8 @@ oss_cubeb_devid_intern(cubeb *context, char const * devid)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
oss_init(cubeb **context, char const *context_name) {
|
oss_init(cubeb ** context, char const * context_name)
|
||||||
|
{
|
||||||
cubeb * c;
|
cubeb * c;
|
||||||
|
|
||||||
(void)context_name;
|
(void)context_name;
|
||||||
|
@ -230,8 +230,8 @@ oss_free_cubeb_device_info_strings(cubeb_device_info *cdi)
|
||||||
* Return 0 if OK, otherwise 1.
|
* Return 0 if OK, otherwise 1.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
oss_probe_open(const char *dsppath, cubeb_device_type type,
|
oss_probe_open(const char * dsppath, cubeb_device_type type, int * fdp,
|
||||||
int *fdp, oss_audioinfo *resai)
|
oss_audioinfo * resai)
|
||||||
{
|
{
|
||||||
oss_audioinfo ai;
|
oss_audioinfo ai;
|
||||||
int error;
|
int error;
|
||||||
|
@ -286,7 +286,7 @@ oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo)
|
||||||
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
|
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
snprintf(res.devname, sizeof(res.devname), "/dev/");
|
strlcpy(res.devname, "/dev/", sizeof(res.devname));
|
||||||
strncat(res.devname, matchptr, n - matchptr);
|
strncat(res.devname, matchptr, n - matchptr);
|
||||||
}
|
}
|
||||||
matchptr = n + 1;
|
matchptr = n + 1;
|
||||||
|
@ -373,7 +373,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
skipall = 0;
|
skipall = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) {
|
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR,
|
||||||
|
strlen(SNDSTAT_USER_BEGIN_STR))) {
|
||||||
is_ud = 1;
|
is_ud = 1;
|
||||||
skipall = 0;
|
skipall = 0;
|
||||||
continue;
|
continue;
|
||||||
|
@ -433,8 +434,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
|
|
||||||
collection_cnt++;
|
collection_cnt++;
|
||||||
|
|
||||||
void *newp = reallocarray(devinfop, collection_cnt + 1,
|
void * newp =
|
||||||
sizeof(cubeb_device_info));
|
reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info));
|
||||||
if (newp == NULL)
|
if (newp == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
devinfop = newp;
|
devinfop = newp;
|
||||||
|
@ -476,7 +477,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
|
|
||||||
error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si);
|
error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si);
|
||||||
if (error) {
|
if (error) {
|
||||||
LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno);
|
LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d",
|
||||||
|
OSS_DEFAULT_MIXER, errno);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,7 +650,8 @@ oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
/* Mono layout is an exception */
|
/* Mono layout is an exception */
|
||||||
if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) {
|
if (params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||||
|
params->layout != CUBEB_LAYOUT_MONO) {
|
||||||
chnorder = oss_cubeb_layout_to_chnorder(params->layout);
|
chnorder = oss_cubeb_layout_to_chnorder(params->layout);
|
||||||
if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1)
|
if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1)
|
||||||
LOG("Non-fatal error %d occured when setting channel order.", errno);
|
LOG("Non-fatal error %d occured when setting channel order.", errno);
|
||||||
|
@ -748,7 +751,8 @@ oss_get_rec_frames(cubeb_stream * s, unsigned int nframes)
|
||||||
size_t read_ofs = 0;
|
size_t read_ofs = 0;
|
||||||
while (rem > 0) {
|
while (rem > 0) {
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) < 0) {
|
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) <
|
||||||
|
0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -759,7 +763,6 @@ oss_get_rec_frames(cubeb_stream * s, unsigned int nframes)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
oss_put_play_frames(cubeb_stream * s, unsigned int nframes)
|
oss_put_play_frames(cubeb_stream * s, unsigned int nframes)
|
||||||
{
|
{
|
||||||
|
@ -781,15 +784,84 @@ oss_put_play_frames(cubeb_stream * s, unsigned int nframes)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
oss_wait_fds_for_space(cubeb_stream * s, long * nfrp)
|
||||||
|
{
|
||||||
|
audio_buf_info bi;
|
||||||
|
struct pollfd pfds[2];
|
||||||
|
long nfr, tnfr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert(s->play.fd != -1 || s->record.fd != -1);
|
||||||
|
pfds[0].events = POLLOUT | POLLHUP;
|
||||||
|
pfds[0].revents = 0;
|
||||||
|
pfds[0].fd = s->play.fd;
|
||||||
|
pfds[1].events = POLLIN | POLLHUP;
|
||||||
|
pfds[1].revents = 0;
|
||||||
|
pfds[1].fd = s->record.fd;
|
||||||
|
|
||||||
|
retry:
|
||||||
|
nfr = LONG_MAX;
|
||||||
|
|
||||||
|
if (poll(pfds, 2, 1000) == -1) {
|
||||||
|
return CUBEB_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
if (pfds[i].revents & POLLHUP) {
|
||||||
|
return CUBEB_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->play.fd != -1) {
|
||||||
|
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
|
||||||
|
return CUBEB_STATE_ERROR;
|
||||||
|
}
|
||||||
|
tnfr = bi.bytes / s->play.frame_size;
|
||||||
|
if (tnfr <= 0) {
|
||||||
|
/* too little space - stop polling record, if any */
|
||||||
|
pfds[0].fd = s->play.fd;
|
||||||
|
pfds[1].fd = -1;
|
||||||
|
goto retry;
|
||||||
|
} else if (tnfr > (long)s->play.maxframes) {
|
||||||
|
/* too many frames available - limit */
|
||||||
|
tnfr = (long)s->play.maxframes;
|
||||||
|
}
|
||||||
|
if (nfr > tnfr) {
|
||||||
|
nfr = tnfr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s->record.fd != -1) {
|
||||||
|
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi) == -1) {
|
||||||
|
return CUBEB_STATE_ERROR;
|
||||||
|
}
|
||||||
|
tnfr = bi.bytes / s->record.frame_size;
|
||||||
|
if (tnfr <= 0) {
|
||||||
|
/* too little space - stop polling playback, if any */
|
||||||
|
pfds[0].fd = -1;
|
||||||
|
pfds[1].fd = s->record.fd;
|
||||||
|
goto retry;
|
||||||
|
} else if (tnfr > (long)s->record.maxframes) {
|
||||||
|
/* too many frames available - limit */
|
||||||
|
tnfr = (long)s->record.maxframes;
|
||||||
|
}
|
||||||
|
if (nfr > tnfr) {
|
||||||
|
nfr = tnfr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*nfrp = nfr;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 1 - Stopped by cubeb_stream_stop, otherwise 0 */
|
/* 1 - Stopped by cubeb_stream_stop, otherwise 0 */
|
||||||
static int
|
static int
|
||||||
oss_audio_loop(cubeb_stream * s, cubeb_state * new_state)
|
oss_audio_loop(cubeb_stream * s, cubeb_state * new_state)
|
||||||
{
|
{
|
||||||
cubeb_state state = CUBEB_STATE_STOPPED;
|
cubeb_state state = CUBEB_STATE_STOPPED;
|
||||||
int trig = 0;
|
int trig = 0, drain = 0;
|
||||||
int drain = 0;
|
|
||||||
const bool play_on = s->play.fd != -1, record_on = s->record.fd != -1;
|
const bool play_on = s->play.fd != -1, record_on = s->record.fd != -1;
|
||||||
long nfr = s->bufframes;
|
long nfr = 0;
|
||||||
|
|
||||||
if (record_on) {
|
if (record_on) {
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) {
|
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) {
|
||||||
|
@ -797,14 +869,15 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
goto breakdown;
|
goto breakdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
trig |= PCM_ENABLE_INPUT;
|
trig |= PCM_ENABLE_INPUT;
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) {
|
memset(s->record.buf, 0, s->record.bufframes * s->record.frame_size);
|
||||||
|
|
||||||
|
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
|
||||||
LOG("Error %d occured when setting trigger on record fd", errno);
|
LOG("Error %d occured when setting trigger on record fd", errno);
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
goto breakdown;
|
goto breakdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(s->record.buf, 0, s->bufframes * s->record.frame_size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!play_on && !record_on) {
|
if (!play_on && !record_on) {
|
||||||
|
@ -826,12 +899,36 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
|
||||||
|
|
||||||
long got = 0;
|
long got = 0;
|
||||||
if (nfr > 0) {
|
if (nfr > 0) {
|
||||||
|
if (record_on) {
|
||||||
|
if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) {
|
||||||
|
state = CUBEB_STATE_ERROR;
|
||||||
|
goto breakdown;
|
||||||
|
}
|
||||||
|
if (s->record.floating) {
|
||||||
|
oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr);
|
got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr);
|
||||||
if (got == CUBEB_ERROR) {
|
if (got == CUBEB_ERROR) {
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
goto breakdown;
|
goto breakdown;
|
||||||
}
|
}
|
||||||
if (play_on) {
|
if (got < nfr) {
|
||||||
|
if (s->play.fd != -1) {
|
||||||
|
drain = 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* This is a record-only stream and number of frames
|
||||||
|
* returned from data_cb() is smaller than number
|
||||||
|
* of frames required to read. Stop here.
|
||||||
|
*/
|
||||||
|
state = CUBEB_STATE_STOPPED;
|
||||||
|
goto breakdown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (got > 0 && play_on) {
|
||||||
float vol;
|
float vol;
|
||||||
|
|
||||||
pthread_mutex_lock(&s->mtx);
|
pthread_mutex_lock(&s->mtx);
|
||||||
|
@ -844,26 +941,7 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
|
||||||
oss_linear16_set_vol((int16_t *)s->play.buf,
|
oss_linear16_set_vol((int16_t *)s->play.buf,
|
||||||
s->play.info.channels * got, vol);
|
s->play.info.channels * got, vol);
|
||||||
}
|
}
|
||||||
}
|
if (oss_put_play_frames(s, got) == CUBEB_ERROR) {
|
||||||
if (got < nfr) {
|
|
||||||
if (s->play.fd != -1) {
|
|
||||||
drain = 1;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* This is a record-only stream and number of frames
|
|
||||||
* returned from data_cb() is smaller than number
|
|
||||||
* of frames required to read. Stop here.
|
|
||||||
*/
|
|
||||||
|
|
||||||
state = CUBEB_STATE_STOPPED;
|
|
||||||
goto breakdown;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nfr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (got > 0) {
|
|
||||||
if (play_on && oss_put_play_frames(s, got) < 0) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
goto breakdown;
|
goto breakdown;
|
||||||
}
|
}
|
||||||
|
@ -872,35 +950,12 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
|
||||||
state = CUBEB_STATE_DRAINED;
|
state = CUBEB_STATE_DRAINED;
|
||||||
goto breakdown;
|
goto breakdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_buf_info bi;
|
|
||||||
if (play_on) {
|
|
||||||
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi)) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
|
||||||
goto breakdown;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
* In duplex mode, playback direction drives recording direction to
|
|
||||||
* prevent building up latencies.
|
|
||||||
*/
|
|
||||||
nfr = bi.fragsize * bi.fragments / s->play.frame_size;
|
|
||||||
if (nfr > s->bufframes) {
|
|
||||||
nfr = s->bufframes;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (record_on) {
|
if (oss_wait_fds_for_space(s, &nfr) != 0) {
|
||||||
if (nfr == 0) {
|
|
||||||
nfr = s->nfr;
|
|
||||||
}
|
|
||||||
if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) {
|
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
goto breakdown;
|
goto breakdown;
|
||||||
}
|
}
|
||||||
if (s->record.floating) {
|
|
||||||
oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -957,9 +1012,10 @@ static inline int
|
||||||
oss_calc_frag_shift(unsigned int frames, unsigned int frame_size)
|
oss_calc_frag_shift(unsigned int frames, unsigned int frame_size)
|
||||||
{
|
{
|
||||||
int n = 4;
|
int n = 4;
|
||||||
int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS;
|
int blksize = frames * frame_size;
|
||||||
while ((1 << n) < blksize)
|
while ((1 << n) < blksize) {
|
||||||
n++;
|
n++;
|
||||||
|
}
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -970,20 +1026,15 @@ oss_get_frag_params(unsigned int shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
oss_stream_init(cubeb * context,
|
oss_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_stream ** stream,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
int ret = CUBEB_OK;
|
int ret = CUBEB_OK;
|
||||||
unsigned int playnfr = 0, recnfr = 0;
|
|
||||||
cubeb_stream * s = NULL;
|
cubeb_stream * s = NULL;
|
||||||
const char * defdsp;
|
const char * defdsp;
|
||||||
|
|
||||||
|
@ -997,19 +1048,20 @@ oss_stream_init(cubeb * context,
|
||||||
}
|
}
|
||||||
s->state = CUBEB_STATE_STOPPED;
|
s->state = CUBEB_STATE_STOPPED;
|
||||||
s->record.fd = s->play.fd = -1;
|
s->record.fd = s->play.fd = -1;
|
||||||
s->nfr = latency_frames;
|
|
||||||
if (input_device != NULL) {
|
if (input_device != NULL) {
|
||||||
snprintf(s->record.name, sizeof(s->record.name), "%s", input_device);
|
strlcpy(s->record.name, input_device, sizeof(s->record.name));
|
||||||
} else {
|
} else {
|
||||||
snprintf(s->record.name, sizeof(s->record.name), "%s", defdsp);
|
strlcpy(s->record.name, defdsp, sizeof(s->record.name));
|
||||||
}
|
}
|
||||||
if (output_device != NULL) {
|
if (output_device != NULL) {
|
||||||
snprintf(s->play.name, sizeof(s->play.name), "%s", output_device);
|
strlcpy(s->play.name, output_device, sizeof(s->play.name));
|
||||||
} else {
|
} else {
|
||||||
snprintf(s->play.name, sizeof(s->play.name), "%s", defdsp);
|
strlcpy(s->play.name, defdsp, sizeof(s->play.name));
|
||||||
}
|
}
|
||||||
if (input_stream_params != NULL) {
|
if (input_stream_params != NULL) {
|
||||||
unsigned int nb_channels;
|
unsigned int nb_channels;
|
||||||
|
uint32_t minframes;
|
||||||
|
|
||||||
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||||
LOG("Loopback not supported");
|
LOG("Loopback not supported");
|
||||||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
|
@ -1018,49 +1070,57 @@ oss_stream_init(cubeb * context,
|
||||||
nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout);
|
nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout);
|
||||||
if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||||
nb_channels != input_stream_params->channels) {
|
nb_channels != input_stream_params->channels) {
|
||||||
LOG("input_stream_params->layout does not match input_stream_params->channels");
|
LOG("input_stream_params->layout does not match "
|
||||||
|
"input_stream_params->channels");
|
||||||
ret = CUBEB_ERROR_INVALID_PARAMETER;
|
ret = CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (s->record.fd == -1) {
|
|
||||||
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
|
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
|
||||||
LOG("Audio device \"%s\" could not be opened as read-only",
|
LOG("Audio device \"%s\" could not be opened as read-only",
|
||||||
s->record.name);
|
s->record.name);
|
||||||
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ((ret = oss_copy_params(s->record.fd, s, input_stream_params,
|
if ((ret = oss_copy_params(s->record.fd, s, input_stream_params,
|
||||||
&s->record.info)) != CUBEB_OK) {
|
&s->record.info)) != CUBEB_OK) {
|
||||||
LOG("Setting record params failed");
|
LOG("Setting record params failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
s->record.floating =
|
||||||
s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8);
|
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||||
recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size;
|
s->record.frame_size =
|
||||||
|
s->record.info.channels * (s->record.info.precision / 8);
|
||||||
|
s->record.bufframes = latency_frames;
|
||||||
|
|
||||||
|
oss_get_min_latency(context, *input_stream_params, &minframes);
|
||||||
|
if (s->record.bufframes < minframes) {
|
||||||
|
s->record.bufframes = minframes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (output_stream_params != NULL) {
|
if (output_stream_params != NULL) {
|
||||||
unsigned int nb_channels;
|
unsigned int nb_channels;
|
||||||
|
uint32_t minframes;
|
||||||
|
|
||||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||||
LOG("Loopback not supported");
|
LOG("Loopback not supported");
|
||||||
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
ret = CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout);
|
nb_channels =
|
||||||
|
cubeb_channel_layout_nb_channels(output_stream_params->layout);
|
||||||
if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||||
nb_channels != output_stream_params->channels) {
|
nb_channels != output_stream_params->channels) {
|
||||||
LOG("output_stream_params->layout does not match output_stream_params->channels");
|
LOG("output_stream_params->layout does not match "
|
||||||
|
"output_stream_params->channels");
|
||||||
ret = CUBEB_ERROR_INVALID_PARAMETER;
|
ret = CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
if (s->play.fd == -1) {
|
|
||||||
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
|
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
|
||||||
LOG("Audio device \"%s\" could not be opened as write-only",
|
LOG("Audio device \"%s\" could not be opened as write-only",
|
||||||
s->play.name);
|
s->play.name);
|
||||||
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if ((ret = oss_copy_params(s->play.fd, s, output_stream_params,
|
if ((ret = oss_copy_params(s->play.fd, s, output_stream_params,
|
||||||
&s->play.info)) != CUBEB_OK) {
|
&s->play.info)) != CUBEB_OK) {
|
||||||
LOG("Setting play params failed");
|
LOG("Setting play params failed");
|
||||||
|
@ -1068,17 +1128,16 @@ oss_stream_init(cubeb * context,
|
||||||
}
|
}
|
||||||
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||||
s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8);
|
s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8);
|
||||||
playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size;
|
s->play.bufframes = latency_frames;
|
||||||
|
|
||||||
|
oss_get_min_latency(context, *output_stream_params, &minframes);
|
||||||
|
if (s->play.bufframes < minframes) {
|
||||||
|
s->play.bufframes = minframes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
* Use the largest nframes among playing and recording streams to set OSS buffer size.
|
|
||||||
* After that, use the smallest allocated nframes among both direction to allocate our
|
|
||||||
* temporary buffers.
|
|
||||||
*/
|
|
||||||
s->nfr = (playnfr > recnfr) ? playnfr : recnfr;
|
|
||||||
s->nfrags = OSS_NFRAGS;
|
|
||||||
if (s->play.fd != -1) {
|
if (s->play.fd != -1) {
|
||||||
int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->play.frame_size));
|
int frag = oss_get_frag_params(
|
||||||
|
oss_calc_frag_shift(s->play.bufframes, s->play.frame_size));
|
||||||
if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
if (ioctl(s->play.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
||||||
LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
||||||
frag);
|
frag);
|
||||||
|
@ -1086,12 +1145,28 @@ oss_stream_init(cubeb * context,
|
||||||
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi))
|
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi))
|
||||||
LOG("Failed to get play fd's buffer info.");
|
LOG("Failed to get play fd's buffer info.");
|
||||||
else {
|
else {
|
||||||
if (bi.fragsize / s->play.frame_size < s->nfr)
|
s->play.bufframes = (bi.fragsize * bi.fragstotal) / s->play.frame_size;
|
||||||
s->nfr = bi.fragsize / s->play.frame_size;
|
|
||||||
}
|
}
|
||||||
|
int lw;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Force 32 ms service intervals at most, or when recording is
|
||||||
|
* active, use the recording service intervals as a reference.
|
||||||
|
*/
|
||||||
|
s->play.maxframes = (32 * output_stream_params->rate) / 1000;
|
||||||
|
if (s->record.fd != -1 || s->play.maxframes >= s->play.bufframes) {
|
||||||
|
lw = s->play.frame_size; /* Feed data when possible. */
|
||||||
|
s->play.maxframes = s->play.bufframes;
|
||||||
|
} else {
|
||||||
|
lw = (s->play.bufframes - s->play.maxframes) * s->play.frame_size;
|
||||||
|
}
|
||||||
|
if (ioctl(s->play.fd, SNDCTL_DSP_LOW_WATER, &lw))
|
||||||
|
LOG("Audio device \"%s\" (play) could not set trigger threshold",
|
||||||
|
s->play.name);
|
||||||
}
|
}
|
||||||
if (s->record.fd != -1) {
|
if (s->record.fd != -1) {
|
||||||
int frag = oss_get_frag_params(oss_calc_frag_shift(s->nfr, s->record.frame_size));
|
int frag = oss_get_frag_params(
|
||||||
|
oss_calc_frag_shift(s->record.bufframes, s->record.frame_size));
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
if (ioctl(s->record.fd, SNDCTL_DSP_SETFRAGMENT, &frag))
|
||||||
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
|
||||||
frag);
|
frag);
|
||||||
|
@ -1099,11 +1174,16 @@ oss_stream_init(cubeb * context,
|
||||||
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi))
|
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi))
|
||||||
LOG("Failed to get record fd's buffer info.");
|
LOG("Failed to get record fd's buffer info.");
|
||||||
else {
|
else {
|
||||||
if (bi.fragsize / s->record.frame_size < s->nfr)
|
s->record.bufframes =
|
||||||
s->nfr = bi.fragsize / s->record.frame_size;
|
(bi.fragsize * bi.fragstotal) / s->record.frame_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s->record.maxframes = s->record.bufframes;
|
||||||
|
int lw = s->record.frame_size;
|
||||||
|
if (ioctl(s->record.fd, SNDCTL_DSP_LOW_WATER, &lw))
|
||||||
|
LOG("Audio device \"%s\" (record) could not set trigger threshold",
|
||||||
|
s->record.name);
|
||||||
}
|
}
|
||||||
s->bufframes = s->nfr * s->nfrags;
|
|
||||||
s->context = context;
|
s->context = context;
|
||||||
s->volume = 1.0;
|
s->volume = 1.0;
|
||||||
s->state_cb = state_callback;
|
s->state_cb = state_callback;
|
||||||
|
@ -1125,13 +1205,14 @@ oss_stream_init(cubeb * context,
|
||||||
s->doorbell = false;
|
s->doorbell = false;
|
||||||
|
|
||||||
if (s->play.fd != -1) {
|
if (s->play.fd != -1) {
|
||||||
if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) {
|
if ((s->play.buf = calloc(s->play.bufframes, s->play.frame_size)) == NULL) {
|
||||||
ret = CUBEB_ERROR;
|
ret = CUBEB_ERROR;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s->record.fd != -1) {
|
if (s->record.fd != -1) {
|
||||||
if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) {
|
if ((s->record.buf = calloc(s->record.bufframes, s->record.frame_size)) ==
|
||||||
|
NULL) {
|
||||||
ret = CUBEB_ERROR;
|
ret = CUBEB_ERROR;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -1225,10 +1306,10 @@ oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
|
||||||
if (*device == NULL) {
|
if (*device == NULL) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
(*device)->input_name = stream->record.fd != -1 ?
|
(*device)->input_name =
|
||||||
strdup(stream->record.name) : NULL;
|
stream->record.fd != -1 ? strdup(stream->record.name) : NULL;
|
||||||
(*device)->output_name = stream->play.fd != -1 ?
|
(*device)->output_name =
|
||||||
strdup(stream->play.name) : NULL;
|
stream->play.fd != -1 ? strdup(stream->play.name) : NULL;
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1255,7 +1336,6 @@ static struct cubeb_ops const oss_ops = {
|
||||||
.stream_destroy = oss_stream_destroy,
|
.stream_destroy = oss_stream_destroy,
|
||||||
.stream_start = oss_stream_start,
|
.stream_start = oss_stream_start,
|
||||||
.stream_stop = oss_stream_stop,
|
.stream_stop = oss_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = oss_stream_get_position,
|
.stream_get_position = oss_stream_get_position,
|
||||||
.stream_get_latency = oss_stream_get_latency,
|
.stream_get_latency = oss_stream_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
|
|
@ -5,31 +5,29 @@
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <cubeb/cubeb.h>
|
|
||||||
#include "cubeb_osx_run_loop.h"
|
#include "cubeb_osx_run_loop.h"
|
||||||
#include "cubeb_log.h"
|
#include "cubeb_log.h"
|
||||||
#include <AudioUnit/AudioUnit.h>
|
#include <AudioUnit/AudioUnit.h>
|
||||||
#include <CoreAudio/AudioHardware.h>
|
#include <CoreAudio/AudioHardware.h>
|
||||||
#include <CoreAudio/HostTime.h>
|
#include <CoreAudio/HostTime.h>
|
||||||
#include <CoreFoundation/CoreFoundation.h>
|
#include <CoreFoundation/CoreFoundation.h>
|
||||||
|
#include <cubeb/cubeb.h>
|
||||||
|
|
||||||
void cubeb_set_coreaudio_notification_runloop()
|
void
|
||||||
|
cubeb_set_coreaudio_notification_runloop()
|
||||||
{
|
{
|
||||||
/* This is needed so that AudioUnit listeners get called on this thread, and
|
/* 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
|
* not the main thread. If we don't do that, they are not called, or a crash
|
||||||
* occur, depending on the OSX version. */
|
* occur, depending on the OSX version. */
|
||||||
AudioObjectPropertyAddress runloop_address = {
|
AudioObjectPropertyAddress runloop_address = {
|
||||||
kAudioHardwarePropertyRunLoop,
|
kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,
|
||||||
kAudioObjectPropertyScopeGlobal,
|
kAudioObjectPropertyElementMaster};
|
||||||
kAudioObjectPropertyElementMaster
|
|
||||||
};
|
|
||||||
|
|
||||||
CFRunLoopRef run_loop = nullptr;
|
CFRunLoopRef run_loop = nullptr;
|
||||||
|
|
||||||
OSStatus r;
|
OSStatus r;
|
||||||
r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
|
r = AudioObjectSetPropertyData(kAudioObjectSystemObject, &runloop_address, 0,
|
||||||
&runloop_address,
|
NULL, sizeof(CFRunLoopRef), &run_loop);
|
||||||
0, NULL, sizeof(CFRunLoopRef), &run_loop);
|
|
||||||
if (r != noErr) {
|
if (r != noErr) {
|
||||||
LOG("Could not make global CoreAudio notifications use their own thread.");
|
LOG("Could not make global CoreAudio notifications use their own thread.");
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void cubeb_set_coreaudio_notification_runloop();
|
void
|
||||||
|
cubeb_set_coreaudio_notification_runloop();
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,21 @@
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
#undef NDEBUG
|
#undef NDEBUG
|
||||||
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
#include "cubeb_mixer.h"
|
||||||
|
#include "cubeb_strings.h"
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "cubeb-internal.h"
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb_mixer.h"
|
|
||||||
#include "cubeb_strings.h"
|
|
||||||
|
|
||||||
#ifdef DISABLE_LIBPULSE_DLOPEN
|
#ifdef DISABLE_LIBPULSE_DLOPEN
|
||||||
#define WRAP(x) x
|
#define WRAP(x) x
|
||||||
#else
|
#else
|
||||||
#define WRAP(x) cubeb_##x
|
#define WRAP(x) (*cubeb_##x)
|
||||||
#define LIBPULSE_API_VISIT(X) \
|
#define LIBPULSE_API_VISIT(X) \
|
||||||
X(pa_channel_map_can_balance) \
|
X(pa_channel_map_can_balance) \
|
||||||
X(pa_channel_map_init) \
|
X(pa_channel_map_init) \
|
||||||
|
@ -86,7 +86,7 @@
|
||||||
X(pa_mainloop_api_once) \
|
X(pa_mainloop_api_once) \
|
||||||
X(pa_get_library_version) \
|
X(pa_get_library_version) \
|
||||||
X(pa_channel_map_init_auto) \
|
X(pa_channel_map_init_auto) \
|
||||||
X(pa_stream_set_name) \
|
X(pa_stream_set_name)
|
||||||
|
|
||||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
||||||
LIBPULSE_API_VISIT(MAKE_TYPEDEF);
|
LIBPULSE_API_VISIT(MAKE_TYPEDEF);
|
||||||
|
@ -139,11 +139,7 @@ struct cubeb_stream {
|
||||||
|
|
||||||
static const float PULSE_NO_GAIN = -1.0;
|
static const float PULSE_NO_GAIN = -1.0;
|
||||||
|
|
||||||
enum cork_state {
|
enum cork_state { UNCORK = 0, CORK = 1 << 0, NOTIFY = 1 << 1 };
|
||||||
UNCORK = 0,
|
|
||||||
CORK = 1 << 0,
|
|
||||||
NOTIFY = 1 << 1
|
|
||||||
};
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
intern_device_id(cubeb * ctx, char const ** id)
|
intern_device_id(cubeb * ctx, char const ** id)
|
||||||
|
@ -164,14 +160,16 @@ intern_device_id(cubeb * ctx, char const ** id)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
|
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol,
|
||||||
|
void * u)
|
||||||
{
|
{
|
||||||
(void)context;
|
(void)context;
|
||||||
cubeb * ctx = u;
|
cubeb * ctx = u;
|
||||||
if (!eol) {
|
if (!eol) {
|
||||||
free(ctx->default_sink_info);
|
free(ctx->default_sink_info);
|
||||||
ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info));
|
ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info));
|
||||||
memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, sizeof(pa_channel_map));
|
memcpy(&ctx->default_sink_info->channel_map, &info->channel_map,
|
||||||
|
sizeof(pa_channel_map));
|
||||||
ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate;
|
ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate;
|
||||||
ctx->default_sink_info->flags = info->flags;
|
ctx->default_sink_info->flags = info->flags;
|
||||||
}
|
}
|
||||||
|
@ -179,10 +177,12 @@ sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, voi
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
server_info_callback(pa_context * context, const pa_server_info * info, void * u)
|
server_info_callback(pa_context * context, const pa_server_info * info,
|
||||||
|
void * u)
|
||||||
{
|
{
|
||||||
pa_operation * o;
|
pa_operation * o;
|
||||||
o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u);
|
o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name,
|
||||||
|
sink_info_callback, u);
|
||||||
if (o) {
|
if (o) {
|
||||||
WRAP(pa_operation_unref)(o);
|
WRAP(pa_operation_unref)(o);
|
||||||
}
|
}
|
||||||
|
@ -223,7 +223,8 @@ stream_state_change_callback(cubeb_stream * stm, cubeb_state s)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u)
|
stream_drain_callback(pa_mainloop_api * a, pa_time_event * e,
|
||||||
|
struct timeval const * tv, void * u)
|
||||||
{
|
{
|
||||||
(void)a;
|
(void)a;
|
||||||
(void)tv;
|
(void)tv;
|
||||||
|
@ -247,7 +248,8 @@ stream_state_callback(pa_stream * s, void * u)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cubeb_stream * stm)
|
trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes,
|
||||||
|
cubeb_stream * stm)
|
||||||
{
|
{
|
||||||
void * buffer;
|
void * buffer;
|
||||||
size_t size;
|
size_t size;
|
||||||
|
@ -264,16 +266,21 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
|
||||||
while (towrite) {
|
while (towrite) {
|
||||||
size = towrite;
|
size = towrite;
|
||||||
r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
|
r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
|
||||||
// Note: this has failed running under rr on occassion - needs investigation.
|
// Note: this has failed running under rr on occassion - needs
|
||||||
|
// investigation.
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
assert(size > 0);
|
assert(size > 0);
|
||||||
assert(size % frame_size == 0);
|
assert(size % frame_size == 0);
|
||||||
|
|
||||||
LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", size, read_offset);
|
LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd",
|
||||||
got = stm->data_callback(stm, stm->user_ptr, (uint8_t const *)input_data + read_offset, buffer, size / frame_size);
|
size, read_offset);
|
||||||
|
got = stm->data_callback(stm, stm->user_ptr,
|
||||||
|
(uint8_t const *)input_data + read_offset, buffer,
|
||||||
|
size / frame_size);
|
||||||
if (got < 0) {
|
if (got < 0) {
|
||||||
WRAP(pa_stream_cancel_write)(s);
|
WRAP(pa_stream_cancel_write)(s);
|
||||||
stm->shutdown = 1;
|
stm->shutdown = 1;
|
||||||
|
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If more iterations move offset of read buffer
|
// If more iterations move offset of read buffer
|
||||||
|
@ -299,7 +306,8 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
|
r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0,
|
||||||
|
PA_SEEK_RELATIVE);
|
||||||
assert(r == 0);
|
assert(r == 0);
|
||||||
|
|
||||||
if ((size_t)got < size / frame_size) {
|
if ((size_t)got < size / frame_size) {
|
||||||
|
@ -313,7 +321,9 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
|
||||||
/* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
|
/* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
|
||||||
/* arbitrary safety margin: double the current latency. */
|
/* arbitrary safety margin: double the current latency. */
|
||||||
assert(!stm->drain_timer);
|
assert(!stm->drain_timer);
|
||||||
stm->drain_timer = WRAP(pa_context_rttime_new)(stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, stream_drain_callback, stm);
|
stm->drain_timer = WRAP(pa_context_rttime_new)(
|
||||||
|
stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency,
|
||||||
|
stream_drain_callback, stm);
|
||||||
stm->shutdown = 1;
|
stm->shutdown = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -341,8 +351,7 @@ stream_write_callback(pa_stream * s, size_t nbytes, void * u)
|
||||||
{
|
{
|
||||||
LOGV("Output callback to be written buffer size %zd", nbytes);
|
LOGV("Output callback to be written buffer size %zd", nbytes);
|
||||||
cubeb_stream * stm = u;
|
cubeb_stream * stm = u;
|
||||||
if (stm->shutdown ||
|
if (stm->shutdown || stm->state != CUBEB_STATE_STARTED) {
|
||||||
stm->state != CUBEB_STATE_STARTED) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,10 +388,14 @@ stream_read_callback(pa_stream * s, size_t nbytes, void * u)
|
||||||
trigger_user_callback(stm->output_stream, read_data, write_size, stm);
|
trigger_user_callback(stm->output_stream, read_data, write_size, stm);
|
||||||
} else {
|
} else {
|
||||||
// input/capture only operation. Call callback directly
|
// input/capture only operation. Call callback directly
|
||||||
long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, read_frames);
|
long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL,
|
||||||
|
read_frames);
|
||||||
if (got < 0 || (size_t)got != read_frames) {
|
if (got < 0 || (size_t)got != read_frames) {
|
||||||
WRAP(pa_stream_cancel_write)(s);
|
WRAP(pa_stream_cancel_write)(s);
|
||||||
stm->shutdown = 1;
|
stm->shutdown = 1;
|
||||||
|
if (got < 0) {
|
||||||
|
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -432,11 +445,13 @@ static int
|
||||||
wait_until_stream_ready(cubeb_stream * stm)
|
wait_until_stream_ready(cubeb_stream * stm)
|
||||||
{
|
{
|
||||||
if (stm->output_stream &&
|
if (stm->output_stream &&
|
||||||
wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == -1) {
|
wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) ==
|
||||||
|
-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
if (stm->input_stream &&
|
if (stm->input_stream &&
|
||||||
wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) {
|
wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) ==
|
||||||
|
-1) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -464,7 +479,8 @@ cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state)
|
||||||
if (!io_stream) {
|
if (!io_stream) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm);
|
o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback,
|
||||||
|
stm);
|
||||||
if (o) {
|
if (o) {
|
||||||
operation_wait(stm->context, io_stream, o);
|
operation_wait(stm->context, io_stream, o);
|
||||||
WRAP(pa_operation_unref)(o);
|
WRAP(pa_operation_unref)(o);
|
||||||
|
@ -491,7 +507,8 @@ stream_update_timing_info(cubeb_stream * stm)
|
||||||
int r = -1;
|
int r = -1;
|
||||||
pa_operation * o = NULL;
|
pa_operation * o = NULL;
|
||||||
if (stm->output_stream) {
|
if (stm->output_stream) {
|
||||||
o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm);
|
o = WRAP(pa_stream_update_timing_info)(stm->output_stream,
|
||||||
|
stream_success_callback, stm);
|
||||||
if (o) {
|
if (o) {
|
||||||
r = operation_wait(stm->context, stm->output_stream, o);
|
r = operation_wait(stm->context, stm->output_stream, o);
|
||||||
WRAP(pa_operation_unref)(o);
|
WRAP(pa_operation_unref)(o);
|
||||||
|
@ -502,7 +519,8 @@ stream_update_timing_info(cubeb_stream * stm)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stm->input_stream) {
|
if (stm->input_stream) {
|
||||||
o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm);
|
o = WRAP(pa_stream_update_timing_info)(stm->input_stream,
|
||||||
|
stream_success_callback, stm);
|
||||||
if (o) {
|
if (o) {
|
||||||
r = operation_wait(stm->context, stm->input_stream, o);
|
r = operation_wait(stm->context, stm->input_stream, o);
|
||||||
WRAP(pa_operation_unref)(o);
|
WRAP(pa_operation_unref)(o);
|
||||||
|
@ -584,8 +602,10 @@ layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pulse_context_destroy(cubeb * ctx);
|
static void
|
||||||
static void pulse_destroy(cubeb * ctx);
|
pulse_context_destroy(cubeb * ctx);
|
||||||
|
static void
|
||||||
|
pulse_destroy(cubeb * ctx);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_context_init(cubeb * ctx)
|
pulse_context_init(cubeb * ctx)
|
||||||
|
@ -597,12 +617,13 @@ pulse_context_init(cubeb * ctx)
|
||||||
pulse_context_destroy(ctx);
|
pulse_context_destroy(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop),
|
ctx->context = WRAP(pa_context_new)(
|
||||||
ctx->context_name);
|
WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), ctx->context_name);
|
||||||
if (!ctx->context) {
|
if (!ctx->context) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx);
|
WRAP(pa_context_set_state_callback)
|
||||||
|
(ctx->context, context_state_callback, ctx);
|
||||||
|
|
||||||
WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
|
WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
|
||||||
r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
|
r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
|
||||||
|
@ -621,8 +642,8 @@ pulse_context_init(cubeb * ctx)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pulse_subscribe_notifications(cubeb * context,
|
static int
|
||||||
pa_subscription_mask_t mask);
|
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask);
|
||||||
|
|
||||||
/*static*/ int
|
/*static*/ int
|
||||||
pulse_init(cubeb ** context, char const * context_name)
|
pulse_init(cubeb ** context, char const * context_name)
|
||||||
|
@ -642,7 +663,8 @@ pulse_init(cubeb ** context, char const * context_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOAD(x) { \
|
#define LOAD(x) \
|
||||||
|
{ \
|
||||||
cubeb_##x = dlsym(libpulse, #x); \
|
cubeb_##x = dlsym(libpulse, #x); \
|
||||||
if (!cubeb_##x) { \
|
if (!cubeb_##x) { \
|
||||||
dlclose(libpulse); \
|
dlclose(libpulse); \
|
||||||
|
@ -735,7 +757,8 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||||
|
uint32_t * latency_frames)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
// According to PulseAudio developers, this is a safe minimum.
|
// According to PulseAudio developers, this is a safe minimum.
|
||||||
|
@ -764,6 +787,10 @@ pulse_context_destroy(cubeb * ctx)
|
||||||
static void
|
static void
|
||||||
pulse_destroy(cubeb * ctx)
|
pulse_destroy(cubeb * ctx)
|
||||||
{
|
{
|
||||||
|
assert(!ctx->input_collection_changed_callback &&
|
||||||
|
!ctx->input_collection_changed_user_ptr &&
|
||||||
|
!ctx->output_collection_changed_callback &&
|
||||||
|
!ctx->output_collection_changed_user_ptr);
|
||||||
free(ctx->context_name);
|
free(ctx->context_name);
|
||||||
if (ctx->context) {
|
if (ctx->context) {
|
||||||
pulse_context_destroy(ctx);
|
pulse_context_destroy(ctx);
|
||||||
|
@ -785,7 +812,8 @@ pulse_destroy(cubeb * ctx)
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pulse_stream_destroy(cubeb_stream * stm);
|
static void
|
||||||
|
pulse_stream_destroy(cubeb_stream * stm);
|
||||||
|
|
||||||
static pa_sample_format_t
|
static pa_sample_format_t
|
||||||
to_pulse_format(cubeb_sample_format format)
|
to_pulse_format(cubeb_sample_format format)
|
||||||
|
@ -809,31 +837,38 @@ pulse_default_layout_for_channels(uint32_t ch)
|
||||||
{
|
{
|
||||||
assert(ch > 0 && ch <= 8);
|
assert(ch > 0 && ch <= 8);
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 1: return CUBEB_LAYOUT_MONO;
|
case 1:
|
||||||
case 2: return CUBEB_LAYOUT_STEREO;
|
return CUBEB_LAYOUT_MONO;
|
||||||
case 3: return CUBEB_LAYOUT_3F;
|
case 2:
|
||||||
case 4: return CUBEB_LAYOUT_QUAD;
|
return CUBEB_LAYOUT_STEREO;
|
||||||
case 5: return CUBEB_LAYOUT_3F2;
|
case 3:
|
||||||
case 6: return CUBEB_LAYOUT_3F_LFE |
|
return CUBEB_LAYOUT_3F;
|
||||||
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT;
|
case 4:
|
||||||
case 7: return CUBEB_LAYOUT_3F3R_LFE;
|
return CUBEB_LAYOUT_QUAD;
|
||||||
case 8: return CUBEB_LAYOUT_3F4_LFE;
|
case 5:
|
||||||
|
return CUBEB_LAYOUT_3F2;
|
||||||
|
case 6:
|
||||||
|
return CUBEB_LAYOUT_3F_LFE | CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT;
|
||||||
|
case 7:
|
||||||
|
return CUBEB_LAYOUT_3F3R_LFE;
|
||||||
|
case 8:
|
||||||
|
return CUBEB_LAYOUT_3F4_LFE;
|
||||||
}
|
}
|
||||||
// Never get here!
|
// Never get here!
|
||||||
return CUBEB_LAYOUT_UNDEFINED;
|
return CUBEB_LAYOUT_UNDEFINED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
create_pa_stream(cubeb_stream * stm,
|
create_pa_stream(cubeb_stream * stm, pa_stream ** pa_stm,
|
||||||
pa_stream ** pa_stm,
|
cubeb_stream_params * stream_params, char const * stream_name)
|
||||||
cubeb_stream_params * stream_params,
|
|
||||||
char const * stream_name)
|
|
||||||
{
|
{
|
||||||
assert(stm && stream_params);
|
assert(stm && stream_params);
|
||||||
assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
|
assert(&stm->input_stream == pa_stm ||
|
||||||
|
(&stm->output_stream == pa_stm &&
|
||||||
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
|
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
|
||||||
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
|
||||||
cubeb_channel_layout_nb_channels(stream_params->layout) == stream_params->channels))));
|
cubeb_channel_layout_nb_channels(stream_params->layout) ==
|
||||||
|
stream_params->channels))));
|
||||||
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||||
return CUBEB_ERROR_NOT_SUPPORTED;
|
return CUBEB_ERROR_NOT_SUPPORTED;
|
||||||
}
|
}
|
||||||
|
@ -850,13 +885,18 @@ create_pa_stream(cubeb_stream * stm,
|
||||||
if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) {
|
if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) {
|
||||||
pa_channel_map cm;
|
pa_channel_map cm;
|
||||||
if (stream_params->channels <= 8 &&
|
if (stream_params->channels <= 8 &&
|
||||||
!WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, PA_CHANNEL_MAP_DEFAULT)) {
|
!WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels,
|
||||||
LOG("Layout undefined and PulseAudio's default layout has not been configured, guess one.");
|
PA_CHANNEL_MAP_DEFAULT)) {
|
||||||
layout_to_channel_map(pulse_default_layout_for_channels(stream_params->channels), &cm);
|
LOG("Layout undefined and PulseAudio's default layout has not been "
|
||||||
*pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
|
"configured, guess one.");
|
||||||
|
layout_to_channel_map(
|
||||||
|
pulse_default_layout_for_channels(stream_params->channels), &cm);
|
||||||
|
*pa_stm =
|
||||||
|
WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
|
||||||
} else {
|
} else {
|
||||||
LOG("Layout undefined, PulseAudio will use its default.");
|
LOG("Layout undefined, PulseAudio will use its default.");
|
||||||
*pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
|
*pa_stm =
|
||||||
|
WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pa_channel_map cm;
|
pa_channel_map cm;
|
||||||
|
@ -867,7 +907,8 @@ create_pa_stream(cubeb_stream * stm,
|
||||||
}
|
}
|
||||||
|
|
||||||
static pa_buffer_attr
|
static pa_buffer_attr
|
||||||
set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec)
|
set_buffering_attribute(unsigned int latency_frames,
|
||||||
|
pa_sample_spec * sample_spec)
|
||||||
{
|
{
|
||||||
pa_buffer_attr battr;
|
pa_buffer_attr battr;
|
||||||
battr.maxlength = -1;
|
battr.maxlength = -1;
|
||||||
|
@ -876,24 +917,23 @@ set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spe
|
||||||
battr.minreq = battr.tlength / 4;
|
battr.minreq = battr.tlength / 4;
|
||||||
battr.fragsize = battr.minreq;
|
battr.fragsize = battr.minreq;
|
||||||
|
|
||||||
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",
|
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq "
|
||||||
battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
|
"%u, fragsize %u",
|
||||||
|
battr.maxlength, battr.tlength, battr.prebuf, battr.minreq,
|
||||||
|
battr.fragsize);
|
||||||
|
|
||||||
return battr;
|
return battr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_stream_init(cubeb * context,
|
pulse_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_stream ** stream,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_data_callback data_callback,
|
||||||
cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
cubeb_stream * stm;
|
cubeb_stream * stm;
|
||||||
pa_buffer_attr battr;
|
pa_buffer_attr battr;
|
||||||
|
@ -921,22 +961,25 @@ pulse_stream_init(cubeb * context,
|
||||||
|
|
||||||
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
||||||
if (output_stream_params) {
|
if (output_stream_params) {
|
||||||
r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name);
|
r = create_pa_stream(stm, &stm->output_stream, output_stream_params,
|
||||||
|
stream_name);
|
||||||
if (r != CUBEB_OK) {
|
if (r != CUBEB_OK) {
|
||||||
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
||||||
pulse_stream_destroy(stm);
|
pulse_stream_destroy(stm);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
|
stm->output_sample_spec =
|
||||||
|
*(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
|
||||||
|
|
||||||
WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
|
WRAP(pa_stream_set_state_callback)
|
||||||
WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm);
|
(stm->output_stream, stream_state_callback, stm);
|
||||||
|
WRAP(pa_stream_set_write_callback)
|
||||||
|
(stm->output_stream, stream_write_callback, stm);
|
||||||
|
|
||||||
battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
|
battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
|
||||||
WRAP(pa_stream_connect_playback)(stm->output_stream,
|
WRAP(pa_stream_connect_playback)
|
||||||
(char const *) output_device,
|
(stm->output_stream, (char const *)output_device, &battr,
|
||||||
&battr,
|
|
||||||
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
|
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
|
||||||
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
|
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
@ -944,22 +987,25 @@ pulse_stream_init(cubeb * context,
|
||||||
|
|
||||||
// Set up input stream
|
// Set up input stream
|
||||||
if (input_stream_params) {
|
if (input_stream_params) {
|
||||||
r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name);
|
r = create_pa_stream(stm, &stm->input_stream, input_stream_params,
|
||||||
|
stream_name);
|
||||||
if (r != CUBEB_OK) {
|
if (r != CUBEB_OK) {
|
||||||
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
||||||
pulse_stream_destroy(stm);
|
pulse_stream_destroy(stm);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
|
stm->input_sample_spec =
|
||||||
|
*(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
|
||||||
|
|
||||||
WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
|
WRAP(pa_stream_set_state_callback)
|
||||||
WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
|
(stm->input_stream, stream_state_callback, stm);
|
||||||
|
WRAP(pa_stream_set_read_callback)
|
||||||
|
(stm->input_stream, stream_read_callback, stm);
|
||||||
|
|
||||||
battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
|
battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
|
||||||
WRAP(pa_stream_connect_record)(stm->input_stream,
|
WRAP(pa_stream_connect_record)
|
||||||
(char const *) input_device,
|
(stm->input_stream, (char const *)input_device, &battr,
|
||||||
&battr,
|
|
||||||
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
|
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
|
||||||
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
|
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
|
||||||
}
|
}
|
||||||
|
@ -982,15 +1028,19 @@ pulse_stream_init(cubeb * context,
|
||||||
if (output_stream_params) {
|
if (output_stream_params) {
|
||||||
const pa_buffer_attr * output_att;
|
const pa_buffer_attr * output_att;
|
||||||
output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
|
output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
|
||||||
LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",output_att->maxlength, output_att->tlength,
|
LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, "
|
||||||
output_att->prebuf, output_att->minreq, output_att->fragsize);
|
"minreq %u, fragsize %u",
|
||||||
|
output_att->maxlength, output_att->tlength, output_att->prebuf,
|
||||||
|
output_att->minreq, output_att->fragsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_stream_params) {
|
if (input_stream_params) {
|
||||||
const pa_buffer_attr * input_att;
|
const pa_buffer_attr * input_att;
|
||||||
input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
|
input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
|
||||||
LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",input_att->maxlength, input_att->tlength,
|
LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq "
|
||||||
input_att->prebuf, input_att->minreq, input_att->fragsize);
|
"%u, fragsize %u",
|
||||||
|
input_att->maxlength, input_att->tlength, input_att->prebuf,
|
||||||
|
input_att->minreq, input_att->fragsize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1010,7 +1060,8 @@ pulse_stream_destroy(cubeb_stream * stm)
|
||||||
|
|
||||||
if (stm->drain_timer) {
|
if (stm->drain_timer) {
|
||||||
/* there's no pa_rttime_free, so use this instead. */
|
/* there's no pa_rttime_free, so use this instead. */
|
||||||
WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer);
|
WRAP(pa_threaded_mainloop_get_api)
|
||||||
|
(stm->context->mainloop)->time_free(stm->drain_timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
|
WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
|
||||||
|
@ -1054,7 +1105,8 @@ pulse_stream_start(cubeb_stream * stm)
|
||||||
* things roll. This is done via a defer event in order to execute it
|
* things roll. This is done via a defer event in order to execute it
|
||||||
* from PA server thread. */
|
* from PA server thread. */
|
||||||
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
||||||
WRAP(pa_mainloop_api_once)(WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop),
|
WRAP(pa_mainloop_api_once)
|
||||||
|
(WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop),
|
||||||
pulse_defer_event_cb, stm);
|
pulse_defer_event_cb, stm);
|
||||||
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
|
||||||
}
|
}
|
||||||
|
@ -1178,9 +1230,8 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
|
||||||
|
|
||||||
index = WRAP(pa_stream_get_index)(stm->output_stream);
|
index = WRAP(pa_stream_get_index)(stm->output_stream);
|
||||||
|
|
||||||
op = WRAP(pa_context_set_sink_input_volume)(ctx->context,
|
op = WRAP(pa_context_set_sink_input_volume)(ctx->context, index, &cvol,
|
||||||
index, &cvol, volume_success,
|
volume_success, stm);
|
||||||
stm);
|
|
||||||
if (op) {
|
if (op) {
|
||||||
operation_wait(ctx, stm->output_stream, op);
|
operation_wait(ctx, stm->output_stream, op);
|
||||||
WRAP(pa_operation_unref)(op);
|
WRAP(pa_operation_unref)(op);
|
||||||
|
@ -1201,8 +1252,8 @@ pulse_stream_set_name(cubeb_stream * stm, char const * stream_name)
|
||||||
|
|
||||||
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
|
||||||
|
|
||||||
pa_operation * op =
|
pa_operation * op = WRAP(pa_stream_set_name)(stm->output_stream, stream_name,
|
||||||
WRAP(pa_stream_set_name)(stm->output_stream, stream_name, rename_success, stm);
|
rename_success, stm);
|
||||||
|
|
||||||
if (op) {
|
if (op) {
|
||||||
operation_wait(stm->context, stm->output_stream, op);
|
operation_wait(stm->context, stm->output_stream, op);
|
||||||
|
@ -1246,8 +1297,8 @@ pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data)
|
||||||
{
|
{
|
||||||
if (list_data->count == list_data->max) {
|
if (list_data->count == list_data->max) {
|
||||||
list_data->max += 8;
|
list_data->max += 8;
|
||||||
list_data->devinfo = realloc(list_data->devinfo,
|
list_data->devinfo =
|
||||||
sizeof(cubeb_device_info) * list_data->max);
|
realloc(list_data->devinfo, sizeof(cubeb_device_info) * list_data->max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1267,8 +1318,8 @@ pulse_get_state_from_sink_port(pa_sink_port_info * info)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, int eol,
|
||||||
int eol, void * user_data)
|
void * user_data)
|
||||||
{
|
{
|
||||||
pulse_dev_list_data * list_data = user_data;
|
pulse_dev_list_data * list_data = user_data;
|
||||||
cubeb_device_info * devinfo;
|
cubeb_device_info * devinfo;
|
||||||
|
@ -1307,11 +1358,13 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
|
||||||
|
|
||||||
devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
|
||||||
devinfo->state = pulse_get_state_from_sink_port(info->active_port);
|
devinfo->state = pulse_get_state_from_sink_port(info->active_port);
|
||||||
devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ?
|
devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0)
|
||||||
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
? CUBEB_DEVICE_PREF_ALL
|
||||||
|
: CUBEB_DEVICE_PREF_NONE;
|
||||||
|
|
||||||
devinfo->format = CUBEB_DEVICE_FMT_ALL;
|
devinfo->format = CUBEB_DEVICE_FMT_ALL;
|
||||||
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
|
devinfo->default_format =
|
||||||
|
pulse_format_to_cubeb_format(info->sample_spec.format);
|
||||||
devinfo->max_channels = info->channel_map.channels;
|
devinfo->max_channels = info->channel_map.channels;
|
||||||
devinfo->min_rate = 1;
|
devinfo->min_rate = 1;
|
||||||
devinfo->max_rate = PA_RATE_MAX;
|
devinfo->max_rate = PA_RATE_MAX;
|
||||||
|
@ -1339,8 +1392,8 @@ pulse_get_state_from_source_port(pa_source_port_info * info)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
pulse_source_info_cb(pa_context * context, const pa_source_info * info, int eol,
|
||||||
int eol, void * user_data)
|
void * user_data)
|
||||||
{
|
{
|
||||||
pulse_dev_list_data * list_data = user_data;
|
pulse_dev_list_data * list_data = user_data;
|
||||||
cubeb_device_info * devinfo;
|
cubeb_device_info * devinfo;
|
||||||
|
@ -1376,11 +1429,13 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
|
||||||
|
|
||||||
devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
|
devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
|
||||||
devinfo->state = pulse_get_state_from_source_port(info->active_port);
|
devinfo->state = pulse_get_state_from_source_port(info->active_port);
|
||||||
devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ?
|
devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0)
|
||||||
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
? CUBEB_DEVICE_PREF_ALL
|
||||||
|
: CUBEB_DEVICE_PREF_NONE;
|
||||||
|
|
||||||
devinfo->format = CUBEB_DEVICE_FMT_ALL;
|
devinfo->format = CUBEB_DEVICE_FMT_ALL;
|
||||||
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
|
devinfo->default_format =
|
||||||
|
pulse_format_to_cubeb_format(info->sample_spec.format);
|
||||||
devinfo->max_channels = info->channel_map.channels;
|
devinfo->max_channels = info->channel_map.channels;
|
||||||
devinfo->min_rate = 1;
|
devinfo->min_rate = 1;
|
||||||
devinfo->max_rate = PA_RATE_MAX;
|
devinfo->max_rate = PA_RATE_MAX;
|
||||||
|
@ -1418,8 +1473,8 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
|
|
||||||
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
|
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
|
||||||
|
|
||||||
o = WRAP(pa_context_get_server_info)(context->context,
|
o = WRAP(pa_context_get_server_info)(context->context, pulse_server_info_cb,
|
||||||
pulse_server_info_cb, &user_data);
|
&user_data);
|
||||||
if (o) {
|
if (o) {
|
||||||
operation_wait(context, NULL, o);
|
operation_wait(context, NULL, o);
|
||||||
WRAP(pa_operation_unref)(o);
|
WRAP(pa_operation_unref)(o);
|
||||||
|
@ -1454,7 +1509,8 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection)
|
pulse_device_collection_destroy(cubeb * ctx,
|
||||||
|
cubeb_device_collection * collection)
|
||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
|
|
||||||
|
@ -1469,7 +1525,8 @@ pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collectio
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
|
pulse_stream_get_current_device(cubeb_stream * stm,
|
||||||
|
cubeb_device ** const device)
|
||||||
{
|
{
|
||||||
#if PA_CHECK_VERSION(0, 9, 8)
|
#if PA_CHECK_VERSION(0, 9, 8)
|
||||||
*device = calloc(1, sizeof(cubeb_device));
|
*device = calloc(1, sizeof(cubeb_device));
|
||||||
|
@ -1493,8 +1550,7 @@ pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_stream_device_destroy(cubeb_stream * stream,
|
pulse_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
|
||||||
cubeb_device * device)
|
|
||||||
{
|
{
|
||||||
(void)stream;
|
(void)stream;
|
||||||
free(device->input_name);
|
free(device->input_name);
|
||||||
|
@ -1504,8 +1560,7 @@ pulse_stream_device_destroy(cubeb_stream * stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
pulse_subscribe_callback(pa_context * ctx,
|
pulse_subscribe_callback(pa_context * ctx, pa_subscription_event_type_t t,
|
||||||
pa_subscription_event_type_t t,
|
|
||||||
uint32_t index, void * userdata)
|
uint32_t index, void * userdata)
|
||||||
{
|
{
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
@ -1515,36 +1570,49 @@ pulse_subscribe_callback(pa_context * ctx,
|
||||||
case PA_SUBSCRIPTION_EVENT_SERVER:
|
case PA_SUBSCRIPTION_EVENT_SERVER:
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
|
||||||
LOG("Server changed %d", index);
|
LOG("Server changed %d", index);
|
||||||
WRAP(pa_context_get_server_info)(context->context, server_info_callback, context);
|
WRAP(pa_context_get_server_info)
|
||||||
|
(context->context, server_info_callback, context);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PA_SUBSCRIPTION_EVENT_SOURCE:
|
case PA_SUBSCRIPTION_EVENT_SOURCE:
|
||||||
case PA_SUBSCRIPTION_EVENT_SINK:
|
case PA_SUBSCRIPTION_EVENT_SINK:
|
||||||
|
|
||||||
if (g_cubeb_log_level) {
|
if (g_cubeb_log_level) {
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
|
PA_SUBSCRIPTION_EVENT_SOURCE &&
|
||||||
|
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
|
||||||
|
PA_SUBSCRIPTION_EVENT_REMOVE) {
|
||||||
LOG("Removing source index %d", index);
|
LOG("Removing source index %d", index);
|
||||||
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
|
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
|
PA_SUBSCRIPTION_EVENT_SOURCE &&
|
||||||
|
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
|
||||||
|
PA_SUBSCRIPTION_EVENT_NEW) {
|
||||||
LOG("Adding source index %d", index);
|
LOG("Adding source index %d", index);
|
||||||
}
|
}
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
|
PA_SUBSCRIPTION_EVENT_SINK &&
|
||||||
|
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
|
||||||
|
PA_SUBSCRIPTION_EVENT_REMOVE) {
|
||||||
LOG("Removing sink index %d", index);
|
LOG("Removing sink index %d", index);
|
||||||
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
|
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
|
PA_SUBSCRIPTION_EVENT_SINK &&
|
||||||
|
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
|
||||||
|
PA_SUBSCRIPTION_EVENT_NEW) {
|
||||||
LOG("Adding sink index %d", index);
|
LOG("Adding sink index %d", index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
|
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
|
||||||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
|
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr);
|
PA_SUBSCRIPTION_EVENT_SOURCE) {
|
||||||
|
context->input_collection_changed_callback(
|
||||||
|
context, context->input_collection_changed_user_ptr);
|
||||||
}
|
}
|
||||||
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
|
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
|
||||||
context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr);
|
PA_SUBSCRIPTION_EVENT_SINK) {
|
||||||
|
context->output_collection_changed_callback(
|
||||||
|
context, context->output_collection_changed_user_ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1561,13 +1629,16 @@ subscribe_success(pa_context *c, int success, void *userdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) {
|
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask)
|
||||||
|
{
|
||||||
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
|
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
|
||||||
|
|
||||||
WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context);
|
WRAP(pa_context_set_subscribe_callback)
|
||||||
|
(context->context, pulse_subscribe_callback, context);
|
||||||
|
|
||||||
pa_operation * o;
|
pa_operation * o;
|
||||||
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
|
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success,
|
||||||
|
context);
|
||||||
if (o == NULL) {
|
if (o == NULL) {
|
||||||
WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
|
WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
|
||||||
LOG("Context subscribe failed");
|
LOG("Context subscribe failed");
|
||||||
|
@ -1582,8 +1653,8 @@ pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
pulse_register_device_collection_changed(cubeb * context,
|
pulse_register_device_collection_changed(
|
||||||
cubeb_device_type devtype,
|
cubeb * context, cubeb_device_type devtype,
|
||||||
cubeb_device_collection_changed_callback collection_changed_callback,
|
cubeb_device_collection_changed_callback collection_changed_callback,
|
||||||
void * user_ptr)
|
void * user_ptr)
|
||||||
{
|
{
|
||||||
|
@ -1625,7 +1696,6 @@ static struct cubeb_ops const pulse_ops = {
|
||||||
.stream_destroy = pulse_stream_destroy,
|
.stream_destroy = pulse_stream_destroy,
|
||||||
.stream_start = pulse_stream_start,
|
.stream_start = pulse_stream_start,
|
||||||
.stream_stop = pulse_stream_stop,
|
.stream_stop = pulse_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = pulse_stream_get_position,
|
.stream_get_position = pulse_stream_get_position,
|
||||||
.stream_get_latency = pulse_stream_get_latency,
|
.stream_get_latency = pulse_stream_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
@ -1634,5 +1704,5 @@ static struct cubeb_ops const pulse_ops = {
|
||||||
.stream_get_current_device = pulse_stream_get_current_device,
|
.stream_get_current_device = pulse_stream_get_current_device,
|
||||||
.stream_device_destroy = pulse_stream_device_destroy,
|
.stream_device_destroy = pulse_stream_device_destroy,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = pulse_register_device_collection_changed
|
.register_device_collection_changed =
|
||||||
};
|
pulse_register_device_collection_changed};
|
||||||
|
|
|
@ -8,16 +8,16 @@
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#endif // NOMINMAX
|
#endif // NOMINMAX
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
|
||||||
#include <cassert>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdio>
|
|
||||||
#include "cubeb_resampler.h"
|
#include "cubeb_resampler.h"
|
||||||
#include "cubeb-speex-resampler.h"
|
#include "cubeb-speex-resampler.h"
|
||||||
#include "cubeb_resampler_internal.h"
|
#include "cubeb_resampler_internal.h"
|
||||||
#include "cubeb_utils.h"
|
#include "cubeb_utils.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
int
|
int
|
||||||
to_speex_quality(cubeb_resampler_quality q)
|
to_speex_quality(cubeb_resampler_quality q)
|
||||||
|
@ -35,7 +35,8 @@ to_speex_quality(cubeb_resampler_quality q)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t min_buffered_audio_frame(uint32_t sample_rate)
|
uint32_t
|
||||||
|
min_buffered_audio_frame(uint32_t sample_rate)
|
||||||
{
|
{
|
||||||
return sample_rate / 20;
|
return sample_rate / 20;
|
||||||
}
|
}
|
||||||
|
@ -46,23 +47,22 @@ passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
|
||||||
void * ptr,
|
void * ptr,
|
||||||
uint32_t input_channels,
|
uint32_t input_channels,
|
||||||
uint32_t sample_rate)
|
uint32_t sample_rate)
|
||||||
: processor(input_channels)
|
: processor(input_channels), stream(s), data_callback(cb), user_ptr(ptr),
|
||||||
, stream(s)
|
sample_rate(sample_rate)
|
||||||
, data_callback(cb)
|
|
||||||
, user_ptr(ptr)
|
|
||||||
, sample_rate(sample_rate)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
|
long
|
||||||
|
passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
|
||||||
void * output_buffer, long output_frames)
|
void * output_buffer, long output_frames)
|
||||||
{
|
{
|
||||||
if (input_buffer) {
|
if (input_buffer) {
|
||||||
assert(input_frames_count);
|
assert(input_frames_count);
|
||||||
}
|
}
|
||||||
assert((input_buffer && output_buffer) ||
|
assert((input_buffer && output_buffer) ||
|
||||||
(output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
|
(output_buffer && !input_buffer &&
|
||||||
|
(!input_frames_count || *input_frames_count == 0)) ||
|
||||||
(input_buffer && !output_buffer && output_frames == 0));
|
(input_buffer && !output_buffer && output_frames == 0));
|
||||||
|
|
||||||
// When we have no pending input data and exactly as much input
|
// When we have no pending input data and exactly as much input
|
||||||
|
@ -79,7 +79,8 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
|
||||||
// so we can pass it as one pointer to the callback. Or this is a glitch.
|
// 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
|
// 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
|
// 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.
|
// the future is to resample to the output number of frames, when that
|
||||||
|
// happens.
|
||||||
internal_input_buffer.push(static_cast<T *>(input_buffer),
|
internal_input_buffer.push(static_cast<T *>(input_buffer),
|
||||||
frames_to_samples(*input_frames_count));
|
frames_to_samples(*input_frames_count));
|
||||||
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
|
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
|
||||||
|
@ -87,8 +88,8 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
|
||||||
// buffer with silence. First keep the actual number of input samples
|
// buffer with silence. First keep the actual number of input samples
|
||||||
// used without the silence.
|
// used without the silence.
|
||||||
pop_input_count = internal_input_buffer.length();
|
pop_input_count = internal_input_buffer.length();
|
||||||
internal_input_buffer.push_silence(
|
internal_input_buffer.push_silence(frames_to_samples(output_frames) -
|
||||||
frames_to_samples(output_frames) - internal_input_buffer.length());
|
internal_input_buffer.length());
|
||||||
} else {
|
} else {
|
||||||
pop_input_count = frames_to_samples(output_frames);
|
pop_input_count = frames_to_samples(output_frames);
|
||||||
}
|
}
|
||||||
|
@ -100,12 +101,14 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
|
||||||
// pass the current input data directly to the callback
|
// pass the current input data directly to the callback
|
||||||
assert(pop_input_count == 0);
|
assert(pop_input_count == 0);
|
||||||
unsigned long samples_off = frames_to_samples(output_frames);
|
unsigned long samples_off = frames_to_samples(output_frames);
|
||||||
internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
|
internal_input_buffer.push(
|
||||||
|
static_cast<T *>(input_buffer) + samples_off,
|
||||||
frames_to_samples(*input_frames_count - output_frames));
|
frames_to_samples(*input_frames_count - output_frames));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
|
long rv =
|
||||||
|
data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
|
||||||
|
|
||||||
if (input_buffer) {
|
if (input_buffer) {
|
||||||
if (pop_input_count) {
|
if (pop_input_count) {
|
||||||
|
@ -125,17 +128,12 @@ template class passthrough_resampler<float>;
|
||||||
template class passthrough_resampler<short>;
|
template class passthrough_resampler<short>;
|
||||||
|
|
||||||
template <typename T, typename InputProcessor, typename OutputProcessor>
|
template <typename T, typename InputProcessor, typename OutputProcessor>
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::
|
||||||
::cubeb_resampler_speex(InputProcessor * input_processor,
|
cubeb_resampler_speex(InputProcessor * input_processor,
|
||||||
OutputProcessor * output_processor,
|
OutputProcessor * output_processor, cubeb_stream * s,
|
||||||
cubeb_stream * s,
|
cubeb_data_callback cb, void * ptr)
|
||||||
cubeb_data_callback cb,
|
: input_processor(input_processor), output_processor(output_processor),
|
||||||
void * ptr)
|
stream(s), data_callback(cb), user_ptr(ptr)
|
||||||
: input_processor(input_processor)
|
|
||||||
, output_processor(output_processor)
|
|
||||||
, stream(s)
|
|
||||||
, data_callback(cb)
|
|
||||||
, user_ptr(ptr)
|
|
||||||
{
|
{
|
||||||
if (input_processor && output_processor) {
|
if (input_processor && output_processor) {
|
||||||
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
|
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
|
||||||
|
@ -147,28 +145,29 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename InputProcessor, typename OutputProcessor>
|
template <typename T, typename InputProcessor, typename OutputProcessor>
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
cubeb_resampler_speex<T, InputProcessor,
|
||||||
::~cubeb_resampler_speex()
|
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>
|
template <typename T, typename InputProcessor, typename OutputProcessor>
|
||||||
long
|
long
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill(
|
||||||
::fill_internal_output(T * input_buffer, long * input_frames_count,
|
void * input_buffer, long * input_frames_count, void * output_buffer,
|
||||||
T * output_buffer, long output_frames_needed)
|
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) &&
|
assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
|
||||||
output_buffer && output_frames_needed);
|
output_buffer && output_frames_needed);
|
||||||
|
@ -185,8 +184,7 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
||||||
out_unprocessed =
|
out_unprocessed =
|
||||||
output_processor->input_buffer(output_frames_before_processing);
|
output_processor->input_buffer(output_frames_before_processing);
|
||||||
|
|
||||||
got = data_callback(stream, user_ptr,
|
got = data_callback(stream, user_ptr, nullptr, out_unprocessed,
|
||||||
nullptr, out_unprocessed,
|
|
||||||
output_frames_before_processing);
|
output_frames_before_processing);
|
||||||
|
|
||||||
if (got < output_frames_before_processing) {
|
if (got < output_frames_before_processing) {
|
||||||
|
@ -207,27 +205,36 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
||||||
|
|
||||||
template <typename T, typename InputProcessor, typename OutputProcessor>
|
template <typename T, typename InputProcessor, typename OutputProcessor>
|
||||||
long
|
long
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_input(
|
||||||
::fill_internal_input(T * input_buffer, long * input_frames_count,
|
T * input_buffer, long * input_frames_count, T * output_buffer,
|
||||||
T * output_buffer, long /*output_frames_needed*/)
|
long /*output_frames_needed*/)
|
||||||
{
|
{
|
||||||
assert(input_buffer && input_frames_count && *input_frames_count &&
|
assert(input_buffer && input_frames_count && *input_frames_count &&
|
||||||
!output_buffer);
|
!output_buffer);
|
||||||
|
|
||||||
/* The input data, after eventual resampling. This is passed to the callback. */
|
/* The input data, after eventual resampling. This is passed to the callback.
|
||||||
|
*/
|
||||||
T * resampled_input = nullptr;
|
T * resampled_input = nullptr;
|
||||||
uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
|
uint32_t resampled_frame_count =
|
||||||
|
input_processor->output_for_input(*input_frames_count);
|
||||||
|
|
||||||
/* process the input, and present exactly `output_frames_needed` in the
|
/* process the input, and present exactly `output_frames_needed` in the
|
||||||
* callback. */
|
* callback. */
|
||||||
input_processor->input(input_buffer, *input_frames_count);
|
input_processor->input(input_buffer, *input_frames_count);
|
||||||
|
|
||||||
|
/* resampled_frame_count == 0 happens if the resampler
|
||||||
|
* doesn't have enough input frames buffered to produce 1 resampled frame. */
|
||||||
|
if (resampled_frame_count == 0) {
|
||||||
|
return *input_frames_count;
|
||||||
|
}
|
||||||
|
|
||||||
size_t frames_resampled = 0;
|
size_t frames_resampled = 0;
|
||||||
resampled_input = input_processor->output(resampled_frame_count, &frames_resampled);
|
resampled_input =
|
||||||
|
input_processor->output(resampled_frame_count, &frames_resampled);
|
||||||
*input_frames_count = frames_resampled;
|
*input_frames_count = frames_resampled;
|
||||||
|
|
||||||
long got = data_callback(stream, user_ptr,
|
long got = data_callback(stream, user_ptr, resampled_input, nullptr,
|
||||||
resampled_input, nullptr, resampled_frame_count);
|
resampled_frame_count);
|
||||||
|
|
||||||
/* Return the number of initial input frames or part of it.
|
/* Return the number of initial input frames or part of it.
|
||||||
* Since output_frames_needed == 0 in input scenario, the only
|
* Since output_frames_needed == 0 in input scenario, the only
|
||||||
|
@ -237,16 +244,17 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
||||||
|
|
||||||
template <typename T, typename InputProcessor, typename OutputProcessor>
|
template <typename T, typename InputProcessor, typename OutputProcessor>
|
||||||
long
|
long
|
||||||
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_duplex(
|
||||||
::fill_internal_duplex(T * in_buffer, long * input_frames_count,
|
T * in_buffer, long * input_frames_count, T * out_buffer,
|
||||||
T * out_buffer, long output_frames_needed)
|
long output_frames_needed)
|
||||||
{
|
{
|
||||||
if (draining) {
|
if (draining) {
|
||||||
// discard input and drain any signal remaining in the resampler.
|
// discard input and drain any signal remaining in the resampler.
|
||||||
return output_processor->output(out_buffer, output_frames_needed);
|
return output_processor->output(out_buffer, output_frames_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The input data, after eventual resampling. This is passed to the callback. */
|
/* The input data, after eventual resampling. This is passed to the callback.
|
||||||
|
*/
|
||||||
T * resampled_input = nullptr;
|
T * resampled_input = nullptr;
|
||||||
/* The output buffer passed down in the callback, that might be resampled. */
|
/* The output buffer passed down in the callback, that might be resampled. */
|
||||||
T * out_unprocessed = nullptr;
|
T * out_unprocessed = nullptr;
|
||||||
|
@ -277,15 +285,14 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
|
||||||
input_processor->input(in_buffer, *input_frames_count);
|
input_processor->input(in_buffer, *input_frames_count);
|
||||||
|
|
||||||
size_t frames_resampled = 0;
|
size_t frames_resampled = 0;
|
||||||
resampled_input =
|
resampled_input = input_processor->output(output_frames_before_processing,
|
||||||
input_processor->output(output_frames_before_processing, &frames_resampled);
|
&frames_resampled);
|
||||||
*input_frames_count = frames_resampled;
|
*input_frames_count = frames_resampled;
|
||||||
} else {
|
} else {
|
||||||
resampled_input = nullptr;
|
resampled_input = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
got = data_callback(stream, user_ptr,
|
got = data_callback(stream, user_ptr, resampled_input, out_unprocessed,
|
||||||
resampled_input, out_unprocessed,
|
|
||||||
output_frames_before_processing);
|
output_frames_before_processing);
|
||||||
|
|
||||||
if (got < output_frames_before_processing) {
|
if (got < output_frames_before_processing) {
|
||||||
|
@ -315,10 +322,9 @@ cubeb_resampler *
|
||||||
cubeb_resampler_create(cubeb_stream * stream,
|
cubeb_resampler_create(cubeb_stream * stream,
|
||||||
cubeb_stream_params * input_params,
|
cubeb_stream_params * input_params,
|
||||||
cubeb_stream_params * output_params,
|
cubeb_stream_params * output_params,
|
||||||
unsigned int target_rate,
|
unsigned int target_rate, cubeb_data_callback callback,
|
||||||
cubeb_data_callback callback,
|
void * user_ptr, cubeb_resampler_quality quality,
|
||||||
void * user_ptr,
|
cubeb_resampler_reclock reclock)
|
||||||
cubeb_resampler_quality quality)
|
|
||||||
{
|
{
|
||||||
cubeb_sample_format format;
|
cubeb_sample_format format;
|
||||||
|
|
||||||
|
@ -332,21 +338,13 @@ cubeb_resampler_create(cubeb_stream * stream,
|
||||||
|
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case CUBEB_SAMPLE_S16NE:
|
case CUBEB_SAMPLE_S16NE:
|
||||||
return cubeb_resampler_create_internal<short>(stream,
|
return cubeb_resampler_create_internal<short>(
|
||||||
input_params,
|
stream, input_params, output_params, target_rate, callback, user_ptr,
|
||||||
output_params,
|
quality, reclock);
|
||||||
target_rate,
|
|
||||||
callback,
|
|
||||||
user_ptr,
|
|
||||||
quality);
|
|
||||||
case CUBEB_SAMPLE_FLOAT32NE:
|
case CUBEB_SAMPLE_FLOAT32NE:
|
||||||
return cubeb_resampler_create_internal<float>(stream,
|
return cubeb_resampler_create_internal<float>(
|
||||||
input_params,
|
stream, input_params, output_params, target_rate, callback, user_ptr,
|
||||||
output_params,
|
quality, reclock);
|
||||||
target_rate,
|
|
||||||
callback,
|
|
||||||
user_ptr,
|
|
||||||
quality);
|
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -354,14 +352,12 @@ cubeb_resampler_create(cubeb_stream * stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
long
|
long
|
||||||
cubeb_resampler_fill(cubeb_resampler * resampler,
|
cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
|
||||||
void * input_buffer,
|
long * input_frames_count, void * output_buffer,
|
||||||
long * input_frames_count,
|
|
||||||
void * output_buffer,
|
|
||||||
long output_frames_needed)
|
long output_frames_needed)
|
||||||
{
|
{
|
||||||
return resampler->fill(input_buffer, input_frames_count,
|
return resampler->fill(input_buffer, input_frames_count, output_buffer,
|
||||||
output_buffer, output_frames_needed);
|
output_frames_needed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -21,6 +21,11 @@ typedef enum {
|
||||||
CUBEB_RESAMPLER_QUALITY_DESKTOP
|
CUBEB_RESAMPLER_QUALITY_DESKTOP
|
||||||
} cubeb_resampler_quality;
|
} cubeb_resampler_quality;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CUBEB_RESAMPLER_RECLOCK_NONE,
|
||||||
|
CUBEB_RESAMPLER_RECLOCK_INPUT
|
||||||
|
} cubeb_resampler_reclock;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a resampler to adapt the requested sample rate into something that
|
* Create a resampler to adapt the requested sample rate into something that
|
||||||
* is accepted by the audio backend.
|
* is accepted by the audio backend.
|
||||||
|
@ -39,13 +44,13 @@ typedef enum {
|
||||||
* @param quality Quality of the resampler.
|
* @param quality Quality of the resampler.
|
||||||
* @retval A non-null pointer if success.
|
* @retval A non-null pointer if success.
|
||||||
*/
|
*/
|
||||||
cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream,
|
cubeb_resampler *
|
||||||
|
cubeb_resampler_create(cubeb_stream * stream,
|
||||||
cubeb_stream_params * input_params,
|
cubeb_stream_params * input_params,
|
||||||
cubeb_stream_params * output_params,
|
cubeb_stream_params * output_params,
|
||||||
unsigned int target_rate,
|
unsigned int target_rate, cubeb_data_callback callback,
|
||||||
cubeb_data_callback callback,
|
void * user_ptr, cubeb_resampler_quality quality,
|
||||||
void * user_ptr,
|
cubeb_resampler_reclock reclock);
|
||||||
cubeb_resampler_quality quality);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fill the buffer with frames acquired using the data callback. Resampling will
|
* Fill the buffer with frames acquired using the data callback. Resampling will
|
||||||
|
@ -59,24 +64,25 @@ cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream,
|
||||||
* @retval Number of frames that are actually produced.
|
* @retval Number of frames that are actually produced.
|
||||||
* @retval CUBEB_ERROR on error.
|
* @retval CUBEB_ERROR on error.
|
||||||
*/
|
*/
|
||||||
long cubeb_resampler_fill(cubeb_resampler * resampler,
|
long
|
||||||
void * input_buffer,
|
cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
|
||||||
long * input_frame_count,
|
long * input_frame_count, void * output_buffer,
|
||||||
void * output_buffer,
|
|
||||||
long output_frames_needed);
|
long output_frames_needed);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a cubeb_resampler.
|
* Destroy a cubeb_resampler.
|
||||||
* @param resampler A cubeb_resampler instance.
|
* @param resampler A cubeb_resampler instance.
|
||||||
*/
|
*/
|
||||||
void cubeb_resampler_destroy(cubeb_resampler * resampler);
|
void
|
||||||
|
cubeb_resampler_destroy(cubeb_resampler * resampler);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the latency, in frames, of the resampler.
|
* Returns the latency, in frames, of the resampler.
|
||||||
* @param resampler A cubeb resampler instance.
|
* @param resampler A cubeb resampler instance.
|
||||||
* @retval The latency, in frames, induced by the resampler.
|
* @retval The latency, in frames, induced by the resampler.
|
||||||
*/
|
*/
|
||||||
long cubeb_resampler_latency(cubeb_resampler * resampler);
|
long
|
||||||
|
cubeb_resampler_latency(cubeb_resampler * resampler);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,9 @@
|
||||||
#if !defined(CUBEB_RESAMPLER_INTERNAL)
|
#if !defined(CUBEB_RESAMPLER_INTERNAL)
|
||||||
#define CUBEB_RESAMPLER_INTERNAL
|
#define CUBEB_RESAMPLER_INTERNAL
|
||||||
|
|
||||||
#include <cmath>
|
|
||||||
#include <cassert>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#ifdef CUBEB_GECKO_BUILD
|
#ifdef CUBEB_GECKO_BUILD
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
|
@ -31,23 +31,26 @@ MOZ_BEGIN_STD_NAMESPACE
|
||||||
#define unique_ptr UniquePtr
|
#define unique_ptr UniquePtr
|
||||||
MOZ_END_STD_NAMESPACE
|
MOZ_END_STD_NAMESPACE
|
||||||
#endif
|
#endif
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb_utils.h"
|
|
||||||
#include "cubeb-speex-resampler.h"
|
#include "cubeb-speex-resampler.h"
|
||||||
#include "cubeb_resampler.h"
|
#include "cubeb/cubeb.h"
|
||||||
#include "cubeb_log.h"
|
#include "cubeb_log.h"
|
||||||
|
#include "cubeb_resampler.h"
|
||||||
|
#include "cubeb_utils.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/* This header file contains the internal C++ API of the resamplers, for testing. */
|
/* This header file contains the internal C++ API of the resamplers, for
|
||||||
|
* testing. */
|
||||||
|
|
||||||
// When dropping audio input frames to prevent building
|
// When dropping audio input frames to prevent building
|
||||||
// an input delay, this function returns the number of frames
|
// an input delay, this function returns the number of frames
|
||||||
// to keep in the buffer.
|
// to keep in the buffer.
|
||||||
// @parameter sample_rate The sample rate of the stream.
|
// @parameter sample_rate The sample rate of the stream.
|
||||||
// @return A number of frames to keep.
|
// @return A number of frames to keep.
|
||||||
uint32_t min_buffered_audio_frame(uint32_t sample_rate);
|
uint32_t
|
||||||
|
min_buffered_audio_frame(uint32_t sample_rate);
|
||||||
|
|
||||||
int to_speex_quality(cubeb_resampler_quality q);
|
int
|
||||||
|
to_speex_quality(cubeb_resampler_quality q);
|
||||||
|
|
||||||
struct cubeb_resampler {
|
struct cubeb_resampler {
|
||||||
virtual long fill(void * input_buffer, long * input_frames_count,
|
virtual long fill(void * input_buffer, long * input_frames_count,
|
||||||
|
@ -59,14 +62,10 @@ struct cubeb_resampler {
|
||||||
/** Base class for processors. This is just used to share methods for now. */
|
/** Base class for processors. This is just used to share methods for now. */
|
||||||
class processor {
|
class processor {
|
||||||
public:
|
public:
|
||||||
explicit processor(uint32_t channels)
|
explicit processor(uint32_t channels) : channels(channels) {}
|
||||||
: channels(channels)
|
|
||||||
{}
|
|
||||||
protected:
|
protected:
|
||||||
size_t frames_to_samples(size_t frames) const
|
size_t frames_to_samples(size_t frames) const { return frames * channels; }
|
||||||
{
|
|
||||||
return frames * channels;
|
|
||||||
}
|
|
||||||
size_t samples_to_frames(size_t samples) const
|
size_t samples_to_frames(size_t samples) const
|
||||||
{
|
{
|
||||||
assert(!(samples % channels));
|
assert(!(samples % channels));
|
||||||
|
@ -77,29 +76,24 @@ protected:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class passthrough_resampler : public cubeb_resampler
|
class passthrough_resampler : public cubeb_resampler, public processor {
|
||||||
, public processor {
|
|
||||||
public:
|
public:
|
||||||
passthrough_resampler(cubeb_stream * s,
|
passthrough_resampler(cubeb_stream * s, cubeb_data_callback cb, void * ptr,
|
||||||
cubeb_data_callback cb,
|
uint32_t input_channels, uint32_t sample_rate);
|
||||||
void * ptr,
|
|
||||||
uint32_t input_channels,
|
|
||||||
uint32_t sample_rate);
|
|
||||||
|
|
||||||
virtual long fill(void * input_buffer, long * input_frames_count,
|
virtual long fill(void * input_buffer, long * input_frames_count,
|
||||||
void * output_buffer, long output_frames);
|
void * output_buffer, long output_frames);
|
||||||
|
|
||||||
virtual long latency()
|
virtual long latency() { return 0; }
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop_audio_if_needed()
|
void drop_audio_if_needed()
|
||||||
{
|
{
|
||||||
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
||||||
uint32_t available = samples_to_frames(internal_input_buffer.length());
|
uint32_t available = samples_to_frames(internal_input_buffer.length());
|
||||||
if (available > to_keep) {
|
if (available > to_keep) {
|
||||||
internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
ALOGV("Dropping %u frames", available - to_keep);
|
||||||
|
internal_input_buffer.pop(nullptr,
|
||||||
|
frames_to_samples(available - to_keep));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,10 +114,8 @@ template<typename T, typename InputProcessing, typename OutputProcessing>
|
||||||
class cubeb_resampler_speex : public cubeb_resampler {
|
class cubeb_resampler_speex : public cubeb_resampler {
|
||||||
public:
|
public:
|
||||||
cubeb_resampler_speex(InputProcessing * input_processor,
|
cubeb_resampler_speex(InputProcessing * input_processor,
|
||||||
OutputProcessing * output_processor,
|
OutputProcessing * output_processor, cubeb_stream * s,
|
||||||
cubeb_stream * s,
|
cubeb_data_callback cb, void * ptr);
|
||||||
cubeb_data_callback cb,
|
|
||||||
void * ptr);
|
|
||||||
|
|
||||||
virtual ~cubeb_resampler_speex();
|
virtual ~cubeb_resampler_speex();
|
||||||
|
|
||||||
|
@ -143,7 +135,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef long(cubeb_resampler_speex::*processing_callback)(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed);
|
typedef long (cubeb_resampler_speex::*processing_callback)(
|
||||||
|
T * input_buffer, long * input_frames_count, T * output_buffer,
|
||||||
|
long output_frames_needed);
|
||||||
|
|
||||||
long fill_internal_duplex(T * input_buffer, long * input_frames_count,
|
long fill_internal_duplex(T * input_buffer, long * input_frames_count,
|
||||||
T * output_buffer, long output_frames_needed);
|
T * output_buffer, long output_frames_needed);
|
||||||
|
@ -165,8 +159,7 @@ private:
|
||||||
* audio buffers of type T. This class is designed so that the number of frames
|
* 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
|
* 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. */
|
* input buffer, and can use the caller's output buffer, or allocate its own. */
|
||||||
template<typename T>
|
template <typename T> class cubeb_resampler_speex_one_way : public processor {
|
||||||
class cubeb_resampler_speex_one_way : public processor {
|
|
||||||
public:
|
public:
|
||||||
/** The sample type of this resampler, either 16-bit integers or 32-bit
|
/** The sample type of this resampler, either 16-bit integers or 32-bit
|
||||||
* floats. */
|
* floats. */
|
||||||
|
@ -178,19 +171,15 @@ public:
|
||||||
* @parameter target_rate The sample-rate of the audio output.
|
* @parameter target_rate The sample-rate of the audio output.
|
||||||
* @parameter quality A number between 0 (fast, low quality) and 10 (slow,
|
* @parameter quality A number between 0 (fast, low quality) and 10 (slow,
|
||||||
* high quality). */
|
* high quality). */
|
||||||
cubeb_resampler_speex_one_way(uint32_t channels,
|
cubeb_resampler_speex_one_way(uint32_t channels, uint32_t source_rate,
|
||||||
uint32_t source_rate,
|
uint32_t target_rate, int quality)
|
||||||
uint32_t target_rate,
|
: processor(channels),
|
||||||
int quality)
|
resampling_ratio(static_cast<float>(source_rate) / target_rate),
|
||||||
: processor(channels)
|
source_rate(source_rate), additional_latency(0), leftover_samples(0)
|
||||||
, resampling_ratio(static_cast<float>(source_rate) / target_rate)
|
|
||||||
, source_rate(source_rate)
|
|
||||||
, additional_latency(0)
|
|
||||||
, leftover_samples(0)
|
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
speex_resampler = speex_resampler_init(channels, source_rate,
|
speex_resampler =
|
||||||
target_rate, quality, &r);
|
speex_resampler_init(channels, source_rate, target_rate, quality, &r);
|
||||||
assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
|
assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
|
||||||
|
|
||||||
uint32_t input_latency = speex_resampler_get_input_latency(speex_resampler);
|
uint32_t input_latency = speex_resampler_get_input_latency(speex_resampler);
|
||||||
|
@ -200,10 +189,7 @@ public:
|
||||||
uint32_t input_frame_count = input_latency;
|
uint32_t input_frame_count = input_latency;
|
||||||
uint32_t output_frame_count = LATENCY_SAMPLES;
|
uint32_t output_frame_count = LATENCY_SAMPLES;
|
||||||
assert(input_latency * channels <= LATENCY_SAMPLES);
|
assert(input_latency * channels <= LATENCY_SAMPLES);
|
||||||
speex_resample(
|
speex_resample(input_buffer, &input_frame_count, output_buffer,
|
||||||
input_buffer,
|
|
||||||
&input_frame_count,
|
|
||||||
output_buffer,
|
|
||||||
&output_frame_count);
|
&output_frame_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,8 +213,8 @@ public:
|
||||||
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
|
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
|
||||||
uint32_t out_len = output_frame_count;
|
uint32_t out_len = output_frame_count;
|
||||||
|
|
||||||
speex_resample(resampling_in_buffer.data(), &in_len,
|
speex_resample(resampling_in_buffer.data(), &in_len, output_buffer,
|
||||||
output_buffer, &out_len);
|
&out_len);
|
||||||
|
|
||||||
/* This shifts back any unresampled samples to the beginning of the input
|
/* This shifts back any unresampled samples to the beginning of the input
|
||||||
buffer. */
|
buffer. */
|
||||||
|
@ -239,15 +225,17 @@ public:
|
||||||
|
|
||||||
size_t output_for_input(uint32_t input_frames)
|
size_t output_for_input(uint32_t input_frames)
|
||||||
{
|
{
|
||||||
return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
|
return (size_t)floorf(
|
||||||
/ resampling_ratio);
|
(input_frames + samples_to_frames(resampling_in_buffer.length())) /
|
||||||
|
resampling_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns a buffer containing exactly `output_frame_count` resampled frames.
|
/** Returns a buffer containing exactly `output_frame_count` resampled frames.
|
||||||
* The consumer should not hold onto the pointer. */
|
* The consumer should not hold onto the pointer. */
|
||||||
T * output(size_t output_frame_count, size_t * input_frames_used)
|
T * output(size_t output_frame_count, size_t * input_frames_used)
|
||||||
{
|
{
|
||||||
if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
|
if (resampling_out_buffer.capacity() <
|
||||||
|
frames_to_samples(output_frame_count)) {
|
||||||
resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
|
resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,10 +246,12 @@ public:
|
||||||
resampling_out_buffer.data(), &out_len);
|
resampling_out_buffer.data(), &out_len);
|
||||||
|
|
||||||
if (out_len < output_frame_count) {
|
if (out_len < output_frame_count) {
|
||||||
LOGV("underrun during resampling: got %u frames, expected %zu", (unsigned)out_len, output_frame_count);
|
LOGV("underrun during resampling: got %u frames, expected %zu",
|
||||||
|
(unsigned)out_len, output_frame_count);
|
||||||
// silence the rightmost part
|
// silence the rightmost part
|
||||||
T * data = resampling_out_buffer.data();
|
T * data = resampling_out_buffer.data();
|
||||||
for (uint32_t i = frames_to_samples(out_len); i < frames_to_samples(output_frame_count); i++) {
|
for (uint32_t i = frames_to_samples(out_len);
|
||||||
|
i < frames_to_samples(output_frame_count); i++) {
|
||||||
data[i] = 0;
|
data[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -281,8 +271,8 @@ public:
|
||||||
* only consider a single channel here so it's the same number of frames. */
|
* only consider a single channel here so it's the same number of frames. */
|
||||||
int latency = 0;
|
int latency = 0;
|
||||||
|
|
||||||
latency =
|
latency = speex_resampler_get_output_latency(speex_resampler) +
|
||||||
speex_resampler_get_output_latency(speex_resampler) + additional_latency;
|
additional_latency;
|
||||||
|
|
||||||
assert(latency >= 0);
|
assert(latency >= 0);
|
||||||
|
|
||||||
|
@ -296,11 +286,13 @@ public:
|
||||||
uint32_t input_needed_for_output(int32_t output_frame_count) const
|
uint32_t input_needed_for_output(int32_t output_frame_count) const
|
||||||
{
|
{
|
||||||
assert(output_frame_count >= 0); // Check overflow
|
assert(output_frame_count >= 0); // Check overflow
|
||||||
int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length());
|
int32_t unresampled_frames_left =
|
||||||
int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length());
|
samples_to_frames(resampling_in_buffer.length());
|
||||||
|
int32_t resampled_frames_left =
|
||||||
|
samples_to_frames(resampling_out_buffer.length());
|
||||||
float input_frames_needed =
|
float input_frames_needed =
|
||||||
(output_frame_count - unresampled_frames_left) * resampling_ratio
|
(output_frame_count - unresampled_frames_left) * resampling_ratio -
|
||||||
- resampled_frames_left;
|
resampled_frames_left;
|
||||||
if (input_frames_needed < 0) {
|
if (input_frames_needed < 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -334,9 +326,11 @@ public:
|
||||||
uint32_t available = samples_to_frames(resampling_in_buffer.length());
|
uint32_t available = samples_to_frames(resampling_in_buffer.length());
|
||||||
uint32_t to_keep = min_buffered_audio_frame(source_rate);
|
uint32_t to_keep = min_buffered_audio_frame(source_rate);
|
||||||
if (available > to_keep) {
|
if (available > to_keep) {
|
||||||
|
ALOGV("Dropping %u frames", available - to_keep);
|
||||||
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Wrapper for the speex resampling functions to have a typed
|
/** Wrapper for the speex resampling functions to have a typed
|
||||||
* interface. */
|
* interface. */
|
||||||
|
@ -347,10 +341,8 @@ private:
|
||||||
int rv;
|
int rv;
|
||||||
rv =
|
rv =
|
||||||
#endif
|
#endif
|
||||||
speex_resampler_process_interleaved_float(speex_resampler,
|
speex_resampler_process_interleaved_float(
|
||||||
input_buffer,
|
speex_resampler, input_buffer, input_frame_count, output_buffer,
|
||||||
input_frame_count,
|
|
||||||
output_buffer,
|
|
||||||
output_frame_count);
|
output_frame_count);
|
||||||
assert(rv == RESAMPLER_ERR_SUCCESS);
|
assert(rv == RESAMPLER_ERR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -362,10 +354,8 @@ private:
|
||||||
int rv;
|
int rv;
|
||||||
rv =
|
rv =
|
||||||
#endif
|
#endif
|
||||||
speex_resampler_process_interleaved_int(speex_resampler,
|
speex_resampler_process_interleaved_int(
|
||||||
input_buffer,
|
speex_resampler, input_buffer, input_frame_count, output_buffer,
|
||||||
input_frame_count,
|
|
||||||
output_buffer,
|
|
||||||
output_frame_count);
|
output_frame_count);
|
||||||
assert(rv == RESAMPLER_ERR_SUCCESS);
|
assert(rv == RESAMPLER_ERR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
@ -387,18 +377,16 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/** This class allows delaying an audio stream by `frames` frames. */
|
/** This class allows delaying an audio stream by `frames` frames. */
|
||||||
template<typename T>
|
template <typename T> class delay_line : public processor {
|
||||||
class delay_line : public processor {
|
|
||||||
public:
|
public:
|
||||||
/** Constructor
|
/** Constructor
|
||||||
* @parameter frames the number of frames of delay.
|
* @parameter frames the number of frames of delay.
|
||||||
* @parameter channels the number of channels of this delay line.
|
* @parameter channels the number of channels of this delay line.
|
||||||
* @parameter sample_rate sample-rate of the audio going through this delay line */
|
* @parameter sample_rate sample-rate of the audio going through this delay
|
||||||
|
* line */
|
||||||
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
|
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
|
||||||
: processor(channels)
|
: processor(channels), length(frames), leftover_samples(0),
|
||||||
, length(frames)
|
sample_rate(sample_rate)
|
||||||
, leftover_samples(0)
|
|
||||||
, sample_rate(sample_rate)
|
|
||||||
{
|
{
|
||||||
/* Fill the delay line with some silent frames to add latency. */
|
/* Fill the delay line with some silent frames to add latency. */
|
||||||
delay_input_buffer.push_silence(frames * channels);
|
delay_input_buffer.push_silence(frames * channels);
|
||||||
|
@ -436,7 +424,8 @@ public:
|
||||||
T * input_buffer(uint32_t frames_needed)
|
T * input_buffer(uint32_t frames_needed)
|
||||||
{
|
{
|
||||||
leftover_samples = delay_input_buffer.length();
|
leftover_samples = delay_input_buffer.length();
|
||||||
delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed));
|
delay_input_buffer.reserve(leftover_samples +
|
||||||
|
frames_to_samples(frames_needed));
|
||||||
return delay_input_buffer.data() + leftover_samples;
|
return delay_input_buffer.data() + leftover_samples;
|
||||||
}
|
}
|
||||||
/** This method works with `input_buffer`, and allows to inform the processor
|
/** This method works with `input_buffer`, and allows to inform the processor
|
||||||
|
@ -471,26 +460,23 @@ public:
|
||||||
assert(frames_needed >= 0); // Check overflow
|
assert(frames_needed >= 0); // Check overflow
|
||||||
return frames_needed;
|
return frames_needed;
|
||||||
}
|
}
|
||||||
/** Returns the number of frames produces for `input_frames` frames in input */
|
/** Returns the number of frames produces for `input_frames` frames in input
|
||||||
size_t output_for_input(uint32_t input_frames)
|
*/
|
||||||
{
|
size_t output_for_input(uint32_t input_frames) { return input_frames; }
|
||||||
return input_frames;
|
|
||||||
}
|
|
||||||
/** The number of frames this delay line delays the stream by.
|
/** The number of frames this delay line delays the stream by.
|
||||||
* @returns The number of frames of delay. */
|
* @returns The number of frames of delay. */
|
||||||
size_t latency()
|
size_t latency() { return length; }
|
||||||
{
|
|
||||||
return length;
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop_audio_if_needed()
|
void drop_audio_if_needed()
|
||||||
{
|
{
|
||||||
size_t available = samples_to_frames(delay_input_buffer.length());
|
size_t available = samples_to_frames(delay_input_buffer.length());
|
||||||
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
|
||||||
if (available > to_keep) {
|
if (available > to_keep) {
|
||||||
|
ALOGV("Dropping %u frames", available - to_keep);
|
||||||
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** The length, in frames, of this delay line */
|
/** The length, in frames, of this delay line */
|
||||||
uint32_t length;
|
uint32_t length;
|
||||||
|
@ -512,9 +498,9 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
||||||
cubeb_stream_params * input_params,
|
cubeb_stream_params * input_params,
|
||||||
cubeb_stream_params * output_params,
|
cubeb_stream_params * output_params,
|
||||||
unsigned int target_rate,
|
unsigned int target_rate,
|
||||||
cubeb_data_callback callback,
|
cubeb_data_callback callback, void * user_ptr,
|
||||||
void * user_ptr,
|
cubeb_resampler_quality quality,
|
||||||
cubeb_resampler_quality quality)
|
cubeb_resampler_reclock reclock)
|
||||||
{
|
{
|
||||||
std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
|
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<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
|
||||||
|
@ -530,21 +516,19 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
||||||
if (((input_params && input_params->rate == target_rate) &&
|
if (((input_params && input_params->rate == target_rate) &&
|
||||||
(output_params && output_params->rate == target_rate)) ||
|
(output_params && output_params->rate == target_rate)) ||
|
||||||
(input_params && !output_params && (input_params->rate == target_rate)) ||
|
(input_params && !output_params && (input_params->rate == target_rate)) ||
|
||||||
(output_params && !input_params && (output_params->rate == target_rate))) {
|
(output_params && !input_params &&
|
||||||
|
(output_params->rate == target_rate))) {
|
||||||
LOG("Input and output sample-rate match, target rate of %dHz", target_rate);
|
LOG("Input and output sample-rate match, target rate of %dHz", target_rate);
|
||||||
return new passthrough_resampler<T>(stream, callback,
|
return new passthrough_resampler<T>(
|
||||||
user_ptr,
|
stream, callback, user_ptr, input_params ? input_params->channels : 0,
|
||||||
input_params ? input_params->channels : 0,
|
|
||||||
target_rate);
|
target_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine if we need to resampler one or both directions, and create the
|
/* Determine if we need to resampler one or both directions, and create the
|
||||||
resamplers. */
|
resamplers. */
|
||||||
if (output_params && (output_params->rate != target_rate)) {
|
if (output_params && (output_params->rate != target_rate)) {
|
||||||
output_resampler.reset(
|
output_resampler.reset(new cubeb_resampler_speex_one_way<T>(
|
||||||
new cubeb_resampler_speex_one_way<T>(output_params->channels,
|
output_params->channels, target_rate, output_params->rate,
|
||||||
target_rate,
|
|
||||||
output_params->rate,
|
|
||||||
to_speex_quality(quality)));
|
to_speex_quality(quality)));
|
||||||
if (!output_resampler) {
|
if (!output_resampler) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -552,10 +536,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_params && (input_params->rate != target_rate)) {
|
if (input_params && (input_params->rate != target_rate)) {
|
||||||
input_resampler.reset(
|
input_resampler.reset(new cubeb_resampler_speex_one_way<T>(
|
||||||
new cubeb_resampler_speex_one_way<T>(input_params->channels,
|
input_params->channels, input_params->rate, target_rate,
|
||||||
input_params->rate,
|
|
||||||
target_rate,
|
|
||||||
to_speex_quality(quality)));
|
to_speex_quality(quality)));
|
||||||
if (!input_resampler) {
|
if (!input_resampler) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -572,7 +554,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
||||||
if (!output_delay) {
|
if (!output_delay) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
} else if (output_resampler && !input_resampler && input_params && output_params) {
|
} else if (output_resampler && !input_resampler && input_params &&
|
||||||
|
output_params) {
|
||||||
input_delay.reset(new delay_line<T>(output_resampler->latency(),
|
input_delay.reset(new delay_line<T>(output_resampler->latency(),
|
||||||
input_params->channels,
|
input_params->channels,
|
||||||
output_params->rate));
|
output_params->rate));
|
||||||
|
@ -582,29 +565,26 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_resampler && output_resampler) {
|
if (input_resampler && output_resampler) {
|
||||||
LOG("Resampling input (%d) and output (%d) to target rate of %dHz", input_params->rate, output_params->rate, target_rate);
|
LOG("Resampling input (%d) and output (%d) to target rate of %dHz",
|
||||||
return new cubeb_resampler_speex<T,
|
input_params->rate, output_params->rate, target_rate);
|
||||||
cubeb_resampler_speex_one_way<T>,
|
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
|
||||||
cubeb_resampler_speex_one_way<T>>
|
cubeb_resampler_speex_one_way<T>>(
|
||||||
(input_resampler.release(),
|
input_resampler.release(), output_resampler.release(), stream, callback,
|
||||||
output_resampler.release(),
|
user_ptr);
|
||||||
stream, callback, user_ptr);
|
|
||||||
} else if (input_resampler) {
|
} else if (input_resampler) {
|
||||||
LOG("Resampling input (%d) to target and output rate of %dHz", input_params->rate, target_rate);
|
LOG("Resampling input (%d) to target and output rate of %dHz",
|
||||||
return new cubeb_resampler_speex<T,
|
input_params->rate, target_rate);
|
||||||
cubeb_resampler_speex_one_way<T>,
|
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
|
||||||
delay_line<T>>
|
delay_line<T>>(input_resampler.release(),
|
||||||
(input_resampler.release(),
|
|
||||||
output_delay.release(),
|
output_delay.release(),
|
||||||
stream, callback, user_ptr);
|
stream, callback, user_ptr);
|
||||||
} else {
|
} else {
|
||||||
LOG("Resampling output (%dHz) to target and input rate of %dHz", output_params->rate, target_rate);
|
LOG("Resampling output (%dHz) to target and input rate of %dHz",
|
||||||
return new cubeb_resampler_speex<T,
|
output_params->rate, target_rate);
|
||||||
delay_line<T>,
|
return new cubeb_resampler_speex<T, delay_line<T>,
|
||||||
cubeb_resampler_speex_one_way<T>>
|
cubeb_resampler_speex_one_way<T>>(
|
||||||
(input_delay.release(),
|
input_delay.release(), output_resampler.release(), stream, callback,
|
||||||
output_resampler.release(),
|
user_ptr);
|
||||||
stream, callback, user_ptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,17 +16,16 @@
|
||||||
them in the correct order. */
|
them in the correct order. */
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated space for the buffers. */
|
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 tail; /**< Index of the last element (first to deliver). */
|
||||||
unsigned int count; /**< Number of elements in the array. */
|
unsigned int count; /**< Number of elements in the array. */
|
||||||
unsigned int capacity; /**< Total length of the array. */
|
unsigned int capacity; /**< Total length of the array. */
|
||||||
} ring_array;
|
} ring_array;
|
||||||
|
|
||||||
static int
|
static int
|
||||||
single_audiobuffer_init(AudioBuffer * buffer,
|
single_audiobuffer_init(AudioBuffer * buffer, uint32_t bytesPerFrame,
|
||||||
uint32_t bytesPerFrame,
|
uint32_t channelsPerFrame, uint32_t frames)
|
||||||
uint32_t channelsPerFrame,
|
|
||||||
uint32_t frames)
|
|
||||||
{
|
{
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
|
assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
|
||||||
|
@ -48,15 +47,12 @@ single_audiobuffer_init(AudioBuffer * buffer,
|
||||||
@param ra The ring_array pointer of allocated structure.
|
@param ra The ring_array pointer of allocated structure.
|
||||||
@retval 0 on success. */
|
@retval 0 on success. */
|
||||||
int
|
int
|
||||||
ring_array_init(ring_array * ra,
|
ring_array_init(ring_array * ra, uint32_t capacity, uint32_t bytesPerFrame,
|
||||||
uint32_t capacity,
|
uint32_t channelsPerFrame, uint32_t framesPerBuffer)
|
||||||
uint32_t bytesPerFrame,
|
|
||||||
uint32_t channelsPerFrame,
|
|
||||||
uint32_t framesPerBuffer)
|
|
||||||
{
|
{
|
||||||
assert(ra);
|
assert(ra);
|
||||||
if (capacity == 0 || bytesPerFrame == 0 ||
|
if (capacity == 0 || bytesPerFrame == 0 || channelsPerFrame == 0 ||
|
||||||
channelsPerFrame == 0 || framesPerBuffer == 0) {
|
framesPerBuffer == 0) {
|
||||||
return CUBEB_ERROR_INVALID_PARAMETER;
|
return CUBEB_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
ra->capacity = capacity;
|
ra->capacity = capacity;
|
||||||
|
@ -70,8 +66,7 @@ ring_array_init(ring_array * ra,
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < ra->capacity; ++i) {
|
for (unsigned int i = 0; i < ra->capacity; ++i) {
|
||||||
if (single_audiobuffer_init(&ra->buffer_array[i],
|
if (single_audiobuffer_init(&ra->buffer_array[i], bytesPerFrame,
|
||||||
bytesPerFrame,
|
|
||||||
channelsPerFrame,
|
channelsPerFrame,
|
||||||
framesPerBuffer) != CUBEB_OK) {
|
framesPerBuffer) != CUBEB_OK) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -100,7 +95,8 @@ ring_array_destroy(ring_array * ra)
|
||||||
|
|
||||||
/** Get the allocated buffer to be stored with fresh data.
|
/** Get the allocated buffer to be stored with fresh data.
|
||||||
@param ra The ring_array pointer.
|
@param ra The ring_array pointer.
|
||||||
@retval Pointer of the allocated space to be stored with fresh data or NULL if full. */
|
@retval Pointer of the allocated space to be stored with fresh data or NULL
|
||||||
|
if full. */
|
||||||
AudioBuffer *
|
AudioBuffer *
|
||||||
ring_array_get_free_buffer(ring_array * ra)
|
ring_array_get_free_buffer(ring_array * ra)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
/**
|
/**
|
||||||
* Single producer single consumer lock-free and wait-free ring buffer.
|
* Single producer single consumer lock-free and wait-free ring buffer.
|
||||||
*
|
*
|
||||||
* This data structure allows producing data from one thread, and consuming it on
|
* This data structure allows producing data from one thread, and consuming it
|
||||||
* another thread, safely and without explicit synchronization. If used on two
|
* on another thread, safely and without explicit synchronization. If used on
|
||||||
* threads, this data structure uses atomics for thread safety. It is possible
|
* two threads, this data structure uses atomics for thread safety. It is
|
||||||
* to disable the use of atomics at compile time and only use this data
|
* possible to disable the use of atomics at compile time and only use this data
|
||||||
* structure on one thread.
|
* structure on one thread.
|
||||||
*
|
*
|
||||||
* The role for the producer and the consumer must be constant, i.e., the
|
* The role for the producer and the consumer must be constant, i.e., the
|
||||||
|
@ -48,9 +48,7 @@
|
||||||
* providing an external buffer to copy into is an easy way to have linear
|
* providing an external buffer to copy into is an easy way to have linear
|
||||||
* data for further processing.
|
* data for further processing.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T> class ring_buffer_base {
|
||||||
class ring_buffer_base
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Constructor for a ring buffer.
|
* Constructor for a ring buffer.
|
||||||
|
@ -64,8 +62,7 @@ public:
|
||||||
/* One more element to distinguish from empty and full buffer. */
|
/* One more element to distinguish from empty and full buffer. */
|
||||||
: capacity_(capacity + 1)
|
: capacity_(capacity + 1)
|
||||||
{
|
{
|
||||||
assert(storage_capacity() <
|
assert(storage_capacity() < std::numeric_limits<int>::max() / 2 &&
|
||||||
std::numeric_limits<int>::max() / 2 &&
|
|
||||||
"buffer too large for the type of index used.");
|
"buffer too large for the type of index used.");
|
||||||
assert(capacity_ > 0);
|
assert(capacity_ > 0);
|
||||||
|
|
||||||
|
@ -84,10 +81,7 @@ public:
|
||||||
* @param count The number of elements to enqueue.
|
* @param count The number of elements to enqueue.
|
||||||
* @return The number of element enqueued.
|
* @return The number of element enqueued.
|
||||||
*/
|
*/
|
||||||
int enqueue_default(int count)
|
int enqueue_default(int count) { return enqueue(nullptr, count); }
|
||||||
{
|
|
||||||
return enqueue(nullptr, count);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @brief Put an element in the queue
|
* @brief Put an element in the queue
|
||||||
*
|
*
|
||||||
|
@ -97,20 +91,18 @@ public:
|
||||||
*
|
*
|
||||||
* @return 1 if the element was inserted, 0 otherwise.
|
* @return 1 if the element was inserted, 0 otherwise.
|
||||||
*/
|
*/
|
||||||
int enqueue(T& element)
|
int enqueue(T & element) { return enqueue(&element, 1); }
|
||||||
{
|
|
||||||
return enqueue(&element, 1);
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Push `count` elements in the ring buffer.
|
* Push `count` elements in the ring buffer.
|
||||||
*
|
*
|
||||||
* Only safely called on the producer thread.
|
* Only safely called on the producer thread.
|
||||||
*
|
*
|
||||||
* @param elements a pointer to a buffer containing at least `count` elements.
|
* @param elements a pointer to a buffer containing at least `count` elements.
|
||||||
* If `elements` is nullptr, zero or default constructed elements are enqueued.
|
* If `elements` is nullptr, zero or default constructed elements are
|
||||||
|
* enqueued.
|
||||||
* @param count The number of elements to read from `elements`
|
* @param count The number of elements to read from `elements`
|
||||||
* @return The number of elements successfully coped from `elements` and inserted
|
* @return The number of elements successfully coped from `elements` and
|
||||||
* into the ring buffer.
|
* inserted into the ring buffer.
|
||||||
*/
|
*/
|
||||||
int enqueue(T * elements, int count)
|
int enqueue(T * elements, int count)
|
||||||
{
|
{
|
||||||
|
@ -118,19 +110,17 @@ public:
|
||||||
assert_correct_thread(producer_id);
|
assert_correct_thread(producer_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
|
int wr_idx = write_index_.load(std::memory_order_relaxed);
|
||||||
int wr_idx = write_index_.load(std::memory_order::memory_order_relaxed);
|
int rd_idx = read_index_.load(std::memory_order_acquire);
|
||||||
|
|
||||||
if (full_internal(rd_idx, wr_idx)) {
|
if (full_internal(rd_idx, wr_idx)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int to_write =
|
int to_write = std::min(available_write_internal(rd_idx, wr_idx), count);
|
||||||
std::min(available_write_internal(rd_idx, wr_idx), count);
|
|
||||||
|
|
||||||
/* First part, from the write index to the end of the array. */
|
/* First part, from the write index to the end of the array. */
|
||||||
int first_part = std::min(storage_capacity() - wr_idx,
|
int first_part = std::min(storage_capacity() - wr_idx, to_write);
|
||||||
to_write);
|
|
||||||
/* Second part, from the beginning of the array */
|
/* Second part, from the beginning of the array */
|
||||||
int second_part = to_write - first_part;
|
int second_part = to_write - first_part;
|
||||||
|
|
||||||
|
@ -142,7 +132,8 @@ public:
|
||||||
ConstructDefault(data_.get(), second_part);
|
ConstructDefault(data_.get(), second_part);
|
||||||
}
|
}
|
||||||
|
|
||||||
write_index_.store(increment_index(wr_idx, to_write), std::memory_order::memory_order_release);
|
write_index_.store(increment_index(wr_idx, to_write),
|
||||||
|
std::memory_order_release);
|
||||||
|
|
||||||
return to_write;
|
return to_write;
|
||||||
}
|
}
|
||||||
|
@ -163,15 +154,14 @@ public:
|
||||||
assert_correct_thread(consumer_id);
|
assert_correct_thread(consumer_id);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int wr_idx = write_index_.load(std::memory_order::memory_order_acquire);
|
int rd_idx = read_index_.load(std::memory_order_relaxed);
|
||||||
int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
|
int wr_idx = write_index_.load(std::memory_order_acquire);
|
||||||
|
|
||||||
if (empty_internal(rd_idx, wr_idx)) {
|
if (empty_internal(rd_idx, wr_idx)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int to_read =
|
int to_read = std::min(available_read_internal(rd_idx, wr_idx), count);
|
||||||
std::min(available_read_internal(rd_idx, wr_idx), count);
|
|
||||||
|
|
||||||
int first_part = std::min(storage_capacity() - rd_idx, to_read);
|
int first_part = std::min(storage_capacity() - rd_idx, to_read);
|
||||||
int second_part = to_read - first_part;
|
int second_part = to_read - first_part;
|
||||||
|
@ -181,7 +171,8 @@ public:
|
||||||
Copy(elements + first_part, data_.get(), second_part);
|
Copy(elements + first_part, data_.get(), second_part);
|
||||||
}
|
}
|
||||||
|
|
||||||
read_index_.store(increment_index(rd_idx, to_read), std::memory_order::memory_order_relaxed);
|
read_index_.store(increment_index(rd_idx, to_read),
|
||||||
|
std::memory_order_release);
|
||||||
|
|
||||||
return to_read;
|
return to_read;
|
||||||
}
|
}
|
||||||
|
@ -197,8 +188,9 @@ public:
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert_correct_thread(consumer_id);
|
assert_correct_thread(consumer_id);
|
||||||
#endif
|
#endif
|
||||||
return available_read_internal(read_index_.load(std::memory_order::memory_order_relaxed),
|
return available_read_internal(
|
||||||
write_index_.load(std::memory_order::memory_order_relaxed));
|
read_index_.load(std::memory_order_relaxed),
|
||||||
|
write_index_.load(std::memory_order_acquire));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get the number of available elements for consuming.
|
* Get the number of available elements for consuming.
|
||||||
|
@ -212,8 +204,9 @@ public:
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
assert_correct_thread(producer_id);
|
assert_correct_thread(producer_id);
|
||||||
#endif
|
#endif
|
||||||
return available_write_internal(read_index_.load(std::memory_order::memory_order_relaxed),
|
return available_write_internal(
|
||||||
write_index_.load(std::memory_order::memory_order_relaxed));
|
read_index_.load(std::memory_order_acquire),
|
||||||
|
write_index_.load(std::memory_order_relaxed));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get the total capacity, for this ring buffer.
|
* Get the total capacity, for this ring buffer.
|
||||||
|
@ -222,10 +215,7 @@ public:
|
||||||
*
|
*
|
||||||
* @return The maximum capacity of this ring buffer.
|
* @return The maximum capacity of this ring buffer.
|
||||||
*/
|
*/
|
||||||
int capacity() const
|
int capacity() const { return storage_capacity() - 1; }
|
||||||
{
|
|
||||||
return storage_capacity() - 1;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Reset the consumer and producer thread identifier, in case the thread are
|
* 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
|
* being changed. This has to be externally synchronized. This is no-op when
|
||||||
|
@ -237,6 +227,7 @@ public:
|
||||||
consumer_id = producer_id = std::thread::id();
|
consumer_id = producer_id = std::thread::id();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Return true if the ring buffer is empty.
|
/** Return true if the ring buffer is empty.
|
||||||
*
|
*
|
||||||
|
@ -244,8 +235,7 @@ private:
|
||||||
* @param write_index the write index to consider
|
* @param write_index the write index to consider
|
||||||
* @return true if the ring buffer is empty, false otherwise.
|
* @return true if the ring buffer is empty, false otherwise.
|
||||||
**/
|
**/
|
||||||
bool empty_internal(int read_index,
|
bool empty_internal(int read_index, int write_index) const
|
||||||
int write_index) const
|
|
||||||
{
|
{
|
||||||
return write_index == read_index;
|
return write_index == read_index;
|
||||||
}
|
}
|
||||||
|
@ -258,8 +248,7 @@ private:
|
||||||
* @param write_index the write index to consider
|
* @param write_index the write index to consider
|
||||||
* @return true if the ring buffer is full, false otherwise.
|
* @return true if the ring buffer is full, false otherwise.
|
||||||
**/
|
**/
|
||||||
bool full_internal(int read_index,
|
bool full_internal(int read_index, int write_index) const
|
||||||
int write_index) const
|
|
||||||
{
|
{
|
||||||
return (write_index + 1) % storage_capacity() == read_index;
|
return (write_index + 1) % storage_capacity() == read_index;
|
||||||
}
|
}
|
||||||
|
@ -269,18 +258,13 @@ private:
|
||||||
*
|
*
|
||||||
* @return the number of elements that can be stored in the buffer.
|
* @return the number of elements that can be stored in the buffer.
|
||||||
*/
|
*/
|
||||||
int storage_capacity() const
|
int storage_capacity() const { return capacity_; }
|
||||||
{
|
|
||||||
return capacity_;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Returns the number of elements available for reading.
|
* Returns the number of elements available for reading.
|
||||||
*
|
*
|
||||||
* @return the number of available elements for reading.
|
* @return the number of available elements for reading.
|
||||||
*/
|
*/
|
||||||
int
|
int available_read_internal(int read_index, int write_index) const
|
||||||
available_read_internal(int read_index,
|
|
||||||
int write_index) const
|
|
||||||
{
|
{
|
||||||
if (write_index >= read_index) {
|
if (write_index >= read_index) {
|
||||||
return write_index - read_index;
|
return write_index - read_index;
|
||||||
|
@ -293,9 +277,7 @@ private:
|
||||||
*
|
*
|
||||||
* @return the number of elements that can be written into the array.
|
* @return the number of elements that can be written into the array.
|
||||||
*/
|
*/
|
||||||
int
|
int available_write_internal(int read_index, int write_index) const
|
||||||
available_write_internal(int read_index,
|
|
||||||
int write_index) const
|
|
||||||
{
|
{
|
||||||
/* We substract one element here to always keep at least one sample
|
/* We substract one element here to always keep at least one sample
|
||||||
* free in the buffer, to distinguish between full and empty array. */
|
* free in the buffer, to distinguish between full and empty array. */
|
||||||
|
@ -312,8 +294,7 @@ private:
|
||||||
* @param increment the number by which `index` is incremented.
|
* @param increment the number by which `index` is incremented.
|
||||||
* @return the new index.
|
* @return the new index.
|
||||||
*/
|
*/
|
||||||
int
|
int increment_index(int index, int increment) const
|
||||||
increment_index(int index, int increment) const
|
|
||||||
{
|
{
|
||||||
assert(increment >= 0);
|
assert(increment >= 0);
|
||||||
return (index + increment) % storage_capacity();
|
return (index + increment) % storage_capacity();
|
||||||
|
@ -354,9 +335,7 @@ private:
|
||||||
/**
|
/**
|
||||||
* Adapter for `ring_buffer_base` that exposes an interface in frames.
|
* Adapter for `ring_buffer_base` that exposes an interface in frames.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T> class audio_ring_buffer_base {
|
||||||
class audio_ring_buffer_base
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* @brief Constructor.
|
* @brief Constructor.
|
||||||
|
@ -365,8 +344,8 @@ public:
|
||||||
* @param capacity_in_frames The capacity in frames.
|
* @param capacity_in_frames The capacity in frames.
|
||||||
*/
|
*/
|
||||||
audio_ring_buffer_base(int channel_count, int capacity_in_frames)
|
audio_ring_buffer_base(int channel_count, int capacity_in_frames)
|
||||||
: channel_count(channel_count)
|
: channel_count(channel_count),
|
||||||
, ring_buffer(frames_to_samples(capacity_in_frames))
|
ring_buffer(frames_to_samples(capacity_in_frames))
|
||||||
{
|
{
|
||||||
assert(channel_count > 0);
|
assert(channel_count > 0);
|
||||||
}
|
}
|
||||||
|
@ -380,7 +359,8 @@ public:
|
||||||
*/
|
*/
|
||||||
int enqueue_default(int frame_count)
|
int enqueue_default(int frame_count)
|
||||||
{
|
{
|
||||||
return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
|
return samples_to_frames(
|
||||||
|
ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @brief Enqueue `frames_count` frames of audio.
|
* @brief Enqueue `frames_count` frames of audio.
|
||||||
|
@ -396,7 +376,8 @@ public:
|
||||||
|
|
||||||
int enqueue(T * frames, int frame_count)
|
int enqueue(T * frames, int frame_count)
|
||||||
{
|
{
|
||||||
return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
|
return samples_to_frames(
|
||||||
|
ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -413,7 +394,8 @@ public:
|
||||||
*/
|
*/
|
||||||
int dequeue(T * frames, int frame_count)
|
int dequeue(T * frames, int frame_count)
|
||||||
{
|
{
|
||||||
return samples_to_frames(ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
|
return samples_to_frames(
|
||||||
|
ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Get the number of available frames of audio for consuming.
|
* Get the number of available frames of audio for consuming.
|
||||||
|
@ -444,10 +426,8 @@ public:
|
||||||
*
|
*
|
||||||
* @return The maximum capacity of this ring buffer.
|
* @return The maximum capacity of this ring buffer.
|
||||||
*/
|
*/
|
||||||
int capacity() const
|
int capacity() const { return samples_to_frames(ring_buffer.capacity()); }
|
||||||
{
|
|
||||||
return samples_to_frames(ring_buffer.capacity());
|
|
||||||
}
|
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
* @brief Frames to samples conversion.
|
* @brief Frames to samples conversion.
|
||||||
|
@ -456,10 +436,7 @@ private:
|
||||||
*
|
*
|
||||||
* @return A number of samples.
|
* @return A number of samples.
|
||||||
*/
|
*/
|
||||||
int frames_to_samples(int frames) const
|
int frames_to_samples(int frames) const { return frames * channel_count; }
|
||||||
{
|
|
||||||
return frames * channel_count;
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* @brief Samples to frames conversion.
|
* @brief Samples to frames conversion.
|
||||||
*
|
*
|
||||||
|
@ -467,10 +444,7 @@ private:
|
||||||
*
|
*
|
||||||
* @return A number of frames.
|
* @return A number of frames.
|
||||||
*/
|
*/
|
||||||
int samples_to_frames(int samples) const
|
int samples_to_frames(int samples) const { return samples / channel_count; }
|
||||||
{
|
|
||||||
return samples / channel_count;
|
|
||||||
}
|
|
||||||
/** Number of channels of audio that will stream through this ring buffer. */
|
/** Number of channels of audio that will stream through this ring buffer. */
|
||||||
int channel_count;
|
int channel_count;
|
||||||
/** The underlying ring buffer that is used to store the data. */
|
/** The underlying ring buffer that is used to store the data. */
|
||||||
|
@ -482,8 +456,7 @@ private:
|
||||||
* from two threads, one producer, one consumer (that never change role),
|
* from two threads, one producer, one consumer (that never change role),
|
||||||
* without explicit synchronization.
|
* without explicit synchronization.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template <typename T> using lock_free_queue = ring_buffer_base<T>;
|
||||||
using lock_free_queue = ring_buffer_base<T>;
|
|
||||||
/**
|
/**
|
||||||
* Lock-free instantiation of the `audio_ring_buffer` type. This is safe to use
|
* 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),
|
* from two threads, one producer, one consumer (that never change role),
|
||||||
|
|
|
@ -4,29 +4,31 @@
|
||||||
* This program is made available under an ISC-style license. See the
|
* This program is made available under an ISC-style license. See the
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
|
#include <assert.h>
|
||||||
|
#include <dlfcn.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <sndio.h>
|
#include <sndio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <dlfcn.h>
|
#include <stdlib.h>
|
||||||
#include <assert.h>
|
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
|
||||||
|
|
||||||
#if defined(CUBEB_SNDIO_DEBUG)
|
#if defined(CUBEB_SNDIO_DEBUG)
|
||||||
#define DPR(...) fprintf(stderr, __VA_ARGS__);
|
#define DPR(...) fprintf(stderr, __VA_ARGS__);
|
||||||
#else
|
#else
|
||||||
#define DPR(...) do {} while(0)
|
#define DPR(...) \
|
||||||
|
do { \
|
||||||
|
} while (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef DISABLE_LIBSNDIO_DLOPEN
|
#ifdef DISABLE_LIBSNDIO_DLOPEN
|
||||||
#define WRAP(x) x
|
#define WRAP(x) x
|
||||||
#else
|
#else
|
||||||
#define WRAP(x) cubeb_##x
|
#define WRAP(x) (*cubeb_##x)
|
||||||
#define LIBSNDIO_API_VISIT(X) \
|
#define LIBSNDIO_API_VISIT(X) \
|
||||||
X(sio_close) \
|
X(sio_close) \
|
||||||
X(sio_eof) \
|
X(sio_eof) \
|
||||||
|
@ -41,7 +43,7 @@
|
||||||
X(sio_setpar) \
|
X(sio_setpar) \
|
||||||
X(sio_start) \
|
X(sio_start) \
|
||||||
X(sio_stop) \
|
X(sio_stop) \
|
||||||
X(sio_write) \
|
X(sio_write)
|
||||||
|
|
||||||
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
|
||||||
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
|
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
|
||||||
|
@ -319,7 +321,8 @@ sndio_init(cubeb **context, char const *context_name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define LOAD(x) { \
|
#define LOAD(x) \
|
||||||
|
{ \
|
||||||
cubeb_##x = dlsym(libsndio, #x); \
|
cubeb_##x = dlsym(libsndio, #x); \
|
||||||
if (!cubeb_##x) { \
|
if (!cubeb_##x) { \
|
||||||
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
|
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
|
||||||
|
@ -365,17 +368,14 @@ sndio_destroy(cubeb *context)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sndio_stream_init(cubeb * context,
|
sndio_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_stream ** stream,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_data_callback data_callback,
|
||||||
cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
void *user_ptr)
|
|
||||||
{
|
{
|
||||||
cubeb_stream * s;
|
cubeb_stream * s;
|
||||||
struct sio_par wpar, rpar;
|
struct sio_par wpar, rpar;
|
||||||
|
@ -445,8 +445,8 @@ sndio_stream_init(cubeb * context,
|
||||||
DPR("sndio_stream_init(), sio_setpar() failed\n");
|
DPR("sndio_stream_init(), sio_setpar() failed\n");
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
|
if (rpar.bits != wpar.bits || rpar.le != wpar.le || rpar.sig != wpar.sig ||
|
||||||
rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
|
rpar.rate != wpar.rate ||
|
||||||
((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
|
((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
|
||||||
((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
|
((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
|
||||||
DPR("sndio_stream_init() unsupported params\n");
|
DPR("sndio_stream_init() unsupported params\n");
|
||||||
|
@ -522,7 +522,8 @@ sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
|
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||||
|
uint32_t * latency_frames)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* We've no device-independent minimum latency.
|
* We've no device-independent minimum latency.
|
||||||
|
@ -626,7 +627,7 @@ sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
|
||||||
device->preferred = CUBEB_DEVICE_PREF_ALL;
|
device->preferred = CUBEB_DEVICE_PREF_ALL;
|
||||||
device->format = CUBEB_DEVICE_FMT_S16NE;
|
device->format = CUBEB_DEVICE_FMT_S16NE;
|
||||||
device->default_format = CUBEB_DEVICE_FMT_S16NE;
|
device->default_format = CUBEB_DEVICE_FMT_S16NE;
|
||||||
device->max_channels = 16;
|
device->max_channels = (type == CUBEB_DEVICE_TYPE_INPUT) ? 2 : 8;
|
||||||
device->default_rate = 48000;
|
device->default_rate = 48000;
|
||||||
device->min_rate = 4000;
|
device->min_rate = 4000;
|
||||||
device->max_rate = 192000;
|
device->max_rate = 192000;
|
||||||
|
@ -658,7 +659,6 @@ static struct cubeb_ops const sndio_ops = {
|
||||||
.stream_destroy = sndio_stream_destroy,
|
.stream_destroy = sndio_stream_destroy,
|
||||||
.stream_start = sndio_stream_start,
|
.stream_start = sndio_stream_start,
|
||||||
.stream_stop = sndio_stream_stop,
|
.stream_stop = sndio_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = sndio_stream_get_position,
|
.stream_get_position = sndio_stream_get_position,
|
||||||
.stream_get_latency = sndio_stream_get_latency,
|
.stream_get_latency = sndio_stream_get_latency,
|
||||||
.stream_set_volume = sndio_stream_set_volume,
|
.stream_set_volume = sndio_stream_set_volume,
|
||||||
|
@ -666,5 +666,4 @@ static struct cubeb_ops const sndio_ops = {
|
||||||
.stream_get_current_device = NULL,
|
.stream_get_current_device = NULL,
|
||||||
.stream_device_destroy = NULL,
|
.stream_device_destroy = NULL,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = NULL
|
.register_device_collection_changed = NULL};
|
||||||
};
|
|
||||||
|
|
|
@ -152,4 +152,3 @@ cubeb_strings_intern(cubeb_strings * strings, char const * s)
|
||||||
|
|
||||||
return cubeb_strings_push(strings, s);
|
return cubeb_strings_push(strings, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,12 +22,14 @@ typedef struct cubeb_strings cubeb_strings;
|
||||||
interned string storage will be returned.
|
interned string storage will be returned.
|
||||||
@retval CUBEB_OK in case of success.
|
@retval CUBEB_OK in case of success.
|
||||||
@retval CUBEB_ERROR in case of error. */
|
@retval CUBEB_ERROR in case of error. */
|
||||||
CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);
|
CUBEB_EXPORT int
|
||||||
|
cubeb_strings_init(cubeb_strings ** strings);
|
||||||
|
|
||||||
/** Destroy an interned string structure freeing all associated memory.
|
/** Destroy an interned string structure freeing all associated memory.
|
||||||
@param strings An opaque pointer to the interned string storage to
|
@param strings An opaque pointer to the interned string storage to
|
||||||
destroy. */
|
destroy. */
|
||||||
CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
|
CUBEB_EXPORT void
|
||||||
|
cubeb_strings_destroy(cubeb_strings * strings);
|
||||||
|
|
||||||
/** Add string to internal storage.
|
/** Add string to internal storage.
|
||||||
@param strings Opaque pointer to interned string storage.
|
@param strings Opaque pointer to interned string storage.
|
||||||
|
@ -35,7 +37,8 @@ CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
|
||||||
@retval CUBEB_OK
|
@retval CUBEB_OK
|
||||||
@retval CUBEB_ERROR
|
@retval CUBEB_ERROR
|
||||||
*/
|
*/
|
||||||
CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);
|
CUBEB_EXPORT char const *
|
||||||
|
cubeb_strings_intern(cubeb_strings * strings, char const * s);
|
||||||
|
|
||||||
#if defined(__cplusplus)
|
#if defined(__cplusplus)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,18 +4,18 @@
|
||||||
* This program is made available under an ISC-style license. See the
|
* This program is made available under an ISC-style license. See the
|
||||||
* accompanying file LICENSE for details.
|
* accompanying file LICENSE for details.
|
||||||
*/
|
*/
|
||||||
#include <sys/audioio.h>
|
#include "cubeb-internal.h"
|
||||||
#include <sys/ioctl.h>
|
#include "cubeb/cubeb.h"
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <limits.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <sys/audioio.h>
|
||||||
#include "cubeb/cubeb.h"
|
#include <sys/ioctl.h>
|
||||||
#include "cubeb-internal.h"
|
#include <unistd.h>
|
||||||
|
|
||||||
/* Default to 4 + 1 for the default device. */
|
/* Default to 4 + 1 for the default device. */
|
||||||
#ifndef SUN_DEVICE_COUNT
|
#ifndef SUN_DEVICE_COUNT
|
||||||
|
@ -145,8 +145,8 @@ sun_get_min_latency(cubeb * context, cubeb_stream_params params,
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sun_get_hwinfo(const char * device, struct audio_info * format,
|
sun_get_hwinfo(const char * device, struct audio_info * format, int * props,
|
||||||
int * props, struct audio_device * dev)
|
struct audio_device * dev)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
|
||||||
|
@ -183,7 +183,8 @@ sun_prinfo_verify_sanity(struct audio_prinfo * prinfo)
|
||||||
{
|
{
|
||||||
return prinfo->precision >= 8 && prinfo->precision <= 32 &&
|
return prinfo->precision >= 8 && prinfo->precision <= 32 &&
|
||||||
prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
|
prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
|
||||||
prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE;
|
prinfo->sample_rate < SUN_MAX_RATE &&
|
||||||
|
prinfo->sample_rate > SUN_MIN_RATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
@ -262,7 +263,8 @@ sun_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
device.vendor_name = strdup(hwname.name);
|
device.vendor_name = strdup(hwname.name);
|
||||||
device.type = type;
|
device.type = type;
|
||||||
device.state = CUBEB_DEVICE_STATE_ENABLED;
|
device.state = CUBEB_DEVICE_STATE_ENABLED;
|
||||||
device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
device.preferred =
|
||||||
|
(i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
||||||
#ifdef AUDIO_GETFORMAT
|
#ifdef AUDIO_GETFORMAT
|
||||||
device.max_channels = prinfo->channels;
|
device.max_channels = prinfo->channels;
|
||||||
device.default_rate = prinfo->sample_rate;
|
device.default_rate = prinfo->sample_rate;
|
||||||
|
@ -439,8 +441,8 @@ sun_io_routine(void * arg)
|
||||||
sun_linear32_to_float(s->record.buf,
|
sun_linear32_to_float(s->record.buf,
|
||||||
s->record.info.record.channels * SUN_BUFFER_FRAMES);
|
s->record.info.record.channels * SUN_BUFFER_FRAMES);
|
||||||
}
|
}
|
||||||
to_write = s->data_cb(s, s->user_ptr,
|
to_write = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf,
|
||||||
s->record.buf, s->play.buf, SUN_BUFFER_FRAMES);
|
SUN_BUFFER_FRAMES);
|
||||||
if (to_write == CUBEB_ERROR) {
|
if (to_write == CUBEB_ERROR) {
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
break;
|
break;
|
||||||
|
@ -456,8 +458,8 @@ sun_io_routine(void * arg)
|
||||||
sun_float_to_linear32(s->play.buf,
|
sun_float_to_linear32(s->play.buf,
|
||||||
s->play.info.play.channels * to_write, vol);
|
s->play.info.play.channels * to_write, vol);
|
||||||
} else {
|
} else {
|
||||||
sun_linear16_set_vol(s->play.buf,
|
sun_linear16_set_vol(s->play.buf, s->play.info.play.channels * to_write,
|
||||||
s->play.info.play.channels * to_write, vol);
|
vol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (to_write < SUN_BUFFER_FRAMES) {
|
if (to_write < SUN_BUFFER_FRAMES) {
|
||||||
|
@ -473,7 +475,8 @@ sun_io_routine(void * arg)
|
||||||
|
|
||||||
if (to_write > 0) {
|
if (to_write > 0) {
|
||||||
bytes = to_write * s->play.frame_size;
|
bytes = to_write * s->play.frame_size;
|
||||||
if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) {
|
if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) <
|
||||||
|
0) {
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -486,7 +489,8 @@ sun_io_routine(void * arg)
|
||||||
}
|
}
|
||||||
if (to_read > 0) {
|
if (to_read > 0) {
|
||||||
bytes = to_read * s->record.frame_size;
|
bytes = to_read * s->record.frame_size;
|
||||||
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) {
|
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs,
|
||||||
|
bytes)) < 0) {
|
||||||
state = CUBEB_STATE_ERROR;
|
state = CUBEB_STATE_ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -505,17 +509,13 @@ sun_io_routine(void * arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
sun_stream_init(cubeb * context,
|
sun_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_stream ** stream,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
char const * stream_name,
|
|
||||||
cubeb_devid input_device,
|
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned latency_frames,
|
unsigned latency_frames, cubeb_data_callback data_callback,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
cubeb_state_callback state_callback,
|
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
int ret = CUBEB_OK;
|
int ret = CUBEB_OK;
|
||||||
cubeb_stream * s = NULL;
|
cubeb_stream * s = NULL;
|
||||||
|
@ -529,14 +529,14 @@ sun_stream_init(cubeb * context,
|
||||||
s->record.fd = -1;
|
s->record.fd = -1;
|
||||||
s->play.fd = -1;
|
s->play.fd = -1;
|
||||||
if (input_device != 0) {
|
if (input_device != 0) {
|
||||||
snprintf(s->record.name, sizeof(s->record.name),
|
snprintf(s->record.name, sizeof(s->record.name), "/dev/audio%zu",
|
||||||
"/dev/audio%zu", (uintptr_t)input_device - 1);
|
(uintptr_t)input_device - 1);
|
||||||
} else {
|
} else {
|
||||||
snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE);
|
snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE);
|
||||||
}
|
}
|
||||||
if (output_device != 0) {
|
if (output_device != 0) {
|
||||||
snprintf(s->play.name, sizeof(s->play.name),
|
snprintf(s->play.name, sizeof(s->play.name), "/dev/audio%zu",
|
||||||
"/dev/audio%zu", (uintptr_t)output_device - 1);
|
(uintptr_t)output_device - 1);
|
||||||
} else {
|
} else {
|
||||||
snprintf(s->play.name, sizeof(s->play.name), "%s", SUN_DEFAULT_DEVICE);
|
snprintf(s->play.name, sizeof(s->play.name), "%s", SUN_DEFAULT_DEVICE);
|
||||||
}
|
}
|
||||||
|
@ -558,11 +558,13 @@ sun_stream_init(cubeb * context,
|
||||||
s->record.info.mode = AUMODE_RECORD;
|
s->record.info.mode = AUMODE_RECORD;
|
||||||
#endif
|
#endif
|
||||||
if ((ret = sun_copy_params(s->record.fd, s, input_stream_params,
|
if ((ret = sun_copy_params(s->record.fd, s, input_stream_params,
|
||||||
&s->record.info, &s->record.info.record)) != CUBEB_OK) {
|
&s->record.info, &s->record.info.record)) !=
|
||||||
|
CUBEB_OK) {
|
||||||
LOG("Setting record params failed");
|
LOG("Setting record params failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
s->record.floating =
|
||||||
|
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
|
||||||
}
|
}
|
||||||
if (output_stream_params != NULL) {
|
if (output_stream_params != NULL) {
|
||||||
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
|
||||||
|
@ -582,7 +584,8 @@ sun_stream_init(cubeb * context,
|
||||||
s->play.info.mode = AUMODE_PLAY;
|
s->play.info.mode = AUMODE_PLAY;
|
||||||
#endif
|
#endif
|
||||||
if ((ret = sun_copy_params(s->play.fd, s, output_stream_params,
|
if ((ret = sun_copy_params(s->play.fd, s, output_stream_params,
|
||||||
&s->play.info, &s->play.info.play)) != CUBEB_OK) {
|
&s->play.info, &s->play.info.play)) !=
|
||||||
|
CUBEB_OK) {
|
||||||
LOG("Setting play params failed");
|
LOG("Setting play params failed");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -597,17 +600,18 @@ sun_stream_init(cubeb * context,
|
||||||
LOG("Failed to create mutex");
|
LOG("Failed to create mutex");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
s->play.frame_size = s->play.info.play.channels *
|
s->play.frame_size =
|
||||||
(s->play.info.play.precision / 8);
|
s->play.info.play.channels * (s->play.info.play.precision / 8);
|
||||||
if (s->play.fd != -1 &&
|
if (s->play.fd != -1 &&
|
||||||
(s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) {
|
(s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) {
|
||||||
ret = CUBEB_ERROR;
|
ret = CUBEB_ERROR;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
s->record.frame_size = s->record.info.record.channels *
|
s->record.frame_size =
|
||||||
(s->record.info.record.precision / 8);
|
s->record.info.record.channels * (s->record.info.record.precision / 8);
|
||||||
if (s->record.fd != -1 &&
|
if (s->record.fd != -1 &&
|
||||||
(s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == NULL) {
|
(s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) ==
|
||||||
|
NULL) {
|
||||||
ret = CUBEB_ERROR;
|
ret = CUBEB_ERROR;
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
@ -688,10 +692,10 @@ sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
|
||||||
if (*device == NULL) {
|
if (*device == NULL) {
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
(*device)->input_name = stream->record.fd != -1 ?
|
(*device)->input_name =
|
||||||
strdup(stream->record.name) : NULL;
|
stream->record.fd != -1 ? strdup(stream->record.name) : NULL;
|
||||||
(*device)->output_name = stream->play.fd != -1 ?
|
(*device)->output_name =
|
||||||
strdup(stream->play.name) : NULL;
|
stream->play.fd != -1 ? strdup(stream->play.name) : NULL;
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -718,7 +722,6 @@ static struct cubeb_ops const sun_ops = {
|
||||||
.stream_destroy = sun_stream_destroy,
|
.stream_destroy = sun_stream_destroy,
|
||||||
.stream_start = sun_stream_start,
|
.stream_start = sun_stream_start,
|
||||||
.stream_stop = sun_stream_stop,
|
.stream_stop = sun_stream_stop,
|
||||||
.stream_reset_default_device = NULL,
|
|
||||||
.stream_get_position = sun_stream_get_position,
|
.stream_get_position = sun_stream_get_position,
|
||||||
.stream_get_latency = sun_stream_get_latency,
|
.stream_get_latency = sun_stream_get_latency,
|
||||||
.stream_get_input_latency = NULL,
|
.stream_get_input_latency = NULL,
|
||||||
|
@ -727,5 +730,4 @@ static struct cubeb_ops const sun_ops = {
|
||||||
.stream_get_current_device = sun_get_current_device,
|
.stream_get_current_device = sun_get_current_device,
|
||||||
.stream_device_destroy = sun_stream_device_destroy,
|
.stream_device_destroy = sun_stream_device_destroy,
|
||||||
.stream_register_device_changed_callback = NULL,
|
.stream_register_device_changed_callback = NULL,
|
||||||
.register_device_collection_changed = NULL
|
.register_device_collection_changed = NULL};
|
||||||
};
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright © 2022 Mozilla Foundation
|
||||||
|
*
|
||||||
|
* This program is made available under an ISC-style license. See the
|
||||||
|
* accompanying file LICENSE for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CUBEB_TRACING_H
|
||||||
|
#define CUBEB_TRACING_H
|
||||||
|
|
||||||
|
/* Empty header to allow hooking up a frame profiler. */
|
||||||
|
|
||||||
|
// To be called once on a thread to register for tracing.
|
||||||
|
#define CUBEB_REGISTER_THREAD(name)
|
||||||
|
// To be called once before a registered threads exits.
|
||||||
|
#define CUBEB_UNREGISTER_THREAD()
|
||||||
|
// Insert a tracing marker, with a particular name.
|
||||||
|
// Phase can be 'x': instant marker, start time but no duration
|
||||||
|
// 'b': beginning of a marker with a duration
|
||||||
|
// 'e': end of a marker with a duration
|
||||||
|
#define CUBEB_TRACE(name, phase)
|
||||||
|
|
||||||
|
#endif // CUBEB_TRACING_H
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
#include "cubeb_utils.h"
|
#include "cubeb_utils.h"
|
||||||
|
|
||||||
size_t cubeb_sample_size(cubeb_sample_format format)
|
size_t
|
||||||
|
cubeb_sample_size(cubeb_sample_format format)
|
||||||
{
|
{
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case CUBEB_SAMPLE_S16LE:
|
case CUBEB_SAMPLE_S16LE:
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
#include "cubeb_utils_win.h"
|
#include "cubeb_utils_win.h"
|
||||||
|
@ -25,7 +25,8 @@
|
||||||
|
|
||||||
/** Similar to memcpy, but accounts for the size of an element. */
|
/** Similar to memcpy, but accounts for the size of an element. */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void PodCopy(T * destination, const T * source, size_t count)
|
void
|
||||||
|
PodCopy(T * destination, const T * source, size_t count)
|
||||||
{
|
{
|
||||||
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
||||||
assert(destination && source);
|
assert(destination && source);
|
||||||
|
@ -34,7 +35,8 @@ void PodCopy(T * destination, const T * source, size_t count)
|
||||||
|
|
||||||
/** Similar to memmove, but accounts for the size of an element. */
|
/** Similar to memmove, but accounts for the size of an element. */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void PodMove(T * destination, const T * source, size_t count)
|
void
|
||||||
|
PodMove(T * destination, const T * source, size_t count)
|
||||||
{
|
{
|
||||||
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
||||||
assert(destination && source);
|
assert(destination && source);
|
||||||
|
@ -43,7 +45,8 @@ void PodMove(T * destination, const T * source, size_t count)
|
||||||
|
|
||||||
/** Similar to a memset to zero, but accounts for the size of an element. */
|
/** Similar to a memset to zero, but accounts for the size of an element. */
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void PodZero(T * destination, size_t count)
|
void
|
||||||
|
PodZero(T * destination, size_t count)
|
||||||
{
|
{
|
||||||
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
static_assert(std::is_trivial<T>::value, "Requires trivial type");
|
||||||
assert(destination);
|
assert(destination);
|
||||||
|
@ -52,7 +55,8 @@ void PodZero(T * destination, size_t count)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <typename T, typename Trait>
|
template <typename T, typename Trait>
|
||||||
void Copy(T * destination, const T * source, size_t count, Trait)
|
void
|
||||||
|
Copy(T * destination, const T * source, size_t count, Trait)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
destination[i] = source[i];
|
destination[i] = source[i];
|
||||||
|
@ -60,11 +64,12 @@ void Copy(T * destination, const T * source, size_t count, Trait)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Copy(T * destination, const T * source, size_t count, std::true_type)
|
void
|
||||||
|
Copy(T * destination, const T * source, size_t count, std::true_type)
|
||||||
{
|
{
|
||||||
PodCopy(destination, source, count);
|
PodCopy(destination, source, count);
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This allows copying a number of elements from a `source` pointer to a
|
* This allows copying a number of elements from a `source` pointer to a
|
||||||
|
@ -72,7 +77,8 @@ void Copy(T * destination, const T * source, size_t count, std::true_type)
|
||||||
* calls the constructors and destructors otherwise.
|
* calls the constructors and destructors otherwise.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void Copy(T * destination, const T * source, size_t count)
|
void
|
||||||
|
Copy(T * destination, const T * source, size_t count)
|
||||||
{
|
{
|
||||||
assert(destination && source);
|
assert(destination && source);
|
||||||
Copy(destination, source, count, typename std::is_trivial<T>::type());
|
Copy(destination, source, count, typename std::is_trivial<T>::type());
|
||||||
|
@ -80,7 +86,8 @@ void Copy(T * destination, const T * source, size_t count)
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <typename T, typename Trait>
|
template <typename T, typename Trait>
|
||||||
void ConstructDefault(T * destination, size_t count, Trait)
|
void
|
||||||
|
ConstructDefault(T * destination, size_t count, Trait)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < count; i++) {
|
for (size_t i = 0; i < count; i++) {
|
||||||
destination[i] = T();
|
destination[i] = T();
|
||||||
|
@ -88,50 +95,39 @@ void ConstructDefault(T * destination, size_t count, Trait)
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ConstructDefault(T * destination,
|
void
|
||||||
size_t count, std::true_type)
|
ConstructDefault(T * destination, size_t count, std::true_type)
|
||||||
{
|
{
|
||||||
PodZero(destination, count);
|
PodZero(destination, count);
|
||||||
}
|
}
|
||||||
}
|
} // namespace
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This allows zeroing (using memset) or default-constructing a number of
|
* This allows zeroing (using memset) or default-constructing a number of
|
||||||
* elements calling the constructors and destructors if necessary.
|
* elements calling the constructors and destructors if necessary.
|
||||||
*/
|
*/
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void ConstructDefault(T * destination, size_t count)
|
void
|
||||||
|
ConstructDefault(T * destination, size_t count)
|
||||||
{
|
{
|
||||||
assert(destination);
|
assert(destination);
|
||||||
ConstructDefault(destination, count,
|
ConstructDefault(destination, count, typename std::is_arithmetic<T>::type());
|
||||||
typename std::is_arithmetic<T>::type());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> class auto_array {
|
||||||
class auto_array
|
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
explicit auto_array(uint32_t capacity = 0)
|
explicit auto_array(uint32_t capacity = 0)
|
||||||
: data_(capacity ? new T[capacity] : nullptr)
|
: data_(capacity ? new T[capacity] : nullptr), capacity_(capacity),
|
||||||
, capacity_(capacity)
|
length_(0)
|
||||||
, length_(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~auto_array()
|
|
||||||
{
|
{
|
||||||
delete [] data_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~auto_array() { delete[] data_; }
|
||||||
|
|
||||||
/** Get a constant pointer to the underlying data. */
|
/** Get a constant pointer to the underlying data. */
|
||||||
T * data() const
|
T * data() const { return data_; }
|
||||||
{
|
|
||||||
return data_;
|
|
||||||
}
|
|
||||||
|
|
||||||
T * end() const
|
T * end() const { return data_ + length_; }
|
||||||
{
|
|
||||||
return data_ + length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T & at(size_t index) const
|
const T & at(size_t index) const
|
||||||
{
|
{
|
||||||
|
@ -146,22 +142,13 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get how much underlying storage this auto_array has. */
|
/** Get how much underlying storage this auto_array has. */
|
||||||
size_t capacity() const
|
size_t capacity() const { return capacity_; }
|
||||||
{
|
|
||||||
return capacity_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Get how much elements this auto_array contains. */
|
/** Get how much elements this auto_array contains. */
|
||||||
size_t length() const
|
size_t length() const { return length_; }
|
||||||
{
|
|
||||||
return length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Keeps the storage, but removes all the elements from the array. */
|
/** Keeps the storage, but removes all the elements from the array. */
|
||||||
void clear()
|
void clear() { length_ = 0; }
|
||||||
{
|
|
||||||
length_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Change the storage of this auto array, copying the elements to the new
|
/** Change the storage of this auto array, copying the elements to the new
|
||||||
* storage.
|
* storage.
|
||||||
|
@ -227,10 +214,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the number of free elements in the array. */
|
/** Return the number of free elements in the array. */
|
||||||
size_t available() const
|
size_t available() const { return capacity_ - length_; }
|
||||||
{
|
|
||||||
return capacity_ - length_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Copies `length` elements to `elements` if it is not null, and shift
|
/** Copies `length` elements to `elements` if it is not null, and shift
|
||||||
* the remaining elements of the `auto_array` to the beginning.
|
* the remaining elements of the `auto_array` to the beginning.
|
||||||
|
@ -285,56 +269,38 @@ template <typename T>
|
||||||
struct auto_array_wrapper_impl : public auto_array_wrapper {
|
struct auto_array_wrapper_impl : public auto_array_wrapper {
|
||||||
auto_array_wrapper_impl() {}
|
auto_array_wrapper_impl() {}
|
||||||
|
|
||||||
explicit auto_array_wrapper_impl(uint32_t size)
|
explicit auto_array_wrapper_impl(uint32_t size) : ar(size) {}
|
||||||
: ar(size)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void push(void * elements, size_t length) override {
|
void push(void * elements, size_t length) override
|
||||||
|
{
|
||||||
ar.push(static_cast<T *>(elements), length);
|
ar.push(static_cast<T *>(elements), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t length() override {
|
size_t length() override { return ar.length(); }
|
||||||
return ar.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
void push_silence(size_t length) override {
|
void push_silence(size_t length) override { ar.push_silence(length); }
|
||||||
ar.push_silence(length);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pop(size_t length) override {
|
bool pop(size_t length) override { return ar.pop(nullptr, length); }
|
||||||
return ar.pop(nullptr, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
void * data() override {
|
void * data() override { return ar.data(); }
|
||||||
return ar.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
void * end() override {
|
void * end() override { return ar.end(); }
|
||||||
return ar.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear() override {
|
void clear() override { ar.clear(); }
|
||||||
ar.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool reserve(size_t capacity) override {
|
bool reserve(size_t capacity) override { return ar.reserve(capacity); }
|
||||||
return ar.reserve(capacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_length(size_t length) override {
|
void set_length(size_t length) override { ar.set_length(length); }
|
||||||
ar.set_length(length);
|
|
||||||
}
|
|
||||||
|
|
||||||
~auto_array_wrapper_impl() {
|
~auto_array_wrapper_impl() { ar.clear(); }
|
||||||
ar.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
auto_array<T> ar;
|
auto_array<T> ar;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
size_t cubeb_sample_size(cubeb_sample_format format);
|
size_t
|
||||||
|
cubeb_sample_size(cubeb_sample_format format);
|
||||||
}
|
}
|
||||||
|
|
||||||
using auto_lock = std::lock_guard<owned_critical_section>;
|
using auto_lock = std::lock_guard<owned_critical_section>;
|
||||||
|
|
|
@ -8,13 +8,12 @@
|
||||||
#if !defined(CUBEB_UTILS_UNIX)
|
#if !defined(CUBEB_UTILS_UNIX)
|
||||||
#define CUBEB_UTILS_UNIX
|
#define CUBEB_UTILS_UNIX
|
||||||
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
/* This wraps a critical section to track the owner in debug mode. */
|
/* This wraps a critical section to track the owner in debug mode. */
|
||||||
class owned_critical_section
|
class owned_critical_section {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
owned_critical_section()
|
owned_critical_section()
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,30 +8,26 @@
|
||||||
#if !defined(CUBEB_UTILS_WIN)
|
#if !defined(CUBEB_UTILS_WIN)
|
||||||
#define CUBEB_UTILS_WIN
|
#define CUBEB_UTILS_WIN
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include "cubeb-internal.h"
|
#include "cubeb-internal.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
/* This wraps a critical section to track the owner in debug mode, adapted from
|
/* This wraps an SRWLock to track the owner in debug mode, adapted from
|
||||||
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
|
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx
|
||||||
class owned_critical_section
|
*/
|
||||||
{
|
class owned_critical_section {
|
||||||
public:
|
public:
|
||||||
owned_critical_section()
|
owned_critical_section()
|
||||||
|
: srwlock(SRWLOCK_INIT)
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
: owner(0)
|
,
|
||||||
|
owner(0)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
InitializeCriticalSection(&critical_section);
|
|
||||||
}
|
|
||||||
|
|
||||||
~owned_critical_section()
|
|
||||||
{
|
|
||||||
DeleteCriticalSection(&critical_section);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock()
|
void lock()
|
||||||
{
|
{
|
||||||
EnterCriticalSection(&critical_section);
|
AcquireSRWLockExclusive(&srwlock);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
|
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
|
||||||
owner = GetCurrentThreadId();
|
owner = GetCurrentThreadId();
|
||||||
|
@ -44,7 +40,7 @@ public:
|
||||||
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
|
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
|
||||||
owner = 0;
|
owner = 0;
|
||||||
#endif
|
#endif
|
||||||
LeaveCriticalSection(&critical_section);
|
ReleaseSRWLockExclusive(&srwlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
|
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
|
||||||
|
@ -58,12 +54,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CRITICAL_SECTION critical_section;
|
SRWLOCK srwlock;
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
DWORD owner;
|
DWORD owner;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
|
// Disallow copy and assignment because SRWLock cannot be copied.
|
||||||
owned_critical_section(const owned_critical_section &);
|
owned_critical_section(const owned_critical_section &);
|
||||||
owned_critical_section & operator=(const owned_critical_section &);
|
owned_critical_section & operator=(const owned_critical_section &);
|
||||||
};
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,23 +8,28 @@
|
||||||
#define WINVER 0x0501
|
#define WINVER 0x0501
|
||||||
#undef WIN32_LEAN_AND_MEAN
|
#undef WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
|
#include "cubeb-internal.h"
|
||||||
|
#include "cubeb/cubeb.h"
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <windows.h>
|
#include <math.h>
|
||||||
#include <mmreg.h>
|
|
||||||
#include <mmsystem.h>
|
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <math.h>
|
#include <windows.h>
|
||||||
#include "cubeb/cubeb.h"
|
|
||||||
#include "cubeb-internal.h"
|
/* clang-format off */
|
||||||
|
/* These need to be included after windows.h */
|
||||||
|
#include <mmreg.h>
|
||||||
|
#include <mmsystem.h>
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
/* This is missing from the MinGW headers. Use a safe fallback. */
|
/* This is missing from the MinGW headers. Use a safe fallback. */
|
||||||
#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
|
#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
|
||||||
#define MEMORY_ALLOCATION_ALIGNMENT 16
|
#define MEMORY_ALLOCATION_ALIGNMENT 16
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/
|
/**This is also missing from the MinGW headers. It also appears to be
|
||||||
|
* undocumented by Microsoft.*/
|
||||||
#ifndef WAVE_FORMAT_48M08
|
#ifndef WAVE_FORMAT_48M08
|
||||||
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
|
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
|
||||||
#endif
|
#endif
|
||||||
|
@ -68,11 +73,6 @@
|
||||||
#define CUBEB_STREAM_MAX 32
|
#define CUBEB_STREAM_MAX 32
|
||||||
#define NBUFS 4
|
#define NBUFS 4
|
||||||
|
|
||||||
const GUID KSDATAFORMAT_SUBTYPE_PCM =
|
|
||||||
{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
|
||||||
const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
|
|
||||||
{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
|
|
||||||
|
|
||||||
struct cubeb_stream_item {
|
struct cubeb_stream_item {
|
||||||
SLIST_ENTRY head;
|
SLIST_ENTRY head;
|
||||||
cubeb_stream * stream;
|
cubeb_stream * stream;
|
||||||
|
@ -110,6 +110,10 @@ struct cubeb_stream {
|
||||||
CRITICAL_SECTION lock;
|
CRITICAL_SECTION lock;
|
||||||
uint64_t written;
|
uint64_t written;
|
||||||
float soft_volume;
|
float soft_volume;
|
||||||
|
/* For position wrap-around handling: */
|
||||||
|
size_t frame_size;
|
||||||
|
DWORD prev_pos_lo_dword;
|
||||||
|
DWORD pos_hi_dword;
|
||||||
};
|
};
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
|
@ -223,8 +227,7 @@ winmm_refill_stream(cubeb_stream * stm)
|
||||||
LeaveCriticalSection(&stm->lock);
|
LeaveCriticalSection(&stm->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned __stdcall
|
static unsigned __stdcall winmm_buffer_thread(void * user_ptr)
|
||||||
winmm_buffer_thread(void * user_ptr)
|
|
||||||
{
|
{
|
||||||
cubeb * ctx = (cubeb *)user_ptr;
|
cubeb * ctx = (cubeb *)user_ptr;
|
||||||
XASSERT(ctx);
|
XASSERT(ctx);
|
||||||
|
@ -256,7 +259,8 @@ winmm_buffer_thread(void * user_ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CALLBACK
|
static void CALLBACK
|
||||||
winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
|
winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr,
|
||||||
|
DWORD_PTR p1, DWORD_PTR p2)
|
||||||
{
|
{
|
||||||
cubeb_stream * stm = (cubeb_stream *)user_ptr;
|
cubeb_stream * stm = (cubeb_stream *)user_ptr;
|
||||||
struct cubeb_stream_item * item;
|
struct cubeb_stream_item * item;
|
||||||
|
@ -265,7 +269,8 @@ winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT);
|
item = _aligned_malloc(sizeof(struct cubeb_stream_item),
|
||||||
|
MEMORY_ALLOCATION_ALIGNMENT);
|
||||||
XASSERT(item);
|
XASSERT(item);
|
||||||
item->stream = stm;
|
item->stream = stm;
|
||||||
InterlockedPushEntrySList(stm->context->work, &item->head);
|
InterlockedPushEntrySList(stm->context->work, &item->head);
|
||||||
|
@ -284,7 +289,8 @@ calculate_minimum_latency(void)
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Vista's WinMM implementation underruns when less than 200ms of audio is buffered. */
|
/* Vista's WinMM implementation underruns when less than 200ms of audio is
|
||||||
|
* buffered. */
|
||||||
memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
|
memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
|
||||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||||
osvi.dwMajorVersion = 6;
|
osvi.dwMajorVersion = 6;
|
||||||
|
@ -294,14 +300,16 @@ calculate_minimum_latency(void)
|
||||||
VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL);
|
VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL);
|
||||||
VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL);
|
VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL);
|
||||||
|
|
||||||
if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) {
|
if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) !=
|
||||||
|
0) {
|
||||||
return 200;
|
return 200;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void winmm_destroy(cubeb * ctx);
|
static void
|
||||||
|
winmm_destroy(cubeb * ctx);
|
||||||
|
|
||||||
/*static*/ int
|
/*static*/ int
|
||||||
winmm_init(cubeb ** context, char const * context_name)
|
winmm_init(cubeb ** context, char const * context_name)
|
||||||
|
@ -331,7 +339,9 @@ winmm_init(cubeb ** context, char const * context_name)
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
|
ctx->thread =
|
||||||
|
(HANDLE)_beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx,
|
||||||
|
STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
|
||||||
if (!ctx->thread) {
|
if (!ctx->thread) {
|
||||||
winmm_destroy(ctx);
|
winmm_destroy(ctx);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
|
@ -382,18 +392,18 @@ winmm_destroy(cubeb * ctx)
|
||||||
free(ctx);
|
free(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void winmm_stream_destroy(cubeb_stream * stm);
|
static void
|
||||||
|
winmm_stream_destroy(cubeb_stream * stm);
|
||||||
|
|
||||||
static int
|
static int
|
||||||
winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
|
winmm_stream_init(cubeb * context, cubeb_stream ** stream,
|
||||||
cubeb_devid input_device,
|
char const * stream_name, cubeb_devid input_device,
|
||||||
cubeb_stream_params * input_stream_params,
|
cubeb_stream_params * input_stream_params,
|
||||||
cubeb_devid output_device,
|
cubeb_devid output_device,
|
||||||
cubeb_stream_params * output_stream_params,
|
cubeb_stream_params * output_stream_params,
|
||||||
unsigned int latency_frames,
|
unsigned int latency_frames,
|
||||||
cubeb_data_callback data_callback,
|
cubeb_data_callback data_callback,
|
||||||
cubeb_state_callback state_callback,
|
cubeb_state_callback state_callback, void * user_ptr)
|
||||||
void * user_ptr)
|
|
||||||
{
|
{
|
||||||
MMRESULT r;
|
MMRESULT r;
|
||||||
WAVEFORMATEXTENSIBLE wfx;
|
WAVEFORMATEXTENSIBLE wfx;
|
||||||
|
@ -452,8 +462,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||||
return CUBEB_ERROR_INVALID_FORMAT;
|
return CUBEB_ERROR_INVALID_FORMAT;
|
||||||
}
|
}
|
||||||
|
|
||||||
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
wfx.Format.nBlockAlign =
|
||||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
(wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||||
|
wfx.Format.nAvgBytesPerSec =
|
||||||
|
wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||||
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||||
|
|
||||||
EnterCriticalSection(&context->lock);
|
EnterCriticalSection(&context->lock);
|
||||||
|
@ -485,9 +497,11 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||||
latency_ms = context->minimum_latency_ms;
|
latency_ms = context->minimum_latency_ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
|
bufsz = (size_t)(stm->params.rate / 1000.0 * latency_ms *
|
||||||
|
bytes_per_frame(stm->params) / NBUFS);
|
||||||
if (bufsz % bytes_per_frame(stm->params) != 0) {
|
if (bufsz % bytes_per_frame(stm->params) != 0) {
|
||||||
bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
|
bufsz +=
|
||||||
|
bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
|
||||||
}
|
}
|
||||||
XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
|
XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
|
||||||
|
|
||||||
|
@ -536,6 +550,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
|
||||||
winmm_refill_stream(stm);
|
winmm_refill_stream(stm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stm->frame_size = bytes_per_frame(stm->params);
|
||||||
|
stm->prev_pos_lo_dword = 0;
|
||||||
|
stm->pos_hi_dword = 0;
|
||||||
|
|
||||||
*stream = stm;
|
*stream = stm;
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
|
@ -580,7 +598,8 @@ winmm_stream_destroy(cubeb_stream * stm)
|
||||||
|
|
||||||
for (i = 0; i < NBUFS; ++i) {
|
for (i = 0; i < NBUFS; ++i) {
|
||||||
if (stm->buffers[i].dwFlags & WHDR_PREPARED) {
|
if (stm->buffers[i].dwFlags & WHDR_PREPARED) {
|
||||||
waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i]));
|
waveOutUnprepareHeader(stm->waveout, &stm->buffers[i],
|
||||||
|
sizeof(stm->buffers[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,7 +638,8 @@ winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
|
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params,
|
||||||
|
uint32_t * latency)
|
||||||
{
|
{
|
||||||
// 100ms minimum, if we are not in a bizarre configuration.
|
// 100ms minimum, if we are not in a bizarre configuration.
|
||||||
*latency = ctx->minimum_latency_ms * params.rate / 1000;
|
*latency = ctx->minimum_latency_ms * params.rate / 1000;
|
||||||
|
@ -686,6 +706,58 @@ winmm_stream_stop(cubeb_stream * stm)
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Microsoft wave audio docs say "samples are the preferred time format in which
|
||||||
|
to represent the current position", but relying on this causes problems on
|
||||||
|
Windows XP, the only OS cubeb_winmm is used on.
|
||||||
|
|
||||||
|
While the wdmaud.sys driver internally tracks a 64-bit position and ensures no
|
||||||
|
backward movement, the WinMM API limits the position returned from
|
||||||
|
waveOutGetPosition() to a 32-bit DWORD (this applies equally to XP x64). The
|
||||||
|
higher 32 bits are chopped off, and to an API consumer the position can appear
|
||||||
|
to move backward.
|
||||||
|
|
||||||
|
In theory, even a 32-bit TIME_SAMPLES position should provide plenty of
|
||||||
|
playback time for typical use cases before this pseudo wrap-around, e.g:
|
||||||
|
(2^32 - 1)/48000 = ~24:51:18 for 48.0 kHz stereo;
|
||||||
|
(2^32 - 1)/44100 = ~27:03:12 for 44.1 kHz stereo.
|
||||||
|
In reality, wdmaud.sys doesn't provide a TIME_SAMPLES position at all, only a
|
||||||
|
32-bit TIME_BYTES position, from which wdmaud.drv derives TIME_SAMPLES:
|
||||||
|
SamplePos = (BytePos * 8) / BitsPerFrame,
|
||||||
|
where BitsPerFrame = Channels * BitsPerSample,
|
||||||
|
Per dom\media\AudioSampleFormat.h, desktop builds always use 32-bit FLOAT32
|
||||||
|
samples, so the maximum for TIME_SAMPLES should be:
|
||||||
|
(2^29 - 1)/48000 = ~03:06:25;
|
||||||
|
(2^29 - 1)/44100 = ~03:22:54.
|
||||||
|
This might still be OK for typical browser usage, but there's also a bug in the
|
||||||
|
formula above: BytePos * 8 (BytePos << 3) is done on a 32-bit BytePos, without
|
||||||
|
first casting it to 64 bits, so the highest 3 bits, if set, would get shifted
|
||||||
|
out, and the maximum possible TIME_SAMPLES drops unacceptably low:
|
||||||
|
(2^26 - 1)/48000 = ~00:23:18;
|
||||||
|
(2^26 - 1)/44100 = ~00:25:22.
|
||||||
|
|
||||||
|
To work around these limitations, we just get the position in TIME_BYTES,
|
||||||
|
recover the 64-bit value, and do our own conversion to samples.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Convert chopped 32-bit waveOutGetPosition() into 64-bit true position. */
|
||||||
|
static uint64_t
|
||||||
|
update_64bit_position(cubeb_stream * stm, DWORD pos_lo_dword)
|
||||||
|
{
|
||||||
|
/* Caller should be holding stm->lock. */
|
||||||
|
if (pos_lo_dword < stm->prev_pos_lo_dword) {
|
||||||
|
stm->pos_hi_dword++;
|
||||||
|
LOG("waveOutGetPosition() has wrapped around: %#lx -> %#lx",
|
||||||
|
stm->prev_pos_lo_dword, pos_lo_dword);
|
||||||
|
LOG("Wrap-around count = %#lx", stm->pos_hi_dword);
|
||||||
|
LOG("Current 64-bit position = %#llx",
|
||||||
|
(((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword));
|
||||||
|
}
|
||||||
|
stm->prev_pos_lo_dword = pos_lo_dword;
|
||||||
|
|
||||||
|
return (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||||
{
|
{
|
||||||
|
@ -693,15 +765,17 @@ winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
|
||||||
MMTIME time;
|
MMTIME time;
|
||||||
|
|
||||||
EnterCriticalSection(&stm->lock);
|
EnterCriticalSection(&stm->lock);
|
||||||
time.wType = TIME_SAMPLES;
|
/* See the long comment above for why not just use TIME_SAMPLES here. */
|
||||||
|
time.wType = TIME_BYTES;
|
||||||
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
||||||
LeaveCriticalSection(&stm->lock);
|
|
||||||
|
|
||||||
if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
|
if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
|
||||||
|
LeaveCriticalSection(&stm->lock);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
*position = time.u.sample;
|
*position = update_64bit_position(stm, time.u.cb) / stm->frame_size;
|
||||||
|
LeaveCriticalSection(&stm->lock);
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
@ -711,20 +785,24 @@ winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
||||||
{
|
{
|
||||||
MMRESULT r;
|
MMRESULT r;
|
||||||
MMTIME time;
|
MMTIME time;
|
||||||
uint64_t written;
|
uint64_t written, position;
|
||||||
|
|
||||||
EnterCriticalSection(&stm->lock);
|
EnterCriticalSection(&stm->lock);
|
||||||
time.wType = TIME_SAMPLES;
|
/* See the long comment above for why not just use TIME_SAMPLES here. */
|
||||||
|
time.wType = TIME_BYTES;
|
||||||
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
|
||||||
written = stm->written;
|
|
||||||
LeaveCriticalSection(&stm->lock);
|
|
||||||
|
|
||||||
if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
|
if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
|
||||||
|
LeaveCriticalSection(&stm->lock);
|
||||||
return CUBEB_ERROR;
|
return CUBEB_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
XASSERT(written - time.u.sample <= UINT32_MAX);
|
position = update_64bit_position(stm, time.u.cb);
|
||||||
*latency = (uint32_t) (written - time.u.sample);
|
written = stm->written;
|
||||||
|
LeaveCriticalSection(&stm->lock);
|
||||||
|
|
||||||
|
XASSERT((written - (position / stm->frame_size)) <= UINT32_MAX);
|
||||||
|
*latency = (uint32_t)(written - (position / stm->frame_size));
|
||||||
|
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
@ -738,11 +816,18 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume)
|
||||||
return CUBEB_OK;
|
return CUBEB_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
|
#define MM_11025HZ_MASK \
|
||||||
#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
|
(WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
|
||||||
#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
|
#define MM_22050HZ_MASK \
|
||||||
#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)
|
(WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
|
||||||
#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)
|
#define MM_44100HZ_MASK \
|
||||||
|
(WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
|
||||||
|
#define MM_48000HZ_MASK \
|
||||||
|
(WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | \
|
||||||
|
WAVE_FORMAT_48S16)
|
||||||
|
#define MM_96000HZ_MASK \
|
||||||
|
(WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | \
|
||||||
|
WAVE_FORMAT_96S16)
|
||||||
static void
|
static void
|
||||||
winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||||
{
|
{
|
||||||
|
@ -752,17 +837,20 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||||
info->max_rate = 11025;
|
info->max_rate = 11025;
|
||||||
}
|
}
|
||||||
if (formats & MM_22050HZ_MASK) {
|
if (formats & MM_22050HZ_MASK) {
|
||||||
if (info->min_rate == 0) info->min_rate = 22050;
|
if (info->min_rate == 0)
|
||||||
|
info->min_rate = 22050;
|
||||||
info->max_rate = 22050;
|
info->max_rate = 22050;
|
||||||
info->default_rate = 22050;
|
info->default_rate = 22050;
|
||||||
}
|
}
|
||||||
if (formats & MM_44100HZ_MASK) {
|
if (formats & MM_44100HZ_MASK) {
|
||||||
if (info->min_rate == 0) info->min_rate = 44100;
|
if (info->min_rate == 0)
|
||||||
|
info->min_rate = 44100;
|
||||||
info->max_rate = 44100;
|
info->max_rate = 44100;
|
||||||
info->default_rate = 44100;
|
info->default_rate = 44100;
|
||||||
}
|
}
|
||||||
if (formats & MM_48000HZ_MASK) {
|
if (formats & MM_48000HZ_MASK) {
|
||||||
if (info->min_rate == 0) info->min_rate = 48000;
|
if (info->min_rate == 0)
|
||||||
|
info->min_rate = 48000;
|
||||||
info->max_rate = 48000;
|
info->max_rate = 48000;
|
||||||
info->default_rate = 48000;
|
info->default_rate = 48000;
|
||||||
}
|
}
|
||||||
|
@ -775,11 +863,14 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MM_S16_MASK (WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | WAVE_FORMAT_4M16 | \
|
#define MM_S16_MASK \
|
||||||
WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
|
(WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | \
|
||||||
|
WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | \
|
||||||
|
WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
|
||||||
static int
|
static int
|
||||||
winmm_query_supported_formats(UINT devid, DWORD formats,
|
winmm_query_supported_formats(UINT devid, DWORD formats,
|
||||||
cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt)
|
cubeb_device_fmt * supfmt,
|
||||||
|
cubeb_device_fmt * deffmt)
|
||||||
{
|
{
|
||||||
WAVEFORMATEXTENSIBLE wfx;
|
WAVEFORMATEXTENSIBLE wfx;
|
||||||
|
|
||||||
|
@ -793,13 +884,16 @@ winmm_query_supported_formats(UINT devid, DWORD formats,
|
||||||
wfx.Format.nChannels = 2;
|
wfx.Format.nChannels = 2;
|
||||||
wfx.Format.nSamplesPerSec = 44100;
|
wfx.Format.nSamplesPerSec = 44100;
|
||||||
wfx.Format.wBitsPerSample = 32;
|
wfx.Format.wBitsPerSample = 32;
|
||||||
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
wfx.Format.nBlockAlign =
|
||||||
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
(wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
|
||||||
|
wfx.Format.nAvgBytesPerSec =
|
||||||
|
wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
|
||||||
wfx.Format.cbSize = 22;
|
wfx.Format.cbSize = 22;
|
||||||
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
|
||||||
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
|
||||||
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||||
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR)
|
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) ==
|
||||||
|
MMSYSERR_NOERROR)
|
||||||
*supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
|
*supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
|
||||||
|
|
||||||
return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
|
return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
|
||||||
|
@ -813,10 +907,10 @@ guid_to_cstr(LPGUID guid)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
_snprintf_s(ret, 40, _TRUNCATE,
|
_snprintf_s(ret, 40, _TRUNCATE,
|
||||||
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
|
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid->Data1,
|
||||||
guid->Data1, guid->Data2, guid->Data3,
|
guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1],
|
||||||
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5],
|
||||||
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
guid->Data4[6], guid->Data4[7]);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,12 +921,14 @@ winmm_query_preferred_out_device(UINT devid)
|
||||||
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
||||||
|
|
||||||
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||||
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
(DWORD_PTR)&mmpref,
|
||||||
|
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||||
devid == mmpref)
|
devid == mmpref)
|
||||||
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
||||||
|
|
||||||
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||||
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
(DWORD_PTR)&compref,
|
||||||
|
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||||
devid == compref)
|
devid == compref)
|
||||||
ret |= CUBEB_DEVICE_PREF_VOICE;
|
ret |= CUBEB_DEVICE_PREF_VOICE;
|
||||||
|
|
||||||
|
@ -851,7 +947,8 @@ device_id_idx(UINT devid)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, UINT devid)
|
winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
|
||||||
|
UINT devid)
|
||||||
{
|
{
|
||||||
XASSERT(ret);
|
XASSERT(ret);
|
||||||
ret->devid = (cubeb_devid)devid;
|
ret->devid = (cubeb_devid)devid;
|
||||||
|
@ -866,8 +963,8 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
|
||||||
|
|
||||||
ret->max_channels = caps->wChannels;
|
ret->max_channels = caps->wChannels;
|
||||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||||
&ret->format, &ret->default_format);
|
&ret->default_format);
|
||||||
|
|
||||||
/* Hardcoded latency estimates... */
|
/* Hardcoded latency estimates... */
|
||||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||||
|
@ -875,7 +972,8 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, UINT devid)
|
winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps,
|
||||||
|
UINT devid)
|
||||||
{
|
{
|
||||||
XASSERT(ret);
|
XASSERT(ret);
|
||||||
ret->devid = (cubeb_devid)devid;
|
ret->devid = (cubeb_devid)devid;
|
||||||
|
@ -890,8 +988,8 @@ winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, U
|
||||||
|
|
||||||
ret->max_channels = caps->wChannels;
|
ret->max_channels = caps->wChannels;
|
||||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||||
&ret->format, &ret->default_format);
|
&ret->default_format);
|
||||||
|
|
||||||
/* Hardcoded latency estimates... */
|
/* Hardcoded latency estimates... */
|
||||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||||
|
@ -905,12 +1003,14 @@ winmm_query_preferred_in_device(UINT devid)
|
||||||
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
|
||||||
|
|
||||||
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
|
||||||
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
(DWORD_PTR)&mmpref,
|
||||||
|
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||||
devid == mmpref)
|
devid == mmpref)
|
||||||
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
|
||||||
|
|
||||||
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
|
||||||
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
(DWORD_PTR)&compref,
|
||||||
|
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
|
||||||
devid == compref)
|
devid == compref)
|
||||||
ret |= CUBEB_DEVICE_PREF_VOICE;
|
ret |= CUBEB_DEVICE_PREF_VOICE;
|
||||||
|
|
||||||
|
@ -918,7 +1018,8 @@ winmm_query_preferred_in_device(UINT devid)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, UINT devid)
|
winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps,
|
||||||
|
UINT devid)
|
||||||
{
|
{
|
||||||
XASSERT(ret);
|
XASSERT(ret);
|
||||||
ret->devid = (cubeb_devid)devid;
|
ret->devid = (cubeb_devid)devid;
|
||||||
|
@ -933,8 +1034,8 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U
|
||||||
|
|
||||||
ret->max_channels = caps->wChannels;
|
ret->max_channels = caps->wChannels;
|
||||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||||
&ret->format, &ret->default_format);
|
&ret->default_format);
|
||||||
|
|
||||||
/* Hardcoded latency estimates... */
|
/* Hardcoded latency estimates... */
|
||||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||||
|
@ -942,7 +1043,8 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UINT devid)
|
winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps,
|
||||||
|
UINT devid)
|
||||||
{
|
{
|
||||||
XASSERT(ret);
|
XASSERT(ret);
|
||||||
ret->devid = (cubeb_devid)devid;
|
ret->devid = (cubeb_devid)devid;
|
||||||
|
@ -957,8 +1059,8 @@ winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UIN
|
||||||
|
|
||||||
ret->max_channels = caps->wChannels;
|
ret->max_channels = caps->wChannels;
|
||||||
winmm_calculate_device_rate(ret, caps->dwFormats);
|
winmm_calculate_device_rate(ret, caps->dwFormats);
|
||||||
winmm_query_supported_formats(devid, caps->dwFormats,
|
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
|
||||||
&ret->format, &ret->default_format);
|
&ret->default_format);
|
||||||
|
|
||||||
/* Hardcoded latency estimates... */
|
/* Hardcoded latency estimates... */
|
||||||
ret->latency_lo = 100 * ret->default_rate / 1000;
|
ret->latency_lo = 100 * ret->default_rate / 1000;
|
||||||
|
@ -989,7 +1091,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
|
|
||||||
for (i = 0; i < outcount; i++) {
|
for (i = 0; i < outcount; i++) {
|
||||||
dev = &devices[collection->count];
|
dev = &devices[collection->count];
|
||||||
if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR) {
|
if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) ==
|
||||||
|
MMSYSERR_NOERROR) {
|
||||||
winmm_create_device_from_outcaps2(dev, &woc2, i);
|
winmm_create_device_from_outcaps2(dev, &woc2, i);
|
||||||
collection->count += 1;
|
collection->count += 1;
|
||||||
} else if (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR) {
|
} else if (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR) {
|
||||||
|
@ -1008,7 +1111,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
|
||||||
|
|
||||||
for (i = 0; i < incount; i++) {
|
for (i = 0; i < incount; i++) {
|
||||||
dev = &devices[collection->count];
|
dev = &devices[collection->count];
|
||||||
if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR) {
|
if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) ==
|
||||||
|
MMSYSERR_NOERROR) {
|
||||||
winmm_create_device_from_incaps2(dev, &wic2, i);
|
winmm_create_device_from_incaps2(dev, &wic2, i);
|
||||||
collection->count += 1;
|
collection->count += 1;
|
||||||
} else if (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR) {
|
} else if (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR) {
|
||||||
|
@ -1056,7 +1160,6 @@ static struct cubeb_ops const winmm_ops = {
|
||||||
/*.stream_destroy =*/winmm_stream_destroy,
|
/*.stream_destroy =*/winmm_stream_destroy,
|
||||||
/*.stream_start =*/winmm_stream_start,
|
/*.stream_start =*/winmm_stream_start,
|
||||||
/*.stream_stop =*/winmm_stream_stop,
|
/*.stream_stop =*/winmm_stream_stop,
|
||||||
/*.stream_reset_default_device =*/ NULL,
|
|
||||||
/*.stream_get_position =*/winmm_stream_get_position,
|
/*.stream_get_position =*/winmm_stream_get_position,
|
||||||
/*.stream_get_latency = */ winmm_stream_get_latency,
|
/*.stream_get_latency = */ winmm_stream_get_latency,
|
||||||
/*.stream_get_input_latency = */ NULL,
|
/*.stream_get_input_latency = */ NULL,
|
||||||
|
@ -1065,5 +1168,4 @@ static struct cubeb_ops const winmm_ops = {
|
||||||
/*.stream_get_current_device =*/NULL,
|
/*.stream_get_current_device =*/NULL,
|
||||||
/*.stream_device_destroy =*/NULL,
|
/*.stream_device_destroy =*/NULL,
|
||||||
/*.stream_register_device_changed_callback=*/NULL,
|
/*.stream_register_device_changed_callback=*/NULL,
|
||||||
/*.register_device_collection_changed =*/ NULL
|
/*.register_device_collection_changed =*/NULL};
|
||||||
};
|
|
||||||
|
|
Loading…
Reference in New Issue