dep/cubeb: Update to dc511c6

This commit is contained in:
Connor McLaughlin 2022-08-05 17:28:17 +10:00
parent 06ecc50797
commit 8f45bf7f27
50 changed files with 5667 additions and 4813 deletions

View file

@ -1,17 +1,47 @@
# TODO
# - backend selection via command line, rather than simply detecting headers.
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
cmake_minimum_required(VERSION 3.14 FATAL_ERROR)
project(cubeb
VERSION 0.0.0)
if(POLICY CMP0063)
cmake_policy(SET CMP0063 NEW)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(BUILD_RUST_LIBS "Build rust backends" OFF)
option(BUNDLE_SPEEX "Bundle the speex library" OFF)
option(LAZY_LOAD_LIBS "Lazily load shared libraries" ON)
option(USE_SANITIZERS "Use sanitizers" ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
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.
if(NOT OS2)
set(CMAKE_C_VISIBILITY_PRESET hidden)
@ -35,26 +65,169 @@ add_library(cubeb
src/cubeb_log.cpp
src/cubeb_strings.c
src/cubeb_utils.cpp
$<TARGET_OBJECTS:speex>)
)
target_include_directories(cubeb
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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)
target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX)
target_compile_definitions(cubeb PRIVATE FLOATING_POINT)
target_compile_definitions(cubeb PRIVATE EXPORT=)
target_compile_definitions(cubeb PRIVATE RANDOM_PREFIX=speex)
add_sanitizers(cubeb)
add_library(speex OBJECT
src/speex/resample.c)
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
target_compile_definitions(speex PRIVATE OUTSIDE_SPEEX)
target_compile_definitions(speex PRIVATE FLOATING_POINT)
target_compile_definitions(speex PRIVATE EXPORT=)
target_compile_definitions(speex PRIVATE RANDOM_PREFIX=speex)
include(GenerateExportHeader)
generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h)
target_include_directories(cubeb
PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/exports>
)
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)
target_include_directories(speex INTERFACE subprojects)
target_compile_definitions(speex PUBLIC
OUTSIDE_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)
# Threads needed by cubeb_log, _pulse, _alsa, _jack, _sndio, _oss and _sun
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads)
target_link_libraries(cubeb PRIVATE Threads::Threads)
if(LAZY_LOAD_LIBS)
check_include_files(pulse/pulseaudio.h USE_PULSE)
check_include_files(alsa/asoundlib.h USE_ALSA)
check_include_files(jack/jack.h USE_JACK)
check_include_files(sndio.h USE_SNDIO)
check_include_files(aaudio/AAudio.h USE_AAUDIO)
if(USE_PULSE OR USE_ALSA OR USE_JACK OR USE_SNDIO OR USE_AAUDIO)
target_link_libraries(cubeb PRIVATE ${CMAKE_DL_LIBS})
endif()
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(libpulse IMPORTED_TARGET libpulse)
if(libpulse_FOUND)
set(USE_PULSE ON)
target_compile_definitions(cubeb PRIVATE DISABLE_LIBPULSE_DLOPEN)
target_link_libraries(cubeb PRIVATE PkgConfig::libpulse)
endif()
pkg_check_modules(alsa IMPORTED_TARGET alsa)
if(alsa_FOUND)
set(USE_ALSA ON)
target_compile_definitions(cubeb PRIVATE DISABLE_LIBASOUND_DLOPEN)
target_link_libraries(cubeb PRIVATE PkgConfig::alsa)
endif()
pkg_check_modules(jack IMPORTED_TARGET jack)
if(jack_FOUND)
set(USE_JACK ON)
target_compile_definitions(cubeb PRIVATE DISABLE_LIBJACK_DLOPEN)
target_link_libraries(cubeb PRIVATE PkgConfig::jack)
endif()
check_include_files(sndio.h USE_SNDIO)
if(USE_SNDIO)
target_compile_definitions(cubeb PRIVATE DISABLE_LIBSNDIO_DLOPEN)
target_link_libraries(cubeb PRIVATE sndio)
endif()
check_include_files(aaudio/AAudio.h USE_AAUDIO)
if(USE_AAUDIO)
target_compile_definitions(cubeb PRIVATE DISABLE_LIBAAUDIO_DLOPEN)
target_link_libraries(cubeb PRIVATE aaudio)
endif()
endif()
if(USE_PULSE)
target_sources(cubeb PRIVATE src/cubeb_pulse.c)
target_compile_definitions(cubeb PRIVATE USE_PULSE)
endif()
if(USE_ALSA)
target_sources(cubeb PRIVATE src/cubeb_alsa.c)
target_compile_definitions(cubeb PRIVATE USE_ALSA)
endif()
if(USE_JACK)
target_sources(cubeb PRIVATE src/cubeb_jack.cpp)
target_compile_definitions(cubeb PRIVATE USE_JACK)
endif()
if(USE_SNDIO)
target_sources(cubeb PRIVATE src/cubeb_sndio.c)
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
endif()
if(USE_AAUDIO)
target_sources(cubeb PRIVATE src/cubeb_aaudio.cpp)
target_compile_definitions(cubeb PRIVATE USE_AAUDIO)
# set this definition to enable low latency mode. Possibly bad for battery
target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_LATENCY)
# set this definition to enable power saving mode. Possibly resulting
# in high latency
# target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_LOW_POWER_SAVING)
# set this mode to make the backend use an exclusive stream.
# will decrease latency.
# target_compile_definitions(cubeb PRIVATE CUBEB_AAUDIO_EXCLUSIVE_STREAM)
endif()
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
if(USE_AUDIOUNIT)
target_sources(cubeb PRIVATE
@ -64,36 +237,12 @@ if(USE_AUDIOUNIT)
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
endif()
check_include_files(pulse/pulseaudio.h USE_PULSE)
if(USE_PULSE)
target_sources(cubeb PRIVATE
src/cubeb_pulse.c)
target_compile_definitions(cubeb PRIVATE USE_PULSE)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(alsa/asoundlib.h USE_ALSA)
if(USE_ALSA)
target_sources(cubeb PRIVATE
src/cubeb_alsa.c)
target_compile_definitions(cubeb PRIVATE USE_ALSA)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(jack/jack.h USE_JACK)
if(USE_JACK)
target_sources(cubeb PRIVATE
src/cubeb_jack.cpp)
target_compile_definitions(cubeb PRIVATE USE_JACK)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(audioclient.h USE_WASAPI)
if(USE_WASAPI)
target_sources(cubeb PRIVATE
src/cubeb_wasapi.cpp)
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
target_link_libraries(cubeb PRIVATE avrt ole32)
target_link_libraries(cubeb PRIVATE avrt ole32 ksuser)
endif()
check_include_files("windows.h;mmsystem.h" USE_WINMM)
@ -118,33 +267,25 @@ if(HAVE_SYS_SOUNDCARD_H)
try_compile(USE_OSS "${PROJECT_BINARY_DIR}/compile_tests"
${PROJECT_SOURCE_DIR}/cmake/compile_tests/oss_is_v4.c)
if(USE_OSS)
target_sources(cubeb PRIVATE
src/cubeb_oss.c)
target_compile_definitions(cubeb PRIVATE USE_OSS)
target_link_libraries(cubeb PRIVATE pthread)
# strlcpy is not available on BSD systems that use glibc,
# like Debian kfreebsd, so try using libbsd if available
include(CheckSymbolExists)
check_symbol_exists(strlcpy string.h HAVE_STRLCPY)
if(NOT HAVE_STRLCPY)
pkg_check_modules(libbsd-overlay IMPORTED_TARGET libbsd-overlay)
if(libbsd-overlay_FOUND)
target_link_libraries(cubeb PRIVATE PkgConfig::libbsd-overlay)
set(HAVE_STRLCPY true)
endif()
endif()
if (HAVE_STRLCPY)
target_sources(cubeb PRIVATE
src/cubeb_oss.c)
target_compile_definitions(cubeb PRIVATE USE_OSS)
endif()
endif()
endif()
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()
check_include_files(android/log.h USE_AUDIOTRACK)
if(USE_AUDIOTRACK)
target_sources(cubeb PRIVATE
@ -153,20 +294,11 @@ if(USE_AUDIOTRACK)
target_link_libraries(cubeb PRIVATE log)
endif()
check_include_files(sndio.h USE_SNDIO)
if(USE_SNDIO)
target_sources(cubeb PRIVATE
src/cubeb_sndio.c)
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(sys/audioio.h USE_SUN)
if(USE_SUN)
target_sources(cubeb PRIVATE
src/cubeb_sun.c)
target_compile_definitions(cubeb PRIVATE USE_SUN)
target_link_libraries(cubeb PRIVATE pthread)
endif()
check_include_files(kai.h USE_KAI)
@ -177,3 +309,61 @@ if(USE_KAI)
target_link_libraries(cubeb PRIVATE kai)
endif()
if(USE_PULSE AND USE_PULSE_RUST)
include(ExternalProject)
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
ExternalProject_Add(
cubeb_pulse_rs
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND cargo build COMMAND cargo build --release
BUILD_ALWAYS ON
BINARY_DIR "${PROJECT_SOURCE_DIR}/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)

View file

@ -1,4 +1,4 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/cubebTargets.cmake")
check_required_components(cubeb)
check_required_components(cubeb)

View file

@ -1,13 +1,12 @@
# 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`
2. `mkdir cubeb-build`
3. `cd cubeb-build`
3. `cmake ../cubeb`
4. `cmake --build .`
5. `ctest`
1. `git clone --recursive https://github.com/mozilla/cubeb.git`
2. `cd cubeb`
3. `cmake -B ./build .`
4. `cmake --build ./build`
5. `cd build && ctest`
# 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 CMake.
- Run MinGW-w64 Terminal from the Start Menu.
- Follow the build steps at the top of this file, but at step 3 run:
`cmake -G "MinGW Makefiles" ..`
- Follow the build steps at the top of this file, but at step 4 run:
`cmake -G "MinGW Makefiles" ../cubeb`
- Continue the build steps at the top of this file.

View 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://ci.appveyor.com/api/projects/status/osv2r0m1j1nt9csr/branch/master?svg=true)](https://ci.appveyor.com/project/kinetiknz/cubeb/branch/master)
[![Build Status](https://github.com/mozilla/cubeb/actions/workflows/build.yml/badge.svg)](https://github.com/mozilla/cubeb/actions/workflows/build.yml)
See INSTALL.md for build instructions.
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.

View file

@ -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

View file

@ -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)

View file

@ -51,5 +51,11 @@
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<Lib>
<AdditionalDependencies>ksuser.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<Import Project="..\msvc\vsprops\Targets.props" />
</Project>

View file

@ -7,9 +7,9 @@
#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382)
#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
#include "cubeb_export.h"
#include <stdint.h>
#include <stdlib.h>
#include "cubeb_export.h"
#if defined(__cplusplus)
extern "C" {
@ -122,8 +122,10 @@ extern "C" {
/** @file
The <tt>libcubeb</tt> C API. */
typedef struct cubeb cubeb; /**< Opaque handle referencing the application state. */
typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */
typedef struct cubeb
cubeb; /**< Opaque handle referencing the application state. */
typedef struct cubeb_stream
cubeb_stream; /**< Opaque handle referencing the stream state. */
/** Sample format enumeration. */
typedef enum {
@ -155,8 +157,10 @@ typedef void const * cubeb_devid;
/** Level (verbosity) of logging for a particular cubeb context. */
typedef enum {
CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
CUBEB_LOG_NORMAL =
1, /**< Logging lifetime operation (creation/destruction). */
CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance
implications. */
} cubeb_log_level;
typedef enum {
@ -190,10 +194,10 @@ enum {
CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F =
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F1 =
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
@ -222,46 +226,50 @@ enum {
/** Miscellaneous stream preferences. */
typedef enum {
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
specified on the input params and an
output device to loopback from should
be passed in place of an input device. */
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
CUBEB_STREAM_PREF_LOOPBACK =
0x01, /**< Request a loopback stream. Should be
specified on the input params and an
output device to loopback from should
be passed in place of an input device. */
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
default device on OS
changes. */
CUBEB_STREAM_PREF_VOICE = 0x04, /**< This stream is going to transport voice data.
Depending on the backend and platform, this can
change the audio input or output devices
selected, as well as the quality of the stream,
for example to accomodate bluetooth SCO modes on
bluetooth devices. */
CUBEB_STREAM_PREF_RAW = 0x08, /**< Windows only. Bypass all signal processing
except for always on APO, driver and hardware. */
CUBEB_STREAM_PREF_PERSIST = 0x10, /**< Request that the volume and mute settings
should persist across restarts of the stream
and/or application. May not be honored for
all backends and platforms. */
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT = 0x20 /**< Don't automatically try to connect
ports. Only affects the jack
backend. */
CUBEB_STREAM_PREF_VOICE =
0x04, /**< This stream is going to transport voice data.
Depending on the backend and platform, this can
change the audio input or output devices
selected, as well as the quality of the stream,
for example to accomodate bluetooth SCO modes on
bluetooth devices. */
CUBEB_STREAM_PREF_RAW =
0x08, /**< Windows only. Bypass all signal processing
except for always on APO, driver and hardware. */
CUBEB_STREAM_PREF_PERSIST = 0x10, /**< Request that the volume and mute
settings should persist across restarts
of the stream and/or application. This is
obsolete and ignored by all backends. */
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT = 0x20 /**< Don't automatically try to
connect ports. Only affects
the jack backend. */
} cubeb_stream_prefs;
/** Stream format initialization parameters. */
typedef struct {
cubeb_sample_format format; /**< Requested sample format. One of
#cubeb_sample_format. */
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
cubeb_sample_format format; /**< Requested sample format. One of
#cubeb_sample_format. */
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
cubeb_channel_layout
layout; /**< Requested channel layout. This must be consistent with the
provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
} cubeb_stream_params;
/** Audio device description */
typedef struct {
char * output_name; /**< The name of the output device */
char * input_name; /**< The name of the input device */
char * input_name; /**< The name of the input device */
} cubeb_device;
/** Stream states signaled via state_callback. */
@ -274,12 +282,15 @@ typedef enum {
/** Result code enumeration. */
enum {
CUBEB_OK = 0, /**< Success. */
CUBEB_ERROR = -1, /**< Unclassified error. */
CUBEB_ERROR_INVALID_FORMAT = -2, /**< Unsupported #cubeb_stream_params requested. */
CUBEB_OK = 0, /**< Success. */
CUBEB_ERROR = -1, /**< Unclassified error. */
CUBEB_ERROR_INVALID_FORMAT =
-2, /**< Unsupported #cubeb_stream_params requested. */
CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
CUBEB_ERROR_NOT_SUPPORTED = -4, /**< Optional function not implemented in current backend. */
CUBEB_ERROR_DEVICE_UNAVAILABLE = -5 /**< Device specified by #cubeb_devid not available. */
CUBEB_ERROR_NOT_SUPPORTED =
-4, /**< Optional function not implemented in current backend. */
CUBEB_ERROR_DEVICE_UNAVAILABLE =
-5 /**< Device specified by #cubeb_devid not available. */
};
/**
@ -295,50 +306,56 @@ typedef enum {
* The state of a device.
*/
typedef enum {
CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system level. */
CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is plugged into it. */
CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */
CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system
level. */
CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is
plugged into it. */
CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */
} cubeb_device_state;
/**
* Architecture specific sample type.
*/
typedef enum {
CUBEB_DEVICE_FMT_S16LE = 0x0010, /**< 16-bit integers, Little Endian. */
CUBEB_DEVICE_FMT_S16BE = 0x0020, /**< 16-bit integers, Big Endian. */
CUBEB_DEVICE_FMT_F32LE = 0x1000, /**< 32-bit floating point, Little Endian. */
CUBEB_DEVICE_FMT_F32BE = 0x2000 /**< 32-bit floating point, Big Endian. */
CUBEB_DEVICE_FMT_S16LE = 0x0010, /**< 16-bit integers, Little Endian. */
CUBEB_DEVICE_FMT_S16BE = 0x0020, /**< 16-bit integers, Big Endian. */
CUBEB_DEVICE_FMT_F32LE = 0x1000, /**< 32-bit floating point, Little Endian. */
CUBEB_DEVICE_FMT_F32BE = 0x2000 /**< 32-bit floating point, Big Endian. */
} cubeb_device_fmt;
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
/** 16-bit integers, native endianess, when on a Big Endian environment. */
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
/** 32-bit floating points, native endianess, when on a Big Endian environment. */
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
/** 32-bit floating points, native endianess, when on a Big Endian environment.
*/
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
#else
/** 16-bit integers, native endianess, when on a Little Endian environment. */
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE
/** 32-bit floating points, native endianess, when on a Little Endian
* environment. */
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
#endif
/** All the 16-bit integers types. */
#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
#define CUBEB_DEVICE_FMT_S16_MASK \
(CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
/** All the 32-bit floating points types. */
#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
#define CUBEB_DEVICE_FMT_F32_MASK \
(CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
/** All the device formats types. */
#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
#define CUBEB_DEVICE_FMT_ALL \
(CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
/** Channel type for a `cubeb_stream`. Depending on the backend and platform
* used, this can control inter-stream interruption, ducking, and volume
* control.
*/
typedef enum {
CUBEB_DEVICE_PREF_NONE = 0x00,
CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01,
CUBEB_DEVICE_PREF_VOICE = 0x02,
CUBEB_DEVICE_PREF_NOTIFICATION = 0x04,
CUBEB_DEVICE_PREF_ALL = 0x0F
CUBEB_DEVICE_PREF_NONE = 0x00,
CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01,
CUBEB_DEVICE_PREF_VOICE = 0x02,
CUBEB_DEVICE_PREF_NOTIFICATION = 0x04,
CUBEB_DEVICE_PREF_ALL = 0x0F
} cubeb_device_pref;
/** This structure holds the characteristics
@ -347,25 +364,30 @@ typedef enum {
* `cubeb_device_collection` and must be destroyed via
* `cubeb_device_collection_destroy`. */
typedef struct {
cubeb_devid devid; /**< Device identifier handle. */
char const * device_id; /**< Device identifier which might be presented in a UI. */
char const * friendly_name; /**< Friendly device name which might be presented in a UI. */
char const * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
char const * vendor_name; /**< Optional vendor name, may be NULL. */
cubeb_devid devid; /**< Device identifier handle. */
char const *
device_id; /**< Device identifier which might be presented in a UI. */
char const * friendly_name; /**< Friendly device name which might be presented
in a UI. */
char const * group_id; /**< Two devices have the same group identifier if they
belong to the same physical device; for example a
headset and microphone. */
char const * vendor_name; /**< Optional vendor name, may be NULL. */
cubeb_device_type type; /**< Type of device (Input/Output). */
cubeb_device_state state; /**< State of device disabled/enabled/unplugged. */
cubeb_device_pref preferred;/**< Preferred device. */
cubeb_device_type type; /**< Type of device (Input/Output). */
cubeb_device_state state; /**< State of device disabled/enabled/unplugged. */
cubeb_device_pref preferred; /**< Preferred device. */
cubeb_device_fmt format; /**< Sample format supported. */
cubeb_device_fmt default_format; /**< The default sample format for this device. */
uint32_t max_channels; /**< Channels. */
uint32_t default_rate; /**< Default/Preferred sample rate. */
uint32_t max_rate; /**< Maximum sample rate supported. */
uint32_t min_rate; /**< Minimum sample rate supported. */
cubeb_device_fmt format; /**< Sample format supported. */
cubeb_device_fmt
default_format; /**< The default sample format for this device. */
uint32_t max_channels; /**< Channels. */
uint32_t default_rate; /**< Default/Preferred sample rate. */
uint32_t max_rate; /**< Maximum sample rate supported. */
uint32_t min_rate; /**< Minimum sample rate supported. */
uint32_t latency_lo; /**< Lowest possible latency in frames. */
uint32_t latency_hi; /**< Higest possible latency in frames. */
uint32_t latency_lo; /**< Lowest possible latency in frames. */
uint32_t latency_hi; /**< Higest possible latency in frames. */
} cubeb_device_info;
/** Device collection.
@ -398,34 +420,32 @@ typedef struct {
being stopped.
@retval CUBEB_ERROR on error, in which case the data callback will stop
and the stream will enter a shutdown state. */
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
void * user_ptr,
void const * input_buffer,
void * output_buffer,
long nframes);
typedef long (*cubeb_data_callback)(cubeb_stream * stream, void * user_ptr,
void const * input_buffer,
void * output_buffer, long nframes);
/** User supplied state callback.
@param stream The stream for this this callback fired.
@param user_ptr The pointer passed to cubeb_stream_init.
@param state The new state of the stream. */
typedef void (* cubeb_state_callback)(cubeb_stream * stream,
void * user_ptr,
cubeb_state state);
typedef void (*cubeb_state_callback)(cubeb_stream * stream, void * user_ptr,
cubeb_state state);
/**
* User supplied callback called when the underlying device changed.
* @param user The pointer passed to cubeb_stream_init. */
typedef void (* cubeb_device_changed_callback)(void * user_ptr);
typedef void (*cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */
typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
void * user_ptr);
* @param user_ptr The pointer passed to
* cubeb_register_device_collection_changed. */
typedef void (*cubeb_device_collection_changed_callback)(cubeb * context,
void * user_ptr);
/** User supplied callback called when a message needs logging. */
typedef void (* cubeb_log_callback)(char const * fmt, ...);
typedef void (*cubeb_log_callback)(char const * fmt, ...);
/** Initialize an application context. This will perform any library or
application scoped initialization.
@ -445,13 +465,15 @@ typedef void (* cubeb_log_callback)(char const * fmt, ...);
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR in case of error, for example because the host
has no audio hardware. */
CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
CUBEB_EXPORT int
cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
CUBEB_EXPORT char const *
cubeb_get_backend_id(cubeb * context);
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@ -460,7 +482,8 @@ CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
CUBEB_EXPORT int
cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
/** Get the minimal latency value, in frames, that is guaranteed to work
when creating a stream for the specified sample rate. This is platform,
@ -473,9 +496,9 @@ CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_cha
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
cubeb_stream_params * params,
uint32_t * latency_frames);
CUBEB_EXPORT int
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params,
uint32_t * latency_frames);
/** Get the preferred sample rate for this backend: this is hardware and
platform dependent, and can avoid resampling, and/or trigger fastpaths.
@ -484,12 +507,14 @@ CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
CUBEB_EXPORT int
cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
/** Destroy an application context. This must be called after all stream have
* been destroyed.
@param context A pointer to the cubeb context.*/
CUBEB_EXPORT void cubeb_destroy(cubeb * context);
CUBEB_EXPORT void
cubeb_destroy(cubeb * context);
/** Initialize a stream associated with the supplied application context.
@param context A pointer to the cubeb context.
@ -497,17 +522,17 @@ CUBEB_EXPORT void cubeb_destroy(cubeb * context);
cubeb stream.
@param stream_name A name for this stream.
@param input_device Device for the input side of the stream. If NULL the
default input device is used. Passing a valid cubeb_devid
means the stream only ever uses that device. Passing a NULL
cubeb_devid allows the stream to follow that device type's
OS default.
default input device is used. Passing a valid
cubeb_devid means the stream only ever uses that device. Passing a NULL
cubeb_devid allows the stream to follow that device
type's OS default.
@param input_stream_params Parameters for the input side of the stream, or
NULL if this stream is output only.
@param output_device Device for the output side of the stream. If NULL the
default output device is used. Passing a valid cubeb_devid
means the stream only ever uses that device. Passing a NULL
cubeb_devid allows the stream to follow that device type's
OS default.
default output device is used. Passing a valid
cubeb_devid means the stream only ever uses that device. Passing a NULL
cubeb_devid allows the stream to follow that device
type's OS default.
@param output_stream_params Parameters for the output side of the stream, or
NULL if this stream is input only. When input
and output stream parameters are supplied, their
@ -523,49 +548,42 @@ CUBEB_EXPORT void cubeb_destroy(cubeb * context);
@retval CUBEB_ERROR
@retval CUBEB_ERROR_INVALID_FORMAT
@retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
CUBEB_EXPORT int cubeb_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
uint32_t latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
CUBEB_EXPORT int
cubeb_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
uint32_t latency_frames, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr);
/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
stream.
@param stream The stream to destroy. */
CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream);
CUBEB_EXPORT void
cubeb_stream_destroy(cubeb_stream * stream);
/** Start playback.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
CUBEB_EXPORT int
cubeb_stream_start(cubeb_stream * stream);
/** Stop playback.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
/** Reset stream to the default device.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_reset_default_device(cubeb_stream * stream);
CUBEB_EXPORT int
cubeb_stream_stop(cubeb_stream * stream);
/** Get the current stream playback position.
@param stream
@param position Playback position in frames.
@retval CUBEB_OK
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
CUBEB_EXPORT int
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
/** Get the latency for this stream, in frames. This is the number of frames
between the time cubeb acquires the data in the callback and the listener
@ -575,7 +593,8 @@ CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * pos
@retval CUBEB_OK
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
CUBEB_EXPORT int
cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
/** Get the input latency for this stream, in frames. This is the number of
frames between the time the audio input devices records the data, and they
@ -586,7 +605,8 @@ CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * late
@retval CUBEB_OK
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency);
CUBEB_EXPORT int
cubeb_stream_get_input_latency(cubeb_stream * stream, uint32_t * latency);
/** Set the volume for a stream.
@param stream the stream for which to adjust the volume.
@param volume a float between 0.0 (muted) and 1.0 (maximum volume)
@ -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
stream is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
CUBEB_EXPORT int
cubeb_stream_set_volume(cubeb_stream * stream, float volume);
/** Change a stream's name.
@param stream the stream for which to set the name.
@ -602,7 +623,8 @@ CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if any pointer is invalid
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name);
CUBEB_EXPORT int
cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name);
/** Get the current output device for this stream.
@param stm the stream for which to query the current output device
@ -611,8 +633,9 @@ CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * strea
@retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
invalid pointers
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
CUBEB_EXPORT int
cubeb_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
/** Destroy a cubeb_device structure.
@param stream the stream passed in cubeb_stream_get_current_device
@ -620,8 +643,8 @@ CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
cubeb_device * devices);
CUBEB_EXPORT int
cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * devices);
/** Set a callback to be notified when the output device changes.
@param stream the stream for which to set the callback.
@ -631,32 +654,38 @@ CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
@retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
device_changed_callback are invalid pointers.
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
CUBEB_EXPORT int
cubeb_stream_register_device_changed_callback(
cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
/** Return the user data pointer registered with the stream with cubeb_stream_init.
/** Return the user data pointer registered with the stream with
cubeb_stream_init.
@param stream the stream for which to retrieve user data pointer.
@retval user data pointer */
CUBEB_EXPORT void * cubeb_stream_user_ptr(cubeb_stream * stream);
CUBEB_EXPORT void *
cubeb_stream_user_ptr(cubeb_stream * stream);
/** Returns enumerated devices.
@param context
@param devtype device type to include
@param collection output collection. Must be destroyed with cubeb_device_collection_destroy
@param collection output collection. Must be destroyed with
cubeb_device_collection_destroy
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection * collection);
CUBEB_EXPORT int
cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
cubeb_device_collection * collection);
/** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
@param context
@param collection collection to destroy
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
CUBEB_EXPORT int
cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
@ -664,17 +693,18 @@ CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
@param devtype device type to include. Different callbacks and user pointers
can be registered for each devtype. The hybrid devtype
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
and will register the provided callback and user pointer in both sides.
and will register the provided callback and user pointer in both
sides.
@param callback a function called whenever the system device list changes.
Passing NULL allow to unregister a function. You have to unregister
first before you register a new callback.
@param user_ptr pointer to user specified data which will be present in
subsequent callbacks.
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr);
CUBEB_EXPORT int
cubeb_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback, void * user_ptr);
/** Set a callback to be called with a message.
@param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
@ -684,8 +714,9 @@ CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
@retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
invalid pointers, or if level is not
in cubeb_log_level. */
CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback);
CUBEB_EXPORT int
cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback);
#if defined(__cplusplus)
}

View file

@ -22,23 +22,25 @@
*/
/*
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
* From
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
*/
typedef int32_t status_t;
/*
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h
* From
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h
*/
struct Buffer {
uint32_t flags;
int channelCount;
int format;
size_t frameCount;
size_t size;
uint32_t flags;
int channelCount;
int format;
size_t frameCount;
size_t size;
union {
void* raw;
short* i16;
int8_t* i8;
void * raw;
short * i16;
int8_t * i8;
};
};
@ -52,25 +54,28 @@ enum event_type {
};
/**
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
* and
* From
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
* and
* https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h
*/
#define AUDIO_STREAM_TYPE_MUSIC 3
enum {
AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1,
AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1,
AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2,
AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS,
AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS)
AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS,
AUDIO_CHANNEL_OUT_STEREO_ICS =
(AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS)
} AudioTrack_ChannelMapping_ICS;
enum {
AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy = 0x4,
AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy = 0x8,
AUDIO_CHANNEL_OUT_MONO_Legacy = AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy,
AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy)
AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy |
AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy)
} AudioTrack_ChannelMapping_Legacy;
typedef enum {
@ -78,4 +83,3 @@ typedef enum {
AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1,
AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT),
} AudioTrack_SampleType;

View file

@ -1,9 +1,9 @@
#ifndef _CUBEB_OUTPUT_LATENCY_H_
#define _CUBEB_OUTPUT_LATENCY_H_
#include <stdbool.h>
#include "cubeb_media_library.h"
#include "../cubeb-jni.h"
#include "cubeb_media_library.h"
#include <stdbool.h>
struct output_latency_function {
media_lib * from_lib;
@ -23,7 +23,7 @@ cubeb_output_latency_load_method(int version)
ol->version = version;
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) {
ol->from_jni = cubeb_jni_init();
return ol;
}
@ -36,7 +36,7 @@ bool
cubeb_output_latency_method_is_loaded(output_latency_function * ol)
{
assert(ol);
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) {
return !!ol->from_jni;
}
@ -66,7 +66,7 @@ cubeb_get_output_latency(output_latency_function * ol)
{
assert(cubeb_output_latency_method_is_loaded(ol));
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2) {
return cubeb_get_output_latency_from_jni(ol->from_jni);
}

View file

@ -3,7 +3,7 @@
struct media_lib {
void * libmedia;
int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
int32_t (*get_output_latency)(uint32_t * latency, int stream_type);
};
typedef struct media_lib media_lib;
@ -17,15 +17,17 @@ cubeb_load_media_library()
return NULL;
}
// Get the latency, in ms, from AudioFlinger. First, try the most recent signature.
// status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
ml.get_output_latency =
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
// Get the latency, in ms, from AudioFlinger. First, try the most recent
// signature. status_t AudioSystem::getOutputLatency(uint32_t* latency,
// audio_stream_type_t streamType)
ml.get_output_latency = dlsym(
ml.libmedia,
"_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
if (!ml.get_output_latency) {
// In case of failure, try the signature from legacy version.
// status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
ml.get_output_latency =
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
if (!ml.get_output_latency) {
return NULL;
}

View file

@ -29,23 +29,23 @@
/** Audio recording preset */
/** Audio recording preset key */
#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset")
#define SL_ANDROID_KEY_RECORDING_PRESET \
((const SLchar *)"androidRecordingPreset")
/** Audio recording preset values */
/** preset "none" cannot be set, it is used to indicate the current settings
* do not match any of the presets. */
#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32) 0x00000000)
#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32)0x00000000)
/** generic recording configuration on the platform */
#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32) 0x00000001)
#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32)0x00000001)
/** uses the microphone audio source with the same orientation as the camera
* if available, the main device microphone otherwise */
#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32) 0x00000002)
#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32)0x00000002)
/** uses the main microphone tuned for voice recognition */
#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32) 0x00000003)
#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32)0x00000003)
/** uses the main microphone tuned for audio communications */
#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)
#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32)0x00000004)
/** uses the main microphone unprocessed */
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32) 0x00000005)
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32)0x00000005)
/*---------------------------------------------------------------------------*/
/* Android AudioPlayer configuration */
@ -53,22 +53,21 @@
/** Audio playback stream type */
/** Audio playback stream type key */
#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType")
#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar *)"androidPlaybackStreamType")
/** Audio playback stream type values */
/* same as android.media.AudioManager.STREAM_VOICE_CALL */
#define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000)
#define SL_ANDROID_STREAM_VOICE ((SLint32)0x00000000)
/* same as android.media.AudioManager.STREAM_SYSTEM */
#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001)
#define SL_ANDROID_STREAM_SYSTEM ((SLint32)0x00000001)
/* same as android.media.AudioManager.STREAM_RING */
#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002)
#define SL_ANDROID_STREAM_RING ((SLint32)0x00000002)
/* same as android.media.AudioManager.STREAM_MUSIC */
#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003)
#define SL_ANDROID_STREAM_MEDIA ((SLint32)0x00000003)
/* same as android.media.AudioManager.STREAM_ALARM */
#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004)
#define SL_ANDROID_STREAM_ALARM ((SLint32)0x00000004)
/* same as android.media.AudioManager.STREAM_NOTIFICATION */
#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005)
#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32)0x00000005)
/*---------------------------------------------------------------------------*/
/* Android AudioPlayer and AudioRecorder configuration */
@ -85,18 +84,21 @@
* granted or not.
*/
/** Audio Performance mode key */
#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
#define SL_ANDROID_KEY_PERFORMANCE_MODE \
((const SLchar *)"androidPerformanceMode")
/** Audio performance values */
/* No specific performance requirement. Allows HW and SW pre/post processing. */
#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000)
/* No specific performance requirement. Allows HW and SW pre/post
* processing. */
#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32)0x00000000)
/* Priority given to latency. No HW or software pre/post processing.
* This is the default if no performance mode is specified. */
#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001)
/* Priority given to latency while still allowing HW pre and post processing. */
#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002)
#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32)0x00000001)
/* Priority given to latency while still allowing HW pre and post
* processing. */
#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32)0x00000002)
/* Priority given to power saving if latency is not a concern.
* Allows HW and SW pre/post processing. */
#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003)
#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32)0x00000003)
#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */

View file

@ -8,8 +8,8 @@
#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
#include "cubeb/cubeb.h"
#include "cubeb_log.h"
#include "cubeb_assert.h"
#include "cubeb_log.h"
#include <stdio.h>
#include <string.h>
@ -21,7 +21,7 @@
#define CLANG_ANALYZER_NORETURN
#endif // ifndef CLANG_ANALYZER_NORETURN
#endif // __has_feature(attribute_analyzer_noreturn)
#else // __clang__
#else // __clang__
#define CLANG_ANALYZER_NORETURN
#endif
@ -34,48 +34,41 @@ extern "C" {
#endif
struct cubeb_ops {
int (* init)(cubeb ** context, char const * context_name);
char const * (* get_backend_id)(cubeb * context);
int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels);
int (* get_min_latency)(cubeb * context,
cubeb_stream_params params,
uint32_t * latency_ms);
int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection);
int (* device_collection_destroy)(cubeb * context,
cubeb_device_collection * collection);
void (* destroy)(cubeb * context);
int (* stream_init)(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
void (* stream_destroy)(cubeb_stream * stream);
int (* stream_start)(cubeb_stream * stream);
int (* stream_stop)(cubeb_stream * stream);
int (* stream_reset_default_device)(cubeb_stream * stream);
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
int (* stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency);
int (* stream_set_volume)(cubeb_stream * stream, float volumes);
int (* stream_set_name)(cubeb_stream * stream, char const * stream_name);
int (* stream_get_current_device)(cubeb_stream * stream,
cubeb_device ** const device);
int (* stream_device_destroy)(cubeb_stream * stream,
cubeb_device * device);
int (* stream_register_device_changed_callback)(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
int (* register_device_collection_changed)(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr);
int (*init)(cubeb ** context, char const * context_name);
char const * (*get_backend_id)(cubeb * context);
int (*get_max_channel_count)(cubeb * context, uint32_t * max_channels);
int (*get_min_latency)(cubeb * context, cubeb_stream_params params,
uint32_t * latency_ms);
int (*get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
int (*enumerate_devices)(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection);
int (*device_collection_destroy)(cubeb * context,
cubeb_device_collection * collection);
void (*destroy)(cubeb * context);
int (*stream_init)(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr);
void (*stream_destroy)(cubeb_stream * stream);
int (*stream_start)(cubeb_stream * stream);
int (*stream_stop)(cubeb_stream * stream);
int (*stream_get_position)(cubeb_stream * stream, uint64_t * position);
int (*stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
int (*stream_get_input_latency)(cubeb_stream * stream, uint32_t * latency);
int (*stream_set_volume)(cubeb_stream * stream, float volumes);
int (*stream_set_name)(cubeb_stream * stream, char const * stream_name);
int (*stream_get_current_device)(cubeb_stream * stream,
cubeb_device ** const device);
int (*stream_device_destroy)(cubeb_stream * stream, cubeb_device * device);
int (*stream_register_device_changed_callback)(
cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
int (*register_device_collection_changed)(
cubeb * context, cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback, void * user_ptr);
};
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */

View file

@ -1,6 +1,8 @@
/* clang-format off */
#include "jni.h"
#include <assert.h>
#include "cubeb-jni-instances.h"
/* clang-format on */
#define AUDIO_STREAM_TYPE_MUSIC 3
@ -10,8 +12,7 @@ struct cubeb_jni {
jmethodID s_get_output_latency_id = nullptr;
};
extern "C"
cubeb_jni *
extern "C" cubeb_jni *
cubeb_jni_init()
{
jobject ctx_obj = cubeb_jni_get_context_instance();
@ -23,18 +24,28 @@ cubeb_jni_init()
cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
assert(cubeb_jni_ptr);
// Find the audio manager object and make it global to call it from another method
// Find the audio manager object and make it global to call it from another
// method
jclass context_class = jni_env->FindClass("android/content/Context");
jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field);
jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
jfieldID audio_service_field = jni_env->GetStaticFieldID(
context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class,
audio_service_field);
jmethodID get_system_service_id =
jni_env->GetMethodID(context_class, "getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;");
jobject audio_manager_obj =
jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
cubeb_jni_ptr->s_audio_manager_obj =
reinterpret_cast<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");
cubeb_jni_ptr->s_audio_manager_class = reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I");
cubeb_jni_ptr->s_audio_manager_class =
reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
cubeb_jni_ptr->s_get_output_latency_id =
jni_env->GetMethodID(audio_manager_class, "getOutputLatency", "(I)I");
jni_env->DeleteLocalRef(ctx_obj);
jni_env->DeleteLocalRef(context_class);
@ -45,16 +56,19 @@ cubeb_jni_init()
return cubeb_jni_ptr;
}
extern "C"
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
extern "C" int
cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
{
assert(cubeb_jni_ptr);
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC
return jni_env->CallIntMethod(
cubeb_jni_ptr->s_audio_manager_obj,
cubeb_jni_ptr->s_get_output_latency_id,
AUDIO_STREAM_TYPE_MUSIC); // param: AudioManager.STREAM_MUSIC
}
extern "C"
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
extern "C" void
cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
{
assert(cubeb_jni_ptr);

View file

@ -3,8 +3,11 @@
typedef struct cubeb_jni cubeb_jni;
cubeb_jni * cubeb_jni_init();
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
cubeb_jni *
cubeb_jni_init();
int
cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
void
cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
#endif // _CUBEB_JNI_H_

View file

@ -10,27 +10,22 @@
#include <SLES/OpenSLES.h>
static SLresult
cubeb_get_sles_engine(SLObjectItf * pEngine,
SLuint32 numOptions,
cubeb_get_sles_engine(SLObjectItf * pEngine, SLuint32 numOptions,
const SLEngineOption * pEngineOptions,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired)
{
return slCreateEngine(pEngine,
numOptions,
pEngineOptions,
numInterfaces,
pInterfaceIds,
pInterfaceRequired);
return slCreateEngine(pEngine, numOptions, pEngineOptions, numInterfaces,
pInterfaceIds, pInterfaceRequired);
}
static void
cubeb_destroy_sles_engine(SLObjectItf * self)
{
if (*self != NULL) {
(**self)->Destroy(*self);
*self = NULL;
(**self)->Destroy(*self);
*self = NULL;
}
}

View file

@ -5,14 +5,14 @@
* accompanying file LICENSE for details.
*/
#undef NDEBUG
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
#define NELEMS(x) ((int)(sizeof(x) / sizeof(x[0])))
struct cubeb {
struct cubeb_ops * ops;
@ -28,49 +28,64 @@ struct cubeb_stream {
};
#if defined(USE_PULSE)
int pulse_init(cubeb ** context, char const * context_name);
int
pulse_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_PULSE_RUST)
int pulse_rust_init(cubeb ** contet, char const * context_name);
int
pulse_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_JACK)
int jack_init (cubeb ** context, char const * context_name);
int
jack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_ALSA)
int alsa_init(cubeb ** context, char const * context_name);
int
alsa_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT)
int audiounit_init(cubeb ** context, char const * context_name);
int
audiounit_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT_RUST)
int audiounit_rust_init(cubeb ** contet, char const * context_name);
int
audiounit_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_WINMM)
int winmm_init(cubeb ** context, char const * context_name);
int
winmm_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_WASAPI)
int wasapi_init(cubeb ** context, char const * context_name);
int
wasapi_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SNDIO)
int sndio_init(cubeb ** context, char const * context_name);
int
sndio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SUN)
int sun_init(cubeb ** context, char const * context_name);
int
sun_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OPENSL)
int opensl_init(cubeb ** context, char const * context_name);
int
opensl_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OSS)
int oss_init(cubeb ** context, char const * context_name);
int
oss_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AAUDIO)
int aaudio_init(cubeb ** context, char const * context_name);
int
aaudio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOTRACK)
int audiotrack_init(cubeb ** context, char const * context_name);
int
audiotrack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_KAI)
int kai_init(cubeb ** context, char const * context_name);
int
kai_init(cubeb ** context, char const * context_name);
#endif
static int
@ -79,28 +94,32 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
{
XASSERT(input_stream_params || output_stream_params);
if (output_stream_params) {
if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
output_stream_params->channels < 1 || output_stream_params->channels > UINT8_MAX) {
if (output_stream_params->rate < 1000 ||
output_stream_params->rate > 192000 ||
output_stream_params->channels < 1 ||
output_stream_params->channels > UINT8_MAX) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
if (input_stream_params) {
if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
input_stream_params->channels < 1 || input_stream_params->channels > UINT8_MAX) {
if (input_stream_params->rate < 1000 ||
input_stream_params->rate > 192000 ||
input_stream_params->channels < 1 ||
input_stream_params->channels > UINT8_MAX) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
// Rate and sample format must be the same for input and output, if using a
// duplex stream
if (input_stream_params && output_stream_params) {
if (input_stream_params->rate != output_stream_params->rate ||
if (input_stream_params->rate != output_stream_params->rate ||
input_stream_params->format != output_stream_params->format) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
cubeb_stream_params * params = input_stream_params ?
input_stream_params : output_stream_params;
cubeb_stream_params * params =
input_stream_params ? input_stream_params : output_stream_params;
switch (params->format) {
case CUBEB_SAMPLE_S16LE:
@ -123,9 +142,10 @@ validate_latency(int latency)
}
int
cubeb_init(cubeb ** context, char const * context_name, char const * backend_name)
cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name)
{
int (* init_oneshot)(cubeb **, char const *) = NULL;
int (*init_oneshot)(cubeb **, char const *) = NULL;
if (backend_name != NULL) {
if (!strcmp(backend_name, "pulse")) {
@ -193,7 +213,7 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
}
}
int (* default_init[])(cubeb **, char const *) = {
int (*default_init[])(cubeb **, char const *) = {
/*
* init_oneshot must be at the top to allow user
* to override all other choices
@ -214,7 +234,7 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
#if defined(USE_ALSA)
alsa_init,
#endif
#if defined (USE_OSS)
#if defined(USE_OSS)
oss_init,
#endif
#if defined(USE_AUDIOUNIT_RUST)
@ -235,8 +255,8 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
#if defined(USE_OPENSL)
opensl_init,
#endif
// TODO: should probably be preferred over OpenSLES when available.
// Initialization will fail on old android devices.
// TODO: should probably be preferred over OpenSLES when available.
// Initialization will fail on old android devices.
#if defined(USE_AAUDIO)
aaudio_init,
#endif
@ -253,7 +273,7 @@ cubeb_init(cubeb ** context, char const * context_name, char const * backend_nam
return CUBEB_ERROR_INVALID_PARAMETER;
}
#define OK(fn) assert((* context)->ops->fn)
#define OK(fn) assert((*context)->ops->fn)
for (i = 0; i < NELEMS(default_init); ++i) {
if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
/* Assert that the minimal API is implemented. */
@ -295,7 +315,8 @@ cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
}
int
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms)
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params,
uint32_t * latency_ms)
{
if (!context || !params || !latency_ms) {
return CUBEB_ERROR_INVALID_PARAMETER;
@ -333,15 +354,13 @@ cubeb_destroy(cubeb * context)
}
int
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cubeb_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
unsigned int latency, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
int r;
@ -349,24 +368,20 @@ cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return CUBEB_ERROR_INVALID_PARAMETER;
}
if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
if ((r = validate_stream_params(input_stream_params, output_stream_params)) !=
CUBEB_OK ||
(r = validate_latency(latency)) != CUBEB_OK) {
return r;
}
r = context->ops->stream_init(context, stream, stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency,
data_callback,
state_callback,
user_ptr);
r = context->ops->stream_init(context, stream, stream_name, input_device,
input_stream_params, output_device,
output_stream_params, latency, data_callback,
state_callback, user_ptr);
if (r == CUBEB_ERROR_INVALID_FORMAT) {
LOG("Invalid format, %p %p %d %d",
output_stream_params, input_stream_params,
LOG("Invalid format, %p %p %d %d", output_stream_params,
input_stream_params,
output_stream_params && output_stream_params->format,
input_stream_params && input_stream_params->format);
}
@ -404,20 +419,6 @@ cubeb_stream_stop(cubeb_stream * stream)
return stream->context->ops->stream_stop(stream);
}
int
cubeb_stream_reset_default_device(cubeb_stream * stream)
{
if (!stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_reset_default_device) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_reset_default_device(stream);
}
int
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
{
@ -484,8 +485,9 @@ cubeb_stream_set_name(cubeb_stream * stream, char const * stream_name)
return stream->context->ops->stream_set_name(stream, stream_name);
}
int cubeb_stream_get_current_device(cubeb_stream * stream,
cubeb_device ** const device)
int
cubeb_stream_get_current_device(cubeb_stream * stream,
cubeb_device ** const device)
{
if (!stream || !device) {
return CUBEB_ERROR_INVALID_PARAMETER;
@ -498,8 +500,8 @@ int cubeb_stream_get_current_device(cubeb_stream * stream,
return stream->context->ops->stream_get_current_device(stream, device);
}
int cubeb_stream_device_destroy(cubeb_stream * stream,
cubeb_device * device)
int
cubeb_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
{
if (!stream || !device) {
return CUBEB_ERROR_INVALID_PARAMETER;
@ -512,8 +514,10 @@ int cubeb_stream_device_destroy(cubeb_stream * stream,
return stream->context->ops->stream_device_destroy(stream, device);
}
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback)
int
cubeb_stream_register_device_changed_callback(
cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback)
{
if (!stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
@ -523,10 +527,12 @@ int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
return stream->context->ops->stream_register_device_changed_callback(
stream, device_changed_callback);
}
void * cubeb_stream_user_ptr(cubeb_stream * stream)
void *
cubeb_stream_user_ptr(cubeb_stream * stream)
{
if (!stream) {
return NULL;
@ -535,56 +541,56 @@ void * cubeb_stream_user_ptr(cubeb_stream * stream)
return stream->user_ptr;
}
static
void log_device(cubeb_device_info * device_info)
static void
log_device(cubeb_device_info * device_info)
{
char devfmts[128] = "";
const char * devtype, * devstate, * devdeffmt;
const char *devtype, *devstate, *devdeffmt;
switch (device_info->type) {
case CUBEB_DEVICE_TYPE_INPUT:
devtype = "input";
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
devtype = "output";
break;
case CUBEB_DEVICE_TYPE_UNKNOWN:
default:
devtype = "unknown?";
break;
case CUBEB_DEVICE_TYPE_INPUT:
devtype = "input";
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
devtype = "output";
break;
case CUBEB_DEVICE_TYPE_UNKNOWN:
default:
devtype = "unknown?";
break;
};
switch (device_info->state) {
case CUBEB_DEVICE_STATE_DISABLED:
devstate = "disabled";
break;
case CUBEB_DEVICE_STATE_UNPLUGGED:
devstate = "unplugged";
break;
case CUBEB_DEVICE_STATE_ENABLED:
devstate = "enabled";
break;
default:
devstate = "unknown?";
break;
case CUBEB_DEVICE_STATE_DISABLED:
devstate = "disabled";
break;
case CUBEB_DEVICE_STATE_UNPLUGGED:
devstate = "unplugged";
break;
case CUBEB_DEVICE_STATE_ENABLED:
devstate = "enabled";
break;
default:
devstate = "unknown?";
break;
};
switch (device_info->default_format) {
case CUBEB_DEVICE_FMT_S16LE:
devdeffmt = "S16LE";
break;
case CUBEB_DEVICE_FMT_S16BE:
devdeffmt = "S16BE";
break;
case CUBEB_DEVICE_FMT_F32LE:
devdeffmt = "F32LE";
break;
case CUBEB_DEVICE_FMT_F32BE:
devdeffmt = "F32BE";
break;
default:
devdeffmt = "unknown?";
break;
case CUBEB_DEVICE_FMT_S16LE:
devdeffmt = "S16LE";
break;
case CUBEB_DEVICE_FMT_S16BE:
devdeffmt = "S16BE";
break;
case CUBEB_DEVICE_FMT_F32LE:
devdeffmt = "F32LE";
break;
case CUBEB_DEVICE_FMT_F32BE:
devdeffmt = "F32BE";
break;
default:
devdeffmt = "unknown?";
break;
};
if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
@ -611,20 +617,17 @@ void log_device(cubeb_device_info * device_info)
"\tRate:\t[%u, %u] (default: %u)\n"
"\tLatency: lo %u frames, hi %u frames",
device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
device_info->friendly_name,
device_info->group_id,
device_info->vendor_name,
devtype,
devstate,
device_info->max_channels,
(devfmts[0] == '\0') ? devfmts : devfmts + 1, (unsigned int)device_info->format, devdeffmt,
device_info->min_rate, device_info->max_rate, device_info->default_rate,
device_info->latency_lo, device_info->latency_hi);
device_info->friendly_name, device_info->group_id,
device_info->vendor_name, devtype, devstate, device_info->max_channels,
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
(unsigned int)device_info->format, devdeffmt, device_info->min_rate,
device_info->max_rate, device_info->default_rate, device_info->latency_lo,
device_info->latency_hi);
}
int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection * collection)
int
cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
cubeb_device_collection * collection)
{
int rv;
if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
@ -645,8 +648,9 @@ int cubeb_enumerate_devices(cubeb * context,
return rv;
}
int cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
int
cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
int r;
@ -668,23 +672,26 @@ int cubeb_device_collection_destroy(cubeb * context,
return r;
}
int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr)
int
cubeb_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback, void * user_ptr)
{
if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
if (context == NULL ||
(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
return CUBEB_ERROR_INVALID_PARAMETER;
if (!context->ops->register_device_collection_changed) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
return context->ops->register_device_collection_changed(context, devtype,
callback, user_ptr);
}
int cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback)
int
cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback)
{
if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
return CUBEB_ERROR_INVALID_FORMAT;
@ -712,4 +719,3 @@ int cubeb_set_log_callback(cubeb_log_level log_level,
return CUBEB_OK;
}

View file

@ -24,7 +24,7 @@
#ifdef DISABLE_LIBAAUDIO_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) cubeb_##x
#define WRAP(x) (*cubeb_##x)
#define LIBAAUDIO_API_VISIT(X) \
X(AAudio_convertResultToText) \
X(AAudio_convertStreamStateToText) \
@ -78,6 +78,7 @@
// X(AAudioStream_getContentType) \
// X(AAudioStream_getInputPreset) \
// X(AAudioStream_getSessionId) \
// END: not needed or added later on
#define MAKE_TYPEDEF(x) static decltype(x) * cubeb_##x;
LIBAAUDIO_API_VISIT(MAKE_TYPEDEF)
@ -934,7 +935,8 @@ aaudio_stream_init_impl(cubeb_stream * stm, cubeb_devid input_device,
stm->resampler = cubeb_resampler_create(
stm, input_stream_params ? &in_params : NULL,
output_stream_params ? &out_params : NULL, target_sample_rate,
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT);
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT,
CUBEB_RESAMPLER_RECLOCK_NONE);
if (!stm->resampler) {
LOG("Failed to create resampler");
@ -997,8 +999,10 @@ aaudio_stream_init(cubeb * ctx, cubeb_stream ** stream,
stm->user_ptr = user_ptr;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->voice_input = input_stream_params && !!(input_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
stm->voice_output = output_stream_params && !!(output_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
stm->voice_input = input_stream_params &&
!!(input_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
stm->voice_output = output_stream_params &&
!!(output_stream_params->prefs & CUBEB_STREAM_PREF_VOICE);
stm->previous_clock = 0;
LOG("cubeb stream prefs: voice_input: %s voice_output: %s",
@ -1450,7 +1454,6 @@ const static struct cubeb_ops aaudio_ops = {
/*.stream_destroy =*/aaudio_stream_destroy,
/*.stream_start =*/aaudio_stream_start,
/*.stream_stop =*/aaudio_stream_stop,
/*.stream_reset_default_device =*/NULL,
/*.stream_get_position =*/aaudio_stream_get_position,
/*.stream_get_latency =*/aaudio_stream_get_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) \
{ \
WRAP(x) = (decltype(WRAP(x)))(dlsym(libaaudio, #x)); \
cubeb_##x = (decltype(x) *)(dlsym(libaaudio, #x)); \
if (!WRAP(x)) { \
LOG("AAudio: Failed to load %s", #x); \
dlclose(libaaudio); \

View file

@ -8,56 +8,56 @@
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#define _XOPEN_SOURCE 500
#include <pthread.h>
#include <sys/time.h>
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include <alsa/asoundlib.h>
#include <assert.h>
#include <dlfcn.h>
#include <limits.h>
#include <poll.h>
#include <pthread.h>
#include <sys/time.h>
#include <unistd.h>
#include <dlfcn.h>
#include <alsa/asoundlib.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#ifdef DISABLE_LIBASOUND_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) cubeb_##x
#define LIBASOUND_API_VISIT(X) \
X(snd_config) \
X(snd_config_add) \
X(snd_config_copy) \
X(snd_config_delete) \
X(snd_config_get_id) \
X(snd_config_get_string) \
X(snd_config_imake_integer) \
X(snd_config_search) \
X(snd_config_search_definition) \
X(snd_lib_error_set_handler) \
X(snd_pcm_avail_update) \
X(snd_pcm_close) \
X(snd_pcm_delay) \
X(snd_pcm_drain) \
X(snd_pcm_frames_to_bytes) \
X(snd_pcm_get_params) \
X(snd_pcm_hw_params_any) \
X(snd_pcm_hw_params_get_channels_max) \
X(snd_pcm_hw_params_get_rate) \
X(snd_pcm_hw_params_set_rate_near) \
X(snd_pcm_hw_params_sizeof) \
X(snd_pcm_nonblock) \
X(snd_pcm_open) \
X(snd_pcm_open_lconf) \
X(snd_pcm_pause) \
X(snd_pcm_poll_descriptors) \
X(snd_pcm_poll_descriptors_count) \
X(snd_pcm_poll_descriptors_revents) \
X(snd_pcm_readi) \
X(snd_pcm_recover) \
X(snd_pcm_set_params) \
X(snd_pcm_start) \
X(snd_pcm_state) \
X(snd_pcm_writei) \
#define WRAP(x) (*cubeb_##x)
#define LIBASOUND_API_VISIT(X) \
X(snd_config) \
X(snd_config_add) \
X(snd_config_copy) \
X(snd_config_delete) \
X(snd_config_get_id) \
X(snd_config_get_string) \
X(snd_config_imake_integer) \
X(snd_config_search) \
X(snd_config_search_definition) \
X(snd_lib_error_set_handler) \
X(snd_pcm_avail_update) \
X(snd_pcm_close) \
X(snd_pcm_delay) \
X(snd_pcm_drain) \
X(snd_pcm_frames_to_bytes) \
X(snd_pcm_get_params) \
X(snd_pcm_hw_params_any) \
X(snd_pcm_hw_params_get_channels_max) \
X(snd_pcm_hw_params_get_rate) \
X(snd_pcm_hw_params_set_rate_near) \
X(snd_pcm_hw_params_sizeof) \
X(snd_pcm_nonblock) \
X(snd_pcm_open) \
X(snd_pcm_open_lconf) \
X(snd_pcm_pause) \
X(snd_pcm_poll_descriptors) \
X(snd_pcm_poll_descriptors_count) \
X(snd_pcm_poll_descriptors_revents) \
X(snd_pcm_readi) \
X(snd_pcm_recover) \
X(snd_pcm_set_params) \
X(snd_pcm_start) \
X(snd_pcm_state) \
X(snd_pcm_writei)
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBASOUND_API_VISIT(MAKE_TYPEDEF);
@ -101,7 +101,8 @@ struct cubeb {
int shutdown;
/* Control pipe for forcing poll to wake and rebuild fds or recalculate the timeout. */
/* Control pipe for forcing poll to wake and rebuild fds or recalculate the
* timeout. */
int control_fd_read;
int control_fd_write;
@ -116,13 +117,7 @@ struct cubeb {
int is_pa;
};
enum stream_state {
INACTIVE,
RUNNING,
DRAINING,
PROCESSING,
ERROR
};
enum stream_state { INACTIVE, RUNNING, DRAINING, PROCESSING, ERROR };
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
@ -146,7 +141,8 @@ struct cubeb_stream {
enum stream_state state;
struct pollfd * saved_fds; /* A copy of the pollfds passed in at init time. */
struct pollfd * fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
struct pollfd *
fds; /* Pointer to this waitable's pollfds within struct cubeb's fds. */
nfds_t nfds;
struct timeval drain_timeout;
@ -294,8 +290,10 @@ set_timeout(struct timeval * timeout, unsigned int ms)
static void
stream_buffer_decrement(cubeb_stream * stm, long count)
{
char * bufremains = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
memmove(stm->buffer, bufremains, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
char * bufremains =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, count);
memmove(stm->buffer, bufremains,
WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes - count));
stm->bufframes -= count;
}
@ -327,7 +325,8 @@ alsa_process_stream(cubeb_stream * stm)
/* Call _poll_descriptors_revents() even if we don't use it
to let underlying plugins clear null events. Otherwise poll()
may wake up again and again, producing unnecessary CPU usage. */
WRAP(snd_pcm_poll_descriptors_revents)(stm->pcm, stm->fds, stm->nfds, &revents);
WRAP(snd_pcm_poll_descriptors_revents)
(stm->pcm, stm->fds, stm->nfds, &revents);
avail = WRAP(snd_pcm_avail_update)(stm->pcm);
@ -337,8 +336,9 @@ alsa_process_stream(cubeb_stream * stm)
return RUNNING;
}
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. */
if ((unsigned int) avail > stm->buffer_size) {
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
*/
if ((unsigned int)avail > stm->buffer_size) {
avail = stm->buffer_size;
}
@ -352,7 +352,7 @@ alsa_process_stream(cubeb_stream * stm)
// TODO: should it be marked as DRAINING?
}
got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer+stm->bufframes, avail);
got = WRAP(snd_pcm_readi)(stm->pcm, stm->buffer + stm->bufframes, avail);
if (got < 0) {
avail = got; // the error handler below will recover us
@ -366,18 +366,24 @@ alsa_process_stream(cubeb_stream * stm)
/* Capture: Pass read frames to callback function */
if (stm->stream_type == SND_PCM_STREAM_CAPTURE && stm->bufframes > 0 &&
(!stm->other_stream || stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
(!stm->other_stream ||
stm->other_stream->bufframes < stm->other_stream->buffer_size)) {
snd_pcm_sframes_t wrote = stm->bufframes;
struct cubeb_stream * mainstm = stm->other_stream ? stm->other_stream : stm;
void * other_buffer = stm->other_stream ? stm->other_stream->buffer + stm->other_stream->bufframes : NULL;
void * other_buffer = stm->other_stream ? stm->other_stream->buffer +
stm->other_stream->bufframes
: NULL;
/* Correct write size to the other stream available space */
if (stm->other_stream && wrote > (snd_pcm_sframes_t) (stm->other_stream->buffer_size - stm->other_stream->bufframes)) {
if (stm->other_stream &&
wrote > (snd_pcm_sframes_t)(stm->other_stream->buffer_size -
stm->other_stream->bufframes)) {
wrote = stm->other_stream->buffer_size - stm->other_stream->bufframes;
}
pthread_mutex_unlock(&stm->mutex);
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer, other_buffer, wrote);
wrote = stm->data_callback(mainstm, stm->user_ptr, stm->buffer,
other_buffer, wrote);
pthread_mutex_lock(&stm->mutex);
if (wrote < 0) {
@ -392,14 +398,17 @@ alsa_process_stream(cubeb_stream * stm)
}
/* Playback: Don't have enough data? Let's ask for more. */
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes &&
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
avail > (snd_pcm_sframes_t)stm->bufframes &&
(!stm->other_stream || stm->other_stream->bufframes > 0)) {
long got = avail - stm->bufframes;
void * other_buffer = stm->other_stream ? stm->other_stream->buffer : NULL;
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
char * buftail =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
/* Correct read size to the other stream available frames */
if (stm->other_stream && got > (snd_pcm_sframes_t) stm->other_stream->bufframes) {
if (stm->other_stream &&
got > (snd_pcm_sframes_t)stm->other_stream->bufframes) {
got = stm->other_stream->bufframes;
}
@ -419,11 +428,13 @@ alsa_process_stream(cubeb_stream * stm)
}
/* Playback: Still don't have enough data? Add some silence. */
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK && avail > (snd_pcm_sframes_t) stm->bufframes) {
if (stm->stream_type == SND_PCM_STREAM_PLAYBACK &&
avail > (snd_pcm_sframes_t)stm->bufframes) {
long drain_frames = avail - stm->bufframes;
double drain_time = (double) drain_frames / stm->params.rate;
double drain_time = (double)drain_frames / stm->params.rate;
char * buftail = stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
char * buftail =
stm->buffer + WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->bufframes);
memset(buftail, 0, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, drain_frames));
stm->bufframes = avail;
@ -440,12 +451,12 @@ alsa_process_stream(cubeb_stream * stm)
snd_pcm_sframes_t wrote;
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
float * b = (float *) stm->buffer;
float * b = (float *)stm->buffer;
for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
b[i] *= stm->volume;
}
} else {
short * b = (short *) stm->buffer;
short * b = (short *)stm->buffer;
for (uint32_t i = 0; i < avail * stm->params.channels; i++) {
b[i] *= stm->volume;
}
@ -467,8 +478,7 @@ alsa_process_stream(cubeb_stream * stm)
avail = WRAP(snd_pcm_recover)(stm->pcm, avail, 0);
/* Capture pcm must be started after initial setup/recover */
if (avail >= 0 &&
stm->stream_type == SND_PCM_STREAM_CAPTURE &&
if (avail >= 0 && stm->stream_type == SND_PCM_STREAM_CAPTURE &&
WRAP(snd_pcm_state)(stm->pcm) == SND_PCM_STATE_PREPARED) {
avail = WRAP(snd_pcm_start)(stm->pcm);
}
@ -533,7 +543,8 @@ alsa_run(cubeb * ctx)
stm = ctx->streams[i];
/* We can't use snd_pcm_poll_descriptors_revents here because of
https://github.com/kinetiknz/cubeb/issues/135. */
if (stm && stm->state == RUNNING && stm->fds && any_revents(stm->fds, stm->nfds)) {
if (stm && stm->state == RUNNING && stm->fds &&
any_revents(stm->fds, stm->nfds)) {
alsa_set_stream_state(stm, PROCESSING);
pthread_mutex_unlock(&ctx->mutex);
state = alsa_process_stream(stm);
@ -548,7 +559,8 @@ alsa_run(cubeb * ctx)
if (stm->state == DRAINING && ms_since(&stm->drain_timeout) >= 0) {
alsa_set_stream_state(stm, INACTIVE);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
} else if (stm->state == RUNNING && ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
} else if (stm->state == RUNNING &&
ms_since(&stm->last_activity) > CUBEB_WATCHDOG_MS) {
alsa_set_stream_state(stm, ERROR);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
}
@ -593,7 +605,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
r = WRAP(snd_config_get_string)(slave_pcm, &string);
if (r >= 0) {
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string, &slave_def);
r = WRAP(snd_config_search_definition)(lconf, "pcm_slave", string,
&slave_def);
if (r < 0) {
return NULL;
}
@ -611,7 +624,7 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
}
r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
if (r < 0 || r > (int) sizeof(node_name)) {
if (r < 0 || r > (int)sizeof(node_name)) {
break;
}
r = WRAP(snd_config_search)(lconf, node_name, &pcm);
@ -633,7 +646,8 @@ get_slave_pcm_node(snd_config_t * lconf, snd_config_t * root_pcm)
higher than requested latency, but the plugin does not update its (and
ALSA's) internal state to reflect that, leading to an immediate underrun
situation. Inspired by WINE's make_handle_underrun_config.
Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05 */
Reference: http://mailman.alsa-project.org/pipermail/alsa-devel/2012-July/05
*/
static snd_config_t *
init_local_config_with_workaround(char const * pcm_name)
{
@ -646,11 +660,11 @@ init_local_config_with_workaround(char const * pcm_name)
lconf = NULL;
if (*WRAP(snd_config) == NULL) {
if (WRAP(snd_config) == NULL) {
return NULL;
}
r = WRAP(snd_config_copy)(&lconf, *WRAP(snd_config));
r = WRAP(snd_config_copy)(&lconf, WRAP(snd_config));
if (r < 0) {
return NULL;
}
@ -667,7 +681,7 @@ init_local_config_with_workaround(char const * pcm_name)
}
r = snprintf(node_name, sizeof(node_name), "pcm.%s", string);
if (r < 0 || r > (int) sizeof(node_name)) {
if (r < 0 || r > (int)sizeof(node_name)) {
break;
}
r = WRAP(snd_config_search)(lconf, node_name, &pcm_node);
@ -675,12 +689,14 @@ init_local_config_with_workaround(char const * pcm_name)
break;
}
/* If this PCM has a slave, walk the slave configurations until we reach the bottom. */
/* If this PCM has a slave, walk the slave configurations until we reach the
* bottom. */
while ((node = get_slave_pcm_node(lconf, pcm_node)) != NULL) {
pcm_node = node;
}
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio plugin. */
/* Fetch the PCM node's type, and bail out if it's not the PulseAudio
* plugin. */
r = WRAP(snd_config_search)(pcm_node, "type", &node);
if (r < 0) {
break;
@ -722,13 +738,15 @@ init_local_config_with_workaround(char const * pcm_name)
}
static int
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name, snd_pcm_stream_t stream, snd_config_t * local_config)
alsa_locked_pcm_open(snd_pcm_t ** pcm, char const * pcm_name,
snd_pcm_stream_t stream, snd_config_t * local_config)
{
int r;
pthread_mutex_lock(&cubeb_alsa_mutex);
if (local_config) {
r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK, local_config);
r = WRAP(snd_pcm_open_lconf)(pcm, pcm_name, stream, SND_PCM_NONBLOCK,
local_config);
} else {
r = WRAP(snd_pcm_open)(pcm, pcm_name, stream, SND_PCM_NONBLOCK);
}
@ -819,12 +837,13 @@ alsa_init(cubeb ** context, char const * context_name)
}
}
#define LOAD(x) { \
cubeb_##x = dlsym(libasound, #x); \
if (!cubeb_##x) { \
dlclose(libasound); \
return CUBEB_ERROR; \
} \
#define LOAD(x) \
{ \
cubeb_##x = dlsym(libasound, #x); \
if (!cubeb_##x) { \
dlclose(libasound); \
return CUBEB_ERROR; \
} \
}
LIBASOUND_API_VISIT(LOAD);
@ -876,7 +895,8 @@ alsa_init(cubeb ** context, char const * context_name)
/* Open a dummy PCM to force the configuration space to be evaluated so that
init_local_config_with_workaround can find and modify the default node. */
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, NULL);
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
NULL);
if (r >= 0) {
alsa_locked_pcm_close(dummy);
}
@ -886,7 +906,8 @@ alsa_init(cubeb ** context, char const * context_name)
pthread_mutex_unlock(&cubeb_alsa_mutex);
if (ctx->local_config) {
ctx->is_pa = 1;
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, ctx->local_config);
r = alsa_locked_pcm_open(&dummy, CUBEB_ALSA_PCM_NAME,
SND_PCM_STREAM_PLAYBACK, ctx->local_config);
/* If we got a local_config, we found a PA PCM. If opening a PCM with that
config fails with EINVAL, the PA PCM is too old for this workaround. */
if (r == -EINVAL) {
@ -944,17 +965,17 @@ alsa_destroy(cubeb * ctx)
free(ctx);
}
static void alsa_stream_destroy(cubeb_stream * stm);
static void
alsa_stream_destroy(cubeb_stream * stm);
static int
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
snd_pcm_stream_t stream_type,
alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream,
char const * stream_name, snd_pcm_stream_t stream_type,
cubeb_devid deviceid,
cubeb_stream_params * stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
cubeb_state_callback state_callback, void * user_ptr)
{
(void)stream_name;
cubeb_stream * stm;
@ -962,7 +983,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
snd_pcm_format_t format;
snd_pcm_uframes_t period_size;
int latency_us = 0;
char const * pcm_name = deviceid ? (char const *) deviceid : CUBEB_ALSA_PCM_NAME;
char const * pcm_name =
deviceid ? (char const *)deviceid : CUBEB_ALSA_PCM_NAME;
assert(ctx && stream);
@ -1018,7 +1040,8 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
r = pthread_cond_init(&stm->cond, NULL);
assert(r == 0);
r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type, ctx->local_config);
r = alsa_locked_pcm_open(&stm->pcm, pcm_name, stm->stream_type,
ctx->local_config);
if (r < 0) {
alsa_stream_destroy(stm);
return CUBEB_ERROR;
@ -1034,12 +1057,12 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
Only resort to this hack if the handle_underrun workaround failed. */
if (!ctx->local_config && ctx->is_pa) {
const int min_latency = 5e5;
latency_us = latency_us < min_latency ? min_latency: latency_us;
latency_us = latency_us < min_latency ? min_latency : latency_us;
}
r = WRAP(snd_pcm_set_params)(stm->pcm, format, SND_PCM_ACCESS_RW_INTERLEAVED,
stm->params.channels, stm->params.rate, 1,
latency_us);
stm->params.channels, stm->params.rate, 1,
latency_us);
if (r < 0) {
alsa_stream_destroy(stm);
return CUBEB_ERROR_INVALID_FORMAT;
@ -1048,9 +1071,11 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
r = WRAP(snd_pcm_get_params)(stm->pcm, &stm->buffer_size, &period_size);
assert(r == 0);
/* Double internal buffer size to have enough space when waiting for the other side of duplex connection */
/* Double internal buffer size to have enough space when waiting for the other
* side of duplex connection */
stm->buffer_size *= 2;
stm->buffer = calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
stm->buffer =
calloc(1, WRAP(snd_pcm_frames_to_bytes)(stm->pcm, stm->buffer_size));
assert(stm->buffer);
stm->nfds = WRAP(snd_pcm_poll_descriptors_count)(stm->pcm);
@ -1059,7 +1084,7 @@ alsa_stream_init_single(cubeb * ctx, cubeb_stream ** stream, char const * stream
stm->saved_fds = calloc(stm->nfds, sizeof(struct pollfd));
assert(stm->saved_fds);
r = WRAP(snd_pcm_poll_descriptors)(stm->pcm, stm->saved_fds, stm->nfds);
assert((nfds_t) r == stm->nfds);
assert((nfds_t)r == stm->nfds);
if (alsa_register_stream(ctx, stm) != 0) {
alsa_stream_destroy(stm);
@ -1077,22 +1102,23 @@ alsa_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
unsigned int latency_frames, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
int result = CUBEB_OK;
cubeb_stream * instm = NULL, * outstm = NULL;
cubeb_stream *instm = NULL, *outstm = NULL;
if (result == CUBEB_OK && input_stream_params) {
result = alsa_stream_init_single(ctx, &instm, stream_name, SND_PCM_STREAM_CAPTURE,
input_device, input_stream_params, latency_frames,
result = alsa_stream_init_single(ctx, &instm, stream_name,
SND_PCM_STREAM_CAPTURE, input_device,
input_stream_params, latency_frames,
data_callback, state_callback, user_ptr);
}
if (result == CUBEB_OK && output_stream_params) {
result = alsa_stream_init_single(ctx, &outstm, stream_name, SND_PCM_STREAM_PLAYBACK,
output_device, output_stream_params, latency_frames,
result = alsa_stream_init_single(ctx, &outstm, stream_name,
SND_PCM_STREAM_PLAYBACK, output_device,
output_stream_params, latency_frames,
data_callback, state_callback, user_ptr);
}
@ -1116,8 +1142,7 @@ alsa_stream_destroy(cubeb_stream * stm)
int r;
cubeb * ctx;
assert(stm && (stm->state == INACTIVE ||
stm->state == ERROR ||
assert(stm && (stm->state == INACTIVE || stm->state == ERROR ||
stm->state == DRAINING));
ctx = stm->context;
@ -1159,7 +1184,7 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
int r;
cubeb_stream * stm;
snd_pcm_hw_params_t* hw_params;
snd_pcm_hw_params_t * hw_params;
cubeb_stream_params params;
params.rate = 44100;
params.format = CUBEB_SAMPLE_FLOAT32NE;
@ -1169,7 +1194,8 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
assert(ctx);
r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 100, NULL, NULL, NULL);
r = alsa_stream_init(ctx, &stm, "", NULL, NULL, NULL, &params, 100, NULL,
NULL, NULL);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}
@ -1192,7 +1218,8 @@ alsa_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
}
static int
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
(void)ctx;
int r, dir;
snd_pcm_t * pcm;
@ -1202,7 +1229,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
/* get a pcm, disabling resampling, so we get a rate the
* hardware/dmix/pulse/etc. supports. */
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK, SND_PCM_NO_AUTO_RESAMPLE);
r = WRAP(snd_pcm_open)(&pcm, CUBEB_ALSA_PCM_NAME, SND_PCM_STREAM_PLAYBACK,
SND_PCM_NO_AUTO_RESAMPLE);
if (r < 0) {
return CUBEB_ERROR;
}
@ -1235,7 +1263,8 @@ alsa_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate) {
}
static int
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
alsa_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency_frames)
{
(void)ctx;
/* 40ms is found to be an acceptable minimum, even on a super low-end
@ -1331,7 +1360,7 @@ alsa_stream_get_position(cubeb_stream * stm, uint64_t * position)
assert(delay >= 0);
*position = 0;
if (stm->stream_position >= (snd_pcm_uframes_t) delay) {
if (stm->stream_position >= (snd_pcm_uframes_t)delay) {
*position = stm->stream_position - delay;
}
@ -1346,7 +1375,8 @@ alsa_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
snd_pcm_sframes_t delay;
/* This function returns the delay in frames until a frame written using
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways. */
snd_pcm_writei is sent to the DAC. The DAC delay should be < 1ms anyways.
*/
if (WRAP(snd_pcm_delay)(stm->pcm, &delay)) {
return CUBEB_ERROR;
}
@ -1371,7 +1401,7 @@ static int
alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
cubeb_device_info* device = NULL;
cubeb_device_info * device = NULL;
if (!context)
return CUBEB_ERROR;
@ -1390,13 +1420,13 @@ alsa_enumerate_devices(cubeb * context, cubeb_device_type type,
}
char const * a_name = "default";
device = (cubeb_device_info *) calloc(1, sizeof(cubeb_device_info));
device = (cubeb_device_info *)calloc(1, sizeof(cubeb_device_info));
assert(device);
if (!device)
return CUBEB_ERROR;
device->device_id = a_name;
device->devid = (cubeb_devid) device->device_id;
device->devid = (cubeb_devid)device->device_id;
device->friendly_name = a_name;
device->group_id = a_name;
device->vendor_name = a_name;
@ -1423,32 +1453,30 @@ alsa_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
assert(collection->count == 1);
(void) context;
(void)context;
free(collection->device);
return CUBEB_OK;
}
static struct cubeb_ops const alsa_ops = {
.init = alsa_init,
.get_backend_id = alsa_get_backend_id,
.get_max_channel_count = alsa_get_max_channel_count,
.get_min_latency = alsa_get_min_latency,
.get_preferred_sample_rate = alsa_get_preferred_sample_rate,
.enumerate_devices = alsa_enumerate_devices,
.device_collection_destroy = alsa_device_collection_destroy,
.destroy = alsa_destroy,
.stream_init = alsa_stream_init,
.stream_destroy = alsa_stream_destroy,
.stream_start = alsa_stream_start,
.stream_stop = alsa_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = alsa_stream_get_position,
.stream_get_latency = alsa_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = alsa_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.init = alsa_init,
.get_backend_id = alsa_get_backend_id,
.get_max_channel_count = alsa_get_max_channel_count,
.get_min_latency = alsa_get_min_latency,
.get_preferred_sample_rate = alsa_get_preferred_sample_rate,
.enumerate_devices = alsa_enumerate_devices,
.device_collection_destroy = alsa_device_collection_destroy,
.destroy = alsa_destroy,
.stream_init = alsa_stream_init,
.stream_destroy = alsa_stream_destroy,
.stream_start = alsa_stream_start,
.stream_stop = alsa_stream_stop,
.stream_get_position = alsa_stream_get_position,
.stream_get_latency = alsa_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = alsa_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL};

View file

@ -16,8 +16,7 @@
extern "C" {
#endif
typedef struct
{
typedef struct {
void ** buf;
size_t num;
size_t writePos;
@ -25,10 +24,11 @@ typedef struct
pthread_mutex_t mutex;
} array_queue;
array_queue * array_queue_create(size_t num)
array_queue *
array_queue_create(size_t num)
{
assert(num != 0);
array_queue * new_queue = (array_queue*)calloc(1, sizeof(array_queue));
array_queue * new_queue = (array_queue *)calloc(1, sizeof(array_queue));
new_queue->buf = (void **)calloc(1, sizeof(void *) * num);
new_queue->readPos = 0;
new_queue->writePos = 0;
@ -39,7 +39,8 @@ array_queue * array_queue_create(size_t num)
return new_queue;
}
void array_queue_destroy(array_queue * aq)
void
array_queue_destroy(array_queue * aq)
{
assert(aq);
@ -48,14 +49,14 @@ void array_queue_destroy(array_queue * aq)
free(aq);
}
int array_queue_push(array_queue * aq, void * item)
int
array_queue_push(array_queue * aq, void * item)
{
assert(item);
pthread_mutex_lock(&aq->mutex);
int ret = -1;
if(aq->buf[aq->writePos % aq->num] == NULL)
{
if (aq->buf[aq->writePos % aq->num] == NULL) {
aq->buf[aq->writePos % aq->num] = item;
aq->writePos = (aq->writePos + 1) % aq->num;
ret = 0;
@ -65,12 +66,12 @@ int array_queue_push(array_queue * aq, void * item)
return ret;
}
void* array_queue_pop(array_queue * aq)
void *
array_queue_pop(array_queue * aq)
{
pthread_mutex_lock(&aq->mutex);
void * value = aq->buf[aq->readPos % aq->num];
if(value)
{
if (value) {
aq->buf[aq->readPos % aq->num] = NULL;
aq->readPos = (aq->readPos + 1) % aq->num;
}
@ -78,7 +79,8 @@ void* array_queue_pop(array_queue * aq)
return value;
}
size_t array_queue_get_size(array_queue * aq)
size_t
array_queue_get_size(array_queue * aq)
{
pthread_mutex_lock(&aq->mutex);
ssize_t r = aq->writePos - aq->readPos;
@ -94,4 +96,4 @@ size_t array_queue_get_size(array_queue * aq)
}
#endif
#endif //CUBE_ARRAY_QUEUE_H
#endif // CUBE_ARRAY_QUEUE_H

View file

@ -16,7 +16,8 @@
* export a function or macro called XASSERT that aborts the program.
*/
#define XASSERT(expr) do { \
#define XASSERT(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
abort(); \

View file

@ -8,20 +8,21 @@
#if !defined(NDEBUG)
#define NDEBUG
#endif
#include <android/log.h>
#include <assert.h>
#include <dlfcn.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <dlfcn.h>
#include <android/log.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include "android/audiotrack_definitions.h"
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#ifndef ALOG
#if defined(DEBUG) || defined(FORCE_ALOG)
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args)
#define ALOG(args...) \
__android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb", ##args)
#else
#define ALOG(args...)
#endif
@ -35,37 +36,44 @@
* call dlsym to get the symbol |mangled_name|, handle the error and store the
* pointer in |pointer|. Because depending on Android version, we want different
* symbols, not finding a symbol is not an error. */
#define DLSYM_DLERROR(mangled_name, pointer, lib) \
do { \
pointer = dlsym(lib, mangled_name); \
if (!pointer) { \
ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \
} else { \
ALOG("%stm: OK", mangled_name); \
} \
} while(0);
#define DLSYM_DLERROR(mangled_name, pointer, lib) \
do { \
pointer = dlsym(lib, mangled_name); \
if (!pointer) { \
ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \
} else { \
ALOG("%stm: OK", mangled_name); \
} \
} while (0);
static struct cubeb_ops const audiotrack_ops;
void audiotrack_destroy(cubeb * context);
void audiotrack_stream_destroy(cubeb_stream * stream);
void
audiotrack_destroy(cubeb * context);
void
audiotrack_stream_destroy(cubeb_stream * stream);
struct AudioTrack {
/* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */
/* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate);
/* only available on ICS and later. The second int paramter is in fact of type
* audio_stream_type_t. */
/* static */ status_t (*get_min_frame_count)(int * frame_count,
int stream_type, uint32_t rate);
/* if we have a recent ctor, but can't find the above symbol, we
* can get the minimum frame count with this signature, and we are
* running gingerbread. */
/* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate);
void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int);
void* (*dtor)(void* instance);
void (*start)(void* instance);
void (*pause)(void* instance);
uint32_t (*latency)(void* instance);
status_t (*check)(void* instance);
status_t (*get_position)(void* instance, uint32_t* position);
/* static */ int (*get_output_samplingrate)(int* samplerate, int stream);
status_t (*set_marker_position)(void* instance, unsigned int);
status_t (*set_volume)(void* instance, float left, float right);
/* static */ status_t (*get_min_frame_count_gingerbread)(int * frame_count,
int stream_type,
uint32_t rate);
void * (*ctor)(void * instance, int, unsigned int, int, int, int,
unsigned int, void (*)(int, void *, void *), void *, int, int);
void * (*dtor)(void * instance);
void (*start)(void * instance);
void (*pause)(void * instance);
uint32_t (*latency)(void * instance);
status_t (*check)(void * instance);
status_t (*get_position)(void * instance, uint32_t * position);
/* static */ int (*get_output_samplingrate)(int * samplerate, int stream);
status_t (*set_marker_position)(void * instance, unsigned int);
status_t (*set_volume)(void * instance, float left, float right);
};
struct cubeb {
@ -89,19 +97,20 @@ struct cubeb_stream {
};
static void
audiotrack_refill(int event, void* user, void* info)
audiotrack_refill(int event, void * user, void * info)
{
cubeb_stream * stream = user;
switch (event) {
case EVENT_MORE_DATA: {
long got = 0;
struct Buffer * b = (struct Buffer*)info;
struct Buffer * b = (struct Buffer *)info;
if (stream->draining) {
return;
}
got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount);
got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw,
b->frameCount);
stream->written += got;
@ -109,7 +118,8 @@ audiotrack_refill(int event, void* user, void* info)
stream->draining = 1;
/* set a marker so we are notified when the are done draining, that is,
* when every frame has been played by android. */
stream->context->klass.set_marker_position(stream->instance, stream->written);
stream->context->klass.set_marker_position(stream->instance,
stream->written);
}
break;
@ -125,7 +135,9 @@ audiotrack_refill(int event, void* user, void* info)
stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
break;
case EVENT_NEW_POS:
assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack.");
assert(
0 &&
"We don't support the setPositionUpdatePeriod feature of audiotrack.");
break;
case EVENT_BUFFER_END:
assert(0 && "Should not happen.");
@ -142,14 +154,17 @@ audiotrack_version_is_gingerbread(cubeb * ctx)
}
int
audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count)
audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params,
int * min_frame_count)
{
status_t status;
/* Recent Android have a getMinFrameCount method. */
if (!audiotrack_version_is_gingerbread(ctx)) {
status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
status = ctx->klass.get_min_frame_count(
min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
} else {
status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
status = ctx->klass.get_min_frame_count_gingerbread(
min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
}
if (status != 0) {
ALOG("error getting the min frame count");
@ -162,7 +177,7 @@ int
audiotrack_init(cubeb ** context, char const * context_name)
{
cubeb * ctx;
struct AudioTrack* c;
struct AudioTrack * c;
assert(context);
*context = NULL;
@ -182,34 +197,45 @@ audiotrack_init(cubeb ** context, char const * context_name)
}
/* Recent Android first, then Gingerbread. */
DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii",
ctx->klass.ctor, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library);
DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library);
DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library);
DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency,
ctx->library);
DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check,
ctx->library);
DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library);
DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii",
ctx->klass.get_output_samplingrate, ctx->library);
/* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library);
/* |getMinFrameCount| is available on gingerbread and ICS with different
* signatures. */
DLSYM_DLERROR(
"_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj",
ctx->klass.get_min_frame_count, ctx->library);
if (!ctx->klass.get_min_frame_count) {
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij",
ctx->klass.get_min_frame_count_gingerbread, ctx->library);
}
DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start,
ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause,
ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj",
ctx->klass.get_position, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj",
ctx->klass.set_marker_position, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume,
ctx->library);
/* check that we have a combination of symbol that makes sense */
c = &ctx->klass;
if(!(c->ctor &&
c->dtor && c->latency && c->check &&
/* at least one way to get the minimum frame count to request. */
(c->get_min_frame_count ||
c->get_min_frame_count_gingerbread) &&
c->start && c->pause && c->get_position && c->set_marker_position)) {
if (!(c->ctor && c->dtor && c->latency && c->check &&
/* at least one way to get the minimum frame count to request. */
(c->get_min_frame_count || c->get_min_frame_count_gingerbread) &&
c->start && c->pause && c->get_position && c->set_marker_position)) {
ALOG("Could not find all the symbols we need.");
audiotrack_destroy(ctx);
return CUBEB_ERROR;
@ -234,14 +260,16 @@ audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
assert(ctx && max_channels);
/* The android mixer handles up to two channels, see
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67
*/
*max_channels = 2;
return CUBEB_OK;
}
static int
audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency_ms)
{
/* We always use the lowest latency possible when using this backend (see
* audiotrack_stream_init), so this value is not going to be used. */
@ -276,15 +304,13 @@ audiotrack_destroy(cubeb * context)
}
int
audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
unsigned int latency, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
cubeb_stream * stm;
int32_t channels;
@ -303,7 +329,8 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
return CUBEB_ERROR_INVALID_FORMAT;
}
if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) {
if (audiotrack_get_min_frame_count(ctx, output_stream_params,
(int *)&min_frame_count)) {
return CUBEB_ERROR;
}
@ -317,21 +344,25 @@ audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_
stm->params = *output_stream_params;
stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1);
(*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad;
(*(uint32_t *)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) =
0xbaadbaad;
assert(stm->instance && "cubeb: EOM");
/* gingerbread uses old channel layout enum */
if (audiotrack_version_is_gingerbread(ctx)) {
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy;
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy
: AUDIO_CHANNEL_OUT_MONO_Legacy;
} else {
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS;
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS
: AUDIO_CHANNEL_OUT_MONO_ICS;
}
ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate,
AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
audiotrack_refill, stm, 0, 0);
assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad);
assert((*(uint32_t *)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE -
4)) == 0xbaadbaad);
if (ctx->klass.check(stm->instance)) {
ALOG("stream not initialized properly.");
@ -418,26 +449,24 @@ audiotrack_stream_set_volume(cubeb_stream * stream, float volume)
}
static struct cubeb_ops const audiotrack_ops = {
.init = audiotrack_init,
.get_backend_id = audiotrack_get_backend_id,
.get_max_channel_count = audiotrack_get_max_channel_count,
.get_min_latency = audiotrack_get_min_latency,
.get_preferred_sample_rate = audiotrack_get_preferred_sample_rate,
.enumerate_devices = NULL,
.device_collection_destroy = NULL,
.destroy = audiotrack_destroy,
.stream_init = audiotrack_stream_init,
.stream_destroy = audiotrack_stream_destroy,
.stream_start = audiotrack_stream_start,
.stream_stop = audiotrack_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = audiotrack_stream_get_position,
.stream_get_latency = audiotrack_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = audiotrack_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.init = audiotrack_init,
.get_backend_id = audiotrack_get_backend_id,
.get_max_channel_count = audiotrack_get_max_channel_count,
.get_min_latency = audiotrack_get_min_latency,
.get_preferred_sample_rate = audiotrack_get_preferred_sample_rate,
.enumerate_devices = NULL,
.device_collection_destroy = NULL,
.destroy = audiotrack_destroy,
.stream_init = audiotrack_stream_init,
.stream_destroy = audiotrack_stream_destroy,
.stream_start = audiotrack_stream_start,
.stream_stop = audiotrack_stream_stop,
.stream_get_position = audiotrack_stream_get_position,
.stream_get_latency = audiotrack_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = audiotrack_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL};

File diff suppressed because it is too large Load diff

View file

@ -8,53 +8,59 @@
*/
#define _DEFAULT_SOURCE
#define _BSD_SOURCE
#if !defined(__FreeBSD__) && !defined(__APPLE__)
#ifndef __FreeBSD__
#define _POSIX_SOURCE
#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/cubeb.h"
#include "cubeb_resampler.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/statistics.h>
#define JACK_API_VISIT(X) \
X(jack_activate) \
X(jack_client_close) \
X(jack_client_open) \
X(jack_connect) \
X(jack_free) \
X(jack_get_ports) \
X(jack_get_sample_rate) \
X(jack_get_xrun_delayed_usecs) \
X(jack_get_buffer_size) \
X(jack_port_get_buffer) \
X(jack_port_name) \
X(jack_port_register) \
X(jack_port_unregister) \
X(jack_port_get_latency_range) \
X(jack_set_process_callback) \
X(jack_set_xrun_callback) \
X(jack_set_graph_order_callback) \
X(jack_set_error_function) \
#ifdef DISABLE_LIBJACK_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) (*api_##x)
#define JACK_API_VISIT(X) \
X(jack_activate) \
X(jack_client_close) \
X(jack_client_open) \
X(jack_connect) \
X(jack_free) \
X(jack_get_ports) \
X(jack_get_sample_rate) \
X(jack_get_xrun_delayed_usecs) \
X(jack_get_buffer_size) \
X(jack_port_get_buffer) \
X(jack_port_name) \
X(jack_port_register) \
X(jack_port_unregister) \
X(jack_port_get_latency_range) \
X(jack_set_process_callback) \
X(jack_set_xrun_callback) \
X(jack_set_graph_order_callback) \
X(jack_set_error_function) \
X(jack_set_info_function)
#define IMPORT_FUNC(x) static decltype(x) * api_##x;
JACK_API_VISIT(IMPORT_FUNC);
#undef IMPORT_FUNC
#endif
#define JACK_DEFAULT_IN "JACK capture"
#define JACK_DEFAULT_OUT "JACK playback"
static const int MAX_STREAMS = 16;
static const int MAX_CHANNELS = 8;
static const int MAX_CHANNELS = 8;
static const int FIFO_SIZE = 4096 * sizeof(float);
enum devstream {
@ -64,6 +70,12 @@ enum devstream {
DUPLEX,
};
enum cbjack_connect_ports_options {
CBJACK_CP_OPTIONS_NONE = 0x0,
CBJACK_CP_OPTIONS_SKIP_OUTPUT = 0x1,
CBJACK_CP_OPTIONS_SKIP_INPUT = 0x2,
};
static void
s16ne_to_float(float * dst, const int16_t * src, size_t n)
{
@ -75,71 +87,95 @@ static void
float_to_s16ne(int16_t * dst, float * src, size_t n)
{
for (size_t i = 0; i < n; i++) {
if (*src > 1.f) *src = 1.f;
if (*src < -1.f) *src = -1.f;
if (*src > 1.f)
*src = 1.f;
if (*src < -1.f)
*src = -1.f;
*(dst++) = (int16_t)((int16_t)(*(src++) * 32767));
}
}
extern "C"
{
/*static*/ int jack_init (cubeb ** context, char const * context_name);
extern "C" {
/*static*/ int
jack_init(cubeb ** context, char const * context_name);
}
static char const * cbjack_get_backend_id(cubeb * context);
static int cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
static int cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames);
static int cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
static int cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
static void cbjack_destroy(cubeb * context);
static void cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch);
static void cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short **bufs_in, float **bufs_out, jack_nframes_t nframes);
static void cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float **bufs_in, float **bufs_out, jack_nframes_t nframes);
static int cbjack_stream_device_destroy(cubeb_stream * stream,
cubeb_device * device);
static int cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device);
static int cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection);
static int cbjack_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
static int cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
static void cbjack_stream_destroy(cubeb_stream * stream);
static int cbjack_stream_start(cubeb_stream * stream);
static int cbjack_stream_stop(cubeb_stream * stream);
static int cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
static int cbjack_stream_set_volume(cubeb_stream * stm, float volume);
static char const *
cbjack_get_backend_id(cubeb * context);
static int
cbjack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels);
static int
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency_frames);
static int
cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_frames);
static int
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate);
static void
cbjack_destroy(cubeb * context);
static void
cbjack_interleave_capture(cubeb_stream * stream, float ** in,
jack_nframes_t nframes, bool format_mismatch);
static void
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream,
short ** bufs_in, float ** bufs_out,
jack_nframes_t nframes);
static void
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream,
float ** bufs_in, float ** bufs_out,
jack_nframes_t nframes);
static int
cbjack_stream_device_destroy(cubeb_stream * stream, cubeb_device * device);
static int
cbjack_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
static int
cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection);
static int
cbjack_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
static int
cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr);
static void
cbjack_stream_destroy(cubeb_stream * stream);
static int
cbjack_stream_start(cubeb_stream * stream);
static int
cbjack_stream_stop(cubeb_stream * stream);
static int
cbjack_stream_get_position(cubeb_stream * stream, uint64_t * position);
static int
cbjack_stream_set_volume(cubeb_stream * stm, float volume);
static struct cubeb_ops const cbjack_ops = {
.init = jack_init,
.get_backend_id = cbjack_get_backend_id,
.get_max_channel_count = cbjack_get_max_channel_count,
.get_min_latency = cbjack_get_min_latency,
.get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
.enumerate_devices = cbjack_enumerate_devices,
.device_collection_destroy = cbjack_device_collection_destroy,
.destroy = cbjack_destroy,
.stream_init = cbjack_stream_init,
.stream_destroy = cbjack_stream_destroy,
.stream_start = cbjack_stream_start,
.stream_stop = cbjack_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = cbjack_stream_get_position,
.stream_get_latency = cbjack_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = cbjack_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = cbjack_stream_get_current_device,
.stream_device_destroy = cbjack_stream_device_destroy,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.init = jack_init,
.get_backend_id = cbjack_get_backend_id,
.get_max_channel_count = cbjack_get_max_channel_count,
.get_min_latency = cbjack_get_min_latency,
.get_preferred_sample_rate = cbjack_get_preferred_sample_rate,
.enumerate_devices = cbjack_enumerate_devices,
.device_collection_destroy = cbjack_device_collection_destroy,
.destroy = cbjack_destroy,
.stream_init = cbjack_stream_init,
.stream_destroy = cbjack_stream_destroy,
.stream_start = cbjack_stream_start,
.stream_stop = cbjack_stream_stop,
.stream_get_position = cbjack_stream_get_position,
.stream_get_latency = cbjack_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = cbjack_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = cbjack_stream_get_current_device,
.stream_device_destroy = cbjack_stream_device_destroy,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL};
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
@ -150,7 +186,7 @@ struct cubeb_stream {
/**< Mutex for each stream */
pthread_mutex_t mutex;
bool in_use; /**< Set to false iff the stream is free */
bool in_use; /**< Set to false iff the stream is free */
bool ports_ready; /**< Set to true iff the JACK ports are ready */
cubeb_data_callback data_callback;
@ -205,15 +241,16 @@ struct cubeb {
static int
load_jack_lib(cubeb * context)
{
#ifndef DISABLE_LIBJACK_DLOPEN
#ifdef __APPLE__
context->libjack = dlopen("libjack.0.dylib", RTLD_LAZY);
context->libjack = dlopen("/usr/local/lib/libjack.0.dylib", RTLD_LAZY);
#elif defined(__WIN32__)
# ifdef _WIN64
context->libjack = LoadLibrary("libjack64.dll");
# else
context->libjack = LoadLibrary("libjack.dll");
# endif
#ifdef _WIN64
context->libjack = LoadLibrary("libjack64.dll");
#else
context->libjack = LoadLibrary("libjack.dll");
#endif
#else
context->libjack = dlopen("libjack.so.0", RTLD_LAZY);
if (!context->libjack) {
@ -224,56 +261,59 @@ load_jack_lib(cubeb * context)
return CUBEB_ERROR;
}
#define LOAD(x) \
{ \
api_##x = (decltype(x)*)dlsym(context->libjack, #x); \
if (!api_##x) { \
dlclose(context->libjack); \
return CUBEB_ERROR; \
} \
#define LOAD(x) \
{ \
api_##x = (decltype(x) *)dlsym(context->libjack, #x); \
if (!api_##x) { \
dlclose(context->libjack); \
return CUBEB_ERROR; \
} \
}
JACK_API_VISIT(LOAD);
#undef LOAD
#endif
return CUBEB_OK;
}
static void
cbjack_connect_port_out (cubeb_stream * stream, const size_t out_port, const char * const phys_in_port)
cbjack_connect_port_out(cubeb_stream * stream, const size_t out_port,
const char * const phys_in_port)
{
const char *src_port = api_jack_port_name (stream->output_ports[out_port]);
const char * src_port = WRAP(jack_port_name)(stream->output_ports[out_port]);
api_jack_connect (stream->context->jack_client, src_port, phys_in_port);
WRAP(jack_connect)(stream->context->jack_client, src_port, phys_in_port);
}
static void
cbjack_connect_port_in (cubeb_stream * stream, const char * const phys_out_port, size_t in_port)
cbjack_connect_port_in(cubeb_stream * stream, const char * const phys_out_port,
size_t in_port)
{
const char *src_port = api_jack_port_name (stream->input_ports[in_port]);
const char * src_port = WRAP(jack_port_name)(stream->input_ports[in_port]);
api_jack_connect (stream->context->jack_client, phys_out_port, src_port);
WRAP(jack_connect)(stream->context->jack_client, phys_out_port, src_port);
}
static int
cbjack_connect_ports (cubeb_stream * stream)
cbjack_connect_ports(cubeb_stream * stream,
enum cbjack_connect_ports_options options)
{
int r = CUBEB_ERROR;
const char ** phys_in_ports = api_jack_get_ports (stream->context->jack_client,
NULL, NULL,
JackPortIsInput
| JackPortIsPhysical);
const char ** phys_out_ports = api_jack_get_ports (stream->context->jack_client,
NULL, NULL,
JackPortIsOutput
| JackPortIsPhysical);
const char ** phys_in_ports =
WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
JackPortIsInput | JackPortIsPhysical);
const char ** phys_out_ports =
WRAP(jack_get_ports)(stream->context->jack_client, NULL, NULL,
JackPortIsOutput | JackPortIsPhysical);
if (phys_in_ports == NULL || *phys_in_ports == NULL) {
if (phys_in_ports == NULL || *phys_in_ports == NULL ||
options & CBJACK_CP_OPTIONS_SKIP_OUTPUT) {
goto skipplayback;
}
// Connect outputs to playback
for (unsigned int c = 0; c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
for (unsigned int c = 0;
c < stream->out_params.channels && phys_in_ports[c] != NULL; c++) {
cbjack_connect_port_out(stream, c, phys_in_ports[c]);
}
@ -285,20 +325,22 @@ cbjack_connect_ports (cubeb_stream * stream)
r = CUBEB_OK;
skipplayback:
if (phys_out_ports == NULL || *phys_out_ports == NULL) {
if (phys_out_ports == NULL || *phys_out_ports == NULL ||
options & CBJACK_CP_OPTIONS_SKIP_INPUT) {
goto end;
}
// Connect inputs to capture
for (unsigned int c = 0; c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
for (unsigned int c = 0;
c < stream->in_params.channels && phys_out_ports[c] != NULL; c++) {
cbjack_connect_port_in(stream, phys_out_ports[c], c);
}
r = CUBEB_OK;
end:
if (phys_out_ports) {
api_jack_free(phys_out_ports);
WRAP(jack_free)(phys_out_ports);
}
if (phys_in_ports) {
api_jack_free(phys_in_ports);
WRAP(jack_free)(phys_in_ports);
}
return r;
}
@ -308,8 +350,9 @@ cbjack_xrun_callback(void * arg)
{
cubeb * ctx = (cubeb *)arg;
float delay = api_jack_get_xrun_delayed_usecs(ctx->jack_client);
float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) / ctx->jack_buffer_size);
float delay = WRAP(jack_get_xrun_delayed_usecs)(ctx->jack_client);
float fragments = ceilf(((delay / 1000000.0) * ctx->jack_sample_rate) /
ctx->jack_buffer_size);
ctx->jack_xruns += (unsigned int)fragments;
return 0;
@ -324,7 +367,7 @@ cbjack_graph_order_callback(void * arg)
jack_nframes_t port_latency, max_latency = 0;
for (int j = 0; j < MAX_STREAMS; j++) {
cubeb_stream *stm = &ctx->streams[j];
cubeb_stream * stm = &ctx->streams[j];
if (!stm->in_use)
continue;
@ -332,10 +375,11 @@ cbjack_graph_order_callback(void * arg)
continue;
for (i = 0; i < (int)stm->out_params.channels; ++i) {
api_jack_port_get_latency_range(stm->output_ports[i], JackPlaybackLatency, &latency_range);
WRAP(jack_port_get_latency_range)
(stm->output_ports[i], JackPlaybackLatency, &latency_range);
port_latency = latency_range.max;
if (port_latency > max_latency)
max_latency = port_latency;
max_latency = port_latency;
}
/* Cap minimum latency to 128 frames */
if (max_latency < 128)
@ -357,9 +401,9 @@ cbjack_process(jack_nframes_t nframes, void * arg)
ctx->jack_xruns = 0;
for (int j = 0; j < MAX_STREAMS; j++) {
cubeb_stream *stm = &ctx->streams[j];
float *bufs_out[stm->out_params.channels];
float *bufs_in[stm->in_params.channels];
cubeb_stream * stm = &ctx->streams[j];
float * bufs_out[stm->out_params.channels];
float * bufs_in[stm->in_params.channels];
if (!stm->in_use)
continue;
@ -373,18 +417,20 @@ cbjack_process(jack_nframes_t nframes, void * arg)
if (stm->devs & OUT_ONLY) {
// get jack output buffers
for (i = 0; i < (int)stm->out_params.channels; i++)
bufs_out[i] = (float*)api_jack_port_get_buffer(stm->output_ports[i], nframes);
bufs_out[i] =
(float *)WRAP(jack_port_get_buffer)(stm->output_ports[i], nframes);
}
if (stm->devs & IN_ONLY) {
// get jack input buffers
for (i = 0; i < (int)stm->in_params.channels; i++)
bufs_in[i] = (float*)api_jack_port_get_buffer(stm->input_ports[i], nframes);
bufs_in[i] =
(float *)WRAP(jack_port_get_buffer)(stm->input_ports[i], nframes);
}
if (stm->pause) {
// paused, play silence on output
if (stm->devs & OUT_ONLY) {
for (unsigned int c = 0; c < stm->out_params.channels; c++) {
float* buffer_out = bufs_out[c];
float * buffer_out = bufs_out[c];
for (long f = 0; f < nframes; f++) {
buffer_out[f] = 0.f;
}
@ -393,7 +439,7 @@ cbjack_process(jack_nframes_t nframes, void * arg)
if (stm->devs & IN_ONLY) {
// paused, capture silence
for (unsigned int c = 0; c < stm->in_params.channels; c++) {
float* buffer_in = bufs_in[c];
float * buffer_in = bufs_in[c];
for (long f = 0; f < nframes; f++) {
buffer_in[f] = 0.f;
}
@ -404,31 +450,38 @@ cbjack_process(jack_nframes_t nframes, void * arg)
// try to lock stream mutex
if (pthread_mutex_trylock(&stm->mutex) == 0) {
int16_t *in_s16ne = stm->context->in_resampled_interleaved_buffer_s16ne;
float *in_float = stm->context->in_resampled_interleaved_buffer_float;
int16_t * in_s16ne =
stm->context->in_resampled_interleaved_buffer_s16ne;
float * in_float = stm->context->in_resampled_interleaved_buffer_float;
// unpaused, play audio
if (stm->devs == DUPLEX) {
if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
cbjack_interleave_capture(stm, bufs_in, nframes, true);
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out, nframes);
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, bufs_out,
nframes);
} else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
cbjack_interleave_capture(stm, bufs_in, nframes, false);
cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out, nframes);
cbjack_deinterleave_playback_refill_float(stm, &in_float, bufs_out,
nframes);
}
} else if (stm->devs == IN_ONLY) {
if (stm->in_params.format == CUBEB_SAMPLE_S16NE) {
cbjack_interleave_capture(stm, bufs_in, nframes, true);
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr, nframes);
cbjack_deinterleave_playback_refill_s16ne(stm, &in_s16ne, nullptr,
nframes);
} else if (stm->in_params.format == CUBEB_SAMPLE_FLOAT32NE) {
cbjack_interleave_capture(stm, bufs_in, nframes, false);
cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr, nframes);
cbjack_deinterleave_playback_refill_float(stm, &in_float, nullptr,
nframes);
}
} else if (stm->devs == OUT_ONLY) {
if (stm->out_params.format == CUBEB_SAMPLE_S16NE) {
cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out, nframes);
cbjack_deinterleave_playback_refill_s16ne(stm, nullptr, bufs_out,
nframes);
} else if (stm->out_params.format == CUBEB_SAMPLE_FLOAT32NE) {
cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out, nframes);
cbjack_deinterleave_playback_refill_float(stm, nullptr, bufs_out,
nframes);
}
}
// unlock stream mutex
@ -439,7 +492,7 @@ cbjack_process(jack_nframes_t nframes, void * arg)
// output silence
if (stm->devs & OUT_ONLY) {
for (unsigned int c = 0; c < stm->out_params.channels; c++) {
float* buffer_out = bufs_out[c];
float * buffer_out = bufs_out[c];
for (long f = 0; f < nframes; f++) {
buffer_out[f] = 0.f;
}
@ -448,7 +501,7 @@ cbjack_process(jack_nframes_t nframes, void * arg)
if (stm->devs & IN_ONLY) {
// capture silence
for (unsigned int c = 0; c < stm->in_params.channels; c++) {
float* buffer_in = bufs_in[c];
float * buffer_in = bufs_in[c];
for (long f = 0; f < nframes; f++) {
buffer_in[f] = 0.f;
}
@ -461,7 +514,9 @@ cbjack_process(jack_nframes_t nframes, void * arg)
}
static void
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, float ** bufs_out, jack_nframes_t nframes)
cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in,
float ** bufs_out,
jack_nframes_t nframes)
{
float * out_interleaved_buffer = nullptr;
@ -472,20 +527,24 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl
long done_frames = 0;
long input_frames_count = (in != NULL) ? nframes : 0;
done_frames = cubeb_resampler_fill(stream->resampler,
inptr,
&input_frames_count,
(bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_float : NULL,
needed_frames);
done_frames = cubeb_resampler_fill(
stream->resampler, inptr, &input_frames_count,
(bufs_out != NULL)
? stream->context->out_resampled_interleaved_buffer_float
: NULL,
needed_frames);
out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;
out_interleaved_buffer =
stream->context->out_resampled_interleaved_buffer_float;
if (outptr) {
// convert interleaved output buffers to contiguous buffers
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
float* buffer = bufs_out[c];
float * buffer = bufs_out[c];
for (long f = 0; f < done_frames; f++) {
buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
buffer[f] =
out_interleaved_buffer[(f * stream->out_params.channels) + c] *
stream->volume;
}
if (done_frames < needed_frames) {
// draining
@ -519,7 +578,9 @@ cbjack_deinterleave_playback_refill_float(cubeb_stream * stream, float ** in, fl
}
static void
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, float ** bufs_out, jack_nframes_t nframes)
cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in,
float ** bufs_out,
jack_nframes_t nframes)
{
float * out_interleaved_buffer = nullptr;
@ -530,22 +591,28 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl
long done_frames = 0;
long input_frames_count = (in != NULL) ? nframes : 0;
done_frames = cubeb_resampler_fill(stream->resampler,
inptr,
&input_frames_count,
(bufs_out != NULL) ? stream->context->out_resampled_interleaved_buffer_s16ne : NULL,
needed_frames);
done_frames = cubeb_resampler_fill(
stream->resampler, inptr, &input_frames_count,
(bufs_out != NULL)
? stream->context->out_resampled_interleaved_buffer_s16ne
: NULL,
needed_frames);
s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float, stream->context->out_resampled_interleaved_buffer_s16ne, done_frames * stream->out_params.channels);
s16ne_to_float(stream->context->out_resampled_interleaved_buffer_float,
stream->context->out_resampled_interleaved_buffer_s16ne,
done_frames * stream->out_params.channels);
out_interleaved_buffer = stream->context->out_resampled_interleaved_buffer_float;
out_interleaved_buffer =
stream->context->out_resampled_interleaved_buffer_float;
if (outptr) {
// convert interleaved output buffers to contiguous buffers
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
float* buffer = bufs_out[c];
float * buffer = bufs_out[c];
for (long f = 0; f < done_frames; f++) {
buffer[f] = out_interleaved_buffer[(f * stream->out_params.channels) + c] * stream->volume;
buffer[f] =
out_interleaved_buffer[(f * stream->out_params.channels) + c] *
stream->volume;
}
if (done_frames < needed_frames) {
// draining
@ -579,20 +646,25 @@ cbjack_deinterleave_playback_refill_s16ne(cubeb_stream * stream, short ** in, fl
}
static void
cbjack_interleave_capture(cubeb_stream * stream, float **in, jack_nframes_t nframes, bool format_mismatch)
cbjack_interleave_capture(cubeb_stream * stream, float ** in,
jack_nframes_t nframes, bool format_mismatch)
{
float *in_buffer = stream->context->in_float_interleaved_buffer;
float * in_buffer = stream->context->in_float_interleaved_buffer;
for (unsigned int c = 0; c < stream->in_params.channels; c++) {
for (long f = 0; f < nframes; f++) {
in_buffer[(f * stream->in_params.channels) + c] = in[c][f] * stream->volume;
in_buffer[(f * stream->in_params.channels) + c] =
in[c][f] * stream->volume;
}
}
if (format_mismatch) {
float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne, in_buffer, nframes * stream->in_params.channels);
float_to_s16ne(stream->context->in_resampled_interleaved_buffer_s16ne,
in_buffer, nframes * stream->in_params.channels);
} else {
memset(stream->context->in_resampled_interleaved_buffer_float, 0, (FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer, (FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
memset(stream->context->in_resampled_interleaved_buffer_float, 0,
(FIFO_SIZE * MAX_CHANNELS * 3) * sizeof(float));
memcpy(stream->context->in_resampled_interleaved_buffer_float, in_buffer,
(FIFO_SIZE * MAX_CHANNELS * 2) * sizeof(float));
}
}
@ -602,7 +674,7 @@ silent_jack_error_callback(char const * /*msg*/)
}
/*static*/ int
jack_init (cubeb ** context, char const * context_name)
jack_init(cubeb ** context, char const * context_name)
{
int r;
@ -619,8 +691,8 @@ jack_init (cubeb ** context, char const * context_name)
return CUBEB_ERROR;
}
api_jack_set_error_function(silent_jack_error_callback);
api_jack_set_info_function(silent_jack_error_callback);
WRAP(jack_set_error_function)(silent_jack_error_callback);
WRAP(jack_set_info_function)(silent_jack_error_callback);
ctx->ops = &cbjack_ops;
@ -633,9 +705,8 @@ jack_init (cubeb ** context, char const * context_name)
if (context_name)
jack_client_name = context_name;
ctx->jack_client = api_jack_client_open(jack_client_name,
JackNoStartServer,
NULL);
ctx->jack_client =
WRAP(jack_client_open)(jack_client_name, JackNoStartServer, NULL);
if (ctx->jack_client == NULL) {
cbjack_destroy(ctx);
@ -644,16 +715,17 @@ jack_init (cubeb ** context, char const * context_name)
ctx->jack_xruns = 0;
api_jack_set_process_callback (ctx->jack_client, cbjack_process, ctx);
api_jack_set_xrun_callback (ctx->jack_client, cbjack_xrun_callback, ctx);
api_jack_set_graph_order_callback (ctx->jack_client, cbjack_graph_order_callback, ctx);
WRAP(jack_set_process_callback)(ctx->jack_client, cbjack_process, ctx);
WRAP(jack_set_xrun_callback)(ctx->jack_client, cbjack_xrun_callback, ctx);
WRAP(jack_set_graph_order_callback)
(ctx->jack_client, cbjack_graph_order_callback, ctx);
if (api_jack_activate (ctx->jack_client)) {
if (WRAP(jack_activate)(ctx->jack_client)) {
cbjack_destroy(ctx);
return CUBEB_ERROR;
}
ctx->jack_sample_rate = api_jack_get_sample_rate(ctx->jack_client);
ctx->jack_sample_rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
ctx->jack_latency = 128 * 1000 / ctx->jack_sample_rate;
ctx->active = true;
@ -683,7 +755,8 @@ cbjack_get_latency(cubeb_stream * stm, unsigned int * latency_ms)
}
static int
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/, uint32_t * latency_ms)
cbjack_get_min_latency(cubeb * ctx, cubeb_stream_params /*params*/,
uint32_t * latency_ms)
{
*latency_ms = ctx->jack_latency;
return CUBEB_OK;
@ -693,18 +766,17 @@ static int
cbjack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
if (!ctx->jack_client) {
jack_client_t * testclient = api_jack_client_open("test-samplerate",
JackNoStartServer,
NULL);
jack_client_t * testclient =
WRAP(jack_client_open)("test-samplerate", JackNoStartServer, NULL);
if (!testclient) {
return CUBEB_ERROR;
}
*rate = api_jack_get_sample_rate(testclient);
api_jack_client_close(testclient);
*rate = WRAP(jack_get_sample_rate)(testclient);
WRAP(jack_client_close)(testclient);
} else {
*rate = api_jack_get_sample_rate(ctx->jack_client);
*rate = WRAP(jack_get_sample_rate)(ctx->jack_client);
}
return CUBEB_OK;
}
@ -715,7 +787,7 @@ cbjack_destroy(cubeb * context)
context->active = false;
if (context->jack_client != NULL)
api_jack_client_close (context->jack_client);
WRAP(jack_client_close)(context->jack_client);
if (context->libjack)
dlclose(context->libjack);
@ -738,30 +810,27 @@ context_alloc_stream(cubeb * context, char const * stream_name)
}
static int
cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cbjack_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int /*latency_frames*/,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
cubeb_state_callback state_callback, void * user_ptr)
{
int stream_actual_rate = 0;
int jack_rate = api_jack_get_sample_rate(context->jack_client);
int jack_rate = WRAP(jack_get_sample_rate)(context->jack_client);
if (output_stream_params
&& (output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
output_stream_params->format != CUBEB_SAMPLE_S16NE)
) {
if (output_stream_params &&
(output_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
output_stream_params->format != CUBEB_SAMPLE_S16NE)) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if (input_stream_params
&& (input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
input_stream_params->format != CUBEB_SAMPLE_S16NE)
) {
if (input_stream_params &&
(input_stream_params->format != CUBEB_SAMPLE_FLOAT32NE &&
input_stream_params->format != CUBEB_SAMPLE_S16NE)) {
return CUBEB_ERROR_INVALID_FORMAT;
}
@ -771,8 +840,10 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
}
// Loopback is unsupported
if ((input_stream_params && (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
(output_stream_params && (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
if ((input_stream_params &&
(input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK)) ||
(output_stream_params &&
(output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
@ -841,7 +912,7 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
stm->state_callback = state_callback;
stm->position = 0;
stm->volume = 1.0f;
context->jack_buffer_size = api_jack_get_buffer_size(context->jack_client);
context->jack_buffer_size = WRAP(jack_get_buffer_size)(context->jack_client);
context->fragment_size = context->jack_buffer_size;
if (stm->devs == NONE) {
@ -852,29 +923,20 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
stm->resampler = NULL;
if (stm->devs == DUPLEX) {
stm->resampler = cubeb_resampler_create(stm,
&stm->in_params,
&stm->out_params,
stream_actual_rate,
stm->data_callback,
stm->user_ptr,
CUBEB_RESAMPLER_QUALITY_DESKTOP);
stm->resampler = cubeb_resampler_create(
stm, &stm->in_params, &stm->out_params, stream_actual_rate,
stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
CUBEB_RESAMPLER_RECLOCK_NONE);
} else if (stm->devs == IN_ONLY) {
stm->resampler = cubeb_resampler_create(stm,
&stm->in_params,
nullptr,
stream_actual_rate,
stm->data_callback,
stm->user_ptr,
CUBEB_RESAMPLER_QUALITY_DESKTOP);
stm->resampler = cubeb_resampler_create(
stm, &stm->in_params, nullptr, stream_actual_rate, stm->data_callback,
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
CUBEB_RESAMPLER_RECLOCK_NONE);
} else if (stm->devs == OUT_ONLY) {
stm->resampler = cubeb_resampler_create(stm,
nullptr,
&stm->out_params,
stream_actual_rate,
stm->data_callback,
stm->user_ptr,
CUBEB_RESAMPLER_QUALITY_DESKTOP);
stm->resampler = cubeb_resampler_create(
stm, nullptr, &stm->out_params, stream_actual_rate, stm->data_callback,
stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
CUBEB_RESAMPLER_RECLOCK_NONE);
}
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++) {
char portname[256];
snprintf(portname, 255, "%s_out_%d", stm->stream_name, c);
stm->output_ports[c] = api_jack_port_register(stm->context->jack_client,
portname,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput,
0);
stm->output_ports[c] = WRAP(jack_port_register)(
stm->context->jack_client, portname, JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, 0);
if (!(output_stream_params->prefs &
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_INPUT) !=
CUBEB_OK) {
pthread_mutex_unlock(&stm->mutex);
cbjack_stream_destroy(stm);
return CUBEB_ERROR;
}
}
}
}
@ -899,19 +968,18 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
for (unsigned int c = 0; c < stm->in_params.channels; c++) {
char portname[256];
snprintf(portname, 255, "%s_in_%d", stm->stream_name, c);
stm->input_ports[c] = api_jack_port_register(stm->context->jack_client,
portname,
JACK_DEFAULT_AUDIO_TYPE,
JackPortIsInput,
0);
}
}
if (!input_stream_params->prefs & CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT) {
if (cbjack_connect_ports(stm) != CUBEB_OK) {
pthread_mutex_unlock(&stm->mutex);
cbjack_stream_destroy(stm);
return CUBEB_ERROR;
stm->input_ports[c] =
WRAP(jack_port_register)(stm->context->jack_client, portname,
JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
if (!(input_stream_params->prefs &
CUBEB_STREAM_PREF_JACK_NO_AUTO_CONNECT)) {
if (cbjack_connect_ports(stm, CBJACK_CP_OPTIONS_SKIP_OUTPUT) !=
CUBEB_OK) {
pthread_mutex_unlock(&stm->mutex);
cbjack_stream_destroy(stm);
return CUBEB_ERROR;
}
}
}
}
@ -933,7 +1001,8 @@ cbjack_stream_destroy(cubeb_stream * stream)
if (stream->devs == DUPLEX || stream->devs == OUT_ONLY) {
for (unsigned int c = 0; c < stream->out_params.channels; c++) {
if (stream->output_ports[c]) {
api_jack_port_unregister (stream->context->jack_client, stream->output_ports[c]);
WRAP(jack_port_unregister)
(stream->context->jack_client, stream->output_ports[c]);
stream->output_ports[c] = NULL;
}
}
@ -942,7 +1011,8 @@ cbjack_stream_destroy(cubeb_stream * stream)
if (stream->devs == DUPLEX || stream->devs == IN_ONLY) {
for (unsigned int c = 0; c < stream->in_params.channels; c++) {
if (stream->input_ports[c]) {
api_jack_port_unregister (stream->context->jack_client, stream->input_ports[c]);
WRAP(jack_port_unregister)
(stream->context->jack_client, stream->input_ports[c]);
stream->input_ports[c] = NULL;
}
}
@ -987,7 +1057,8 @@ cbjack_stream_set_volume(cubeb_stream * stm, float volume)
}
static int
cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
cbjack_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device)
{
*device = (cubeb_device *)calloc(1, sizeof(cubeb_device));
if (*device == NULL)
@ -1012,8 +1083,7 @@ cbjack_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const devic
}
static int
cbjack_stream_device_destroy(cubeb_stream * /*stream*/,
cubeb_device * device)
cbjack_stream_device_destroy(cubeb_stream * /*stream*/, cubeb_device * device)
{
if (device->input_name)
free(device->input_name);
@ -1042,7 +1112,7 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
cubeb_device_info * cur = &devices[collection->count];
cur->device_id = JACK_DEFAULT_OUT;
cur->devid = (cubeb_devid) cur->device_id;
cur->devid = (cubeb_devid)cur->device_id;
cur->friendly_name = JACK_DEFAULT_OUT;
cur->group_id = JACK_DEFAULT_OUT;
cur->vendor_name = JACK_DEFAULT_OUT;
@ -1057,13 +1127,13 @@ cbjack_enumerate_devices(cubeb * context, cubeb_device_type type,
cur->default_rate = rate;
cur->latency_lo = 0;
cur->latency_hi = 0;
collection->count +=1 ;
collection->count += 1;
}
if (type & CUBEB_DEVICE_TYPE_INPUT) {
cubeb_device_info * cur = &devices[collection->count];
cur->device_id = JACK_DEFAULT_IN;
cur->devid = (cubeb_devid) cur->device_id;
cur->devid = (cubeb_devid)cur->device_id;
cur->friendly_name = JACK_DEFAULT_IN;
cur->group_id = JACK_DEFAULT_IN;
cur->vendor_name = JACK_DEFAULT_IN;
@ -1091,6 +1161,6 @@ cbjack_device_collection_destroy(cubeb * /*ctx*/,
cubeb_device_collection * collection)
{
XASSERT(collection);
delete [] collection->device;
delete[] collection->device;
return CUBEB_OK;
}

View file

@ -4,15 +4,15 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include <math.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/fmutex.h>
#include <kai.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
/* We don't support more than 2 channels in KAI */
#define MAX_CHANNELS 2
@ -59,7 +59,8 @@ bytes_to_frames(long bytes, cubeb_stream_params params)
return bytes / 2 / params.channels; /* 2 bytes per frame */
}
static void kai_destroy(cubeb * ctx);
static void
kai_destroy(cubeb * ctx);
/*static*/ int
kai_init(cubeb ** context, char const * context_name)
@ -97,7 +98,7 @@ kai_destroy(cubeb * ctx)
}
static void
float_to_s16ne(int16_t *dst, float *src, size_t n)
float_to_s16ne(int16_t * dst, float * src, size_t n)
{
long l;
@ -115,14 +116,13 @@ static ULONG APIENTRY
kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
{
cubeb_stream * stm = cbdata;
void *p;
void * p;
long wanted_frames;
long frames;
float soft_volume;
int elements = len / sizeof(int16_t);
p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE
? stm->float_buffer : buffer;
p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE ? stm->float_buffer : buffer;
wanted_frames = bytes_to_frames(len, stm->params);
frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames);
@ -139,7 +139,7 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
float_to_s16ne(buffer, p, elements);
if (soft_volume != -1.0f) {
int16_t *b = buffer;
int16_t * b = buffer;
int i;
for (i = 0; i < elements; i++)
@ -149,12 +149,12 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
return frames_to_bytes(frames, stm->params);
}
static void kai_stream_destroy(cubeb_stream * stm);
static void
kai_stream_destroy(cubeb_stream * stm);
static int
kai_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
@ -202,17 +202,17 @@ kai_stream_init(cubeb * context, cubeb_stream ** stream,
return CUBEB_ERROR;
}
wanted_spec.usDeviceIndex = 0;
wanted_spec.ulType = KAIT_PLAY;
wanted_spec.usDeviceIndex = 0;
wanted_spec.ulType = KAIT_PLAY;
wanted_spec.ulBitsPerSample = BPS_16;
wanted_spec.ulSamplingRate = stm->params.rate;
wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM;
wanted_spec.ulChannels = stm->params.channels;
wanted_spec.ulNumBuffers = NBUFS;
wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, stm->params);
wanted_spec.fShareable = TRUE;
wanted_spec.pfnCallBack = kai_callback;
wanted_spec.pCallBackData = stm;
wanted_spec.ulSamplingRate = stm->params.rate;
wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM;
wanted_spec.ulChannels = stm->params.channels;
wanted_spec.ulNumBuffers = NBUFS;
wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, stm->params);
wanted_spec.fShareable = TRUE;
wanted_spec.pfnCallBack = kai_callback;
wanted_spec.pCallBackData = stm;
if (kaiOpen(&wanted_spec, &stm->spec, &stm->hkai)) {
_fmutex_close(&stm->mutex);
@ -265,17 +265,17 @@ kai_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
params.rate = 48000;
params.channels = 2;
wanted_spec.usDeviceIndex = 0;
wanted_spec.ulType = KAIT_PLAY;
wanted_spec.usDeviceIndex = 0;
wanted_spec.ulType = KAIT_PLAY;
wanted_spec.ulBitsPerSample = BPS_16;
wanted_spec.ulSamplingRate = params.rate;
wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM;
wanted_spec.ulChannels = params.channels;
wanted_spec.ulNumBuffers = NBUFS;
wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, params);
wanted_spec.fShareable = TRUE;
wanted_spec.pfnCallBack = kai_callback;
wanted_spec.pCallBackData = NULL;
wanted_spec.ulSamplingRate = params.rate;
wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM;
wanted_spec.ulChannels = params.channels;
wanted_spec.ulNumBuffers = NBUFS;
wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, params);
wanted_spec.fShareable = TRUE;
wanted_spec.pfnCallBack = kai_callback;
wanted_spec.pCallBackData = NULL;
/* Test 48KHz */
if (kaiOpen(&wanted_spec, &spec, &hkai)) {
@ -328,8 +328,8 @@ kai_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
/* Out of buffers, one is being played, the others are being filled.
So there is as much latency as total buffers - 1. */
*latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params)
* (stm->spec.ulNumBuffers - 1);
*latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params) *
(stm->spec.ulNumBuffers - 1);
return CUBEB_OK;
}
@ -345,27 +345,25 @@ kai_stream_set_volume(cubeb_stream * stm, float volume)
}
static struct cubeb_ops const kai_ops = {
/*.init =*/ kai_init,
/*.get_backend_id =*/ kai_get_backend_id,
/*.get_max_channel_count=*/ kai_get_max_channel_count,
/*.get_min_latency=*/ kai_get_min_latency,
/*.get_preferred_sample_rate =*/ kai_get_preferred_sample_rate,
/*.get_preferred_channel_layout =*/ NULL,
/*.enumerate_devices =*/ NULL,
/*.device_collection_destroy =*/ NULL,
/*.destroy =*/ kai_destroy,
/*.stream_init =*/ kai_stream_init,
/*.stream_destroy =*/ kai_stream_destroy,
/*.stream_start =*/ kai_stream_start,
/*.stream_stop =*/ kai_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ kai_stream_get_position,
/*.stream_get_latency = */ kai_stream_get_latency,
/*.stream_get_input_latency = */ NULL,
/*.stream_set_volume =*/ kai_stream_set_volume,
/*.stream_set_name =*/ NULL,
/*.stream_get_current_device =*/ NULL,
/*.stream_device_destroy =*/ NULL,
/*.stream_register_device_changed_callback=*/ NULL,
/*.register_device_collection_changed=*/ NULL
};
/*.init =*/kai_init,
/*.get_backend_id =*/kai_get_backend_id,
/*.get_max_channel_count=*/kai_get_max_channel_count,
/*.get_min_latency=*/kai_get_min_latency,
/*.get_preferred_sample_rate =*/kai_get_preferred_sample_rate,
/*.get_preferred_channel_layout =*/NULL,
/*.enumerate_devices =*/NULL,
/*.device_collection_destroy =*/NULL,
/*.destroy =*/kai_destroy,
/*.stream_init =*/kai_stream_init,
/*.stream_destroy =*/kai_stream_destroy,
/*.stream_start =*/kai_stream_start,
/*.stream_stop =*/kai_stream_stop,
/*.stream_get_position =*/kai_stream_get_position,
/*.stream_get_latency = */ kai_stream_get_latency,
/*.stream_get_input_latency = */ NULL,
/*.stream_set_volume =*/kai_stream_set_volume,
/*.stream_set_name =*/NULL,
/*.stream_get_current_device =*/NULL,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback=*/NULL,
/*.register_device_collection_changed=*/NULL};

View file

@ -8,6 +8,7 @@
#include "cubeb_log.h"
#include "cubeb_ringbuffer.h"
#include "cubeb_tracing.h"
#include <cstdarg>
#ifdef _WIN32
#include <windows.h>
@ -27,17 +28,13 @@ const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
#define CUBEB_LOG_BATCH_PRINT_INTERVAL_MS 10
/**
* This wraps an inline buffer, that represents a log message, that must be
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message
{
* This wraps an inline buffer, that represents a log message, that must be
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message {
public:
cubeb_log_message()
{
*storage = '\0';
}
cubeb_log_message() { *storage = '\0'; }
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
size_t length = strlen(str);
@ -49,20 +46,19 @@ public:
PodCopy(storage, str, length);
storage[length] = '\0';
}
char const * get() {
return storage;
}
char const * get() { return storage; }
private:
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
};
/** Lock-free asynchronous logger, made so that logging from a
* real-time audio callback does not block the audio thread. */
class cubeb_async_logger
{
class cubeb_async_logger {
public:
/* This is thread-safe since C++11 */
static cubeb_async_logger & get() {
static cubeb_async_logger & get()
{
static cubeb_async_logger instance;
return instance;
}
@ -74,10 +70,11 @@ public:
void run()
{
std::thread([this]() {
CUBEB_REGISTER_THREAD("cubeb_log");
while (true) {
cubeb_log_message msg;
while (msg_queue.dequeue(&msg, 1)) {
LOGV("%s", msg.get());
LOG_INTERNAL_NO_FORMAT(CUBEB_LOG_NORMAL, "%s", msg.get());
}
#ifdef _WIN32
Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
@ -85,41 +82,34 @@ public:
timespec sleep_duration = sleep_for;
timespec remainder;
do {
if (nanosleep(&sleep_duration, &remainder) == 0 ||
errno != EINTR) {
if (nanosleep(&sleep_duration, &remainder) == 0 || errno != EINTR) {
break;
}
sleep_duration = remainder;
} while (remainder.tv_sec || remainder.tv_nsec);
#endif
}
CUBEB_UNREGISTER_THREAD();
}).detach();
}
// Tell the underlying queue the producer thread has changed, so it does not
// assert in debug. This should be called with the thread stopped.
void reset_producer_thread()
{
msg_queue.reset_thread_ids();
}
void reset_producer_thread() { msg_queue.reset_thread_ids(); }
private:
#ifndef _WIN32
const struct timespec sleep_for = {
CUBEB_LOG_BATCH_PRINT_INTERVAL_MS/1000,
(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000
};
CUBEB_LOG_BATCH_PRINT_INTERVAL_MS / 1000,
(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS % 1000) * 1000 * 1000};
#endif
cubeb_async_logger()
: msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH)
{
run();
}
cubeb_async_logger() : msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH) { run(); }
/** This is quite a big data structure, but is only instantiated if the
* asynchronous logger is used.*/
lock_free_queue<cubeb_log_message> msg_queue;
};
void cubeb_async_log(char const * fmt, ...)
void
cubeb_async_log(char const * fmt, ...)
{
if (!g_cubeb_log_callback) {
return;
@ -135,7 +125,8 @@ void cubeb_async_log(char const * fmt, ...)
va_end(args);
}
void cubeb_async_log_reset_threads()
void
cubeb_async_log_reset_threads(void)
{
if (!g_cubeb_log_callback) {
return;

View file

@ -19,18 +19,23 @@ extern "C" {
#if defined(__FILE_NAME__)
#define __FILENAME__ __FILE_NAME__
#else
#define __FILENAME__ (__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 : __FILE__)
#define __FILENAME__ \
(__builtin_strrchr(__FILE__, '/') ? __builtin_strrchr(__FILE__, '/') + 1 \
: __FILE__)
#endif
#else
#define PRINTF_FORMAT(fmt, args)
#include <string.h>
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define __FILENAME__ \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#endif
extern cubeb_log_level g_cubeb_log_level;
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
void cubeb_async_log(const char * fmt, ...);
void cubeb_async_log_reset_threads();
void
cubeb_async_log(const char * fmt, ...);
void
cubeb_async_log_reset_threads(void);
#ifdef __cplusplus
}
@ -39,17 +44,31 @@ void cubeb_async_log_reset_threads();
#define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#define LOG_INTERNAL(level, fmt, ...) do { \
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, ##__VA_ARGS__); \
} \
} while(0)
#define LOG_INTERNAL_NO_FORMAT(level, fmt, ...) \
do { \
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
g_cubeb_log_callback(fmt, __VA_ARGS__); \
} \
} while (0)
/* Asynchronous verbose logging, to log in real-time callbacks. */
#define LOG_INTERNAL(level, fmt, ...) \
do { \
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILENAME__, __LINE__, \
##__VA_ARGS__); \
} \
} while (0)
#define 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(fmt, ...) \
do { \
cubeb_async_log(fmt, ##__VA_ARGS__); \
} while(0)
#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

View file

@ -9,6 +9,9 @@
#define NOMINMAX
#include "cubeb_mixer.h"
#include "cubeb-internal.h"
#include "cubeb_utils.h"
#include <algorithm>
#include <cassert>
#include <climits>
@ -16,67 +19,67 @@
#include <cstdlib>
#include <memory>
#include <type_traits>
#include "cubeb-internal.h"
#include "cubeb_mixer.h"
#include "cubeb_utils.h"
#ifndef FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
#endif
#define CHANNELS_MAX 32
#define FRONT_LEFT 0
#define FRONT_RIGHT 1
#define FRONT_CENTER 2
#define LOW_FREQUENCY 3
#define BACK_LEFT 4
#define BACK_RIGHT 5
#define FRONT_LEFT_OF_CENTER 6
#define FRONT_RIGHT_OF_CENTER 7
#define BACK_CENTER 8
#define SIDE_LEFT 9
#define SIDE_RIGHT 10
#define TOP_CENTER 11
#define TOP_FRONT_LEFT 12
#define TOP_FRONT_CENTER 13
#define TOP_FRONT_RIGHT 14
#define TOP_BACK_LEFT 15
#define TOP_BACK_CENTER 16
#define TOP_BACK_RIGHT 17
#define NUM_NAMED_CHANNELS 18
#define FRONT_LEFT 0
#define FRONT_RIGHT 1
#define FRONT_CENTER 2
#define LOW_FREQUENCY 3
#define BACK_LEFT 4
#define BACK_RIGHT 5
#define FRONT_LEFT_OF_CENTER 6
#define FRONT_RIGHT_OF_CENTER 7
#define BACK_CENTER 8
#define SIDE_LEFT 9
#define SIDE_RIGHT 10
#define TOP_CENTER 11
#define TOP_FRONT_LEFT 12
#define TOP_FRONT_CENTER 13
#define TOP_FRONT_RIGHT 14
#define TOP_BACK_LEFT 15
#define TOP_BACK_CENTER 16
#define TOP_BACK_RIGHT 17
#define NUM_NAMED_CHANNELS 18
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
#endif
#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */
#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */
#define C30DB M_SQRT2
#define C15DB 1.189207115
#define C__0DB 1.0
#define C_15DB 0.840896415
#define C_30DB M_SQRT1_2
#define C_45DB 0.594603558
#define C_60DB 0.5
#define C30DB M_SQRT2
#define C15DB 1.189207115
#define C__0DB 1.0
#define C_15DB 0.840896415
#define C_30DB M_SQRT1_2
#define C_45DB 0.594603558
#define C_60DB 0.5
static cubeb_channel_layout
cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
{
if (l == CUBEB_LAYOUT_UNDEFINED) {
switch (c) {
case 1: return CUBEB_LAYOUT_MONO;
case 2: return CUBEB_LAYOUT_STEREO;
}
if (l == CUBEB_LAYOUT_UNDEFINED) {
switch (c) {
case 1:
return CUBEB_LAYOUT_MONO;
case 2:
return CUBEB_LAYOUT_STEREO;
}
return l;
}
return l;
}
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
unsigned int
cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
{
#if __GNUC__ || __clang__
return __builtin_popcount (x);
return __builtin_popcount(x);
#else
x -= (x >> 1) & 0x55555555;
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
@ -87,16 +90,12 @@ unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
}
struct MixerContext {
MixerContext(cubeb_sample_format f,
uint32_t in_channels,
cubeb_channel_layout in,
uint32_t out_channels,
MixerContext(cubeb_sample_format f, uint32_t in_channels,
cubeb_channel_layout in, uint32_t out_channels,
cubeb_channel_layout out)
: _format(f)
, _in_ch_layout(cubeb_channel_layout_check(in, in_channels))
, _out_ch_layout(cubeb_channel_layout_check(out, out_channels))
, _in_ch_count(in_channels)
, _out_ch_count(out_channels)
: _format(f), _in_ch_layout(cubeb_channel_layout_check(in, in_channels)),
_out_ch_layout(cubeb_channel_layout_check(out, out_channels)),
_in_ch_count(in_channels), _out_ch_count(out_channels)
{
if (in_channels != cubeb_channel_layout_nb_channels(in) ||
out_channels != cubeb_channel_layout_nb_channels(out)) {
@ -159,24 +158,30 @@ struct MixerContext {
int init();
const cubeb_sample_format _format;
const cubeb_channel_layout _in_ch_layout; ///< input channel layout
const cubeb_channel_layout _out_ch_layout; ///< output channel layout
const uint32_t _in_ch_count; ///< input channel count
const uint32_t _out_ch_count; ///< output channel count
const float _surround_mix_level = C_30DB; ///< surround mixing level
const float _center_mix_level = C_30DB; ///< center mixing level
const float _lfe_mix_level = 1; ///< LFE mixing level
double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< floating point rematrixing coefficients
float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< single precision floating point rematrixing coefficients
int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< 17.15 fixed point rematrixing coefficients
uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }}; ///< Lists of input channels per output channel that have non zero rematrixing coefficients
bool _clipping = false; ///< Set to true if clipping detection is required
bool _valid = false; ///< Set to true if context is valid.
const cubeb_channel_layout _in_ch_layout; ///< input channel layout
const cubeb_channel_layout _out_ch_layout; ///< output channel layout
const uint32_t _in_ch_count; ///< input channel count
const uint32_t _out_ch_count; ///< output channel count
const float _surround_mix_level = C_30DB; ///< surround mixing level
const float _center_mix_level = C_30DB; ///< center mixing level
const float _lfe_mix_level = 1; ///< LFE mixing level
double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {
{0}}; ///< floating point rematrixing coefficients
float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {
{0}}; ///< single precision floating point rematrixing coefficients
int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {
{0}}; ///< 17.15 fixed point rematrixing coefficients
uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX + 1] = {
{0}}; ///< Lists of input channels per output channel that have non zero
///< rematrixing coefficients
bool _clipping = false; ///< Set to true if clipping detection is required
bool _valid = false; ///< Set to true if context is valid.
};
int MixerContext::auto_matrix()
int
MixerContext::auto_matrix()
{
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } };
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = {{0}};
double maxcoef = 0;
float maxval;
@ -239,8 +244,7 @@ int MixerContext::auto_matrix()
matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][BACK_CENTER] +=
_surround_mix_level * M_SQRT1_2;
matrix[FRONT_CENTER][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
}
}
if (unaccounted & CHANNEL_BACK_LEFT) {
@ -321,7 +325,7 @@ int MixerContext::auto_matrix()
_matrix[out_i][in_i] = matrix[i][j];
} else {
_matrix[out_i][in_i] =
i == j && (in_ch_layout & out_ch_layout & (1U << i));
i == j && (in_ch_layout & out_ch_layout & (1U << i));
}
sum += fabs(_matrix[out_i][in_i]);
in_i++;
@ -356,7 +360,8 @@ int MixerContext::auto_matrix()
return 0;
}
int MixerContext::init()
int
MixerContext::init()
{
int r = auto_matrix();
if (r) {
@ -398,22 +403,15 @@ int MixerContext::init()
return 0;
}
template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
void
sum2(TYPE_SAMPLE * out,
uint32_t stride_out,
const TYPE_SAMPLE * in1,
const TYPE_SAMPLE * in2,
uint32_t stride_in,
TYPE_COEFF coeff1,
TYPE_COEFF coeff2,
F&& operand,
uint32_t frames)
sum2(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in1,
const TYPE_SAMPLE * in2, uint32_t stride_in, TYPE_COEFF coeff1,
TYPE_COEFF coeff2, F && operand, uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF,
typename std::result_of<F(TYPE_COEFF)>::type>::value,
"function must return the same type as used by matrix_coeff");
std::is_same<TYPE_COEFF, decltype(operand(coeff1))>::value,
"function must return the same type as used by coeff1 and coeff2");
for (uint32_t i = 0; i < frames; i++) {
*out = operand(coeff1 * *in1 + coeff2 * *in2);
out += stride_out;
@ -422,20 +420,13 @@ sum2(TYPE_SAMPLE * out,
}
}
template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
template <typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
void
copy(TYPE_SAMPLE * out,
uint32_t stride_out,
const TYPE_SAMPLE * in,
uint32_t stride_in,
TYPE_COEFF coeff,
F&& operand,
uint32_t frames)
copy(TYPE_SAMPLE * out, uint32_t stride_out, const TYPE_SAMPLE * in,
uint32_t stride_in, TYPE_COEFF coeff, F && operand, uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF,
typename std::result_of<F(TYPE_COEFF)>::type>::value,
"function must return the same type as used by matrix_coeff");
static_assert(std::is_same<TYPE_COEFF, decltype(operand(coeff))>::value,
"function must return the same type as used by coeff");
for (uint32_t i = 0; i < frames; i++) {
*out = operand(coeff * *in);
out += stride_out;
@ -444,74 +435,58 @@ copy(TYPE_SAMPLE * out,
}
template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
const TYPE_COEFF (&matrix_coeff)[COLS][COLS],
F&& aF, uint32_t frames)
static int
rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
const TYPE_COEFF (&matrix_coeff)[COLS][COLS], F && aF, uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF,
typename std::result_of<F(TYPE_COEFF)>::type>::value,
"function must return the same type as used by matrix_coeff");
std::is_same<TYPE_COEFF, decltype(aF(matrix_coeff[0][0]))>::value,
"function must return the same type as used by matrix_coeff");
for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
TYPE* out = aOut + out_i;
TYPE * out = aOut + out_i;
switch (s->_matrix_ch[out_i][0]) {
case 0:
for (uint32_t i = 0; i < frames; i++) {
out[i * s->_out_ch_count] = 0;
case 0:
for (uint32_t i = 0; i < frames; i++) {
out[i * s->_out_ch_count] = 0;
}
break;
case 1: {
int in_i = s->_matrix_ch[out_i][1];
copy(out, s->_out_ch_count, aIn + in_i, s->_in_ch_count,
matrix_coeff[out_i][in_i], aF, frames);
} break;
case 2:
sum2(out, s->_out_ch_count, aIn + s->_matrix_ch[out_i][1],
aIn + s->_matrix_ch[out_i][2], s->_in_ch_count,
matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
matrix_coeff[out_i][s->_matrix_ch[out_i][2]], aF, frames);
break;
default:
for (uint32_t i = 0; i < frames; i++) {
TYPE_COEFF v = 0;
for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
v += *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
}
break;
case 1: {
int in_i = s->_matrix_ch[out_i][1];
copy(out,
s->_out_ch_count,
aIn + in_i,
s->_in_ch_count,
matrix_coeff[out_i][in_i],
aF,
frames);
} break;
case 2:
sum2(out,
s->_out_ch_count,
aIn + s->_matrix_ch[out_i][1],
aIn + s->_matrix_ch[out_i][2],
s->_in_ch_count,
matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
matrix_coeff[out_i][s->_matrix_ch[out_i][2]],
aF,
frames);
break;
default:
for (uint32_t i = 0; i < frames; i++) {
TYPE_COEFF v = 0;
for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
v +=
*(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
}
out[i * s->_out_ch_count] = aF(v);
}
break;
out[i * s->_out_ch_count] = aF(v);
}
break;
}
}
return 0;
}
struct cubeb_mixer
{
cubeb_mixer(cubeb_sample_format format,
uint32_t in_channels,
cubeb_channel_layout in_layout,
uint32_t out_channels,
struct cubeb_mixer {
cubeb_mixer(cubeb_sample_format format, uint32_t in_channels,
cubeb_channel_layout in_layout, uint32_t out_channels,
cubeb_channel_layout out_layout)
: _context(format, in_channels, in_layout, out_channels, out_layout)
: _context(format, in_channels, in_layout, out_channels, out_layout)
{
}
template<typename T>
void copy_and_trunc(size_t frames,
const T * input_buffer,
template <typename T>
void copy_and_trunc(size_t frames, const T * input_buffer,
T * output_buffer) const
{
if (_context._in_ch_count <= _context._out_ch_count) {
@ -545,11 +520,8 @@ struct cubeb_mixer
}
}
int mix(size_t frames,
const void * input_buffer,
size_t input_buffer_size,
void * output_buffer,
size_t output_buffer_size) const
int mix(size_t frames, const void * input_buffer, size_t input_buffer_size,
void * output_buffer, size_t output_buffer_size) const
{
if (frames <= 0 || _context._out_ch_count == 0) {
return 0;
@ -557,7 +529,7 @@ struct cubeb_mixer
// Check if output buffer is of sufficient size.
size_t size_read_needed =
frames * _context._in_ch_count * cubeb_sample_size(_context._format);
frames * _context._in_ch_count * cubeb_sample_size(_context._format);
if (input_buffer_size < size_read_needed) {
// We don't have enough data to read!
return -1;
@ -571,58 +543,46 @@ struct cubeb_mixer
// The channel layouts were invalid or unsupported, instead we will simply
// either drop the extra channels, or fill with silence the missing ones
if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
copy_and_trunc(frames,
static_cast<const float*>(input_buffer),
static_cast<float*>(output_buffer));
copy_and_trunc(frames, static_cast<const float *>(input_buffer),
static_cast<float *>(output_buffer));
} else {
assert(_context._format == CUBEB_SAMPLE_S16NE);
copy_and_trunc(frames,
static_cast<const int16_t*>(input_buffer),
reinterpret_cast<int16_t*>(output_buffer));
copy_and_trunc(frames, static_cast<const int16_t *>(input_buffer),
reinterpret_cast<int16_t *>(output_buffer));
}
return 0;
}
switch (_context._format)
{
case CUBEB_SAMPLE_FLOAT32NE: {
auto f = [](float x) { return x; };
return rematrix(&_context,
static_cast<float*>(output_buffer),
static_cast<const float*>(input_buffer),
_context._matrix_flt,
f,
frames);
switch (_context._format) {
case CUBEB_SAMPLE_FLOAT32NE: {
auto f = [](float x) { return x; };
return rematrix(&_context, static_cast<float *>(output_buffer),
static_cast<const float *>(input_buffer),
_context._matrix_flt, f, frames);
}
case CUBEB_SAMPLE_S16NE:
if (_context._clipping) {
auto f = [](int x) {
int y = (x + 16384) >> 15;
// clip the signed integer value into the -32768,32767 range.
if ((y + 0x8000U) & ~0xFFFF) {
return (y >> 31) ^ 0x7FFF;
}
return y;
};
return rematrix(&_context, static_cast<int16_t *>(output_buffer),
static_cast<const int16_t *>(input_buffer),
_context._matrix32, f, frames);
} else {
auto f = [](int x) { return (x + 16384) >> 15; };
return rematrix(&_context, static_cast<int16_t *>(output_buffer),
static_cast<const int16_t *>(input_buffer),
_context._matrix32, f, frames);
}
case CUBEB_SAMPLE_S16NE:
if (_context._clipping) {
auto f = [](int x) {
int y = (x + 16384) >> 15;
// clip the signed integer value into the -32768,32767 range.
if ((y + 0x8000U) & ~0xFFFF) {
return (y >> 31) ^ 0x7FFF;
}
return y;
};
return rematrix(&_context,
static_cast<int16_t*>(output_buffer),
static_cast<const int16_t*>(input_buffer),
_context._matrix32,
f,
frames);
} else {
auto f = [](int x) { return (x + 16384) >> 15; };
return rematrix(&_context,
static_cast<int16_t*>(output_buffer),
static_cast<const int16_t*>(input_buffer),
_context._matrix32,
f,
frames);
}
break;
default:
assert(false);
break;
break;
default:
assert(false);
break;
}
return -1;
@ -636,28 +596,26 @@ struct cubeb_mixer
MixerContext _context;
};
cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format,
uint32_t in_channels,
cubeb_channel_layout in_layout,
uint32_t out_channels,
cubeb_channel_layout out_layout)
cubeb_mixer *
cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels,
cubeb_channel_layout in_layout, uint32_t out_channels,
cubeb_channel_layout out_layout)
{
return new cubeb_mixer(
format, in_channels, in_layout, out_channels, out_layout);
return new cubeb_mixer(format, in_channels, in_layout, out_channels,
out_layout);
}
void cubeb_mixer_destroy(cubeb_mixer * mixer)
void
cubeb_mixer_destroy(cubeb_mixer * mixer)
{
delete mixer;
}
int cubeb_mixer_mix(cubeb_mixer * mixer,
size_t frames,
const void * input_buffer,
size_t input_buffer_size,
void * output_buffer,
size_t output_buffer_size)
int
cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer,
size_t input_buffer_size, void * output_buffer,
size_t output_buffer_size)
{
return mixer->mix(
frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size);
return mixer->mix(frames, input_buffer, input_buffer_size, output_buffer,
output_buffer_size);
}

View file

@ -15,20 +15,19 @@ extern "C" {
#endif
typedef struct cubeb_mixer cubeb_mixer;
cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
uint32_t in_channels,
cubeb_channel_layout in_layout,
uint32_t out_channels,
cubeb_channel_layout out_layout);
void cubeb_mixer_destroy(cubeb_mixer * mixer);
int cubeb_mixer_mix(cubeb_mixer * mixer,
size_t frames,
const void * input_buffer,
size_t input_buffer_size,
void * output_buffer,
size_t output_buffer_size);
cubeb_mixer *
cubeb_mixer_create(cubeb_sample_format format, uint32_t in_channels,
cubeb_channel_layout in_layout, uint32_t out_channels,
cubeb_channel_layout out_layout);
void
cubeb_mixer_destroy(cubeb_mixer * mixer);
int
cubeb_mixer_mix(cubeb_mixer * mixer, size_t frames, const void * input_buffer,
size_t input_buffer_size, void * output_buffer,
size_t output_buffer_size);
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
unsigned int
cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
#if defined(__cplusplus)
}

File diff suppressed because it is too large Load diff

View file

@ -10,25 +10,25 @@
* accompanying file LICENSE for details.
*/
#include <assert.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-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_mixer.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. */
#ifndef OSS_PREFER_RATE
@ -55,25 +55,25 @@
#define ENV_AUDIO_DEVICE "AUDIO_DEVICE"
#ifndef OSS_MAX_CHANNELS
# if defined(__FreeBSD__) || defined(__DragonFly__)
#if defined(__FreeBSD__) || defined(__DragonFly__)
/*
* The current maximum number of channels supported
* on FreeBSD is 8.
*
* Reference: FreeBSD 12.1-RELEASE
*/
# define OSS_MAX_CHANNELS (8)
# elif defined(__sun__)
#define OSS_MAX_CHANNELS (8)
#elif defined(__sun__)
/*
* The current maximum number of channels supported
* on Illumos is 16.
*
* Reference: PSARC 2008/318
*/
# define OSS_MAX_CHANNELS (16)
# else
# define OSS_MAX_CHANNELS (2)
# endif
#define OSS_MAX_CHANNELS (16)
#else
#define OSS_MAX_CHANNELS (2)
#endif
#endif
#if defined(__FreeBSD__) || defined(__DragonFly__)
@ -89,13 +89,15 @@ struct cubeb {
/* Our intern string store */
pthread_mutex_t mutex; /* protects devid_strs */
cubeb_strings *devid_strs;
cubeb_strings * devid_strs;
};
struct oss_stream {
oss_devnode_t name;
int fd;
void * buf;
unsigned int bufframes;
unsigned int maxframes;
struct stream_info {
int channels;
@ -112,29 +114,26 @@ struct cubeb_stream {
struct cubeb * context;
void * user_ptr;
pthread_t thread;
bool doorbell; /* (m) */
bool doorbell; /* (m) */
pthread_cond_t doorbell_cv; /* (m) */
pthread_cond_t stopped_cv; /* (m) */
pthread_cond_t stopped_cv; /* (m) */
pthread_mutex_t mtx; /* Members protected by this should be marked (m) */
bool thread_created; /* (m) */
bool running; /* (m) */
bool destroying; /* (m) */
cubeb_state state; /* (m) */
bool running; /* (m) */
bool destroying; /* (m) */
cubeb_state state; /* (m) */
float volume /* (m) */;
struct oss_stream play;
struct oss_stream record;
cubeb_data_callback data_cb;
cubeb_state_callback state_cb;
uint64_t frames_written /* (m) */;
unsigned int nfr; /* Number of frames allocated */
unsigned int nfrags;
unsigned int bufframes;
};
static char const *
oss_cubeb_devid_intern(cubeb *context, char const * devid)
oss_cubeb_devid_intern(cubeb * context, char const * devid)
{
char const *is;
char const * is;
pthread_mutex_lock(&context->mutex);
is = cubeb_strings_intern(context->devid_strs, devid);
pthread_mutex_unlock(&context->mutex);
@ -142,7 +141,8 @@ oss_cubeb_devid_intern(cubeb *context, char const * devid)
}
int
oss_init(cubeb **context, char const *context_name) {
oss_init(cubeb ** context, char const * context_name)
{
cubeb * c;
(void)context_name;
@ -211,7 +211,7 @@ oss_get_min_latency(cubeb * context, cubeb_stream_params params,
}
static void
oss_free_cubeb_device_info_strings(cubeb_device_info *cdi)
oss_free_cubeb_device_info_strings(cubeb_device_info * cdi)
{
free((char *)cdi->device_id);
free((char *)cdi->friendly_name);
@ -230,8 +230,8 @@ oss_free_cubeb_device_info_strings(cubeb_device_info *cdi)
* Return 0 if OK, otherwise 1.
*/
static int
oss_probe_open(const char *dsppath, cubeb_device_type type,
int *fdp, oss_audioinfo *resai)
oss_probe_open(const char * dsppath, cubeb_device_type type, int * fdp,
oss_audioinfo * resai)
{
oss_audioinfo ai;
int error;
@ -258,81 +258,81 @@ oss_probe_open(const char *dsppath, cubeb_device_type type,
struct sndstat_info {
oss_devnode_t devname;
const char *desc;
const char * desc;
cubeb_device_type type;
int preferred;
};
static int
oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo)
oss_sndstat_line_parse(char * line, int is_ud, struct sndstat_info * sinfo)
{
char *matchptr = line, *n = NULL;
struct sndstat_info res;
char *matchptr = line, *n = NULL;
struct sndstat_info res;
memset(&res, 0, sizeof(res));
memset(&res, 0, sizeof(res));
n = strchr(matchptr, ':');
if (n == NULL)
n = strchr(matchptr, ':');
if (n == NULL)
goto fail;
if (is_ud == 0) {
unsigned int devunit;
if (sscanf(matchptr, "pcm%u: ", &devunit) < 1)
goto fail;
if (is_ud == 0) {
unsigned int devunit;
if (sscanf(matchptr, "pcm%u: ", &devunit) < 1)
goto fail;
if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1)
goto fail;
} else {
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
goto fail;
snprintf(res.devname, sizeof(res.devname), "/dev/");
strncat(res.devname, matchptr, n - matchptr);
}
matchptr = n + 1;
n = strchr(matchptr, '<');
if (n == NULL)
if (snprintf(res.devname, sizeof(res.devname), "/dev/dsp%u", devunit) < 1)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, '>');
if (n == NULL)
} else {
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
goto fail;
*n = 0;
res.desc = matchptr;
matchptr = n + 1;
n = strchr(matchptr, '(');
if (n == NULL)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, ')');
if (n == NULL)
goto fail;
*n = 0;
if (!isdigit(matchptr[0])) {
if (strstr(matchptr, "play") != NULL)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (strstr(matchptr, "rec") != NULL)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
} else {
int p, r;
if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2)
goto fail;
if (p > 0)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (r > 0)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
}
matchptr = n + 1;
if (strstr(matchptr, "default") != NULL)
res.preferred = 1;
strlcpy(res.devname, "/dev/", sizeof(res.devname));
strncat(res.devname, matchptr, n - matchptr);
}
matchptr = n + 1;
*sinfo = res;
return 0;
n = strchr(matchptr, '<');
if (n == NULL)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, '>');
if (n == NULL)
goto fail;
*n = 0;
res.desc = matchptr;
matchptr = n + 1;
n = strchr(matchptr, '(');
if (n == NULL)
goto fail;
matchptr = n + 1;
n = strrchr(matchptr, ')');
if (n == NULL)
goto fail;
*n = 0;
if (!isdigit(matchptr[0])) {
if (strstr(matchptr, "play") != NULL)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (strstr(matchptr, "rec") != NULL)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
} else {
int p, r;
if (sscanf(matchptr, "%dp:%*dv/%dr:%*dv", &p, &r) != 2)
goto fail;
if (p > 0)
res.type |= CUBEB_DEVICE_TYPE_OUTPUT;
if (r > 0)
res.type |= CUBEB_DEVICE_TYPE_INPUT;
}
matchptr = n + 1;
if (strstr(matchptr, "default") != NULL)
res.preferred = 1;
*sinfo = res;
return 0;
fail:
return 1;
return 1;
}
/*
@ -344,10 +344,10 @@ static int
oss_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
cubeb_device_info *devinfop = NULL;
char *line = NULL;
cubeb_device_info * devinfop = NULL;
char * line = NULL;
size_t linecap = 0;
FILE *sndstatfp = NULL;
FILE * sndstatfp = NULL;
int collection_cnt = 0;
int is_ud = 0;
int skipall = 0;
@ -360,7 +360,7 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
if (sndstatfp == NULL)
goto fail;
while (getline(&line, &linecap, sndstatfp) > 0) {
const char *devid = NULL;
const char * devid = NULL;
struct sndstat_info sinfo;
oss_audioinfo ai;
@ -373,7 +373,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
skipall = 0;
continue;
}
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR, strlen(SNDSTAT_USER_BEGIN_STR))) {
if (!strncmp(line, SNDSTAT_USER_BEGIN_STR,
strlen(SNDSTAT_USER_BEGIN_STR))) {
is_ud = 1;
skipall = 0;
continue;
@ -433,8 +434,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
collection_cnt++;
void *newp = reallocarray(devinfop, collection_cnt + 1,
sizeof(cubeb_device_info));
void * newp =
reallocarray(devinfop, collection_cnt + 1, sizeof(cubeb_device_info));
if (newp == NULL)
goto fail;
devinfop = newp;
@ -464,7 +465,7 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
{
oss_sysinfo si;
int error, i;
cubeb_device_info *devinfop = NULL;
cubeb_device_info * devinfop = NULL;
int collection_cnt = 0;
int mixer_fd = -1;
@ -476,7 +477,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
error = ioctl(mixer_fd, SNDCTL_SYSINFO, &si);
if (error) {
LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d", OSS_DEFAULT_MIXER, errno);
LOG("Failed to run SNDCTL_SYSINFO on mixer %s. errno: %d",
OSS_DEFAULT_MIXER, errno);
goto fail;
}
@ -487,8 +489,8 @@ oss_enumerate_devices(cubeb * context, cubeb_device_type type,
collection->count = 0;
for (i = 0; i < si.numaudios; i++) {
oss_audioinfo ai;
cubeb_device_info cdi = { 0 };
const char *devid = NULL;
cubeb_device_info cdi = {0};
const char * devid = NULL;
ai.dev = i;
error = ioctl(mixer_fd, SNDCTL_AUDIOINFO, &ai);
@ -574,24 +576,24 @@ static unsigned int
oss_chn_from_cubeb(cubeb_channel chn)
{
switch (chn) {
case CHANNEL_FRONT_LEFT:
return CHID_L;
case CHANNEL_FRONT_RIGHT:
return CHID_R;
case CHANNEL_FRONT_CENTER:
return CHID_C;
case CHANNEL_LOW_FREQUENCY:
return CHID_LFE;
case CHANNEL_BACK_LEFT:
return CHID_LR;
case CHANNEL_BACK_RIGHT:
return CHID_RR;
case CHANNEL_SIDE_LEFT:
return CHID_LS;
case CHANNEL_SIDE_RIGHT:
return CHID_RS;
default:
return CHID_UNDEF;
case CHANNEL_FRONT_LEFT:
return CHID_L;
case CHANNEL_FRONT_RIGHT:
return CHID_R;
case CHANNEL_FRONT_CENTER:
return CHID_C;
case CHANNEL_LOW_FREQUENCY:
return CHID_LFE;
case CHANNEL_BACK_LEFT:
return CHID_LR;
case CHANNEL_BACK_RIGHT:
return CHID_RR;
case CHANNEL_SIDE_LEFT:
return CHID_LS;
case CHANNEL_SIDE_RIGHT:
return CHID_RS;
default:
return CHID_UNDEF;
}
}
@ -648,7 +650,8 @@ oss_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
return CUBEB_ERROR;
}
/* Mono layout is an exception */
if (params->layout != CUBEB_LAYOUT_UNDEFINED && params->layout != CUBEB_LAYOUT_MONO) {
if (params->layout != CUBEB_LAYOUT_UNDEFINED &&
params->layout != CUBEB_LAYOUT_MONO) {
chnorder = oss_cubeb_layout_to_chnorder(params->layout);
if (ioctl(fd, SNDCTL_DSP_SET_CHNORDER, &chnorder) == -1)
LOG("Non-fatal error %d occured when setting channel order.", errno);
@ -748,7 +751,8 @@ oss_get_rec_frames(cubeb_stream * s, unsigned int nframes)
size_t read_ofs = 0;
while (rem > 0) {
ssize_t n;
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) < 0) {
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, rem)) <
0) {
if (errno == EINTR)
continue;
return CUBEB_ERROR;
@ -759,7 +763,6 @@ oss_get_rec_frames(cubeb_stream * s, unsigned int nframes)
return 0;
}
static int
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;
}
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 */
static int
oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
oss_audio_loop(cubeb_stream * s, cubeb_state * new_state)
{
cubeb_state state = CUBEB_STATE_STOPPED;
int trig = 0;
int drain = 0;
int trig = 0, drain = 0;
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 (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;
goto breakdown;
}
trig |= PCM_ENABLE_INPUT;
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig)) {
memset(s->record.buf, 0, s->record.bufframes * s->record.frame_size);
if (ioctl(s->record.fd, SNDCTL_DSP_SETTRIGGER, &trig) == -1) {
LOG("Error %d occured when setting trigger on record fd", errno);
state = CUBEB_STATE_ERROR;
goto breakdown;
}
memset(s->record.buf, 0, s->bufframes * s->record.frame_size);
}
if (!play_on && !record_on) {
@ -826,12 +899,36 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
long got = 0;
if (nfr > 0) {
if (record_on) {
if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (s->record.floating) {
oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr);
}
}
got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr);
if (got == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (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;
pthread_mutex_lock(&s->mtx);
@ -844,63 +941,21 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
oss_linear16_set_vol((int16_t *)s->play.buf,
s->play.info.channels * got, vol);
}
}
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;
if (oss_put_play_frames(s, got) == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
}
nfr = 0;
}
if (got > 0) {
if (play_on && oss_put_play_frames(s, got) < 0) {
state = CUBEB_STATE_ERROR;
goto breakdown;
if (drain) {
state = CUBEB_STATE_DRAINED;
goto breakdown;
}
}
if (drain) {
state = CUBEB_STATE_DRAINED;
if (oss_wait_fds_for_space(s, &nfr) != 0) {
state = CUBEB_STATE_ERROR;
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 (nfr == 0) {
nfr = s->nfr;
}
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);
}
}
}
return 1;
@ -914,9 +969,9 @@ breakdown:
}
static void *
oss_io_routine(void *arg)
oss_io_routine(void * arg)
{
cubeb_stream *s = arg;
cubeb_stream * s = arg;
cubeb_state new_state;
int stopped;
@ -957,9 +1012,10 @@ static inline int
oss_calc_frag_shift(unsigned int frames, unsigned int frame_size)
{
int n = 4;
int blksize = (frames * frame_size + OSS_NFRAGS - 1) / OSS_NFRAGS;
while ((1 << n) < blksize)
int blksize = frames * frame_size;
while ((1 << n) < blksize) {
n++;
}
return n;
}
@ -970,22 +1026,17 @@ oss_get_frag_params(unsigned int shift)
}
static int
oss_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
oss_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
unsigned int latency_frames, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
int ret = CUBEB_OK;
unsigned int playnfr = 0, recnfr = 0;
cubeb_stream *s = NULL;
const char *defdsp;
cubeb_stream * s = NULL;
const char * defdsp;
if (!(defdsp = getenv(ENV_AUDIO_DEVICE)) || *defdsp == '\0')
defdsp = OSS_DEFAULT_DEVICE;
@ -997,19 +1048,20 @@ oss_stream_init(cubeb * context,
}
s->state = CUBEB_STATE_STOPPED;
s->record.fd = s->play.fd = -1;
s->nfr = latency_frames;
if (input_device != NULL) {
snprintf(s->record.name, sizeof(s->record.name), "%s", input_device);
strlcpy(s->record.name, input_device, sizeof(s->record.name));
} else {
snprintf(s->record.name, sizeof(s->record.name), "%s", defdsp);
strlcpy(s->record.name, defdsp, sizeof(s->record.name));
}
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 {
snprintf(s->play.name, sizeof(s->play.name), "%s", defdsp);
strlcpy(s->play.name, defdsp, sizeof(s->play.name));
}
if (input_stream_params != NULL) {
unsigned int nb_channels;
uint32_t minframes;
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
LOG("Loopback not supported");
ret = CUBEB_ERROR_NOT_SUPPORTED;
@ -1018,48 +1070,56 @@ oss_stream_init(cubeb * context,
nb_channels = cubeb_channel_layout_nb_channels(input_stream_params->layout);
if (input_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
nb_channels != input_stream_params->channels) {
LOG("input_stream_params->layout does not match input_stream_params->channels");
LOG("input_stream_params->layout does not match "
"input_stream_params->channels");
ret = CUBEB_ERROR_INVALID_PARAMETER;
goto error;
}
if (s->record.fd == -1) {
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
LOG("Audio device \"%s\" could not be opened as read-only",
s->record.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((s->record.fd = open(s->record.name, O_RDONLY)) == -1) {
LOG("Audio device \"%s\" could not be opened as read-only",
s->record.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((ret = oss_copy_params(s->record.fd, s, input_stream_params,
&s->record.info)) != CUBEB_OK) {
LOG("Setting record params failed");
goto error;
}
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->record.frame_size = s->record.info.channels * (s->record.info.precision / 8);
recnfr = (1 << oss_calc_frag_shift(s->nfr, s->record.frame_size)) / s->record.frame_size;
s->record.floating =
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->record.frame_size =
s->record.info.channels * (s->record.info.precision / 8);
s->record.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) {
unsigned int nb_channels;
uint32_t minframes;
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
LOG("Loopback not supported");
ret = CUBEB_ERROR_NOT_SUPPORTED;
goto error;
}
nb_channels = cubeb_channel_layout_nb_channels(output_stream_params->layout);
nb_channels =
cubeb_channel_layout_nb_channels(output_stream_params->layout);
if (output_stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
nb_channels != output_stream_params->channels) {
LOG("output_stream_params->layout does not match output_stream_params->channels");
LOG("output_stream_params->layout does not match "
"output_stream_params->channels");
ret = CUBEB_ERROR_INVALID_PARAMETER;
goto error;
}
if (s->play.fd == -1) {
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
LOG("Audio device \"%s\" could not be opened as write-only",
s->play.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((s->play.fd = open(s->play.name, O_WRONLY)) == -1) {
LOG("Audio device \"%s\" could not be opened as write-only",
s->play.name);
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
if ((ret = oss_copy_params(s->play.fd, s, output_stream_params,
&s->play.info)) != CUBEB_OK) {
@ -1068,17 +1128,16 @@ oss_stream_init(cubeb * context,
}
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->play.frame_size = s->play.info.channels * (s->play.info.precision / 8);
playnfr = (1 << oss_calc_frag_shift(s->nfr, s->play.frame_size)) / s->play.frame_size;
s->play.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) {
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))
LOG("Failed to set play fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
frag);
@ -1086,12 +1145,28 @@ oss_stream_init(cubeb * context,
if (ioctl(s->play.fd, SNDCTL_DSP_GETOSPACE, &bi))
LOG("Failed to get play fd's buffer info.");
else {
if (bi.fragsize / s->play.frame_size < s->nfr)
s->nfr = bi.fragsize / s->play.frame_size;
s->play.bufframes = (bi.fragsize * bi.fragstotal) / 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) {
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))
LOG("Failed to set record fd with SNDCTL_DSP_SETFRAGMENT. frag: 0x%x",
frag);
@ -1099,11 +1174,16 @@ oss_stream_init(cubeb * context,
if (ioctl(s->record.fd, SNDCTL_DSP_GETISPACE, &bi))
LOG("Failed to get record fd's buffer info.");
else {
if (bi.fragsize / s->record.frame_size < s->nfr)
s->nfr = bi.fragsize / s->record.frame_size;
s->record.bufframes =
(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->volume = 1.0;
s->state_cb = state_callback;
@ -1125,13 +1205,14 @@ oss_stream_init(cubeb * context,
s->doorbell = false;
if (s->play.fd != -1) {
if ((s->play.buf = calloc(s->bufframes, s->play.frame_size)) == NULL) {
if ((s->play.buf = calloc(s->play.bufframes, s->play.frame_size)) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
}
if (s->record.fd != -1) {
if ((s->record.buf = calloc(s->bufframes, s->record.frame_size)) == NULL) {
if ((s->record.buf = calloc(s->record.bufframes, s->record.frame_size)) ==
NULL) {
ret = CUBEB_ERROR;
goto error;
}
@ -1225,10 +1306,10 @@ oss_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
if (*device == NULL) {
return CUBEB_ERROR;
}
(*device)->input_name = stream->record.fd != -1 ?
strdup(stream->record.name) : NULL;
(*device)->output_name = stream->play.fd != -1 ?
strdup(stream->play.name) : NULL;
(*device)->input_name =
stream->record.fd != -1 ? strdup(stream->record.name) : NULL;
(*device)->output_name =
stream->play.fd != -1 ? strdup(stream->play.name) : NULL;
return CUBEB_OK;
}
@ -1255,7 +1336,6 @@ static struct cubeb_ops const oss_ops = {
.stream_destroy = oss_stream_destroy,
.stream_start = oss_stream_start,
.stream_stop = oss_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = oss_stream_get_position,
.stream_get_latency = oss_stream_get_latency,
.stream_get_input_latency = NULL,

View file

@ -5,31 +5,29 @@
* accompanying file LICENSE for details.
*/
#include <cubeb/cubeb.h>
#include "cubeb_osx_run_loop.h"
#include "cubeb_log.h"
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/AudioHardware.h>
#include <CoreAudio/HostTime.h>
#include <CoreFoundation/CoreFoundation.h>
#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
* not the main thread. If we don't do that, they are not called, or a crash
* occur, depending on the OSX version. */
AudioObjectPropertyAddress runloop_address = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster};
CFRunLoopRef run_loop = nullptr;
OSStatus r;
r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
&runloop_address,
0, NULL, sizeof(CFRunLoopRef), &run_loop);
r = AudioObjectSetPropertyData(kAudioObjectSystemObject, &runloop_address, 0,
NULL, sizeof(CFRunLoopRef), &run_loop);
if (r != noErr) {
LOG("Could not make global CoreAudio notifications use their own thread.");
}

View file

@ -15,7 +15,8 @@
extern "C" {
#endif
void cubeb_set_coreaudio_notification_runloop();
void
cubeb_set_coreaudio_notification_runloop();
#if defined(__cplusplus)
}

File diff suppressed because it is too large Load diff

View file

@ -8,21 +8,21 @@
#define NOMINMAX
#endif // NOMINMAX
#include <algorithm>
#include <cmath>
#include <cassert>
#include <cstring>
#include <cstddef>
#include <cstdio>
#include "cubeb_resampler.h"
#include "cubeb-speex-resampler.h"
#include "cubeb_resampler_internal.h"
#include "cubeb_utils.h"
#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
int
to_speex_quality(cubeb_resampler_quality q)
{
switch(q) {
switch (q) {
case CUBEB_RESAMPLER_QUALITY_VOIP:
return SPEEX_RESAMPLER_QUALITY_VOIP;
case CUBEB_RESAMPLER_QUALITY_DEFAULT:
@ -35,34 +35,34 @@ to_speex_quality(cubeb_resampler_quality q)
}
}
uint32_t min_buffered_audio_frame(uint32_t sample_rate)
uint32_t
min_buffered_audio_frame(uint32_t sample_rate)
{
return sample_rate / 20;
}
template<typename T>
template <typename T>
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels,
uint32_t sample_rate)
: processor(input_channels)
, stream(s)
, data_callback(cb)
, user_ptr(ptr)
, sample_rate(sample_rate)
: processor(input_channels), stream(s), data_callback(cb), user_ptr(ptr),
sample_rate(sample_rate)
{
}
template<typename T>
long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames)
template <typename T>
long
passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames)
{
if (input_buffer) {
assert(input_frames_count);
}
assert((input_buffer && output_buffer) ||
(output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
(output_buffer && !input_buffer &&
(!input_frames_count || *input_frames_count == 0)) ||
(input_buffer && !output_buffer && output_frames == 0));
// When we have no pending input data and exactly as much input
@ -71,41 +71,44 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
void * in_buf = input_buffer;
unsigned long pop_input_count = 0u;
if (input_buffer && !output_buffer) {
output_frames = *input_frames_count;
} else if(input_buffer) {
output_frames = *input_frames_count;
} else if (input_buffer) {
if (internal_input_buffer.length() != 0 ||
*input_frames_count < output_frames) {
// If we have pending input data left and have to first append the input
// so we can pass it as one pointer to the callback. Or this is a glitch.
// It can happen when system's performance is poor. Audible silence is
// being pushed at the end of the short input buffer. An improvement for
// the future is to resample to the output number of frames, when that happens.
internal_input_buffer.push(static_cast<T*>(input_buffer),
// the future is to resample to the output number of frames, when that
// happens.
internal_input_buffer.push(static_cast<T *>(input_buffer),
frames_to_samples(*input_frames_count));
if (internal_input_buffer.length() < frames_to_samples(output_frames)) {
// This is unxpected but it can happen when a glitch occurs. Fill the
// buffer with silence. First keep the actual number of input samples
// used without the silence.
pop_input_count = internal_input_buffer.length();
internal_input_buffer.push_silence(
frames_to_samples(output_frames) - internal_input_buffer.length());
internal_input_buffer.push_silence(frames_to_samples(output_frames) -
internal_input_buffer.length());
} else {
pop_input_count = frames_to_samples(output_frames);
}
in_buf = internal_input_buffer.data();
} else if(*input_frames_count > output_frames) {
} else if (*input_frames_count > output_frames) {
// In this case we have more input that we need output and
// fill the overflowing input into internal_input_buffer
// Since we have no other pending data, we can nonetheless
// pass the current input data directly to the callback
assert(pop_input_count == 0);
unsigned long samples_off = frames_to_samples(output_frames);
internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
frames_to_samples(*input_frames_count - output_frames));
internal_input_buffer.push(
static_cast<T *>(input_buffer) + samples_off,
frames_to_samples(*input_frames_count - output_frames));
}
}
long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
long rv =
data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
if (input_buffer) {
if (pop_input_count) {
@ -124,51 +127,47 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
template class passthrough_resampler<float>;
template class passthrough_resampler<short>;
template<typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::cubeb_resampler_speex(InputProcessor * input_processor,
OutputProcessor * output_processor,
cubeb_stream * s,
cubeb_data_callback cb,
void * ptr)
: input_processor(input_processor)
, output_processor(output_processor)
, stream(s)
, data_callback(cb)
, user_ptr(ptr)
template <typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::
cubeb_resampler_speex(InputProcessor * input_processor,
OutputProcessor * output_processor, cubeb_stream * s,
cubeb_data_callback cb, void * ptr)
: input_processor(input_processor), output_processor(output_processor),
stream(s), data_callback(cb), user_ptr(ptr)
{
if (input_processor && output_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
} else if (input_processor) {
} else if (input_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_input;
} else if (output_processor) {
} else if (output_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_output;
}
}
template<typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::~cubeb_resampler_speex()
{ }
template<typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed)
template <typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor,
OutputProcessor>::~cubeb_resampler_speex()
{
/* 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
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill_internal_output(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed)
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill(
void * input_buffer, long * input_frames_count, void * output_buffer,
long output_frames_needed)
{
/* Input and output buffers, typed */
T * in_buffer = reinterpret_cast<T *>(input_buffer);
T * out_buffer = reinterpret_cast<T *>(output_buffer);
return (this->*fill_internal)(in_buffer, input_frames_count, out_buffer,
output_frames_needed);
}
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_output(
T * input_buffer, long * input_frames_count, T * output_buffer,
long output_frames_needed)
{
assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
output_buffer && output_frames_needed);
@ -180,13 +179,12 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
/* fill directly the input buffer of the output processor to save a copy */
output_frames_before_processing =
output_processor->input_needed_for_output(output_frames_needed);
output_processor->input_needed_for_output(output_frames_needed);
out_unprocessed =
output_processor->input_buffer(output_frames_before_processing);
output_processor->input_buffer(output_frames_before_processing);
got = data_callback(stream, user_ptr,
nullptr, out_unprocessed,
got = data_callback(stream, user_ptr, nullptr, out_unprocessed,
output_frames_before_processing);
if (got < output_frames_before_processing) {
@ -201,52 +199,62 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
}
/* Process the output. If not enough frames have been returned from the
* callback, drain the processors. */
* callback, drain the processors. */
return output_processor->output(output_buffer, output_frames_needed);
}
template<typename T, typename InputProcessor, typename OutputProcessor>
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill_internal_input(T * input_buffer, long * input_frames_count,
T * output_buffer, long /*output_frames_needed*/)
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_input(
T * input_buffer, long * input_frames_count, T * output_buffer,
long /*output_frames_needed*/)
{
assert(input_buffer && input_frames_count && *input_frames_count &&
!output_buffer);
/* The input data, after eventual resampling. This is passed to the callback. */
/* The input data, after eventual resampling. This is passed to the callback.
*/
T * resampled_input = nullptr;
uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
uint32_t resampled_frame_count =
input_processor->output_for_input(*input_frames_count);
/* process the input, and present exactly `output_frames_needed` in the
* callback. */
* callback. */
input_processor->input(input_buffer, *input_frames_count);
/* resampled_frame_count == 0 happens if the resampler
* doesn't have enough input frames buffered to produce 1 resampled frame. */
if (resampled_frame_count == 0) {
return *input_frames_count;
}
size_t frames_resampled = 0;
resampled_input = input_processor->output(resampled_frame_count, &frames_resampled);
resampled_input =
input_processor->output(resampled_frame_count, &frames_resampled);
*input_frames_count = frames_resampled;
long got = data_callback(stream, user_ptr,
resampled_input, nullptr, resampled_frame_count);
long got = data_callback(stream, user_ptr, resampled_input, nullptr,
resampled_frame_count);
/* Return the number of initial input frames or part of it.
* Since output_frames_needed == 0 in input scenario, the only
* available number outside resampler is the initial number of frames. */
* Since output_frames_needed == 0 in input scenario, the only
* available number outside resampler is the initial number of frames. */
return (*input_frames_count) * (got / resampled_frame_count);
}
template<typename T, typename InputProcessor, typename OutputProcessor>
template <typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill_internal_duplex(T * in_buffer, long * input_frames_count,
T * out_buffer, long output_frames_needed)
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>::fill_internal_duplex(
T * in_buffer, long * input_frames_count, T * out_buffer,
long output_frames_needed)
{
if (draining) {
// discard input and drain any signal remaining in the resampler.
return output_processor->output(out_buffer, output_frames_needed);
}
/* The input data, after eventual resampling. This is passed to the callback. */
/* The input data, after eventual resampling. This is passed to the callback.
*/
T * resampled_input = nullptr;
/* The output buffer passed down in the callback, that might be resampled. */
T * out_unprocessed = nullptr;
@ -266,26 +274,25 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
* caller. */
output_frames_before_processing =
output_processor->input_needed_for_output(output_frames_needed);
/* fill directly the input buffer of the output processor to save a copy */
output_processor->input_needed_for_output(output_frames_needed);
/* fill directly the input buffer of the output processor to save a copy */
out_unprocessed =
output_processor->input_buffer(output_frames_before_processing);
output_processor->input_buffer(output_frames_before_processing);
if (in_buffer) {
/* process the input, and present exactly `output_frames_needed` in the
* callback. */
* callback. */
input_processor->input(in_buffer, *input_frames_count);
size_t frames_resampled = 0;
resampled_input =
input_processor->output(output_frames_before_processing, &frames_resampled);
resampled_input = input_processor->output(output_frames_before_processing,
&frames_resampled);
*input_frames_count = frames_resampled;
} else {
resampled_input = nullptr;
}
got = data_callback(stream, user_ptr,
resampled_input, out_unprocessed,
got = data_callback(stream, user_ptr, resampled_input, out_unprocessed,
output_frames_before_processing);
if (got < output_frames_before_processing) {
@ -315,10 +322,9 @@ cubeb_resampler *
cubeb_resampler_create(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback,
void * user_ptr,
cubeb_resampler_quality quality)
unsigned int target_rate, cubeb_data_callback callback,
void * user_ptr, cubeb_resampler_quality quality,
cubeb_resampler_reclock reclock)
{
cubeb_sample_format format;
@ -330,38 +336,28 @@ cubeb_resampler_create(cubeb_stream * stream,
format = output_params->format;
}
switch(format) {
case CUBEB_SAMPLE_S16NE:
return cubeb_resampler_create_internal<short>(stream,
input_params,
output_params,
target_rate,
callback,
user_ptr,
quality);
case CUBEB_SAMPLE_FLOAT32NE:
return cubeb_resampler_create_internal<float>(stream,
input_params,
output_params,
target_rate,
callback,
user_ptr,
quality);
default:
assert(false);
return nullptr;
switch (format) {
case CUBEB_SAMPLE_S16NE:
return cubeb_resampler_create_internal<short>(
stream, input_params, output_params, target_rate, callback, user_ptr,
quality, reclock);
case CUBEB_SAMPLE_FLOAT32NE:
return cubeb_resampler_create_internal<float>(
stream, input_params, output_params, target_rate, callback, user_ptr,
quality, reclock);
default:
assert(false);
return nullptr;
}
}
long
cubeb_resampler_fill(cubeb_resampler * resampler,
void * input_buffer,
long * input_frames_count,
void * output_buffer,
cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
long * input_frames_count, void * output_buffer,
long output_frames_needed)
{
return resampler->fill(input_buffer, input_frames_count,
output_buffer, output_frames_needed);
return resampler->fill(input_buffer, input_frames_count, output_buffer,
output_frames_needed);
}
void

View file

@ -21,6 +21,11 @@ typedef enum {
CUBEB_RESAMPLER_QUALITY_DESKTOP
} 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
* is accepted by the audio backend.
@ -39,13 +44,13 @@ typedef enum {
* @param quality Quality of the resampler.
* @retval A non-null pointer if success.
*/
cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback,
void * user_ptr,
cubeb_resampler_quality quality);
cubeb_resampler *
cubeb_resampler_create(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate, cubeb_data_callback callback,
void * user_ptr, cubeb_resampler_quality quality,
cubeb_resampler_reclock reclock);
/**
* 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 CUBEB_ERROR on error.
*/
long cubeb_resampler_fill(cubeb_resampler * resampler,
void * input_buffer,
long * input_frame_count,
void * output_buffer,
long output_frames_needed);
long
cubeb_resampler_fill(cubeb_resampler * resampler, void * input_buffer,
long * input_frame_count, void * output_buffer,
long output_frames_needed);
/**
* Destroy a cubeb_resampler.
* @param resampler A cubeb_resampler instance.
*/
void cubeb_resampler_destroy(cubeb_resampler * resampler);
void
cubeb_resampler_destroy(cubeb_resampler * resampler);
/**
* Returns the latency, in frames, of the resampler.
* @param resampler A cubeb resampler instance.
* @retval The latency, in frames, induced by the resampler.
*/
long cubeb_resampler_latency(cubeb_resampler * resampler);
long
cubeb_resampler_latency(cubeb_resampler * resampler);
#if defined(__cplusplus)
}

View file

@ -8,9 +8,9 @@
#if !defined(CUBEB_RESAMPLER_INTERNAL)
#define CUBEB_RESAMPLER_INTERNAL
#include <cmath>
#include <cassert>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <memory>
#ifdef CUBEB_GECKO_BUILD
#include "mozilla/UniquePtr.h"
@ -25,29 +25,32 @@
#define MOZ_END_STD_NAMESPACE }
#endif
MOZ_BEGIN_STD_NAMESPACE
using mozilla::DefaultDelete;
using mozilla::UniquePtr;
#define default_delete DefaultDelete
#define unique_ptr UniquePtr
using mozilla::DefaultDelete;
using mozilla::UniquePtr;
#define default_delete DefaultDelete
#define unique_ptr UniquePtr
MOZ_END_STD_NAMESPACE
#endif
#include "cubeb/cubeb.h"
#include "cubeb_utils.h"
#include "cubeb-speex-resampler.h"
#include "cubeb_resampler.h"
#include "cubeb/cubeb.h"
#include "cubeb_log.h"
#include "cubeb_resampler.h"
#include "cubeb_utils.h"
#include <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
// an input delay, this function returns the number of frames
// to keep in the buffer.
// @parameter sample_rate The sample rate of the stream.
// @return A number of frames to keep.
uint32_t min_buffered_audio_frame(uint32_t sample_rate);
uint32_t
min_buffered_audio_frame(uint32_t sample_rate);
int to_speex_quality(cubeb_resampler_quality q);
int
to_speex_quality(cubeb_resampler_quality q);
struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
@ -59,14 +62,10 @@ struct cubeb_resampler {
/** Base class for processors. This is just used to share methods for now. */
class processor {
public:
explicit processor(uint32_t channels)
: channels(channels)
{}
explicit processor(uint32_t channels) : channels(channels) {}
protected:
size_t frames_to_samples(size_t frames) const
{
return frames * channels;
}
size_t frames_to_samples(size_t frames) const { return frames * channels; }
size_t samples_to_frames(size_t samples) const
{
assert(!(samples % channels));
@ -76,30 +75,25 @@ protected:
const uint32_t channels;
};
template<typename T>
class passthrough_resampler : public cubeb_resampler
, public processor {
template <typename T>
class passthrough_resampler : public cubeb_resampler, public processor {
public:
passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels,
uint32_t sample_rate);
passthrough_resampler(cubeb_stream * s, cubeb_data_callback cb, void * ptr,
uint32_t input_channels, uint32_t sample_rate);
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames);
virtual long latency()
{
return 0;
}
virtual long latency() { return 0; }
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
uint32_t available = samples_to_frames(internal_input_buffer.length());
if (available > to_keep) {
internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
ALOGV("Dropping %u frames", available - to_keep);
internal_input_buffer.pop(nullptr,
frames_to_samples(available - to_keep));
}
}
@ -116,14 +110,12 @@ private:
/** Bidirectional resampler, can resample an input and an output stream, or just
* an input stream or output stream. In this case a delay is inserted in the
* opposite direction to keep the streams synchronized. */
template<typename T, typename InputProcessing, typename OutputProcessing>
template <typename T, typename InputProcessing, typename OutputProcessing>
class cubeb_resampler_speex : public cubeb_resampler {
public:
cubeb_resampler_speex(InputProcessing * input_processor,
OutputProcessing * output_processor,
cubeb_stream * s,
cubeb_data_callback cb,
void * ptr);
OutputProcessing * output_processor, cubeb_stream * s,
cubeb_data_callback cb, void * ptr);
virtual ~cubeb_resampler_speex();
@ -143,7 +135,9 @@ public:
}
private:
typedef long(cubeb_resampler_speex::*processing_callback)(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed);
typedef long (cubeb_resampler_speex::*processing_callback)(
T * input_buffer, long * input_frames_count, T * output_buffer,
long output_frames_needed);
long fill_internal_duplex(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
@ -165,8 +159,7 @@ private:
* audio buffers of type T. This class is designed so that the number of frames
* coming out of the resampler can be precisely controled. It manages its own
* input buffer, and can use the caller's output buffer, or allocate its own. */
template<typename T>
class cubeb_resampler_speex_one_way : public processor {
template <typename T> class cubeb_resampler_speex_one_way : public processor {
public:
/** The sample type of this resampler, either 16-bit integers or 32-bit
* floats. */
@ -178,19 +171,15 @@ public:
* @parameter target_rate The sample-rate of the audio output.
* @parameter quality A number between 0 (fast, low quality) and 10 (slow,
* high quality). */
cubeb_resampler_speex_one_way(uint32_t channels,
uint32_t source_rate,
uint32_t target_rate,
int quality)
: processor(channels)
, resampling_ratio(static_cast<float>(source_rate) / target_rate)
, source_rate(source_rate)
, additional_latency(0)
, leftover_samples(0)
cubeb_resampler_speex_one_way(uint32_t channels, uint32_t source_rate,
uint32_t target_rate, int quality)
: processor(channels),
resampling_ratio(static_cast<float>(source_rate) / target_rate),
source_rate(source_rate), additional_latency(0), leftover_samples(0)
{
int r;
speex_resampler = speex_resampler_init(channels, source_rate,
target_rate, quality, &r);
speex_resampler =
speex_resampler_init(channels, source_rate, target_rate, quality, &r);
assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
uint32_t input_latency = speex_resampler_get_input_latency(speex_resampler);
@ -200,11 +189,8 @@ public:
uint32_t input_frame_count = input_latency;
uint32_t output_frame_count = LATENCY_SAMPLES;
assert(input_latency * channels <= LATENCY_SAMPLES);
speex_resample(
input_buffer,
&input_frame_count,
output_buffer,
&output_frame_count);
speex_resample(input_buffer, &input_frame_count, output_buffer,
&output_frame_count);
}
/** Destructor, deallocate the resampler */
@ -221,14 +207,14 @@ public:
}
/** Outputs exactly `output_frame_count` into `output_buffer`.
* `output_buffer` has to be at least `output_frame_count` long. */
* `output_buffer` has to be at least `output_frame_count` long. */
size_t output(T * output_buffer, size_t output_frame_count)
{
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
uint32_t out_len = output_frame_count;
speex_resample(resampling_in_buffer.data(), &in_len,
output_buffer, &out_len);
speex_resample(resampling_in_buffer.data(), &in_len, output_buffer,
&out_len);
/* This shifts back any unresampled samples to the beginning of the input
buffer. */
@ -239,15 +225,17 @@ public:
size_t output_for_input(uint32_t input_frames)
{
return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
/ resampling_ratio);
return (size_t)floorf(
(input_frames + samples_to_frames(resampling_in_buffer.length())) /
resampling_ratio);
}
/** Returns a buffer containing exactly `output_frame_count` resampled frames.
* The consumer should not hold onto the pointer. */
* The consumer should not hold onto the pointer. */
T * output(size_t output_frame_count, size_t * input_frames_used)
{
if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
if (resampling_out_buffer.capacity() <
frames_to_samples(output_frame_count)) {
resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
}
@ -258,10 +246,12 @@ public:
resampling_out_buffer.data(), &out_len);
if (out_len < output_frame_count) {
LOGV("underrun during resampling: got %u frames, expected %zu", (unsigned)out_len, output_frame_count);
LOGV("underrun during resampling: got %u frames, expected %zu",
(unsigned)out_len, output_frame_count);
// silence the rightmost part
T* data = resampling_out_buffer.data();
for (uint32_t i = frames_to_samples(out_len); i < frames_to_samples(output_frame_count); i++) {
T * data = resampling_out_buffer.data();
for (uint32_t i = frames_to_samples(out_len);
i < frames_to_samples(output_frame_count); i++) {
data[i] = 0;
}
}
@ -281,8 +271,8 @@ public:
* only consider a single channel here so it's the same number of frames. */
int latency = 0;
latency =
speex_resampler_get_output_latency(speex_resampler) + additional_latency;
latency = speex_resampler_get_output_latency(speex_resampler) +
additional_latency;
assert(latency >= 0);
@ -296,11 +286,13 @@ public:
uint32_t input_needed_for_output(int32_t output_frame_count) const
{
assert(output_frame_count >= 0); // Check overflow
int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length());
int32_t unresampled_frames_left =
samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left =
samples_to_frames(resampling_out_buffer.length());
float input_frames_needed =
(output_frame_count - unresampled_frames_left) * resampling_ratio
- resampled_frames_left;
(output_frame_count - unresampled_frames_left) * resampling_ratio -
resampled_frames_left;
if (input_frames_needed < 0) {
return 0;
}
@ -334,12 +326,14 @@ public:
uint32_t available = samples_to_frames(resampling_in_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(source_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
* interface. */
void speex_resample(float * input_buffer, uint32_t * input_frame_count,
float * output_buffer, uint32_t * output_frame_count)
{
@ -347,11 +341,9 @@ private:
int rv;
rv =
#endif
speex_resampler_process_interleaved_float(speex_resampler,
input_buffer,
input_frame_count,
output_buffer,
output_frame_count);
speex_resampler_process_interleaved_float(
speex_resampler, input_buffer, input_frame_count, output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
@ -362,11 +354,9 @@ private:
int rv;
rv =
#endif
speex_resampler_process_interleaved_int(speex_resampler,
input_buffer,
input_frame_count,
output_buffer,
output_frame_count);
speex_resampler_process_interleaved_int(
speex_resampler, input_buffer, input_frame_count, output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
/** The state for the speex resampler used internaly. */
@ -387,18 +377,16 @@ private:
};
/** This class allows delaying an audio stream by `frames` frames. */
template<typename T>
class delay_line : public processor {
template <typename T> class delay_line : public processor {
public:
/** Constructor
* @parameter frames the number of frames of delay.
* @parameter channels the number of channels of this delay line.
* @parameter sample_rate sample-rate of the audio going through this delay line */
* @parameter sample_rate sample-rate of the audio going through this delay
* line */
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
: processor(channels)
, length(frames)
, leftover_samples(0)
, sample_rate(sample_rate)
: processor(channels), length(frames), leftover_samples(0),
sample_rate(sample_rate)
{
/* Fill the delay line with some silent frames to add latency. */
delay_input_buffer.push_silence(frames * channels);
@ -436,7 +424,8 @@ public:
T * input_buffer(uint32_t frames_needed)
{
leftover_samples = delay_input_buffer.length();
delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed));
delay_input_buffer.reserve(leftover_samples +
frames_to_samples(frames_needed));
return delay_input_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
@ -471,26 +460,23 @@ public:
assert(frames_needed >= 0); // Check overflow
return frames_needed;
}
/** Returns the number of frames produces for `input_frames` frames in input */
size_t output_for_input(uint32_t input_frames)
{
return input_frames;
}
/** Returns the number of frames produces for `input_frames` frames in input
*/
size_t output_for_input(uint32_t input_frames) { return input_frames; }
/** The number of frames this delay line delays the stream by.
* @returns The number of frames of delay. */
size_t latency()
{
return length;
}
size_t latency() { return length; }
void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
ALOGV("Dropping %u frames", available - to_keep);
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
/** The length, in frames, of this delay line */
uint32_t length;
@ -506,15 +492,15 @@ private:
};
/** This sits behind the C API and is more typed. */
template<typename T>
template <typename T>
cubeb_resampler *
cubeb_resampler_create_internal(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback,
void * user_ptr,
cubeb_resampler_quality quality)
cubeb_data_callback callback, void * user_ptr,
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>> output_resampler = nullptr;
@ -528,35 +514,31 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
sample rate, use a no-op resampler, that simply forwards the buffers to the
callback. */
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
(output_params && !input_params && (output_params->rate == target_rate))) {
(output_params && !input_params &&
(output_params->rate == target_rate))) {
LOG("Input and output sample-rate match, target rate of %dHz", target_rate);
return new passthrough_resampler<T>(stream, callback,
user_ptr,
input_params ? input_params->channels : 0,
target_rate);
return new passthrough_resampler<T>(
stream, callback, user_ptr, input_params ? input_params->channels : 0,
target_rate);
}
/* Determine if we need to resampler one or both directions, and create the
resamplers. */
if (output_params && (output_params->rate != target_rate)) {
output_resampler.reset(
new cubeb_resampler_speex_one_way<T>(output_params->channels,
target_rate,
output_params->rate,
to_speex_quality(quality)));
output_resampler.reset(new cubeb_resampler_speex_one_way<T>(
output_params->channels, target_rate, output_params->rate,
to_speex_quality(quality)));
if (!output_resampler) {
return NULL;
}
}
if (input_params && (input_params->rate != target_rate)) {
input_resampler.reset(
new cubeb_resampler_speex_one_way<T>(input_params->channels,
input_params->rate,
target_rate,
to_speex_quality(quality)));
input_resampler.reset(new cubeb_resampler_speex_one_way<T>(
input_params->channels, input_params->rate, target_rate,
to_speex_quality(quality)));
if (!input_resampler) {
return NULL;
}
@ -572,7 +554,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
if (!output_delay) {
return NULL;
}
} else if (output_resampler && !input_resampler && input_params && output_params) {
} else if (output_resampler && !input_resampler && input_params &&
output_params) {
input_delay.reset(new delay_line<T>(output_resampler->latency(),
input_params->channels,
output_params->rate));
@ -582,29 +565,26 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
}
if (input_resampler && output_resampler) {
LOG("Resampling input (%d) and output (%d) to target rate of %dHz", input_params->rate, output_params->rate, target_rate);
return new cubeb_resampler_speex<T,
cubeb_resampler_speex_one_way<T>,
cubeb_resampler_speex_one_way<T>>
(input_resampler.release(),
output_resampler.release(),
stream, callback, user_ptr);
LOG("Resampling input (%d) and output (%d) to target rate of %dHz",
input_params->rate, output_params->rate, target_rate);
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
cubeb_resampler_speex_one_way<T>>(
input_resampler.release(), output_resampler.release(), stream, callback,
user_ptr);
} else if (input_resampler) {
LOG("Resampling input (%d) to target and output rate of %dHz", input_params->rate, target_rate);
return new cubeb_resampler_speex<T,
cubeb_resampler_speex_one_way<T>,
delay_line<T>>
(input_resampler.release(),
output_delay.release(),
stream, callback, user_ptr);
LOG("Resampling input (%d) to target and output rate of %dHz",
input_params->rate, target_rate);
return new cubeb_resampler_speex<T, cubeb_resampler_speex_one_way<T>,
delay_line<T>>(input_resampler.release(),
output_delay.release(),
stream, callback, user_ptr);
} else {
LOG("Resampling output (%dHz) to target and input rate of %dHz", output_params->rate, target_rate);
return new cubeb_resampler_speex<T,
delay_line<T>,
cubeb_resampler_speex_one_way<T>>
(input_delay.release(),
output_resampler.release(),
stream, callback, user_ptr);
LOG("Resampling output (%dHz) to target and input rate of %dHz",
output_params->rate, target_rate);
return new cubeb_resampler_speex<T, delay_line<T>,
cubeb_resampler_speex_one_way<T>>(
input_delay.release(), output_resampler.release(), stream, callback,
user_ptr);
}
}

View file

@ -16,17 +16,16 @@
them in the correct order. */
typedef struct {
AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated space for the buffers. */
unsigned int tail; /**< Index of the last element (first to deliver). */
unsigned int count; /**< Number of elements in the array. */
unsigned int capacity; /**< Total length of the array. */
AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated
space for the buffers. */
unsigned int tail; /**< Index of the last element (first to deliver). */
unsigned int count; /**< Number of elements in the array. */
unsigned int capacity; /**< Total length of the array. */
} ring_array;
static int
single_audiobuffer_init(AudioBuffer * buffer,
uint32_t bytesPerFrame,
uint32_t channelsPerFrame,
uint32_t frames)
single_audiobuffer_init(AudioBuffer * buffer, uint32_t bytesPerFrame,
uint32_t channelsPerFrame, uint32_t frames)
{
assert(buffer);
assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
@ -36,7 +35,7 @@ single_audiobuffer_init(AudioBuffer * buffer,
if (buffer->mData == NULL) {
return CUBEB_ERROR;
}
PodZero(static_cast<char*>(buffer->mData), size);
PodZero(static_cast<char *>(buffer->mData), size);
buffer->mNumberChannels = channelsPerFrame;
buffer->mDataByteSize = size;
@ -48,15 +47,12 @@ single_audiobuffer_init(AudioBuffer * buffer,
@param ra The ring_array pointer of allocated structure.
@retval 0 on success. */
int
ring_array_init(ring_array * ra,
uint32_t capacity,
uint32_t bytesPerFrame,
uint32_t channelsPerFrame,
uint32_t framesPerBuffer)
ring_array_init(ring_array * ra, uint32_t capacity, uint32_t bytesPerFrame,
uint32_t channelsPerFrame, uint32_t framesPerBuffer)
{
assert(ra);
if (capacity == 0 || bytesPerFrame == 0 ||
channelsPerFrame == 0 || framesPerBuffer == 0) {
if (capacity == 0 || bytesPerFrame == 0 || channelsPerFrame == 0 ||
framesPerBuffer == 0) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
ra->capacity = capacity;
@ -70,8 +66,7 @@ ring_array_init(ring_array * ra,
}
for (unsigned int i = 0; i < ra->capacity; ++i) {
if (single_audiobuffer_init(&ra->buffer_array[i],
bytesPerFrame,
if (single_audiobuffer_init(&ra->buffer_array[i], bytesPerFrame,
channelsPerFrame,
framesPerBuffer) != CUBEB_OK) {
return CUBEB_ERROR;
@ -87,7 +82,7 @@ void
ring_array_destroy(ring_array * ra)
{
assert(ra);
if (ra->buffer_array == NULL){
if (ra->buffer_array == NULL) {
return;
}
for (unsigned int i = 0; i < ra->capacity; ++i) {
@ -95,12 +90,13 @@ ring_array_destroy(ring_array * ra)
operator delete(ra->buffer_array[i].mData);
}
}
delete [] ra->buffer_array;
delete[] ra->buffer_array;
}
/** Get the allocated buffer to be stored with fresh data.
@param ra The ring_array pointer.
@retval Pointer of the allocated space to be stored with fresh data or NULL if full. */
@retval Pointer of the allocated space to be stored with fresh data or NULL
if full. */
AudioBuffer *
ring_array_get_free_buffer(ring_array * ra)
{
@ -156,4 +152,4 @@ ring_array_get_dummy_buffer(ring_array * ra)
return &ra->buffer_array[0];
}
#endif //CUBEB_RING_ARRAY_H
#endif // CUBEB_RING_ARRAY_H

View file

@ -18,10 +18,10 @@
/**
* Single producer single consumer lock-free and wait-free ring buffer.
*
* This data structure allows producing data from one thread, and consuming it on
* another thread, safely and without explicit synchronization. If used on two
* threads, this data structure uses atomics for thread safety. It is possible
* to disable the use of atomics at compile time and only use this data
* This data structure allows producing data from one thread, and consuming it
* on another thread, safely and without explicit synchronization. If used on
* two threads, this data structure uses atomics for thread safety. It is
* possible to disable the use of atomics at compile time and only use this data
* structure on one thread.
*
* The role for the producer and the consumer must be constant, i.e., the
@ -48,9 +48,7 @@
* providing an external buffer to copy into is an easy way to have linear
* data for further processing.
*/
template <typename T>
class ring_buffer_base
{
template <typename T> class ring_buffer_base {
public:
/**
* Constructor for a ring buffer.
@ -61,11 +59,10 @@ public:
* @param capacity The maximum number of element this ring buffer will hold.
*/
ring_buffer_base(int capacity)
/* One more element to distinguish from empty and full buffer. */
: capacity_(capacity + 1)
/* One more element to distinguish from empty and full buffer. */
: capacity_(capacity + 1)
{
assert(storage_capacity() <
std::numeric_limits<int>::max() / 2 &&
assert(storage_capacity() < std::numeric_limits<int>::max() / 2 &&
"buffer too large for the type of index used.");
assert(capacity_ > 0);
@ -84,10 +81,7 @@ public:
* @param count The number of elements to enqueue.
* @return The number of element enqueued.
*/
int enqueue_default(int count)
{
return enqueue(nullptr, count);
}
int enqueue_default(int count) { return enqueue(nullptr, count); }
/**
* @brief Put an element in the queue
*
@ -97,20 +91,18 @@ public:
*
* @return 1 if the element was inserted, 0 otherwise.
*/
int enqueue(T& element)
{
return enqueue(&element, 1);
}
int enqueue(T & element) { return enqueue(&element, 1); }
/**
* Push `count` elements in the ring buffer.
*
* Only safely called on the producer thread.
*
* @param elements a pointer to a buffer containing at least `count` elements.
* If `elements` is nullptr, zero or default constructed elements are enqueued.
* If `elements` is nullptr, zero or default constructed elements are
* enqueued.
* @param count The number of elements to read from `elements`
* @return The number of elements successfully coped from `elements` and inserted
* into the ring buffer.
* @return The number of elements successfully coped from `elements` and
* inserted into the ring buffer.
*/
int enqueue(T * elements, int count)
{
@ -118,19 +110,17 @@ public:
assert_correct_thread(producer_id);
#endif
int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
int wr_idx = write_index_.load(std::memory_order::memory_order_relaxed);
int wr_idx = write_index_.load(std::memory_order_relaxed);
int rd_idx = read_index_.load(std::memory_order_acquire);
if (full_internal(rd_idx, wr_idx)) {
return 0;
}
int to_write =
std::min(available_write_internal(rd_idx, wr_idx), count);
int to_write = std::min(available_write_internal(rd_idx, wr_idx), count);
/* First part, from the write index to the end of the array. */
int first_part = std::min(storage_capacity() - wr_idx,
to_write);
int first_part = std::min(storage_capacity() - wr_idx, to_write);
/* Second part, from the beginning of the array */
int second_part = to_write - first_part;
@ -142,7 +132,8 @@ public:
ConstructDefault(data_.get(), second_part);
}
write_index_.store(increment_index(wr_idx, to_write), std::memory_order::memory_order_release);
write_index_.store(increment_index(wr_idx, to_write),
std::memory_order_release);
return to_write;
}
@ -163,15 +154,14 @@ public:
assert_correct_thread(consumer_id);
#endif
int wr_idx = write_index_.load(std::memory_order::memory_order_acquire);
int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
int rd_idx = read_index_.load(std::memory_order_relaxed);
int wr_idx = write_index_.load(std::memory_order_acquire);
if (empty_internal(rd_idx, wr_idx)) {
return 0;
}
int to_read =
std::min(available_read_internal(rd_idx, wr_idx), count);
int to_read = std::min(available_read_internal(rd_idx, wr_idx), count);
int first_part = std::min(storage_capacity() - rd_idx, to_read);
int second_part = to_read - first_part;
@ -181,7 +171,8 @@ public:
Copy(elements + first_part, data_.get(), second_part);
}
read_index_.store(increment_index(rd_idx, to_read), std::memory_order::memory_order_relaxed);
read_index_.store(increment_index(rd_idx, to_read),
std::memory_order_release);
return to_read;
}
@ -197,8 +188,9 @@ public:
#ifndef NDEBUG
assert_correct_thread(consumer_id);
#endif
return available_read_internal(read_index_.load(std::memory_order::memory_order_relaxed),
write_index_.load(std::memory_order::memory_order_relaxed));
return available_read_internal(
read_index_.load(std::memory_order_relaxed),
write_index_.load(std::memory_order_acquire));
}
/**
* Get the number of available elements for consuming.
@ -212,8 +204,9 @@ public:
#ifndef NDEBUG
assert_correct_thread(producer_id);
#endif
return available_write_internal(read_index_.load(std::memory_order::memory_order_relaxed),
write_index_.load(std::memory_order::memory_order_relaxed));
return available_write_internal(
read_index_.load(std::memory_order_acquire),
write_index_.load(std::memory_order_relaxed));
}
/**
* Get the total capacity, for this ring buffer.
@ -222,10 +215,7 @@ public:
*
* @return The maximum capacity of this ring buffer.
*/
int capacity() const
{
return storage_capacity() - 1;
}
int capacity() const { return storage_capacity() - 1; }
/**
* Reset the consumer and producer thread identifier, in case the thread are
* being changed. This has to be externally synchronized. This is no-op when
@ -237,6 +227,7 @@ public:
consumer_id = producer_id = std::thread::id();
#endif
}
private:
/** Return true if the ring buffer is empty.
*
@ -244,8 +235,7 @@ private:
* @param write_index the write index to consider
* @return true if the ring buffer is empty, false otherwise.
**/
bool empty_internal(int read_index,
int write_index) const
bool empty_internal(int read_index, int write_index) const
{
return write_index == read_index;
}
@ -258,8 +248,7 @@ private:
* @param write_index the write index to consider
* @return true if the ring buffer is full, false otherwise.
**/
bool full_internal(int read_index,
int write_index) const
bool full_internal(int read_index, int write_index) const
{
return (write_index + 1) % storage_capacity() == read_index;
}
@ -269,18 +258,13 @@ private:
*
* @return the number of elements that can be stored in the buffer.
*/
int storage_capacity() const
{
return capacity_;
}
int storage_capacity() const { return capacity_; }
/**
* Returns the number of elements available for reading.
*
* @return the number of available elements for reading.
*/
int
available_read_internal(int read_index,
int write_index) const
int available_read_internal(int read_index, int write_index) const
{
if (write_index >= read_index) {
return write_index - read_index;
@ -293,9 +277,7 @@ private:
*
* @return the number of elements that can be written into the array.
*/
int
available_write_internal(int read_index,
int write_index) const
int available_write_internal(int read_index, int write_index) const
{
/* We substract one element here to always keep at least one sample
* free in the buffer, to distinguish between full and empty array. */
@ -312,8 +294,7 @@ private:
* @param increment the number by which `index` is incremented.
* @return the new index.
*/
int
increment_index(int index, int increment) const
int increment_index(int index, int increment) const
{
assert(increment >= 0);
return (index + increment) % storage_capacity();
@ -325,7 +306,7 @@ private:
* @param id the id of the thread that has called the calling method first.
*/
#ifndef NDEBUG
static void assert_correct_thread(std::thread::id& id)
static void assert_correct_thread(std::thread::id & id)
{
if (id == std::thread::id()) {
id = std::this_thread::get_id();
@ -354,9 +335,7 @@ private:
/**
* Adapter for `ring_buffer_base` that exposes an interface in frames.
*/
template <typename T>
class audio_ring_buffer_base
{
template <typename T> class audio_ring_buffer_base {
public:
/**
* @brief Constructor.
@ -365,8 +344,8 @@ public:
* @param capacity_in_frames The capacity in frames.
*/
audio_ring_buffer_base(int channel_count, int capacity_in_frames)
: channel_count(channel_count)
, ring_buffer(frames_to_samples(capacity_in_frames))
: channel_count(channel_count),
ring_buffer(frames_to_samples(capacity_in_frames))
{
assert(channel_count > 0);
}
@ -380,7 +359,8 @@ public:
*/
int enqueue_default(int frame_count)
{
return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
return samples_to_frames(
ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
}
/**
* @brief Enqueue `frames_count` frames of audio.
@ -396,7 +376,8 @@ public:
int enqueue(T * frames, int frame_count)
{
return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
return samples_to_frames(
ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
}
/**
@ -413,7 +394,8 @@ public:
*/
int dequeue(T * frames, int frame_count)
{
return samples_to_frames(ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
return samples_to_frames(
ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
}
/**
* Get the number of available frames of audio for consuming.
@ -444,10 +426,8 @@ public:
*
* @return The maximum capacity of this ring buffer.
*/
int capacity() const
{
return samples_to_frames(ring_buffer.capacity());
}
int capacity() const { return samples_to_frames(ring_buffer.capacity()); }
private:
/**
* @brief Frames to samples conversion.
@ -456,10 +436,7 @@ private:
*
* @return A number of samples.
*/
int frames_to_samples(int frames) const
{
return frames * channel_count;
}
int frames_to_samples(int frames) const { return frames * channel_count; }
/**
* @brief Samples to frames conversion.
*
@ -467,10 +444,7 @@ private:
*
* @return A number of frames.
*/
int samples_to_frames(int samples) const
{
return samples / channel_count;
}
int samples_to_frames(int samples) const { return samples / channel_count; }
/** Number of channels of audio that will stream through this ring buffer. */
int channel_count;
/** The underlying ring buffer that is used to store the data. */
@ -482,14 +456,13 @@ private:
* from two threads, one producer, one consumer (that never change role),
* without explicit synchronization.
*/
template<typename T>
using lock_free_queue = ring_buffer_base<T>;
template <typename T> using lock_free_queue = ring_buffer_base<T>;
/**
* Lock-free instantiation of the `audio_ring_buffer` type. This is safe to use
* from two threads, one producer, one consumer (that never change role),
* without explicit synchronization.
*/
template<typename T>
template <typename T>
using lock_free_audio_ring_buffer = audio_ring_buffer_base<T>;
#endif // CUBEB_RING_BUFFER_H

View file

@ -4,44 +4,46 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include <assert.h>
#include <dlfcn.h>
#include <inttypes.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <sndio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <assert.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include <stdlib.h>
#if defined(CUBEB_SNDIO_DEBUG)
#define DPR(...) fprintf(stderr, __VA_ARGS__);
#else
#define DPR(...) do {} while(0)
#define DPR(...) \
do { \
} while (0)
#endif
#ifdef DISABLE_LIBSNDIO_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) cubeb_##x
#define LIBSNDIO_API_VISIT(X) \
X(sio_close) \
X(sio_eof) \
X(sio_getpar) \
X(sio_initpar) \
X(sio_nfds) \
X(sio_onmove) \
X(sio_open) \
X(sio_pollfd) \
X(sio_read) \
X(sio_revents) \
X(sio_setpar) \
X(sio_start) \
X(sio_stop) \
X(sio_write) \
#define WRAP(x) (*cubeb_##x)
#define LIBSNDIO_API_VISIT(X) \
X(sio_close) \
X(sio_eof) \
X(sio_getpar) \
X(sio_initpar) \
X(sio_nfds) \
X(sio_onmove) \
X(sio_open) \
X(sio_pollfd) \
X(sio_read) \
X(sio_revents) \
X(sio_setpar) \
X(sio_start) \
X(sio_stop) \
X(sio_write)
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
@ -58,33 +60,33 @@ struct cubeb {
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context;
void * arg; /* user arg to {data,state}_cb */
void * arg; /* user arg to {data,state}_cb */
/**/
pthread_t th; /* to run real-time audio i/o */
pthread_mutex_t mtx; /* protects hdl and pos */
struct sio_hdl *hdl; /* link us to sndio */
int mode; /* bitmap of SIO_{PLAY,REC} */
int active; /* cubec_start() called */
int conv; /* need float->s16 conversion */
unsigned char *rbuf; /* rec data consumed from here */
unsigned char *pbuf; /* play data is prepared here */
unsigned int nfr; /* number of frames in ibuf and obuf */
unsigned int rbpf; /* rec bytes per frame */
unsigned int pbpf; /* play bytes per frame */
unsigned int rchan; /* number of rec channels */
unsigned int pchan; /* number of play channels */
unsigned int nblks; /* number of blocks in the buffer */
uint64_t hwpos; /* frame number Joe hears right now */
uint64_t swpos; /* number of frames produced/consumed */
cubeb_data_callback data_cb; /* cb to preapare data */
cubeb_state_callback state_cb; /* cb to notify about state changes */
float volume; /* current volume */
pthread_t th; /* to run real-time audio i/o */
pthread_mutex_t mtx; /* protects hdl and pos */
struct sio_hdl * hdl; /* link us to sndio */
int mode; /* bitmap of SIO_{PLAY,REC} */
int active; /* cubec_start() called */
int conv; /* need float->s16 conversion */
unsigned char * rbuf; /* rec data consumed from here */
unsigned char * pbuf; /* play data is prepared here */
unsigned int nfr; /* number of frames in ibuf and obuf */
unsigned int rbpf; /* rec bytes per frame */
unsigned int pbpf; /* play bytes per frame */
unsigned int rchan; /* number of rec channels */
unsigned int pchan; /* number of play channels */
unsigned int nblks; /* number of blocks in the buffer */
uint64_t hwpos; /* frame number Joe hears right now */
uint64_t swpos; /* number of frames produced/consumed */
cubeb_data_callback data_cb; /* cb to preapare data */
cubeb_state_callback state_cb; /* cb to notify about state changes */
float volume; /* current volume */
};
static void
s16_setvol(void *ptr, long nsamp, float volume)
s16_setvol(void * ptr, long nsamp, float volume)
{
int16_t *dst = ptr;
int16_t * dst = ptr;
int32_t mult = volume * 32768;
int32_t s;
@ -96,10 +98,10 @@ s16_setvol(void *ptr, long nsamp, float volume)
}
static void
float_to_s16(void *ptr, long nsamp, float volume)
float_to_s16(void * ptr, long nsamp, float volume)
{
int16_t *dst = ptr;
float *src = ptr;
int16_t * dst = ptr;
float * src = ptr;
float mult = volume * 32768;
int s;
@ -114,10 +116,10 @@ float_to_s16(void *ptr, long nsamp, float volume)
}
static void
s16_to_float(void *ptr, long nsamp)
s16_to_float(void * ptr, long nsamp)
{
int16_t *src = ptr;
float *dst = ptr;
int16_t * src = ptr;
float * dst = ptr;
src += nsamp;
dst += nsamp;
@ -133,9 +135,9 @@ sndio_get_device()
* On other platforms default to sndio devices,
* so cubebs other backends can be used instead.
*/
const char *dev = getenv("AUDIODEVICE");
const char * dev = getenv("AUDIODEVICE");
if (dev == NULL || *dev == '\0')
return "snd/0";
return "snd/0";
return dev;
#else
return SIO_DEVANY;
@ -143,26 +145,26 @@ sndio_get_device()
}
static void
sndio_onmove(void *arg, int delta)
sndio_onmove(void * arg, int delta)
{
cubeb_stream *s = (cubeb_stream *)arg;
cubeb_stream * s = (cubeb_stream *)arg;
s->hwpos += delta;
}
static void *
sndio_mainloop(void *arg)
sndio_mainloop(void * arg)
{
struct pollfd *pfds;
cubeb_stream *s = arg;
struct pollfd * pfds;
cubeb_stream * s = arg;
int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED;
size_t pstart = 0, pend = 0, rstart = 0, rend = 0;
long nfr;
nfds = WRAP(sio_nfds)(s->hdl);
pfds = calloc(nfds, sizeof (struct pollfd));
pfds = calloc(nfds, sizeof(struct pollfd));
if (pfds == NULL)
return NULL;
return NULL;
DPR("sndio_mainloop()\n");
s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
@ -196,7 +198,7 @@ sndio_mainloop(void *arg)
/* do we have a complete block? */
if ((!(s->mode & SIO_PLAY) || pstart == pend) &&
(!(s->mode & SIO_REC) || rstart == rend)) {
(!(s->mode & SIO_REC) || rstart == rend)) {
if (eof) {
DPR("sndio_mainloop() drained\n");
@ -224,7 +226,7 @@ sndio_mainloop(void *arg)
if (!(s->mode & SIO_PLAY) || nfr == 0) {
state = CUBEB_STATE_DRAINED;
break;
}
}
/* need to write (aka drain) the partial play block we got */
pend = nfr * s->pbpf;
@ -302,10 +304,10 @@ sndio_mainloop(void *arg)
}
/*static*/ int
sndio_init(cubeb **context, char const *context_name)
sndio_init(cubeb ** context, char const * context_name)
{
void * libsndio = NULL;
struct sio_hdl *hdl;
struct sio_hdl * hdl;
assert(context);
@ -319,13 +321,14 @@ sndio_init(cubeb **context, char const *context_name)
}
}
#define LOAD(x) { \
cubeb_##x = dlsym(libsndio, #x); \
if (!cubeb_##x) { \
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
dlclose(libsndio); \
return CUBEB_ERROR; \
} \
#define LOAD(x) \
{ \
cubeb_##x = dlsym(libsndio, #x); \
if (!cubeb_##x) { \
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
dlclose(libsndio); \
return CUBEB_ERROR; \
} \
}
LIBSNDIO_API_VISIT(LOAD);
@ -342,7 +345,7 @@ sndio_init(cubeb **context, char const *context_name)
DPR("sndio_init(%s)\n", context_name);
*context = malloc(sizeof(**context));
if (*context == NULL)
return CUBEB_ERROR;
return CUBEB_ERROR;
(*context)->libsndio = libsndio;
(*context)->ops = &sndio_ops;
(void)context_name;
@ -350,13 +353,13 @@ sndio_init(cubeb **context, char const *context_name)
}
static char const *
sndio_get_backend_id(cubeb *context)
sndio_get_backend_id(cubeb * context)
{
return "sndio";
}
static void
sndio_destroy(cubeb *context)
sndio_destroy(cubeb * context)
{
DPR("sndio_destroy()\n");
if (context->libsndio)
@ -365,19 +368,16 @@ sndio_destroy(cubeb *context)
}
static int
sndio_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
sndio_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void *user_ptr)
cubeb_state_callback state_callback, void * user_ptr)
{
cubeb_stream *s;
cubeb_stream * s;
struct sio_par wpar, rpar;
cubeb_sample_format format;
int rate;
@ -445,8 +445,8 @@ sndio_stream_init(cubeb * context,
DPR("sndio_stream_init(), sio_setpar() failed\n");
goto err;
}
if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
if (rpar.bits != wpar.bits || rpar.le != wpar.le || rpar.sig != wpar.sig ||
rpar.rate != wpar.rate ||
((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
DPR("sndio_stream_init() unsupported params\n");
@ -522,7 +522,8 @@ sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
}
static int
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency_frames)
{
/*
* We've no device-independent minimum latency.
@ -533,7 +534,7 @@ sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latenc
}
static void
sndio_stream_destroy(cubeb_stream *s)
sndio_stream_destroy(cubeb_stream * s)
{
DPR("sndio_stream_destroy()\n");
WRAP(sio_close)(s->hdl);
@ -545,7 +546,7 @@ sndio_stream_destroy(cubeb_stream *s)
}
static int
sndio_stream_start(cubeb_stream *s)
sndio_stream_start(cubeb_stream * s)
{
int err;
@ -560,9 +561,9 @@ sndio_stream_start(cubeb_stream *s)
}
static int
sndio_stream_stop(cubeb_stream *s)
sndio_stream_stop(cubeb_stream * s)
{
void *dummy;
void * dummy;
DPR("sndio_stream_stop()\n");
if (s->active) {
@ -573,7 +574,7 @@ sndio_stream_stop(cubeb_stream *s)
}
static int
sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
sndio_stream_get_position(cubeb_stream * s, uint64_t * p)
{
pthread_mutex_lock(&s->mtx);
DPR("sndio_stream_get_position() %" PRId64 "\n", s->hwpos);
@ -583,7 +584,7 @@ sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
}
static int
sndio_stream_set_volume(cubeb_stream *s, float volume)
sndio_stream_set_volume(cubeb_stream * s, float volume)
{
DPR("sndio_stream_set_volume(%f)\n", volume);
pthread_mutex_lock(&s->mtx);
@ -606,27 +607,27 @@ sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
}
static int
sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
cubeb_device_collection *collection)
sndio_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
static char dev[] = SIO_DEVANY;
cubeb_device_info *device;
cubeb_device_info * device;
device = malloc(sizeof(cubeb_device_info));
if (device == NULL)
return CUBEB_ERROR;
device->devid = dev; /* passed to stream_init() */
device->device_id = dev; /* printable in UI */
device->friendly_name = dev; /* same, but friendly */
device->group_id = dev; /* actual device if full-duplex */
device->vendor_name = NULL; /* may be NULL */
device->type = type; /* Input/Output */
device->devid = dev; /* passed to stream_init() */
device->device_id = dev; /* printable in UI */
device->friendly_name = dev; /* same, but friendly */
device->group_id = dev; /* actual device if full-duplex */
device->vendor_name = NULL; /* may be NULL */
device->type = type; /* Input/Output */
device->state = CUBEB_DEVICE_STATE_ENABLED;
device->preferred = CUBEB_DEVICE_PREF_ALL;
device->format = CUBEB_DEVICE_FMT_S16NE;
device->default_format = CUBEB_DEVICE_FMT_S16NE;
device->max_channels = 16;
device->max_channels = (type == CUBEB_DEVICE_TYPE_INPUT) ? 2 : 8;
device->default_rate = 48000;
device->min_rate = 4000;
device->max_rate = 192000;
@ -639,32 +640,30 @@ sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
static int
sndio_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
cubeb_device_collection * collection)
{
free(collection->device);
return CUBEB_OK;
}
static struct cubeb_ops const sndio_ops = {
.init = sndio_init,
.get_backend_id = sndio_get_backend_id,
.get_max_channel_count = sndio_get_max_channel_count,
.get_min_latency = sndio_get_min_latency,
.get_preferred_sample_rate = sndio_get_preferred_sample_rate,
.enumerate_devices = sndio_enumerate_devices,
.device_collection_destroy = sndio_device_collection_destroy,
.destroy = sndio_destroy,
.stream_init = sndio_stream_init,
.stream_destroy = sndio_stream_destroy,
.stream_start = sndio_stream_start,
.stream_stop = sndio_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = sndio_stream_get_position,
.stream_get_latency = sndio_stream_get_latency,
.stream_set_volume = sndio_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.init = sndio_init,
.get_backend_id = sndio_get_backend_id,
.get_max_channel_count = sndio_get_max_channel_count,
.get_min_latency = sndio_get_min_latency,
.get_preferred_sample_rate = sndio_get_preferred_sample_rate,
.enumerate_devices = sndio_enumerate_devices,
.device_collection_destroy = sndio_device_collection_destroy,
.destroy = sndio_destroy,
.stream_init = sndio_stream_init,
.stream_destroy = sndio_stream_destroy,
.stream_start = sndio_stream_start,
.stream_stop = sndio_stream_stop,
.stream_get_position = sndio_stream_get_position,
.stream_get_latency = sndio_stream_get_latency,
.stream_set_volume = sndio_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL};

View file

@ -23,7 +23,7 @@ struct cubeb_strings {
int
cubeb_strings_init(cubeb_strings ** strings)
{
cubeb_strings* strs = NULL;
cubeb_strings * strs = NULL;
if (!strings) {
return CUBEB_ERROR;
@ -58,7 +58,7 @@ cubeb_strings_destroy(cubeb_strings * strings)
sp = strings->data;
se = sp + strings->count;
for ( ; sp != se; sp++) {
for (; sp != se; sp++) {
if (*sp) {
free(*sp);
}
@ -88,7 +88,7 @@ cubeb_strings_lookup(cubeb_strings * strings, char const * s)
sp = strings->data;
se = sp + strings->count;
for ( ; sp != se; sp++) {
for (; sp != se; sp++) {
if (*sp && strcmp(*sp, s) == 0) {
return *sp;
}
@ -152,4 +152,3 @@ cubeb_strings_intern(cubeb_strings * strings, char const * s)
return cubeb_strings_push(strings, s);
}

View file

@ -22,12 +22,14 @@ typedef struct cubeb_strings cubeb_strings;
interned string storage will be returned.
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR in case of error. */
CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);
CUBEB_EXPORT int
cubeb_strings_init(cubeb_strings ** strings);
/** Destroy an interned string structure freeing all associated memory.
@param strings An opaque pointer to the interned string storage to
destroy. */
CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
CUBEB_EXPORT void
cubeb_strings_destroy(cubeb_strings * strings);
/** Add string to internal storage.
@param strings Opaque pointer to interned string storage.
@ -35,7 +37,8 @@ CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
@retval CUBEB_OK
@retval CUBEB_ERROR
*/
CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);
CUBEB_EXPORT char const *
cubeb_strings_intern(cubeb_strings * strings, char const * s);
#if defined(__cplusplus)
}

View file

@ -4,18 +4,18 @@
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include <sys/audioio.h>
#include <sys/ioctl.h>
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include <fcntl.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include <sys/audioio.h>
#include <sys/ioctl.h>
#include <unistd.h>
/* Default to 4 + 1 for the default device. */
#ifndef SUN_DEVICE_COUNT
@ -45,11 +45,11 @@
*/
#ifndef SUN_MAX_CHANNELS
# ifdef __NetBSD__
# define SUN_MAX_CHANNELS (12)
# else
# define SUN_MAX_CHANNELS (2)
# endif
#ifdef __NetBSD__
#define SUN_MAX_CHANNELS (12)
#else
#define SUN_MAX_CHANNELS (2)
#endif
#endif
#ifndef SUN_MIN_RATE
@ -145,8 +145,8 @@ sun_get_min_latency(cubeb * context, cubeb_stream_params params,
}
static int
sun_get_hwinfo(const char * device, struct audio_info * format,
int * props, struct audio_device * dev)
sun_get_hwinfo(const char * device, struct audio_info * format, int * props,
struct audio_device * dev)
{
int fd = -1;
@ -181,9 +181,10 @@ error:
static int
sun_prinfo_verify_sanity(struct audio_prinfo * prinfo)
{
return prinfo->precision >= 8 && prinfo->precision <= 32 &&
prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE;
return prinfo->precision >= 8 && prinfo->precision <= 32 &&
prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
prinfo->sample_rate < SUN_MAX_RATE &&
prinfo->sample_rate > SUN_MIN_RATE;
}
static int
@ -196,7 +197,7 @@ sun_enumerate_devices(cubeb * context, cubeb_device_type type,
char dev_friendly[64];
struct audio_info hwfmt;
struct audio_device hwname;
struct audio_prinfo *prinfo = NULL;
struct audio_prinfo * prinfo = NULL;
int hwprops;
collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info));
@ -262,7 +263,8 @@ sun_enumerate_devices(cubeb * context, cubeb_device_type type,
device.vendor_name = strdup(hwname.name);
device.type = type;
device.state = CUBEB_DEVICE_STATE_ENABLED;
device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
device.preferred =
(i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
#ifdef AUDIO_GETFORMAT
device.max_channels = prinfo->channels;
device.default_rate = prinfo->sample_rate;
@ -393,7 +395,7 @@ sun_float_to_linear32(void * buf, unsigned sample_count, float vol)
}
static void
sun_linear32_to_float(void * buf, unsigned sample_count)
sun_linear32_to_float(void * buf, unsigned sample_count)
{
int32_t * in = buf;
float * out = buf;
@ -418,7 +420,7 @@ sun_linear16_set_vol(int16_t * buf, unsigned sample_count, float vol)
static void *
sun_io_routine(void * arg)
{
cubeb_stream *s = arg;
cubeb_stream * s = arg;
cubeb_state state = CUBEB_STATE_STARTED;
size_t to_read = 0;
long to_write = 0;
@ -439,8 +441,8 @@ sun_io_routine(void * arg)
sun_linear32_to_float(s->record.buf,
s->record.info.record.channels * SUN_BUFFER_FRAMES);
}
to_write = s->data_cb(s, s->user_ptr,
s->record.buf, s->play.buf, SUN_BUFFER_FRAMES);
to_write = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf,
SUN_BUFFER_FRAMES);
if (to_write == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
break;
@ -456,8 +458,8 @@ sun_io_routine(void * arg)
sun_float_to_linear32(s->play.buf,
s->play.info.play.channels * to_write, vol);
} else {
sun_linear16_set_vol(s->play.buf,
s->play.info.play.channels * to_write, vol);
sun_linear16_set_vol(s->play.buf, s->play.info.play.channels * to_write,
vol);
}
}
if (to_write < SUN_BUFFER_FRAMES) {
@ -473,7 +475,8 @@ sun_io_routine(void * arg)
if (to_write > 0) {
bytes = to_write * s->play.frame_size;
if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) < 0) {
if ((n = write(s->play.fd, (uint8_t *)s->play.buf + write_ofs, bytes)) <
0) {
state = CUBEB_STATE_ERROR;
break;
}
@ -486,7 +489,8 @@ sun_io_routine(void * arg)
}
if (to_read > 0) {
bytes = to_read * s->record.frame_size;
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs, bytes)) < 0) {
if ((n = read(s->record.fd, (uint8_t *)s->record.buf + read_ofs,
bytes)) < 0) {
state = CUBEB_STATE_ERROR;
break;
}
@ -505,20 +509,16 @@ sun_io_routine(void * arg)
}
static int
sun_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
sun_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
unsigned latency_frames, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
int ret = CUBEB_OK;
cubeb_stream *s = NULL;
cubeb_stream * s = NULL;
(void)stream_name;
(void)latency_frames;
@ -529,14 +529,14 @@ sun_stream_init(cubeb * context,
s->record.fd = -1;
s->play.fd = -1;
if (input_device != 0) {
snprintf(s->record.name, sizeof(s->record.name),
"/dev/audio%zu", (uintptr_t)input_device - 1);
snprintf(s->record.name, sizeof(s->record.name), "/dev/audio%zu",
(uintptr_t)input_device - 1);
} else {
snprintf(s->record.name, sizeof(s->record.name), "%s", SUN_DEFAULT_DEVICE);
}
if (output_device != 0) {
snprintf(s->play.name, sizeof(s->play.name),
"/dev/audio%zu", (uintptr_t)output_device - 1);
snprintf(s->play.name, sizeof(s->play.name), "/dev/audio%zu",
(uintptr_t)output_device - 1);
} else {
snprintf(s->play.name, sizeof(s->play.name), "%s", SUN_DEFAULT_DEVICE);
}
@ -558,11 +558,13 @@ sun_stream_init(cubeb * context,
s->record.info.mode = AUMODE_RECORD;
#endif
if ((ret = sun_copy_params(s->record.fd, s, input_stream_params,
&s->record.info, &s->record.info.record)) != CUBEB_OK) {
&s->record.info, &s->record.info.record)) !=
CUBEB_OK) {
LOG("Setting record params failed");
goto error;
}
s->record.floating = (input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->record.floating =
(input_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
}
if (output_stream_params != NULL) {
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
@ -582,7 +584,8 @@ sun_stream_init(cubeb * context,
s->play.info.mode = AUMODE_PLAY;
#endif
if ((ret = sun_copy_params(s->play.fd, s, output_stream_params,
&s->play.info, &s->play.info.play)) != CUBEB_OK) {
&s->play.info, &s->play.info.play)) !=
CUBEB_OK) {
LOG("Setting play params failed");
goto error;
}
@ -597,17 +600,18 @@ sun_stream_init(cubeb * context,
LOG("Failed to create mutex");
goto error;
}
s->play.frame_size = s->play.info.play.channels *
(s->play.info.play.precision / 8);
s->play.frame_size =
s->play.info.play.channels * (s->play.info.play.precision / 8);
if (s->play.fd != -1 &&
(s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) {
(s->play.buf = calloc(SUN_BUFFER_FRAMES, s->play.frame_size)) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
s->record.frame_size = s->record.info.record.channels *
(s->record.info.record.precision / 8);
s->record.frame_size =
s->record.info.record.channels * (s->record.info.record.precision / 8);
if (s->record.fd != -1 &&
(s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) == NULL) {
(s->record.buf = calloc(SUN_BUFFER_FRAMES, s->record.frame_size)) ==
NULL) {
ret = CUBEB_ERROR;
goto error;
}
@ -688,10 +692,10 @@ sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
if (*device == NULL) {
return CUBEB_ERROR;
}
(*device)->input_name = stream->record.fd != -1 ?
strdup(stream->record.name) : NULL;
(*device)->output_name = stream->play.fd != -1 ?
strdup(stream->play.name) : NULL;
(*device)->input_name =
stream->record.fd != -1 ? strdup(stream->record.name) : NULL;
(*device)->output_name =
stream->play.fd != -1 ? strdup(stream->play.name) : NULL;
return CUBEB_OK;
}
@ -706,26 +710,24 @@ sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
}
static struct cubeb_ops const sun_ops = {
.init = sun_init,
.get_backend_id = sun_get_backend_id,
.get_max_channel_count = sun_get_max_channel_count,
.get_min_latency = sun_get_min_latency,
.get_preferred_sample_rate = sun_get_preferred_sample_rate,
.enumerate_devices = sun_enumerate_devices,
.device_collection_destroy = sun_device_collection_destroy,
.destroy = sun_destroy,
.stream_init = sun_stream_init,
.stream_destroy = sun_stream_destroy,
.stream_start = sun_stream_start,
.stream_stop = sun_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = sun_stream_get_position,
.stream_get_latency = sun_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = sun_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = sun_get_current_device,
.stream_device_destroy = sun_stream_device_destroy,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.init = sun_init,
.get_backend_id = sun_get_backend_id,
.get_max_channel_count = sun_get_max_channel_count,
.get_min_latency = sun_get_min_latency,
.get_preferred_sample_rate = sun_get_preferred_sample_rate,
.enumerate_devices = sun_enumerate_devices,
.device_collection_destroy = sun_device_collection_destroy,
.destroy = sun_destroy,
.stream_init = sun_stream_init,
.stream_destroy = sun_stream_destroy,
.stream_start = sun_stream_start,
.stream_stop = sun_stream_stop,
.stream_get_position = sun_stream_get_position,
.stream_get_latency = sun_stream_get_latency,
.stream_get_input_latency = NULL,
.stream_set_volume = sun_stream_set_volume,
.stream_set_name = NULL,
.stream_get_current_device = sun_get_current_device,
.stream_device_destroy = sun_stream_device_destroy,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL};

View file

@ -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

View file

@ -7,18 +7,19 @@
#include "cubeb_utils.h"
size_t cubeb_sample_size(cubeb_sample_format format)
size_t
cubeb_sample_size(cubeb_sample_format format)
{
switch (format) {
case CUBEB_SAMPLE_S16LE:
case CUBEB_SAMPLE_S16BE:
return sizeof(int16_t);
case CUBEB_SAMPLE_FLOAT32LE:
case CUBEB_SAMPLE_FLOAT32BE:
return sizeof(float);
default:
// should never happen as all cases are handled above.
assert(false);
return 0;
case CUBEB_SAMPLE_S16LE:
case CUBEB_SAMPLE_S16BE:
return sizeof(int16_t);
case CUBEB_SAMPLE_FLOAT32LE:
case CUBEB_SAMPLE_FLOAT32BE:
return sizeof(float);
default:
// should never happen as all cases are handled above.
assert(false);
return 0;
}
}

View file

@ -12,10 +12,10 @@
#ifdef __cplusplus
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <mutex>
#include <stdint.h>
#include <string.h>
#include <type_traits>
#if defined(_WIN32)
#include "cubeb_utils_win.h"
@ -24,8 +24,9 @@
#endif
/** Similar to memcpy, but accounts for the size of an element. */
template<typename T>
void PodCopy(T * destination, const T * source, size_t count)
template <typename T>
void
PodCopy(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination && source);
@ -33,8 +34,9 @@ void PodCopy(T * destination, const T * source, size_t count)
}
/** Similar to memmove, but accounts for the size of an element. */
template<typename T>
void PodMove(T * destination, const T * source, size_t count)
template <typename T>
void
PodMove(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination && source);
@ -42,133 +44,118 @@ void PodMove(T * destination, const T * source, size_t count)
}
/** Similar to a memset to zero, but accounts for the size of an element. */
template<typename T>
void PodZero(T * destination, size_t count)
template <typename T>
void
PodZero(T * destination, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination);
memset(destination, 0, count * sizeof(T));
memset(destination, 0, count * sizeof(T));
}
namespace {
template<typename T, typename Trait>
void Copy(T * destination, const T * source, size_t count, Trait)
template <typename T, typename Trait>
void
Copy(T * destination, const T * source, size_t count, Trait)
{
for (size_t i = 0; i < count; i++) {
destination[i] = source[i];
}
}
template<typename T>
void Copy(T * destination, const T * source, size_t count, std::true_type)
template <typename T>
void
Copy(T * destination, const T * source, size_t count, std::true_type)
{
PodCopy(destination, source, count);
}
}
} // namespace
/**
* This allows copying a number of elements from a `source` pointer to a
* `destination` pointer, using `memcpy` if it is safe to do so, or a loop that
* calls the constructors and destructors otherwise.
*/
template<typename T>
void Copy(T * destination, const T * source, size_t count)
template <typename T>
void
Copy(T * destination, const T * source, size_t count)
{
assert(destination && source);
Copy(destination, source, count, typename std::is_trivial<T>::type());
}
namespace {
template<typename T, typename Trait>
void ConstructDefault(T * destination, size_t count, Trait)
template <typename T, typename Trait>
void
ConstructDefault(T * destination, size_t count, Trait)
{
for (size_t i = 0; i < count; i++) {
destination[i] = T();
}
}
template<typename T>
void ConstructDefault(T * destination,
size_t count, std::true_type)
template <typename T>
void
ConstructDefault(T * destination, size_t count, std::true_type)
{
PodZero(destination, count);
}
}
} // namespace
/**
* This allows zeroing (using memset) or default-constructing a number of
* elements calling the constructors and destructors if necessary.
*/
template<typename T>
void ConstructDefault(T * destination, size_t count)
template <typename T>
void
ConstructDefault(T * destination, size_t count)
{
assert(destination);
ConstructDefault(destination, count,
typename std::is_arithmetic<T>::type());
ConstructDefault(destination, count, typename std::is_arithmetic<T>::type());
}
template<typename T>
class auto_array
{
template <typename T> class auto_array {
public:
explicit auto_array(uint32_t capacity = 0)
: data_(capacity ? new T[capacity] : nullptr)
, capacity_(capacity)
, length_(0)
{}
~auto_array()
: data_(capacity ? new T[capacity] : nullptr), capacity_(capacity),
length_(0)
{
delete [] data_;
}
~auto_array() { delete[] data_; }
/** Get a constant pointer to the underlying data. */
T * data() const
{
return data_;
}
T * data() const { return data_; }
T * end() const
{
return data_ + length_;
}
T * end() const { return data_ + length_; }
const T& at(size_t index) const
const T & at(size_t index) const
{
assert(index < length_ && "out of range");
return data_[index];
}
T& at(size_t index)
T & at(size_t index)
{
assert(index < length_ && "out of range");
return data_[index];
}
/** Get how much underlying storage this auto_array has. */
size_t capacity() const
{
return capacity_;
}
size_t capacity() const { return capacity_; }
/** Get how much elements this auto_array contains. */
size_t length() const
{
return length_;
}
size_t length() const { return length_; }
/** Keeps the storage, but removes all the elements from the array. */
void clear()
{
length_ = 0;
}
void clear() { length_ = 0; }
/** Change the storage of this auto array, copying the elements to the new
* storage.
* @returns true in case of success
* @returns false if the new capacity is not big enough to accomodate for the
* elements in the array.
*/
/** Change the storage of this auto array, copying the elements to the new
* storage.
* @returns true in case of success
* @returns false if the new capacity is not big enough to accomodate for the
* elements in the array.
*/
bool reserve(size_t new_capacity)
{
if (new_capacity < length_) {
@ -179,17 +166,17 @@ public:
PodCopy(new_data, data_, length_);
}
capacity_ = new_capacity;
delete [] data_;
delete[] data_;
data_ = new_data;
return true;
}
/** Append `length` elements to the end of the array, resizing the array if
* needed.
* @parameter elements the elements to append to the array.
* @parameter length the number of elements to append to the array.
*/
/** Append `length` elements to the end of the array, resizing the array if
* needed.
* @parameter elements the elements to append to the array.
* @parameter length the number of elements to append to the array.
*/
void push(const T * elements, size_t length)
{
if (length_ + length > capacity_) {
@ -227,17 +214,14 @@ public:
}
/** Return the number of free elements in the array. */
size_t available() const
{
return capacity_ - length_;
}
size_t available() const { return capacity_ - length_; }
/** Copies `length` elements to `elements` if it is not null, and shift
* the remaining elements of the `auto_array` to the beginning.
* @parameter elements a buffer to copy the elements to, or nullptr.
* @parameter length the number of elements to copy.
* @returns true in case of success.
* @returns false if the auto_array contains less than `length` elements. */
* the remaining elements of the `auto_array` to the beginning.
* @parameter elements a buffer to copy the elements to, or nullptr.
* @parameter length the number of elements to copy.
* @returns true in case of success.
* @returns false if the auto_array contains less than `length` elements. */
bool pop(T * elements, size_t length)
{
if (length > length_) {
@ -285,56 +269,38 @@ template <typename T>
struct auto_array_wrapper_impl : public auto_array_wrapper {
auto_array_wrapper_impl() {}
explicit auto_array_wrapper_impl(uint32_t size)
: ar(size)
{}
explicit auto_array_wrapper_impl(uint32_t size) : ar(size) {}
void push(void * elements, size_t length) override {
void push(void * elements, size_t length) override
{
ar.push(static_cast<T *>(elements), length);
}
size_t length() override {
return ar.length();
}
size_t length() override { return ar.length(); }
void push_silence(size_t length) override {
ar.push_silence(length);
}
void push_silence(size_t length) override { ar.push_silence(length); }
bool pop(size_t length) override {
return ar.pop(nullptr, length);
}
bool pop(size_t length) override { return ar.pop(nullptr, length); }
void * data() override {
return ar.data();
}
void * data() override { return ar.data(); }
void * end() override {
return ar.end();
}
void * end() override { return ar.end(); }
void clear() override {
ar.clear();
}
void clear() override { ar.clear(); }
bool reserve(size_t capacity) override {
return ar.reserve(capacity);
}
bool reserve(size_t capacity) override { return ar.reserve(capacity); }
void set_length(size_t length) override {
ar.set_length(length);
}
void set_length(size_t length) override { ar.set_length(length); }
~auto_array_wrapper_impl() {
ar.clear();
}
~auto_array_wrapper_impl() { ar.clear(); }
private:
auto_array<T> ar;
};
extern "C" {
size_t cubeb_sample_size(cubeb_sample_format format);
size_t
cubeb_sample_size(cubeb_sample_format format);
}
using auto_lock = std::lock_guard<owned_critical_section>;

View file

@ -8,13 +8,12 @@
#if !defined(CUBEB_UTILS_UNIX)
#define CUBEB_UTILS_UNIX
#include <pthread.h>
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
/* This wraps a critical section to track the owner in debug mode. */
class owned_critical_section
{
class owned_critical_section {
public:
owned_critical_section()
{
@ -29,7 +28,7 @@ public:
#ifndef NDEBUG
int r =
#endif
pthread_mutex_init(&mutex, &attr);
pthread_mutex_init(&mutex, &attr);
#ifndef NDEBUG
assert(r == 0);
#endif
@ -42,7 +41,7 @@ public:
#ifndef NDEBUG
int r =
#endif
pthread_mutex_destroy(&mutex);
pthread_mutex_destroy(&mutex);
#ifndef NDEBUG
assert(r == 0);
#endif
@ -53,7 +52,7 @@ public:
#ifndef NDEBUG
int r =
#endif
pthread_mutex_lock(&mutex);
pthread_mutex_lock(&mutex);
#ifndef NDEBUG
assert(r == 0 && "Deadlock");
#endif
@ -64,7 +63,7 @@ public:
#ifndef NDEBUG
int r =
#endif
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex);
#ifndef NDEBUG
assert(r == 0 && "Unlocking unlocked mutex");
#endif
@ -82,8 +81,8 @@ private:
pthread_mutex_t mutex;
// Disallow copy and assignment because pthread_mutex_t cannot be copied.
owned_critical_section(const owned_critical_section&);
owned_critical_section& operator=(const owned_critical_section&);
owned_critical_section(const owned_critical_section &);
owned_critical_section & operator=(const owned_critical_section &);
};
#endif /* CUBEB_UTILS_UNIX */

View file

@ -8,30 +8,26 @@
#if !defined(CUBEB_UTILS_WIN)
#define CUBEB_UTILS_WIN
#include <windows.h>
#include "cubeb-internal.h"
#include <windows.h>
/* This wraps a critical section to track the owner in debug mode, adapted from
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
class owned_critical_section
{
/* 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
*/
class owned_critical_section {
public:
owned_critical_section()
: srwlock(SRWLOCK_INIT)
#ifndef NDEBUG
: owner(0)
,
owner(0)
#endif
{
InitializeCriticalSection(&critical_section);
}
~owned_critical_section()
{
DeleteCriticalSection(&critical_section);
}
void lock()
{
EnterCriticalSection(&critical_section);
AcquireSRWLockExclusive(&srwlock);
#ifndef NDEBUG
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
owner = GetCurrentThreadId();
@ -44,7 +40,7 @@ public:
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
owner = 0;
#endif
LeaveCriticalSection(&critical_section);
ReleaseSRWLockExclusive(&srwlock);
}
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
@ -58,14 +54,14 @@ public:
}
private:
CRITICAL_SECTION critical_section;
SRWLOCK srwlock;
#ifndef NDEBUG
DWORD owner;
#endif
// Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
owned_critical_section(const owned_critical_section&);
owned_critical_section& operator=(const owned_critical_section&);
// Disallow copy and assignment because SRWLock cannot be copied.
owned_critical_section(const owned_critical_section &);
owned_critical_section & operator=(const owned_critical_section &);
};
#endif /* CUBEB_UTILS_WIN */

File diff suppressed because it is too large Load diff

View file

@ -8,71 +8,71 @@
#define WINVER 0x0501
#undef WIN32_LEAN_AND_MEAN
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include <malloc.h>
#include <windows.h>
#include <mmreg.h>
#include <mmsystem.h>
#include <math.h>
#include <process.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include <windows.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. */
#if !defined(MEMORY_ALLOCATION_ALIGNMENT)
#define MEMORY_ALLOCATION_ALIGNMENT 16
#endif
/**This is also missing from the MinGW headers. It also appears to be undocumented by Microsoft.*/
/**This is also missing from the MinGW headers. It also appears to be
* undocumented by Microsoft.*/
#ifndef WAVE_FORMAT_48M08
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
#define WAVE_FORMAT_48M08 0x00001000 /* 48 kHz, Mono, 8-bit */
#endif
#ifndef WAVE_FORMAT_48M16
#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */
#define WAVE_FORMAT_48M16 0x00002000 /* 48 kHz, Mono, 16-bit */
#endif
#ifndef WAVE_FORMAT_48S08
#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */
#define WAVE_FORMAT_48S08 0x00004000 /* 48 kHz, Stereo, 8-bit */
#endif
#ifndef WAVE_FORMAT_48S16
#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
#define WAVE_FORMAT_48S16 0x00008000 /* 48 kHz, Stereo, 16-bit */
#endif
#ifndef WAVE_FORMAT_96M08
#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
#define WAVE_FORMAT_96M08 0x00010000 /* 96 kHz, Mono, 8-bit */
#endif
#ifndef WAVE_FORMAT_96M16
#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */
#define WAVE_FORMAT_96M16 0x00020000 /* 96 kHz, Mono, 16-bit */
#endif
#ifndef WAVE_FORMAT_96S08
#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */
#define WAVE_FORMAT_96S08 0x00040000 /* 96 kHz, Stereo, 8-bit */
#endif
#ifndef WAVE_FORMAT_96S16
#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
#define WAVE_FORMAT_96S16 0x00080000 /* 96 kHz, Stereo, 16-bit */
#endif
/**Taken from winbase.h, also not in MinGW.*/
#ifndef STACK_SIZE_PARAM_IS_A_RESERVATION
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only
#define STACK_SIZE_PARAM_IS_A_RESERVATION 0x00010000 // Threads only
#endif
#ifndef DRVM_MAPPER
#define DRVM_MAPPER (0x2000)
#define DRVM_MAPPER (0x2000)
#endif
#ifndef DRVM_MAPPER_PREFERRED_GET
#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21)
#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER + 21)
#endif
#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23)
#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER + 23)
#endif
#define CUBEB_STREAM_MAX 32
#define NBUFS 4
const GUID KSDATAFORMAT_SUBTYPE_PCM =
{ 0x00000001, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
const GUID KSDATAFORMAT_SUBTYPE_IEEE_FLOAT =
{ 0x00000003, 0x0000, 0x0010, { 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 } };
struct cubeb_stream_item {
SLIST_ENTRY head;
cubeb_stream * stream;
@ -110,6 +110,10 @@ struct cubeb_stream {
CRITICAL_SECTION lock;
uint64_t written;
float soft_volume;
/* For position wrap-around handling: */
size_t frame_size;
DWORD prev_pos_lo_dword;
DWORD pos_hi_dword;
};
static size_t
@ -175,7 +179,7 @@ winmm_refill_stream(cubeb_stream * stm)
hdr = winmm_get_next_buffer(stm);
wanted = (DWORD) stm->buffer_size / bytes_per_frame(stm->params);
wanted = (DWORD)stm->buffer_size / bytes_per_frame(stm->params);
/* It is assumed that the caller is holding this lock. It must be dropped
during the callback to avoid deadlocks. */
@ -199,16 +203,16 @@ winmm_refill_stream(cubeb_stream * stm)
if (stm->soft_volume != -1.0) {
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE) {
float * b = (float *) hdr->lpData;
float * b = (float *)hdr->lpData;
uint32_t i;
for (i = 0; i < got * stm->params.channels; i++) {
b[i] *= stm->soft_volume;
}
} else {
short * b = (short *) hdr->lpData;
short * b = (short *)hdr->lpData;
uint32_t i;
for (i = 0; i < got * stm->params.channels; i++) {
b[i] = (short) (b[i] * stm->soft_volume);
b[i] = (short)(b[i] * stm->soft_volume);
}
}
}
@ -223,10 +227,9 @@ winmm_refill_stream(cubeb_stream * stm)
LeaveCriticalSection(&stm->lock);
}
static unsigned __stdcall
winmm_buffer_thread(void * user_ptr)
static unsigned __stdcall winmm_buffer_thread(void * user_ptr)
{
cubeb * ctx = (cubeb *) user_ptr;
cubeb * ctx = (cubeb *)user_ptr;
XASSERT(ctx);
for (;;) {
@ -242,7 +245,7 @@ winmm_buffer_thread(void * user_ptr)
item = InterlockedFlushSList(ctx->work);
while (item != NULL) {
PSLIST_ENTRY tmp = item;
winmm_refill_stream(((struct cubeb_stream_item *) tmp)->stream);
winmm_refill_stream(((struct cubeb_stream_item *)tmp)->stream);
item = item->Next;
_aligned_free(tmp);
}
@ -256,16 +259,18 @@ winmm_buffer_thread(void * user_ptr)
}
static void CALLBACK
winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2)
winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr,
DWORD_PTR p1, DWORD_PTR p2)
{
cubeb_stream * stm = (cubeb_stream *) user_ptr;
cubeb_stream * stm = (cubeb_stream *)user_ptr;
struct cubeb_stream_item * item;
if (msg != WOM_DONE) {
return;
}
item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT);
item = _aligned_malloc(sizeof(struct cubeb_stream_item),
MEMORY_ALLOCATION_ALIGNMENT);
XASSERT(item);
item->stream = stm;
InterlockedPushEntrySList(stm->context->work, &item->head);
@ -284,7 +289,8 @@ calculate_minimum_latency(void)
return 500;
}
/* Vista's WinMM implementation underruns when less than 200ms of audio is buffered. */
/* Vista's WinMM implementation underruns when less than 200ms of audio is
* buffered. */
memset(&osvi, 0, sizeof(OSVERSIONINFOEX));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
osvi.dwMajorVersion = 6;
@ -294,14 +300,16 @@ calculate_minimum_latency(void)
VER_SET_CONDITION(mask, VER_MAJORVERSION, VER_EQUAL);
VER_SET_CONDITION(mask, VER_MINORVERSION, VER_EQUAL);
if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) != 0) {
if (VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, mask) !=
0) {
return 200;
}
return 100;
}
static void winmm_destroy(cubeb * ctx);
static void
winmm_destroy(cubeb * ctx);
/*static*/ int
winmm_init(cubeb ** context, char const * context_name)
@ -331,7 +339,9 @@ winmm_init(cubeb ** context, char const * context_name)
return CUBEB_ERROR;
}
ctx->thread = (HANDLE) _beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx, STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
ctx->thread =
(HANDLE)_beginthreadex(NULL, 256 * 1024, winmm_buffer_thread, ctx,
STACK_SIZE_PARAM_IS_A_RESERVATION, NULL);
if (!ctx->thread) {
winmm_destroy(ctx);
return CUBEB_ERROR;
@ -382,18 +392,18 @@ winmm_destroy(cubeb * ctx)
free(ctx);
}
static void winmm_stream_destroy(cubeb_stream * stm);
static void
winmm_stream_destroy(cubeb_stream * stm);
static int
winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
winmm_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
cubeb_state_callback state_callback, void * user_ptr)
{
MMRESULT r;
WAVEFORMATEXTENSIBLE wfx;
@ -452,8 +462,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
return CUBEB_ERROR_INVALID_FORMAT;
}
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.nBlockAlign =
(wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
wfx.Format.nAvgBytesPerSec =
wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
EnterCriticalSection(&context->lock);
@ -485,9 +497,11 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
latency_ms = context->minimum_latency_ms;
}
bufsz = (size_t) (stm->params.rate / 1000.0 * latency_ms * bytes_per_frame(stm->params) / NBUFS);
bufsz = (size_t)(stm->params.rate / 1000.0 * latency_ms *
bytes_per_frame(stm->params) / NBUFS);
if (bufsz % bytes_per_frame(stm->params) != 0) {
bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
bufsz +=
bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params));
}
XASSERT(bufsz % bytes_per_frame(stm->params) == 0);
@ -506,7 +520,7 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
/* winmm_buffer_callback will be called during waveOutOpen, so all
other initialization must be complete before calling it. */
r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format,
(DWORD_PTR) winmm_buffer_callback, (DWORD_PTR) stm,
(DWORD_PTR)winmm_buffer_callback, (DWORD_PTR)stm,
CALLBACK_FUNCTION);
if (r != MMSYSERR_NOERROR) {
winmm_stream_destroy(stm);
@ -536,6 +550,10 @@ winmm_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_n
winmm_refill_stream(stm);
}
stm->frame_size = bytes_per_frame(stm->params);
stm->prev_pos_lo_dword = 0;
stm->pos_hi_dword = 0;
*stream = stm;
return CUBEB_OK;
@ -580,7 +598,8 @@ winmm_stream_destroy(cubeb_stream * stm)
for (i = 0; i < NBUFS; ++i) {
if (stm->buffers[i].dwFlags & WHDR_PREPARED) {
waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i]));
waveOutUnprepareHeader(stm->waveout, &stm->buffers[i],
sizeof(stm->buffers[i]));
}
}
@ -619,7 +638,8 @@ winmm_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
}
static int
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
winmm_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency)
{
// 100ms minimum, if we are not in a bizarre configuration.
*latency = ctx->minimum_latency_ms * params.rate / 1000;
@ -686,6 +706,58 @@ winmm_stream_stop(cubeb_stream * stm)
return CUBEB_OK;
}
/*
Microsoft wave audio docs say "samples are the preferred time format in which
to represent the current position", but relying on this causes problems on
Windows XP, the only OS cubeb_winmm is used on.
While the wdmaud.sys driver internally tracks a 64-bit position and ensures no
backward movement, the WinMM API limits the position returned from
waveOutGetPosition() to a 32-bit DWORD (this applies equally to XP x64). The
higher 32 bits are chopped off, and to an API consumer the position can appear
to move backward.
In theory, even a 32-bit TIME_SAMPLES position should provide plenty of
playback time for typical use cases before this pseudo wrap-around, e.g:
(2^32 - 1)/48000 = ~24:51:18 for 48.0 kHz stereo;
(2^32 - 1)/44100 = ~27:03:12 for 44.1 kHz stereo.
In reality, wdmaud.sys doesn't provide a TIME_SAMPLES position at all, only a
32-bit TIME_BYTES position, from which wdmaud.drv derives TIME_SAMPLES:
SamplePos = (BytePos * 8) / BitsPerFrame,
where BitsPerFrame = Channels * BitsPerSample,
Per dom\media\AudioSampleFormat.h, desktop builds always use 32-bit FLOAT32
samples, so the maximum for TIME_SAMPLES should be:
(2^29 - 1)/48000 = ~03:06:25;
(2^29 - 1)/44100 = ~03:22:54.
This might still be OK for typical browser usage, but there's also a bug in the
formula above: BytePos * 8 (BytePos << 3) is done on a 32-bit BytePos, without
first casting it to 64 bits, so the highest 3 bits, if set, would get shifted
out, and the maximum possible TIME_SAMPLES drops unacceptably low:
(2^26 - 1)/48000 = ~00:23:18;
(2^26 - 1)/44100 = ~00:25:22.
To work around these limitations, we just get the position in TIME_BYTES,
recover the 64-bit value, and do our own conversion to samples.
*/
/* Convert chopped 32-bit waveOutGetPosition() into 64-bit true position. */
static uint64_t
update_64bit_position(cubeb_stream * stm, DWORD pos_lo_dword)
{
/* Caller should be holding stm->lock. */
if (pos_lo_dword < stm->prev_pos_lo_dword) {
stm->pos_hi_dword++;
LOG("waveOutGetPosition() has wrapped around: %#lx -> %#lx",
stm->prev_pos_lo_dword, pos_lo_dword);
LOG("Wrap-around count = %#lx", stm->pos_hi_dword);
LOG("Current 64-bit position = %#llx",
(((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword));
}
stm->prev_pos_lo_dword = pos_lo_dword;
return (((uint64_t)stm->pos_hi_dword) << 32) | ((uint64_t)pos_lo_dword);
}
static int
winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
@ -693,15 +765,17 @@ winmm_stream_get_position(cubeb_stream * stm, uint64_t * position)
MMTIME time;
EnterCriticalSection(&stm->lock);
time.wType = TIME_SAMPLES;
/* See the long comment above for why not just use TIME_SAMPLES here. */
time.wType = TIME_BYTES;
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
LeaveCriticalSection(&stm->lock);
if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
LeaveCriticalSection(&stm->lock);
return CUBEB_ERROR;
}
*position = time.u.sample;
*position = update_64bit_position(stm, time.u.cb) / stm->frame_size;
LeaveCriticalSection(&stm->lock);
return CUBEB_OK;
}
@ -711,20 +785,24 @@ winmm_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
MMRESULT r;
MMTIME time;
uint64_t written;
uint64_t written, position;
EnterCriticalSection(&stm->lock);
time.wType = TIME_SAMPLES;
/* See the long comment above for why not just use TIME_SAMPLES here. */
time.wType = TIME_BYTES;
r = waveOutGetPosition(stm->waveout, &time, sizeof(time));
written = stm->written;
LeaveCriticalSection(&stm->lock);
if (r != MMSYSERR_NOERROR || time.wType != TIME_SAMPLES) {
if (r != MMSYSERR_NOERROR || time.wType != TIME_BYTES) {
LeaveCriticalSection(&stm->lock);
return CUBEB_ERROR;
}
XASSERT(written - time.u.sample <= UINT32_MAX);
*latency = (uint32_t) (written - time.u.sample);
position = update_64bit_position(stm, time.u.cb);
written = stm->written;
LeaveCriticalSection(&stm->lock);
XASSERT((written - (position / stm->frame_size)) <= UINT32_MAX);
*latency = (uint32_t)(written - (position / stm->frame_size));
return CUBEB_OK;
}
@ -738,11 +816,18 @@ winmm_stream_set_volume(cubeb_stream * stm, float volume)
return CUBEB_OK;
}
#define MM_11025HZ_MASK (WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
#define MM_22050HZ_MASK (WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
#define MM_44100HZ_MASK (WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
#define MM_48000HZ_MASK (WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48S16)
#define MM_96000HZ_MASK (WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96S16)
#define MM_11025HZ_MASK \
(WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16)
#define MM_22050HZ_MASK \
(WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16)
#define MM_44100HZ_MASK \
(WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16)
#define MM_48000HZ_MASK \
(WAVE_FORMAT_48M08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S08 | \
WAVE_FORMAT_48S16)
#define MM_96000HZ_MASK \
(WAVE_FORMAT_96M08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S08 | \
WAVE_FORMAT_96S16)
static void
winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
{
@ -752,17 +837,20 @@ winmm_calculate_device_rate(cubeb_device_info * info, DWORD formats)
info->max_rate = 11025;
}
if (formats & MM_22050HZ_MASK) {
if (info->min_rate == 0) info->min_rate = 22050;
if (info->min_rate == 0)
info->min_rate = 22050;
info->max_rate = 22050;
info->default_rate = 22050;
}
if (formats & MM_44100HZ_MASK) {
if (info->min_rate == 0) info->min_rate = 44100;
if (info->min_rate == 0)
info->min_rate = 44100;
info->max_rate = 44100;
info->default_rate = 44100;
}
if (formats & MM_48000HZ_MASK) {
if (info->min_rate == 0) info->min_rate = 48000;
if (info->min_rate == 0)
info->min_rate = 48000;
info->max_rate = 48000;
info->default_rate = 48000;
}
@ -775,11 +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 | \
WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
#define MM_S16_MASK \
(WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 | \
WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 | WAVE_FORMAT_48M16 | \
WAVE_FORMAT_48S16 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16)
static int
winmm_query_supported_formats(UINT devid, DWORD formats,
cubeb_device_fmt * supfmt, cubeb_device_fmt * deffmt)
cubeb_device_fmt * supfmt,
cubeb_device_fmt * deffmt)
{
WAVEFORMATEXTENSIBLE wfx;
@ -793,13 +884,16 @@ winmm_query_supported_formats(UINT devid, DWORD formats,
wfx.Format.nChannels = 2;
wfx.Format.nSamplesPerSec = 44100;
wfx.Format.wBitsPerSample = 32;
wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.nBlockAlign =
(wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8;
wfx.Format.nAvgBytesPerSec =
wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
wfx.Format.cbSize = 22;
wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) == MMSYSERR_NOERROR)
if (waveOutOpen(NULL, devid, &wfx.Format, 0, 0, WAVE_FORMAT_QUERY) ==
MMSYSERR_NOERROR)
*supfmt = (cubeb_device_fmt)(*supfmt | CUBEB_DEVICE_FMT_F32LE);
return (*deffmt != 0) ? CUBEB_OK : CUBEB_ERROR;
@ -813,10 +907,10 @@ guid_to_cstr(LPGUID guid)
return NULL;
}
_snprintf_s(ret, 40, _TRUNCATE,
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
guid->Data1, guid->Data2, guid->Data3,
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", guid->Data1,
guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1],
guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
return ret;
}
@ -826,13 +920,15 @@ winmm_query_preferred_out_device(UINT devid)
DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
(DWORD_PTR)&mmpref,
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
devid == mmpref)
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
if (waveOutMessage((HWAVEOUT) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
if (waveOutMessage((HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
(DWORD_PTR)&compref,
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
devid == compref)
ret |= CUBEB_DEVICE_PREF_VOICE;
@ -851,10 +947,11 @@ device_id_idx(UINT devid)
}
static void
winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps, UINT devid)
winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
UINT devid)
{
XASSERT(ret);
ret->devid = (cubeb_devid) devid;
ret->devid = (cubeb_devid)devid;
ret->device_id = device_id_idx(devid);
ret->friendly_name = _strdup(caps->szPname);
ret->group_id = guid_to_cstr(&caps->ProductGuid);
@ -866,8 +963,8 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
ret->max_channels = caps->wChannels;
winmm_calculate_device_rate(ret, caps->dwFormats);
winmm_query_supported_formats(devid, caps->dwFormats,
&ret->format, &ret->default_format);
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
&ret->default_format);
/* Hardcoded latency estimates... */
ret->latency_lo = 100 * ret->default_rate / 1000;
@ -875,10 +972,11 @@ winmm_create_device_from_outcaps2(cubeb_device_info * ret, LPWAVEOUTCAPS2A caps,
}
static void
winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, UINT devid)
winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps,
UINT devid)
{
XASSERT(ret);
ret->devid = (cubeb_devid) devid;
ret->devid = (cubeb_devid)devid;
ret->device_id = device_id_idx(devid);
ret->friendly_name = _strdup(caps->szPname);
ret->group_id = NULL;
@ -890,8 +988,8 @@ winmm_create_device_from_outcaps(cubeb_device_info * ret, LPWAVEOUTCAPSA caps, U
ret->max_channels = caps->wChannels;
winmm_calculate_device_rate(ret, caps->dwFormats);
winmm_query_supported_formats(devid, caps->dwFormats,
&ret->format, &ret->default_format);
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
&ret->default_format);
/* Hardcoded latency estimates... */
ret->latency_lo = 100 * ret->default_rate / 1000;
@ -904,13 +1002,15 @@ winmm_query_preferred_in_device(UINT devid)
DWORD mmpref = WAVE_MAPPER, compref = WAVE_MAPPER, status;
cubeb_device_pref ret = CUBEB_DEVICE_PREF_NONE;
if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
(DWORD_PTR)&mmpref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET,
(DWORD_PTR)&mmpref,
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
devid == mmpref)
ret |= CUBEB_DEVICE_PREF_MULTIMEDIA | CUBEB_DEVICE_PREF_NOTIFICATION;
if (waveInMessage((HWAVEIN) WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
(DWORD_PTR)&compref, (DWORD_PTR)&status) == MMSYSERR_NOERROR &&
if (waveInMessage((HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_CONSOLEVOICECOM_GET,
(DWORD_PTR)&compref,
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
devid == compref)
ret |= CUBEB_DEVICE_PREF_VOICE;
@ -918,10 +1018,11 @@ winmm_query_preferred_in_device(UINT devid)
}
static void
winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, UINT devid)
winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps,
UINT devid)
{
XASSERT(ret);
ret->devid = (cubeb_devid) devid;
ret->devid = (cubeb_devid)devid;
ret->device_id = device_id_idx(devid);
ret->friendly_name = _strdup(caps->szPname);
ret->group_id = guid_to_cstr(&caps->ProductGuid);
@ -933,8 +1034,8 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U
ret->max_channels = caps->wChannels;
winmm_calculate_device_rate(ret, caps->dwFormats);
winmm_query_supported_formats(devid, caps->dwFormats,
&ret->format, &ret->default_format);
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
&ret->default_format);
/* Hardcoded latency estimates... */
ret->latency_lo = 100 * ret->default_rate / 1000;
@ -942,10 +1043,11 @@ winmm_create_device_from_incaps2(cubeb_device_info * ret, LPWAVEINCAPS2A caps, U
}
static void
winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UINT devid)
winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps,
UINT devid)
{
XASSERT(ret);
ret->devid = (cubeb_devid) devid;
ret->devid = (cubeb_devid)devid;
ret->device_id = device_id_idx(devid);
ret->friendly_name = _strdup(caps->szPname);
ret->group_id = NULL;
@ -957,8 +1059,8 @@ winmm_create_device_from_incaps(cubeb_device_info * ret, LPWAVEINCAPSA caps, UIN
ret->max_channels = caps->wChannels;
winmm_calculate_device_rate(ret, caps->dwFormats);
winmm_query_supported_formats(devid, caps->dwFormats,
&ret->format, &ret->default_format);
winmm_query_supported_formats(devid, caps->dwFormats, &ret->format,
&ret->default_format);
/* Hardcoded latency estimates... */
ret->latency_lo = 100 * ret->default_rate / 1000;
@ -989,7 +1091,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
for (i = 0; i < outcount; i++) {
dev = &devices[collection->count];
if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) == MMSYSERR_NOERROR) {
if (waveOutGetDevCapsA(i, (LPWAVEOUTCAPSA)&woc2, sizeof(woc2)) ==
MMSYSERR_NOERROR) {
winmm_create_device_from_outcaps2(dev, &woc2, i);
collection->count += 1;
} else if (waveOutGetDevCapsA(i, &woc, sizeof(woc)) == MMSYSERR_NOERROR) {
@ -1008,7 +1111,8 @@ winmm_enumerate_devices(cubeb * context, cubeb_device_type type,
for (i = 0; i < incount; i++) {
dev = &devices[collection->count];
if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) == MMSYSERR_NOERROR) {
if (waveInGetDevCapsA(i, (LPWAVEINCAPSA)&wic2, sizeof(wic2)) ==
MMSYSERR_NOERROR) {
winmm_create_device_from_incaps2(dev, &wic2, i);
collection->count += 1;
} else if (waveInGetDevCapsA(i, &wic, sizeof(wic)) == MMSYSERR_NOERROR) {
@ -1030,13 +1134,13 @@ winmm_device_collection_destroy(cubeb * ctx,
uint32_t i;
XASSERT(collection);
(void) ctx;
(void)ctx;
for (i = 0; i < collection->count; i++) {
free((void *) collection->device[i].device_id);
free((void *) collection->device[i].friendly_name);
free((void *) collection->device[i].group_id);
free((void *) collection->device[i].vendor_name);
free((void *)collection->device[i].device_id);
free((void *)collection->device[i].friendly_name);
free((void *)collection->device[i].group_id);
free((void *)collection->device[i].vendor_name);
}
free(collection->device);
@ -1044,26 +1148,24 @@ winmm_device_collection_destroy(cubeb * ctx,
}
static struct cubeb_ops const winmm_ops = {
/*.init =*/ winmm_init,
/*.get_backend_id =*/ winmm_get_backend_id,
/*.get_max_channel_count=*/ winmm_get_max_channel_count,
/*.get_min_latency=*/ winmm_get_min_latency,
/*.get_preferred_sample_rate =*/ winmm_get_preferred_sample_rate,
/*.enumerate_devices =*/ winmm_enumerate_devices,
/*.device_collection_destroy =*/ winmm_device_collection_destroy,
/*.destroy =*/ winmm_destroy,
/*.stream_init =*/ winmm_stream_init,
/*.stream_destroy =*/ winmm_stream_destroy,
/*.stream_start =*/ winmm_stream_start,
/*.stream_stop =*/ winmm_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ winmm_stream_get_position,
/*.stream_get_latency = */ winmm_stream_get_latency,
/*.stream_get_input_latency = */ NULL,
/*.stream_set_volume =*/ winmm_stream_set_volume,
/*.stream_set_name =*/ NULL,
/*.stream_get_current_device =*/ NULL,
/*.stream_device_destroy =*/ NULL,
/*.stream_register_device_changed_callback=*/ NULL,
/*.register_device_collection_changed =*/ NULL
};
/*.init =*/winmm_init,
/*.get_backend_id =*/winmm_get_backend_id,
/*.get_max_channel_count=*/winmm_get_max_channel_count,
/*.get_min_latency=*/winmm_get_min_latency,
/*.get_preferred_sample_rate =*/winmm_get_preferred_sample_rate,
/*.enumerate_devices =*/winmm_enumerate_devices,
/*.device_collection_destroy =*/winmm_device_collection_destroy,
/*.destroy =*/winmm_destroy,
/*.stream_init =*/winmm_stream_init,
/*.stream_destroy =*/winmm_stream_destroy,
/*.stream_start =*/winmm_stream_start,
/*.stream_stop =*/winmm_stream_stop,
/*.stream_get_position =*/winmm_stream_get_position,
/*.stream_get_latency = */ winmm_stream_get_latency,
/*.stream_get_input_latency = */ NULL,
/*.stream_set_volume =*/winmm_stream_set_volume,
/*.stream_set_name =*/NULL,
/*.stream_get_current_device =*/NULL,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback=*/NULL,
/*.register_device_collection_changed =*/NULL};