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 5658 additions and 4804 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,57 +65,176 @@ 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> $<INSTALL_INTERFACE:include>
)
set_target_properties(cubeb PROPERTIES
VERSION ${cubeb_VERSION}
SOVERSION ${cubeb_VERSION_MAJOR}
)
add_sanitizers(cubeb)
include(GenerateExportHeader)
generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h)
target_include_directories(cubeb
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>)
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})
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)
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPATIBILITY SameMajorVersion
)
add_library(speex OBJECT
src/speex/resample.c)
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_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)
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)
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
if(USE_AUDIOUNIT)
target_sources(cubeb PRIVATE
src/cubeb_audiounit.cpp
src/cubeb_osx_run_loop.cpp)
target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT)
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
endif()
# 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_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_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_sources(cubeb PRIVATE src/cubeb_jack.cpp)
target_compile_definitions(cubeb PRIVATE USE_JACK)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
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
src/cubeb_audiounit.cpp
src/cubeb_osx_run_loop.cpp)
target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT)
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
endif()
check_include_files(audioclient.h USE_WASAPI)
@ -93,7 +242,7 @@ 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,31 +267,23 @@ 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)
# 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)
target_link_libraries(cubeb PRIVATE pthread)
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)
@ -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,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 {
@ -223,29 +227,31 @@ 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
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.
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
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_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. */
@ -254,7 +260,9 @@ typedef struct {
#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_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;
@ -276,10 +284,13 @@ typedef enum {
enum {
CUBEB_OK = 0, /**< Success. */
CUBEB_ERROR = -1, /**< Unclassified error. */
CUBEB_ERROR_INVALID_FORMAT = -2, /**< Unsupported #cubeb_stream_params requested. */
CUBEB_ERROR_INVALID_FORMAT =
-2, /**< Unsupported #cubeb_stream_params requested. */
CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
CUBEB_ERROR_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,8 +306,10 @@ 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_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;
@ -313,7 +326,8 @@ typedef enum {
#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. */
/** 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. */
@ -323,11 +337,14 @@ typedef enum {
#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
@ -348,9 +365,13 @@ typedef enum {
* `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 *
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). */
@ -358,7 +379,8 @@ typedef struct {
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. */
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. */
@ -398,18 +420,15 @@ 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,
typedef long (*cubeb_data_callback)(cubeb_stream * stream, void * user_ptr,
void const * input_buffer,
void * output_buffer,
long nframes);
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,
typedef void (*cubeb_state_callback)(cubeb_stream * stream, void * user_ptr,
cubeb_state state);
/**
@ -420,7 +439,8 @@ typedef void (* cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */
* @param user_ptr The pointer passed to
* cubeb_register_device_collection_changed. */
typedef void (*cubeb_device_collection_changed_callback)(cubeb * context,
void * user_ptr);
@ -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,
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,8 +496,8 @@ 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,
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
@ -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_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);
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,7 +633,8 @@ CUBEB_EXPORT int cubeb_stream_set_name(cubeb_stream * stream, char const * strea
@retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
invalid pointers
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
CUBEB_EXPORT int
cubeb_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
/** Destroy a cubeb_device structure.
@ -620,8 +643,8 @@ CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
@retval CUBEB_OK in case of success
@retval CUBEB_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,23 +654,28 @@ 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_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_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`.
@ -655,7 +683,8 @@ CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * 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_EXPORT int
cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
@ -664,17 +693,18 @@ CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
@param devtype device type to include. Different callbacks and user pointers
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,7 +714,8 @@ CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
@retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
invalid pointers, or if level is not
in cubeb_log_level. */
CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
CUBEB_EXPORT int
cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback);
#if defined(__cplusplus)

View file

@ -22,12 +22,14 @@
*/
/*
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
* From
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
*/
typedef int32_t status_t;
/*
* 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;
@ -52,7 +54,8 @@ enum event_type {
};
/**
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
* From
* https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
* and
* https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h
*/
@ -63,14 +66,16 @@ enum {
AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1,
AUDIO_CHANNEL_OUT_FRONT_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_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;

View file

@ -17,10 +17,12 @@ 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)

View file

@ -29,7 +29,8 @@
/** 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. */
@ -46,7 +47,6 @@
/** uses the main microphone unprocessed */
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32)0x00000005)
/*---------------------------------------------------------------------------*/
/* Android AudioPlayer configuration */
/*---------------------------------------------------------------------------*/
@ -69,7 +69,6 @@
/* same as android.media.AudioManager.STREAM_NOTIFICATION */
#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32)0x00000005)
/*---------------------------------------------------------------------------*/
/* Android AudioPlayer and AudioRecorder configuration */
/*---------------------------------------------------------------------------*/
@ -85,15 +84,18 @@
* 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. */
/* 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. */
/* 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. */

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>
@ -37,8 +37,7 @@ 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,
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,
@ -46,21 +45,16 @@ struct cubeb_ops {
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,
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);
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);
@ -68,14 +62,13 @@ struct cubeb_ops {
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,
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 (*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,19 +10,14 @@
#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

View file

@ -5,12 +5,12 @@
* 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])))
@ -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,14 +94,18 @@ 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;
}
}
@ -99,8 +118,8 @@ validate_stream_params(cubeb_stream_params * input_stream_params,
}
}
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,7 +142,8 @@ 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;
@ -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,7 +485,8 @@ 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,
int
cubeb_stream_get_current_device(cubeb_stream * stream,
cubeb_device ** const device)
{
if (!stream || !device) {
@ -498,8 +500,8 @@ int cubeb_stream_get_current_device(cubeb_stream * stream,
return stream->context->ops->stream_get_current_device(stream, device);
}
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,7 +514,9 @@ 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,
int
cubeb_stream_register_device_changed_callback(
cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback)
{
if (!stream) {
@ -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,8 +541,8 @@ 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;
@ -611,19 +617,16 @@ 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);
}
int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
device_info->friendly_name, device_info->group_id,
device_info->vendor_name, devtype, devstate, device_info->max_channels,
(devfmts[0] == '\0') ? devfmts : devfmts + 1,
(unsigned int)device_info->format, devdeffmt, device_info->min_rate,
device_info->max_rate, device_info->default_rate, device_info->latency_lo,
device_info->latency_hi);
}
int
cubeb_enumerate_devices(cubeb * context, cubeb_device_type devtype,
cubeb_device_collection * collection)
{
int rv;
@ -645,7 +648,8 @@ int cubeb_enumerate_devices(cubeb * context,
return rv;
}
int cubeb_device_collection_destroy(cubeb * context,
int
cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
int r;
@ -668,22 +672,25 @@ 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,
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) {
@ -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,21 +8,21 @@
#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 WRAP(x) (*cubeb_##x)
#define LIBASOUND_API_VISIT(X) \
X(snd_config) \
X(snd_config_add) \
@ -57,7 +57,7 @@
X(snd_pcm_set_params) \
X(snd_pcm_start) \
X(snd_pcm_state) \
X(snd_pcm_writei) \
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,7 +336,8 @@ alsa_process_stream(cubeb_stream * stm)
return RUNNING;
}
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time. */
/* This could happen if we were suspended with SIGSTOP/Ctrl+Z for a long time.
*/
if ((unsigned int)avail > stm->buffer_size) {
avail = stm->buffer_size;
}
@ -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;
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;
@ -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;
}
@ -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;
}
@ -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,7 +837,8 @@ alsa_init(cubeb ** context, char const * context_name)
}
}
#define LOAD(x) { \
#define LOAD(x) \
{ \
cubeb_##x = dlsym(libasound, #x); \
if (!cubeb_##x) { \
dlclose(libasound); \
@ -876,7 +895,8 @@ alsa_init(cubeb ** context, char const * context_name)
/* Open a dummy PCM to force the configuration space to be evaluated so that
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;
@ -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);
@ -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;
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;
@ -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
@ -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;
}
@ -1441,7 +1471,6 @@ static struct cubeb_ops const alsa_ops = {
.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,
@ -1450,5 +1479,4 @@ static struct cubeb_ops const alsa_ops = {
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = 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,7 +24,8 @@ 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));
@ -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;

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
@ -46,17 +47,24 @@
} 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);
/* 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);
@ -101,7 +109,8 @@ audiotrack_refill(int event, void* user, void* info)
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");
@ -182,33 +197,44 @@ 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 &&
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->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);
@ -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.");
@ -430,7 +461,6 @@ static struct cubeb_ops const audiotrack_ops = {
.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,
@ -439,5 +469,4 @@ static struct cubeb_ops const audiotrack_ops = {
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.register_device_collection_changed = NULL};

File diff suppressed because it is too large Load diff

View file

@ -8,24 +8,28 @@
*/
#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>
#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) \
@ -49,6 +53,8 @@
#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"
@ -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,46 +87,72 @@ 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);
}
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,
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,
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,
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);
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,
@ -129,7 +167,6 @@ static struct cubeb_ops const cbjack_ops = {
.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,
@ -138,8 +175,7 @@ static struct cubeb_ops const cbjack_ops = {
.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
};
.register_device_collection_changed = NULL};
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
@ -205,6 +241,7 @@ 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);
@ -235,45 +272,48 @@ load_jack_lib(cubeb * context)
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);
if (phys_in_ports == NULL || *phys_in_ports == NULL) {
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 ||
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;
@ -332,7 +375,8 @@ 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;
@ -373,12 +417,14 @@ 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
@ -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;
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
@ -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,
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];
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,
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];
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;
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));
}
}
@ -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,21 +968,20 @@ cbjack_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_
for (unsigned int c = 0; c < stm->in_params.channels; c++) {
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) {
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;
}
}
}
}
*stream = stm;
@ -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);

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)
@ -121,8 +122,7 @@ kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
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);
@ -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,
@ -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;
}
@ -358,7 +358,6 @@ static struct cubeb_ops const kai_ops = {
/*.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,
@ -367,5 +366,4 @@ static struct cubeb_ops const kai_ops = {
/*.stream_get_current_device =*/NULL,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback=*/NULL,
/*.register_device_collection_changed=*/ 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>
@ -31,13 +32,9 @@ const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message
{
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) * 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 { \
#define LOG_INTERNAL_NO_FORMAT(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__); \
g_cubeb_log_callback(fmt, __VA_ARGS__); \
} \
} while (0)
/* Asynchronous verbose logging, to log in real-time callbacks. */
/* Should not be used on android due to the use of global/static variables. */
#define ALOGV(fmt, ...) \
#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(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,9 +19,6 @@
#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]))
@ -66,14 +66,17 @@ 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;
case 1:
return CUBEB_LAYOUT_MONO;
case 2:
return CUBEB_LAYOUT_STEREO;
}
}
return l;
}
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
unsigned int
cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
{
#if __GNUC__ || __clang__
return __builtin_popcount(x);
@ -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)) {
@ -166,15 +165,21 @@ struct MixerContext {
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
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 maxcoef = 0;
@ -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) {
@ -356,7 +360,8 @@ int MixerContext::auto_matrix()
return 0;
}
int MixerContext::init()
int
MixerContext::init()
{
int r = auto_matrix();
if (r) {
@ -400,20 +405,13 @@ int MixerContext::init()
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;
@ -424,18 +422,11 @@ sum2(TYPE_SAMPLE * out,
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,13 +435,12 @@ 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,
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++) {
@ -463,32 +453,21 @@ static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
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);
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,
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);
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];
v += *(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
}
out[i * s->_out_ch_count] = aF(v);
}
@ -498,20 +477,16 @@ static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
return 0;
}
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)
{
}
template <typename T>
void copy_and_trunc(size_t frames,
const T * input_buffer,
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;
@ -571,28 +543,22 @@ 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),
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),
copy_and_trunc(frames, static_cast<const int16_t *>(input_buffer),
reinterpret_cast<int16_t *>(output_buffer));
}
return 0;
}
switch (_context._format)
{
switch (_context._format) {
case CUBEB_SAMPLE_FLOAT32NE: {
auto f = [](float x) { return x; };
return rematrix(&_context,
static_cast<float*>(output_buffer),
return rematrix(&_context, static_cast<float *>(output_buffer),
static_cast<const float *>(input_buffer),
_context._matrix_flt,
f,
frames);
_context._matrix_flt, f, frames);
}
case CUBEB_SAMPLE_S16NE:
if (_context._clipping) {
@ -604,20 +570,14 @@ struct cubeb_mixer
}
return y;
};
return rematrix(&_context,
static_cast<int16_t*>(output_buffer),
return rematrix(&_context, static_cast<int16_t *>(output_buffer),
static_cast<const int16_t *>(input_buffer),
_context._matrix32,
f,
frames);
_context._matrix32, f, frames);
} else {
auto f = [](int x) { return (x + 16384) >> 15; };
return rematrix(&_context,
static_cast<int16_t*>(output_buffer),
return rematrix(&_context, static_cast<int16_t *>(output_buffer),
static_cast<const int16_t *>(input_buffer),
_context._matrix32,
f,
frames);
_context._matrix32, f, frames);
}
break;
default:
@ -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_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,
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_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,
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)
}

View file

@ -5,29 +5,29 @@
* accompanying file LICENSE for details.
*/
#undef NDEBUG
#include <SLES/OpenSLES.h>
#include <assert.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <SLES/OpenSLES.h>
#include <math.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#if defined(__ANDROID__)
#include <dlfcn.h>
#include <sys/system_properties.h>
#include "android/sles_definitions.h"
#include <SLES/OpenSLES_Android.h>
#include <android/log.h>
#include <android/api-level.h>
#include <android/log.h>
#include <dlfcn.h>
#include <sys/system_properties.h>
#endif
#include "cubeb/cubeb.h"
#include "android/cubeb-output-latency.h"
#include "cubeb-internal.h"
#include "cubeb_resampler.h"
#include "cubeb-sles.h"
#include "cubeb_array_queue.h"
#include "android/cubeb-output-latency.h"
#include "cubeb/cubeb.h"
#include "cubeb_android.h"
#include "cubeb_array_queue.h"
#include "cubeb_resampler.h"
#if defined(__ANDROID__)
#ifdef LOG
@ -35,22 +35,30 @@
#endif
//#define LOGGING_ENABLED
#ifdef LOGGING_ENABLED
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL" , ## args)
#define LOG(args...) \
__android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL", ##args)
#else
#define LOG(...)
#endif
//#define TIMESTAMP_ENABLED
#ifdef TIMESTAMP_ENABLED
#define FILENAME (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define LOG_TS(args...) __android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)" , ## args)
#define TIMESTAMP(msg) do { \
#define FILENAME \
(strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define LOG_TS(args...) \
__android_log_print(ANDROID_LOG_INFO, "Cubeb_OpenSL ES: Timestamp(usec)", \
##args)
#define TIMESTAMP(msg) \
do { \
struct timeval timestamp; \
int ts_ret = gettimeofday(&timestamp, NULL); \
if (ts_ret == 0) { \
LOG_TS("%lld: %s (%s %s:%d)", timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, __FUNCTION__, FILENAME, __LINE__);\
LOG_TS("%lld: %s (%s %s:%d)", \
timestamp.tv_sec * 1000000LL + timestamp.tv_usec, msg, \
__FUNCTION__, FILENAME, __LINE__); \
} else { \
LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, __LINE__);\
LOG_TS("Error: %s (%s %s:%d) - %s", msg, __FUNCTION__, FILENAME, \
__LINE__); \
} \
} while (0)
#else
@ -169,15 +177,18 @@ struct cubeb_stream {
};
/* Forward declaration. */
static int opensl_stop_player(cubeb_stream * stm);
static int opensl_stop_recorder(cubeb_stream * stm);
static int
opensl_stop_player(cubeb_stream * stm);
static int
opensl_stop_recorder(cubeb_stream * stm);
static int
opensl_get_draining(cubeb_stream * stm)
{
#ifdef DEBUG
int r = pthread_mutex_trylock(&stm->mutex);
assert((r == EDEADLK || r == EBUSY) && "get_draining: mutex should be locked but it's not.");
assert((r == EDEADLK || r == EBUSY) &&
"get_draining: mutex should be locked but it's not.");
#endif
return stm->draining;
}
@ -188,7 +199,8 @@ opensl_set_draining(cubeb_stream * stm, int value)
#ifdef DEBUG
int r = pthread_mutex_trylock(&stm->mutex);
LOG("set draining try r = %d", r);
assert((r == EDEADLK || r == EBUSY) && "set_draining: mutex should be locked but it's not.");
assert((r == EDEADLK || r == EBUSY) &&
"set_draining: mutex should be locked but it's not.");
#endif
assert(value == 0 || value == 1);
stm->draining = value;
@ -222,7 +234,8 @@ opensl_get_shutdown(cubeb_stream * stm)
{
#ifdef DEBUG
int r = pthread_mutex_trylock(&stm->mutex);
assert((r == EDEADLK || r == EBUSY) && "get_shutdown: mutex should be locked but it's not.");
assert((r == EDEADLK || r == EBUSY) &&
"get_shutdown: mutex should be locked but it's not.");
#endif
return stm->shutdown;
}
@ -233,7 +246,8 @@ opensl_set_shutdown(cubeb_stream * stm, uint32_t value)
#ifdef DEBUG
int r = pthread_mutex_trylock(&stm->mutex);
LOG("set shutdown try r = %d", r);
assert((r == EDEADLK || r == EBUSY) && "set_shutdown: mutex should be locked but it's not.");
assert((r == EDEADLK || r == EBUSY) &&
"set_shutdown: mutex should be locked but it's not.");
#endif
assert(value == 0 || value == 1);
stm->shutdown = value;
@ -304,9 +318,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
if (!draining && !shutdown) {
written = cubeb_resampler_fill(stm->resampler,
NULL, NULL,
buf, stm->queuebuf_len / stm->framesize);
written = cubeb_resampler_fill(stm->resampler, NULL, NULL, buf,
stm->queuebuf_len / stm->framesize);
LOG("bufferqueue_callback: resampler fill returned %ld frames", written);
if (written < 0 || written * stm->framesize > stm->queuebuf_len) {
r = pthread_mutex_lock(&stm->mutex);
@ -323,7 +336,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
// Keep sending silent data even in draining mode to prevent the audio
// back-end from being stopped automatically by OpenSL/ES.
assert(stm->queuebuf_len >= written * stm->framesize);
memset(buf + written * stm->framesize, 0, stm->queuebuf_len - written * stm->framesize);
memset(buf + written * stm->framesize, 0,
stm->queuebuf_len - written * stm->framesize);
res = (*stm->bufq)->Enqueue(stm->bufq, buf, stm->queuebuf_len);
assert(res == SL_RESULT_SUCCESS);
stm->queuebuf_idx = (stm->queuebuf_idx + 1) % stm->queuebuf_capacity;
@ -338,7 +352,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
LOG("bufferqueue_callback draining");
r = pthread_mutex_lock(&stm->mutex);
assert(r == 0);
int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
int64_t written_duration =
INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
@ -350,7 +365,8 @@ bufferqueue_callback(SLBufferQueueItf caller, void * user_ptr)
} else {
// Use SL_PLAYEVENT_HEADATMARKER event from slPlayCallback of SLPlayItf
// to make sure all the data has been processed.
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
(*stm->play)
->SetMarkerPosition(stm->play, (SLmillisecond)written_duration);
}
return;
}
@ -368,13 +384,15 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer)
// This is the first enqueue
current_index = 0;
} else {
// The current index hold the last filled buffer get it before advance index.
// The current index hold the last filled buffer get it before advance
// index.
last_buffer = stm->input_buffer_array[current_index];
// Advance to get next available buffer
current_index = (current_index + 1) % stm->input_array_capacity;
}
// enqueue next empty buffer to be filled by the recorder
SLresult res = (*stm->recorderBufferQueueItf)->Enqueue(stm->recorderBufferQueueItf,
SLresult res = (*stm->recorderBufferQueueItf)
->Enqueue(stm->recorderBufferQueueItf,
stm->input_buffer_array[current_index],
stm->input_buffer_length);
if (res != SL_RESULT_SUCCESS) {
@ -390,7 +408,8 @@ opensl_enqueue_recorder(cubeb_stream * stm, void ** last_filled_buffer)
}
// input data callback
void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
void
recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
{
assert(context);
cubeb_stream * stm = context;
@ -420,11 +439,8 @@ void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
assert(input_buffer);
// Fill resampler with last input
long input_frame_count = stm->input_buffer_length / stm->input_frame_size;
long got = cubeb_resampler_fill(stm->resampler,
input_buffer,
&input_frame_count,
NULL,
0);
long got = cubeb_resampler_fill(stm->resampler, input_buffer,
&input_frame_count, NULL, 0);
// Error case
if (got < 0 || got > input_frame_count) {
r = pthread_mutex_lock(&stm->mutex);
@ -446,13 +462,16 @@ void recorder_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
int64_t duration = INT64_C(1000) * stm->input_total_frames / stm->input_device_rate;
(*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration);
int64_t duration =
INT64_C(1000) * stm->input_total_frames / stm->input_device_rate;
(*stm->recorderItf)
->SetMarkerPosition(stm->recorderItf, (SLmillisecond)duration);
return;
}
}
void recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
void
recorder_fullduplex_callback(SLAndroidSimpleBufferQueueItf bq, void * context)
{
assert(context);
cubeb_stream * stm = context;
@ -525,9 +544,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
memset(output_buffer, 0, stm->queuebuf_len);
// Enqueue data in player buffer queue
res = (*stm->bufq)->Enqueue(stm->bufq,
output_buffer,
stm->queuebuf_len);
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
assert(res == SL_RESULT_SUCCESS);
return;
}
@ -543,14 +560,12 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
long written = 0;
// Trigger user callback through resampler
written = cubeb_resampler_fill(stm->resampler,
input_buffer,
&input_frame_count,
output_buffer,
frames_needed);
written =
cubeb_resampler_fill(stm->resampler, input_buffer, &input_frame_count,
output_buffer, frames_needed);
LOG("Fill: written %ld, frames_needed %ld, input array size %zu",
written, frames_needed, array_queue_get_size(stm->input_queue));
LOG("Fill: written %ld, frames_needed %ld, input array size %zu", written,
frames_needed, array_queue_get_size(stm->input_queue));
if (written < 0 || written > frames_needed) {
// Error case
@ -565,9 +580,7 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
memset(output_buffer, 0, stm->queuebuf_len);
// Enqueue data in player buffer queue
res = (*stm->bufq)->Enqueue(stm->bufq,
output_buffer,
stm->queuebuf_len);
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
assert(res == SL_RESULT_SUCCESS);
return;
}
@ -582,7 +595,8 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
if (written < frames_needed) {
r = pthread_mutex_lock(&stm->mutex);
assert(r == 0);
int64_t written_duration = INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
int64_t written_duration =
INT64_C(1000) * stm->written * stm->framesize / stm->bytespersec;
opensl_set_draining(stm, 1);
r = pthread_mutex_unlock(&stm->mutex);
assert(r == 0);
@ -598,14 +612,13 @@ player_fullduplex_callback(SLBufferQueueItf caller, void * user_ptr)
stm->queuebuf_len - written * stm->framesize);
// Enqueue data in player buffer queue
res = (*stm->bufq)->Enqueue(stm->bufq,
output_buffer,
stm->queuebuf_len);
res = (*stm->bufq)->Enqueue(stm->bufq, output_buffer, stm->queuebuf_len);
assert(res == SL_RESULT_SUCCESS);
TIMESTAMP("EXIT");
}
static void opensl_destroy(cubeb * ctx);
static void
opensl_destroy(cubeb * ctx);
#if defined(__ANDROID__)
#if (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
@ -619,8 +632,8 @@ wrap_system_property_get(const char* name, char* value)
LOG("Failed to open libc.so");
return -1;
}
system_property_get* func = (system_property_get*)
dlsym(libc, "__system_property_get");
system_property_get * func =
(system_property_get *)dlsym(libc, "__system_property_get");
int ret = -1;
if (func) {
ret = func(name, value);
@ -660,7 +673,8 @@ opensl_init(cubeb ** context, char const * context_name)
#if defined(__ANDROID__)
int android_version = get_android_version();
if (android_version > 0 && android_version <= ANDROID_VERSION_GINGERBREAD_MR1) {
if (android_version > 0 &&
android_version <= ANDROID_VERSION_GINGERBREAD_MR1) {
// Don't even attempt to run on Gingerbread and lower
return CUBEB_ERROR;
}
@ -679,35 +693,34 @@ opensl_init(cubeb ** context, char const * context_name)
return CUBEB_ERROR;
}
typedef SLresult (*slCreateEngine_t)(SLObjectItf *,
SLuint32,
const SLEngineOption *,
SLuint32,
const SLInterfaceID *,
const SLboolean *);
typedef SLresult (*slCreateEngine_t)(
SLObjectItf *, SLuint32, const SLEngineOption *, SLuint32,
const SLInterfaceID *, const SLboolean *);
slCreateEngine_t f_slCreateEngine =
(slCreateEngine_t)dlsym(ctx->lib, "slCreateEngine");
SLInterfaceID SL_IID_ENGINE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
SLInterfaceID SL_IID_OUTPUTMIX = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
SLInterfaceID SL_IID_ENGINE =
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ENGINE");
SLInterfaceID SL_IID_OUTPUTMIX =
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_OUTPUTMIX");
ctx->SL_IID_VOLUME = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_VOLUME");
ctx->SL_IID_BUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
ctx->SL_IID_BUFFERQUEUE =
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_BUFFERQUEUE");
#if defined(__ANDROID__)
ctx->SL_IID_ANDROIDCONFIGURATION = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
ctx->SL_IID_ANDROIDCONFIGURATION =
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDCONFIGURATION");
ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE =
*(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
#endif
ctx->SL_IID_PLAY = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_PLAY");
ctx->SL_IID_RECORD = *(SLInterfaceID *)dlsym(ctx->lib, "SL_IID_RECORD");
if (!f_slCreateEngine ||
!SL_IID_ENGINE ||
!SL_IID_OUTPUTMIX ||
if (!f_slCreateEngine || !SL_IID_ENGINE || !SL_IID_OUTPUTMIX ||
!ctx->SL_IID_BUFFERQUEUE ||
#if defined(__ANDROID__)
!ctx->SL_IID_ANDROIDCONFIGURATION ||
!ctx->SL_IID_ANDROIDSIMPLEBUFFERQUEUE ||
#endif
!ctx->SL_IID_PLAY ||
!ctx->SL_IID_RECORD) {
!ctx->SL_IID_PLAY || !ctx->SL_IID_RECORD) {
opensl_destroy(ctx);
return CUBEB_ERROR;
}
@ -736,7 +749,8 @@ opensl_init(cubeb ** context, char const * context_name)
const SLInterfaceID idsom[] = {SL_IID_OUTPUTMIX};
const SLboolean reqom[] = {SL_BOOLEAN_TRUE};
res = (*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
res =
(*ctx->eng)->CreateOutputMix(ctx->eng, &ctx->outmixObj, 1, idsom, reqom);
if (res != SL_RESULT_SUCCESS) {
opensl_destroy(ctx);
return CUBEB_ERROR;
@ -748,9 +762,11 @@ opensl_init(cubeb ** context, char const * context_name)
return CUBEB_ERROR;
}
ctx->p_output_latency_function = cubeb_output_latency_load_method(android_version);
ctx->p_output_latency_function =
cubeb_output_latency_load_method(android_version);
if (!cubeb_output_latency_method_is_loaded(ctx->p_output_latency_function)) {
LOG("Warning: output latency is not available, cubeb_stream_get_position() is not supported");
LOG("Warning: output latency is not available, cubeb_stream_get_position() "
"is not supported");
}
*context = ctx;
@ -770,7 +786,8 @@ opensl_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
assert(ctx && max_channels);
/* The android mixer handles up to two channels, see
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67
*/
*max_channels = 2;
return CUBEB_OK;
@ -789,11 +806,13 @@ opensl_destroy(cubeb * ctx)
free(ctx);
}
static void opensl_stream_destroy(cubeb_stream * stm);
static void
opensl_stream_destroy(cubeb_stream * stm);
#if defined(__ANDROID__) && (__ANDROID_API__ >= ANDROID_VERSION_LOLLIPOP)
static int
opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params * params)
opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format,
cubeb_stream_params * params)
{
assert(format);
assert(params);
@ -802,9 +821,9 @@ opensl_set_format_ext(SLAndroidDataFormat_PCM_EX * format, cubeb_stream_params *
format->numChannels = params->channels;
// sampleRate is in milliHertz
format->sampleRate = params->rate * 1000;
format->channelMask = params->channels == 1 ?
SL_SPEAKER_FRONT_CENTER :
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
format->channelMask = params->channels == 1
? SL_SPEAKER_FRONT_CENTER
: SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
switch (params->format) {
case CUBEB_SAMPLE_S16LE:
@ -850,9 +869,9 @@ opensl_set_format(SLDataFormat_PCM * format, cubeb_stream_params * params)
format->samplesPerSec = params->rate * 1000;
format->bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
format->containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
format->channelMask = params->channels == 1 ?
SL_SPEAKER_FRONT_CENTER :
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
format->channelMask = params->channels == 1
? SL_SPEAKER_FRONT_CENTER
: SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
switch (params->format) {
case CUBEB_SAMPLE_S16LE:
@ -900,19 +919,19 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
lDataSource.pLocator = &lDataLocatorIn;
lDataSource.pFormat = NULL;
const SLInterfaceID lSoundRecorderIIDs[] = { stm->context->SL_IID_RECORD,
const SLInterfaceID lSoundRecorderIIDs[] = {
stm->context->SL_IID_RECORD,
stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
stm->context->SL_IID_ANDROIDCONFIGURATION};
const SLboolean lSoundRecorderReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
const SLboolean lSoundRecorderReqs[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
SL_BOOLEAN_TRUE};
// create the audio recorder abstract object
SLresult res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
&stm->recorderObj,
&lDataSource,
&lDataSink,
NELEMS(lSoundRecorderIIDs),
lSoundRecorderIIDs,
lSoundRecorderReqs);
SLresult res = (*stm->context->eng)
->CreateAudioRecorder(
stm->context->eng, &stm->recorderObj, &lDataSource,
&lDataSink, NELEMS(lSoundRecorderIIDs),
lSoundRecorderIIDs, lSoundRecorderReqs);
// Sample rate not supported. Try again with default sample rate!
if (res == SL_RESULT_CONTENT_UNSUPPORTED) {
if (stm->output_enabled && stm->output_configured_rate != 0) {
@ -925,13 +944,11 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
stm->input_device_rate = DEFAULT_SAMPLE_RATE;
}
lDataFormat.samplesPerSec = stm->input_device_rate * 1000;
res = (*stm->context->eng)->CreateAudioRecorder(stm->context->eng,
&stm->recorderObj,
&lDataSource,
&lDataSink,
res = (*stm->context->eng)
->CreateAudioRecorder(stm->context->eng, &stm->recorderObj,
&lDataSource, &lDataSink,
NELEMS(lSoundRecorderIIDs),
lSoundRecorderIIDs,
lSoundRecorderReqs);
lSoundRecorderIIDs, lSoundRecorderReqs);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to create recorder. Error code: %lu", res);
@ -939,7 +956,6 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
}
}
if (get_android_version() > ANDROID_VERSION_JELLY_BEAN) {
SLAndroidConfigurationItf recorderConfig;
res = (*stm->recorderObj)
@ -948,7 +964,8 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
&recorderConfig);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get the android configuration interface for recorder. Error "
LOG("Failed to get the android configuration interface for recorder. "
"Error "
"code: %lu",
res);
return CUBEB_ERROR;
@ -956,16 +973,19 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
// Voice recognition is the lowest latency, according to the docs. Camcorder
// uses a microphone that is in the same direction as the camera.
SLint32 streamType = stm->voice_input ? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
SLint32 streamType = stm->voice_input
? SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION
: SL_ANDROID_RECORDING_PRESET_CAMCORDER;
res = (*recorderConfig)
res =
(*recorderConfig)
->SetConfiguration(recorderConfig, SL_ANDROID_KEY_RECORDING_PRESET,
&streamType, sizeof(SLint32));
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set the android configuration to VOICE for the recorder. "
"Error code: %lu", res);
"Error code: %lu",
res);
return CUBEB_ERROR;
}
}
@ -976,15 +996,16 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
return CUBEB_ERROR;
}
// get the record interface
res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
stm->context->SL_IID_RECORD,
res = (*stm->recorderObj)
->GetInterface(stm->recorderObj, stm->context->SL_IID_RECORD,
&stm->recorderItf);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get recorder interface. Error code: %lu", res);
return CUBEB_ERROR;
}
res = (*stm->recorderItf)->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm);
res = (*stm->recorderItf)
->RegisterCallback(stm->recorderItf, recorder_marker_callback, stm);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to register recorder marker callback. Error code: %lu", res);
return CUBEB_ERROR;
@ -992,17 +1013,22 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
(*stm->recorderItf)->SetMarkerPosition(stm->recorderItf, (SLmillisecond)0);
res = (*stm->recorderItf)->SetCallbackEventsMask(stm->recorderItf, (SLuint32)SL_RECORDEVENT_HEADATMARKER);
res = (*stm->recorderItf)
->SetCallbackEventsMask(stm->recorderItf,
(SLuint32)SL_RECORDEVENT_HEADATMARKER);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set headatmarker event mask. Error code: %lu", res);
return CUBEB_ERROR;
}
// get the simple android buffer queue interface
res = (*stm->recorderObj)->GetInterface(stm->recorderObj,
res = (*stm->recorderObj)
->GetInterface(stm->recorderObj,
stm->context->SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&stm->recorderBufferQueueItf);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get recorder (android) buffer queue interface. Error code: %lu", res);
LOG("Failed to get recorder (android) buffer queue interface. Error code: "
"%lu",
res);
return CUBEB_ERROR;
}
@ -1012,11 +1038,11 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
// Register full duplex callback instead.
rec_callback = recorder_fullduplex_callback;
}
res = (*stm->recorderBufferQueueItf)->RegisterCallback(stm->recorderBufferQueueItf,
rec_callback,
stm);
res = (*stm->recorderBufferQueueItf)
->RegisterCallback(stm->recorderBufferQueueItf, rec_callback, stm);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to register recorder buffer queue callback. Error code: %lu", res);
LOG("Failed to register recorder buffer queue callback. Error code: %lu",
res);
return CUBEB_ERROR;
}
@ -1028,10 +1054,12 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
stm->input_array_capacity = NBUFS;
if (stm->output_enabled) {
// Full duplex, update capacity to hold 1 sec of data
stm->input_array_capacity = 1 * stm->input_device_rate / stm->input_buffer_length;
stm->input_array_capacity =
1 * stm->input_device_rate / stm->input_buffer_length;
}
// Allocate input array
stm->input_buffer_array = (void**)calloc(1, sizeof(void*)*stm->input_array_capacity);
stm->input_buffer_array =
(void **)calloc(1, sizeof(void *) * stm->input_array_capacity);
// Buffering has not started yet.
stm->input_buffer_index = -1;
// Prepare input buffers
@ -1059,14 +1087,17 @@ opensl_configure_capture(cubeb_stream * stm, cubeb_stream_params * params)
}
static int
opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params)
{
assert(stm);
assert(params);
stm->user_output_rate = params->rate;
if(params->format == CUBEB_SAMPLE_S16NE || params->format == CUBEB_SAMPLE_S16BE) {
if (params->format == CUBEB_SAMPLE_S16NE ||
params->format == CUBEB_SAMPLE_S16BE) {
stm->framesize = params->channels * sizeof(int16_t);
} else if(params->format == CUBEB_SAMPLE_FLOAT32NE || params->format == CUBEB_SAMPLE_FLOAT32BE) {
} else if (params->format == CUBEB_SAMPLE_FLOAT32NE ||
params->format == CUBEB_SAMPLE_FLOAT32BE) {
stm->framesize = params->channels * sizeof(float);
}
stm->lastPosition = -1;
@ -1124,13 +1155,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
uint32_t preferred_sampling_rate = stm->user_output_rate;
SLresult res = SL_RESULT_CONTENT_UNSUPPORTED;
if (preferred_sampling_rate) {
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
&stm->playerObj,
&source,
&sink,
NELEMS(ids),
ids,
req);
res = (*stm->context->eng)
->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source,
&sink, NELEMS(ids), ids, req);
}
// Sample rate not supported? Try again with primary sample rate!
@ -1138,13 +1165,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
preferred_sampling_rate != DEFAULT_SAMPLE_RATE) {
preferred_sampling_rate = DEFAULT_SAMPLE_RATE;
*format_sample_rate = preferred_sampling_rate * 1000;
res = (*stm->context->eng)->CreateAudioPlayer(stm->context->eng,
&stm->playerObj,
&source,
&sink,
NELEMS(ids),
ids,
req);
res = (*stm->context->eng)
->CreateAudioPlayer(stm->context->eng, &stm->playerObj, &source,
&sink, NELEMS(ids), ids, req);
}
if (res != SL_RESULT_SUCCESS) {
@ -1160,7 +1183,8 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
stm->queuebuf_capacity = NBUFS;
if (stm->output_enabled) {
// Full duplex, update capacity to hold 1 sec of data
stm->queuebuf_capacity = 1 * stm->output_configured_rate / stm->queuebuf_len;
stm->queuebuf_capacity =
1 * stm->output_configured_rate / stm->queuebuf_len;
}
// Allocate input array
stm->queuebuf = (void **)calloc(1, sizeof(void *) * stm->queuebuf_capacity);
@ -1177,7 +1201,8 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
stm->context->SL_IID_ANDROIDCONFIGURATION,
&playerConfig);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get Android configuration interface. Error code: %lu", res);
LOG("Failed to get Android configuration interface. Error code: %lu",
res);
return CUBEB_ERROR;
}
@ -1185,10 +1210,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
if (stm->voice_output) {
streamType = SL_ANDROID_STREAM_VOICE;
}
res = (*playerConfig)->SetConfiguration(playerConfig,
SL_ANDROID_KEY_STREAM_TYPE,
&streamType,
sizeof(streamType));
res = (*playerConfig)
->SetConfiguration(playerConfig, SL_ANDROID_KEY_STREAM_TYPE,
&streamType, sizeof(streamType));
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set Android configuration to %d Error code: %lu",
streamType, res);
@ -1199,13 +1223,14 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
performanceMode = SL_ANDROID_PERFORMANCE_POWER_SAVING;
}
res = (*playerConfig)->SetConfiguration(playerConfig,
SL_ANDROID_KEY_PERFORMANCE_MODE,
&performanceMode,
sizeof(performanceMode));
res = (*playerConfig)
->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE,
&performanceMode, sizeof(performanceMode));
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set Android performance mode to %d Error code: %lu. This is"
" not fatal", performanceMode, res);
LOG("Failed to set Android performance mode to %d Error code: %lu. This "
"is"
" not fatal",
performanceMode, res);
}
}
@ -1229,10 +1254,10 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
SLuint32 paramSize = sizeof(SLuint32);
// The reported latency is in milliseconds.
if (playerConfig) {
res = (*playerConfig)->GetConfiguration(playerConfig,
res = (*playerConfig)
->GetConfiguration(playerConfig,
(const SLchar *)"androidGetAudioLatency",
&paramSize,
&audioLatency);
&paramSize, &audioLatency);
if (res == SL_RESULT_SUCCESS) {
LOG("Got playback latency using android configuration extension");
stm->output_latency_ms = audioLatency;
@ -1241,11 +1266,12 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
// `playerConfig` is available, but the above failed, or `playerConfig` is not
// available. In both cases, we need to acquire the output latency by an other
// mean.
if ((playerConfig && res != SL_RESULT_SUCCESS) ||
!playerConfig) {
if (cubeb_output_latency_method_is_loaded(stm->context->p_output_latency_function)) {
if ((playerConfig && res != SL_RESULT_SUCCESS) || !playerConfig) {
if (cubeb_output_latency_method_is_loaded(
stm->context->p_output_latency_function)) {
LOG("Got playback latency using JNI");
stm->output_latency_ms = cubeb_get_output_latency(stm->context->p_output_latency_function);
stm->output_latency_ms =
cubeb_get_output_latency(stm->context->p_output_latency_function);
} else {
LOG("No alternate latency querying method loaded, A/V sync will be off.");
stm->output_latency_ms = 0;
@ -1254,24 +1280,24 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
LOG("Audio output latency: %dms", stm->output_latency_ms);
res = (*stm->playerObj)->GetInterface(stm->playerObj,
stm->context->SL_IID_PLAY,
&stm->play);
res =
(*stm->playerObj)
->GetInterface(stm->playerObj, stm->context->SL_IID_PLAY, &stm->play);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get play interface. Error code: %lu", res);
return CUBEB_ERROR;
}
res = (*stm->playerObj)->GetInterface(stm->playerObj,
stm->context->SL_IID_BUFFERQUEUE,
res = (*stm->playerObj)
->GetInterface(stm->playerObj, stm->context->SL_IID_BUFFERQUEUE,
&stm->bufq);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get bufferqueue interface. Error code: %lu", res);
return CUBEB_ERROR;
}
res = (*stm->playerObj)->GetInterface(stm->playerObj,
stm->context->SL_IID_VOLUME,
res = (*stm->playerObj)
->GetInterface(stm->playerObj, stm->context->SL_IID_VOLUME,
&stm->volume);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to get volume interface. Error code: %lu", res);
@ -1287,7 +1313,9 @@ opensl_configure_playback(cubeb_stream * stm, cubeb_stream_params * params) {
// Work around wilhelm/AudioTrack badness, bug 1221228
(*stm->play)->SetMarkerPosition(stm->play, (SLmillisecond)0);
res = (*stm->play)->SetCallbackEventsMask(stm->play, (SLuint32)SL_PLAYEVENT_HEADATMARKER);
res = (*stm->play)
->SetCallbackEventsMask(stm->play,
(SLuint32)SL_PLAYEVENT_HEADATMARKER);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to set headatmarker event mask. Error code: %lu", res);
return CUBEB_ERROR;
@ -1325,37 +1353,37 @@ opensl_validate_stream_param(cubeb_stream_params * stream_params)
(stream_params->channels < 1 || stream_params->channels > 32))) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if ((stream_params &&
(stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
if ((stream_params && (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK))) {
LOG("Loopback is not supported");
return CUBEB_ERROR_NOT_SUPPORTED;
}
return CUBEB_OK;
}
int has_pref_set(cubeb_stream_params* input_params,
cubeb_stream_params* output_params,
cubeb_stream_prefs pref)
int
has_pref_set(cubeb_stream_params * input_params,
cubeb_stream_params * output_params, cubeb_stream_prefs pref)
{
return (input_params && input_params->prefs & pref) ||
(output_params && output_params->prefs & pref);
}
static int
opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
opensl_stream_init(cubeb * ctx, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback, cubeb_state_callback state_callback,
void * user_ptr)
cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
cubeb_stream * stm;
assert(ctx);
if (input_device || output_device) {
LOG("Device selection is not supported in Android. The default will be used");
LOG("Device selection is not supported in Android. The default will be "
"used");
}
*stream = NULL;
@ -1378,14 +1406,18 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->buffer_size_frames = latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
stm->buffer_size_frames =
latency_frames ? latency_frames : DEFAULT_NUM_OF_FRAMES;
stm->input_enabled = (input_stream_params) ? 1 : 0;
stm->output_enabled = (output_stream_params) ? 1 : 0;
stm->shutdown = 1;
stm->voice_input = has_pref_set(input_stream_params, NULL, CUBEB_STREAM_PREF_VOICE);
stm->voice_output = has_pref_set(NULL, output_stream_params, CUBEB_STREAM_PREF_VOICE);
stm->voice_input =
has_pref_set(input_stream_params, NULL, CUBEB_STREAM_PREF_VOICE);
stm->voice_output =
has_pref_set(NULL, output_stream_params, CUBEB_STREAM_PREF_VOICE);
LOG("cubeb stream prefs: voice_input: %s voice_output: %s", stm->voice_input ? "true" : "false",
LOG("cubeb stream prefs: voice_input: %s voice_output: %s",
stm->voice_input ? "true" : "false",
stm->voice_output ? "true" : "false");
#ifdef DEBUG
@ -1399,7 +1431,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
assert(r == 0);
if (output_stream_params) {
LOG("Playback params: Rate %d, channels %d, format %d, latency in frames %d.",
LOG("Playback params: Rate %d, channels %d, format %d, latency in frames "
"%d.",
output_stream_params->rate, output_stream_params->channels,
output_stream_params->format, stm->buffer_size_frames);
r = opensl_configure_playback(stm, output_stream_params);
@ -1410,7 +1443,8 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
}
if (input_stream_params) {
LOG("Capture params: Rate %d, channels %d, format %d, latency in frames %d.",
LOG("Capture params: Rate %d, channels %d, format %d, latency in frames "
"%d.",
input_stream_params->rate, input_stream_params->channels,
input_stream_params->format, stm->buffer_size_frames);
r = opensl_configure_capture(stm, input_stream_params);
@ -1442,13 +1476,11 @@ opensl_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name
output_params.rate = stm->output_configured_rate;
}
stm->resampler = cubeb_resampler_create(stm,
input_stream_params ? &input_params : NULL,
output_stream_params ? &output_params : NULL,
target_sample_rate,
data_callback,
user_ptr,
CUBEB_RESAMPLER_QUALITY_DEFAULT);
stm->resampler = cubeb_resampler_create(
stm, input_stream_params ? &input_params : NULL,
output_stream_params ? &output_params : NULL, target_sample_rate,
data_callback, user_ptr, CUBEB_RESAMPLER_QUALITY_DEFAULT,
CUBEB_RESAMPLER_RECLOCK_NONE);
if (!stm->resampler) {
LOG("Failed to create resampler");
opensl_stream_destroy(stm);
@ -1483,7 +1515,9 @@ opensl_start_recorder(cubeb_stream * stm)
SLuint32 recorderState;
(*stm->recorderObj)->GetState(stm->recorderObj, &recorderState);
if (recorderState == SL_OBJECT_STATE_REALIZED) {
SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING);
SLresult res =
(*stm->recorderItf)
->SetRecordState(stm->recorderItf, SL_RECORDSTATE_RECORDING);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to start recorder. Error code: %lu", res);
return CUBEB_ERROR;
@ -1544,7 +1578,8 @@ opensl_stop_recorder(cubeb_stream * stm)
assert(stm->recorderObj);
assert(stm->shutdown || stm->draining);
SLresult res = (*stm->recorderItf)->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED);
SLresult res = (*stm->recorderItf)
->SetRecordState(stm->recorderItf, SL_RECORDSTATE_PAUSED);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to stop recorder. Error code: %lu", res);
return CUBEB_ERROR;
@ -1590,7 +1625,8 @@ opensl_destroy_recorder(cubeb_stream * stm)
assert(stm->recorderObj);
if (stm->recorderBufferQueueItf) {
SLresult res = (*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf);
SLresult res =
(*stm->recorderBufferQueueItf)->Clear(stm->recorderBufferQueueItf);
if (res != SL_RESULT_SUCCESS) {
LOG("Failed to clear recorder buffer queue. Error code: %lu", res);
return CUBEB_ERROR;
@ -1658,7 +1694,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
clock_gettime(CLOCK_MONOTONIC, &t);
if (stm->lastPosition == msec) {
compensation_msec =
(t.tv_sec*1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) / 1000000;
(t.tv_sec * 1000000000LL + t.tv_nsec - stm->lastPositionTimeStamp) /
1000000;
} else {
stm->lastPositionTimeStamp = t.tv_sec * 1000000000LL + t.tv_nsec;
stm->lastPosition = msec;
@ -1668,7 +1705,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
uint32_t output_latency = stm->output_latency_ms;
pthread_mutex_lock(&stm->mutex);
int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate / stm->output_configured_rate;
int64_t maximum_position = stm->written * (int64_t)stm->user_output_rate /
stm->output_configured_rate;
pthread_mutex_unlock(&stm->mutex);
assert(maximum_position >= 0);
@ -1683,8 +1721,8 @@ opensl_stream_get_position(cubeb_stream * stm, uint64_t * position)
samplerate * (msec - output_latency + compensation_msec) / 1000;
stm->lastCompensativePosition = msec + compensation_msec;
}
*position = unadjusted_position < maximum_position ?
unadjusted_position : maximum_position;
*position = unadjusted_position < maximum_position ? unadjusted_position
: maximum_position;
} else {
*position = 0;
}
@ -1747,7 +1785,6 @@ static struct cubeb_ops const opensl_ops = {
.stream_destroy = opensl_stream_destroy,
.stream_start = opensl_stream_start,
.stream_stop = opensl_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = opensl_stream_get_position,
.stream_get_latency = opensl_stream_get_latency,
.stream_get_input_latency = NULL,
@ -1756,5 +1793,4 @@ static struct cubeb_ops const opensl_ops = {
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.register_device_collection_changed = NULL};

View file

@ -10,25 +10,25 @@
* accompanying file LICENSE for details.
*/
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_mixer.h"
#include "cubeb_strings.h"
#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 <limits.h>
#include <poll.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include "cubeb/cubeb.h"
#include "cubeb_mixer.h"
#include "cubeb_strings.h"
#include "cubeb-internal.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
@ -96,6 +96,8 @@ struct oss_stream {
oss_devnode_t name;
int fd;
void * buf;
unsigned int bufframes;
unsigned int maxframes;
struct stream_info {
int channels;
@ -126,9 +128,6 @@ struct cubeb_stream {
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 *
@ -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;
@ -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;
@ -286,7 +286,7 @@ oss_sndstat_line_parse(char *line, int is_ud, struct sndstat_info *sinfo)
if (n - matchptr >= (ssize_t)(sizeof(res.devname) - strlen("/dev/")))
goto fail;
snprintf(res.devname, sizeof(res.devname), "/dev/");
strlcpy(res.devname, "/dev/", sizeof(res.devname));
strncat(res.devname, matchptr, n - matchptr);
}
matchptr = n + 1;
@ -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;
@ -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;
}
@ -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)
{
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,25 +899,21 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
long got = 0;
if (nfr > 0) {
got = s->data_cb(s, s->user_ptr, s->record.buf, s->play.buf, nfr);
if (got == CUBEB_ERROR) {
if (record_on) {
if (oss_get_rec_frames(s, nfr) == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (play_on) {
float vol;
pthread_mutex_lock(&s->mtx);
vol = s->volume;
pthread_mutex_unlock(&s->mtx);
if (s->play.floating) {
oss_float_to_linear32(s->play.buf, s->play.info.channels * got, vol);
} else {
oss_linear16_set_vol((int16_t *)s->play.buf,
s->play.info.channels * got, vol);
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 (got < nfr) {
if (s->play.fd != -1) {
drain = 1;
@ -854,16 +923,25 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
* returned from data_cb() is smaller than number
* of frames required to read. Stop here.
*/
state = CUBEB_STATE_STOPPED;
goto breakdown;
}
}
nfr = 0;
}
if (got > 0) {
if (play_on && oss_put_play_frames(s, got) < 0) {
if (got > 0 && play_on) {
float vol;
pthread_mutex_lock(&s->mtx);
vol = s->volume;
pthread_mutex_unlock(&s->mtx);
if (s->play.floating) {
oss_float_to_linear32(s->play.buf, s->play.info.channels * got, vol);
} else {
oss_linear16_set_vol((int16_t *)s->play.buf,
s->play.info.channels * got, vol);
}
if (oss_put_play_frames(s, got) == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
@ -872,35 +950,12 @@ oss_audio_loop(cubeb_stream * s, cubeb_state *new_state)
state = CUBEB_STATE_DRAINED;
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) {
if (oss_wait_fds_for_space(s, &nfr) != 0) {
state = CUBEB_STATE_ERROR;
goto breakdown;
}
if (s->record.floating) {
oss_linear32_to_float(s->record.buf, s->record.info.channels * nfr);
}
}
}
return 1;
@ -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,20 +1026,15 @@ 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;
@ -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,49 +1070,57 @@ 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 ((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 ((ret = oss_copy_params(s->play.fd, s, output_stream_params,
&s->play.info)) != CUBEB_OK) {
LOG("Setting play params failed");
@ -1068,17 +1128,16 @@ oss_stream_init(cubeb * context,
}
s->play.floating = (output_stream_params->format == CUBEB_SAMPLE_FLOAT32NE);
s->play.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)
}

View file

@ -5,21 +5,21 @@
* accompanying file LICENSE for details.
*/
#undef NDEBUG
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_mixer.h"
#include "cubeb_strings.h"
#include <assert.h>
#include <dlfcn.h>
#include <pulse/pulseaudio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cubeb-internal.h"
#include "cubeb/cubeb.h"
#include "cubeb_mixer.h"
#include "cubeb_strings.h"
#ifdef DISABLE_LIBPULSE_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) cubeb_##x
#define WRAP(x) (*cubeb_##x)
#define LIBPULSE_API_VISIT(X) \
X(pa_channel_map_can_balance) \
X(pa_channel_map_init) \
@ -86,7 +86,7 @@
X(pa_mainloop_api_once) \
X(pa_get_library_version) \
X(pa_channel_map_init_auto) \
X(pa_stream_set_name) \
X(pa_stream_set_name)
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBPULSE_API_VISIT(MAKE_TYPEDEF);
@ -139,11 +139,7 @@ struct cubeb_stream {
static const float PULSE_NO_GAIN = -1.0;
enum cork_state {
UNCORK = 0,
CORK = 1 << 0,
NOTIFY = 1 << 1
};
enum cork_state { UNCORK = 0, CORK = 1 << 0, NOTIFY = 1 << 1 };
static int
intern_device_id(cubeb * ctx, char const ** id)
@ -164,14 +160,16 @@ intern_device_id(cubeb * ctx, char const ** id)
}
static void
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, void * u)
sink_info_callback(pa_context * context, const pa_sink_info * info, int eol,
void * u)
{
(void)context;
cubeb * ctx = u;
if (!eol) {
free(ctx->default_sink_info);
ctx->default_sink_info = malloc(sizeof(struct cubeb_default_sink_info));
memcpy(&ctx->default_sink_info->channel_map, &info->channel_map, sizeof(pa_channel_map));
memcpy(&ctx->default_sink_info->channel_map, &info->channel_map,
sizeof(pa_channel_map));
ctx->default_sink_info->sample_spec_rate = info->sample_spec.rate;
ctx->default_sink_info->flags = info->flags;
}
@ -179,10 +177,12 @@ sink_info_callback(pa_context * context, const pa_sink_info * info, int eol, voi
}
static void
server_info_callback(pa_context * context, const pa_server_info * info, void * u)
server_info_callback(pa_context * context, const pa_server_info * info,
void * u)
{
pa_operation * o;
o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name, sink_info_callback, u);
o = WRAP(pa_context_get_sink_info_by_name)(context, info->default_sink_name,
sink_info_callback, u);
if (o) {
WRAP(pa_operation_unref)(o);
}
@ -223,7 +223,8 @@ stream_state_change_callback(cubeb_stream * stm, cubeb_state s)
}
static void
stream_drain_callback(pa_mainloop_api * a, pa_time_event * e, struct timeval const * tv, void * u)
stream_drain_callback(pa_mainloop_api * a, pa_time_event * e,
struct timeval const * tv, void * u)
{
(void)a;
(void)tv;
@ -247,7 +248,8 @@ stream_state_callback(pa_stream * s, void * u)
}
static void
trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cubeb_stream * stm)
trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes,
cubeb_stream * stm)
{
void * buffer;
size_t size;
@ -264,16 +266,21 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
while (towrite) {
size = towrite;
r = WRAP(pa_stream_begin_write)(s, &buffer, &size);
// Note: this has failed running under rr on occassion - needs investigation.
// Note: this has failed running under rr on occassion - needs
// investigation.
assert(r == 0);
assert(size > 0);
assert(size % frame_size == 0);
LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd", size, read_offset);
got = stm->data_callback(stm, stm->user_ptr, (uint8_t const *)input_data + read_offset, buffer, size / frame_size);
LOGV("Trigger user callback with output buffer size=%zd, read_offset=%zd",
size, read_offset);
got = stm->data_callback(stm, stm->user_ptr,
(uint8_t const *)input_data + read_offset, buffer,
size / frame_size);
if (got < 0) {
WRAP(pa_stream_cancel_write)(s);
stm->shutdown = 1;
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
return;
}
// If more iterations move offset of read buffer
@ -299,7 +306,8 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
}
}
r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0, PA_SEEK_RELATIVE);
r = WRAP(pa_stream_write)(s, buffer, got * frame_size, NULL, 0,
PA_SEEK_RELATIVE);
assert(r == 0);
if ((size_t)got < size / frame_size) {
@ -313,7 +321,9 @@ trigger_user_callback(pa_stream * s, void const * input_data, size_t nbytes, cub
/* pa_stream_drain is useless, see PA bug# 866. this is a workaround. */
/* arbitrary safety margin: double the current latency. */
assert(!stm->drain_timer);
stm->drain_timer = WRAP(pa_context_rttime_new)(stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency, stream_drain_callback, stm);
stm->drain_timer = WRAP(pa_context_rttime_new)(
stm->context->context, WRAP(pa_rtclock_now)() + 2 * latency,
stream_drain_callback, stm);
stm->shutdown = 1;
return;
}
@ -341,8 +351,7 @@ stream_write_callback(pa_stream * s, size_t nbytes, void * u)
{
LOGV("Output callback to be written buffer size %zd", nbytes);
cubeb_stream * stm = u;
if (stm->shutdown ||
stm->state != CUBEB_STATE_STARTED) {
if (stm->shutdown || stm->state != CUBEB_STATE_STARTED) {
return;
}
@ -379,10 +388,14 @@ stream_read_callback(pa_stream * s, size_t nbytes, void * u)
trigger_user_callback(stm->output_stream, read_data, write_size, stm);
} else {
// input/capture only operation. Call callback directly
long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL, read_frames);
long got = stm->data_callback(stm, stm->user_ptr, read_data, NULL,
read_frames);
if (got < 0 || (size_t)got != read_frames) {
WRAP(pa_stream_cancel_write)(s);
stm->shutdown = 1;
if (got < 0) {
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
}
break;
}
}
@ -432,11 +445,13 @@ static int
wait_until_stream_ready(cubeb_stream * stm)
{
if (stm->output_stream &&
wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) == -1) {
wait_until_io_stream_ready(stm->output_stream, stm->context->mainloop) ==
-1) {
return -1;
}
if (stm->input_stream &&
wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) == -1) {
wait_until_io_stream_ready(stm->input_stream, stm->context->mainloop) ==
-1) {
return -1;
}
return 0;
@ -464,7 +479,8 @@ cork_io_stream(cubeb_stream * stm, pa_stream * io_stream, enum cork_state state)
if (!io_stream) {
return;
}
o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback, stm);
o = WRAP(pa_stream_cork)(io_stream, state & CORK, stream_success_callback,
stm);
if (o) {
operation_wait(stm->context, io_stream, o);
WRAP(pa_operation_unref)(o);
@ -491,7 +507,8 @@ stream_update_timing_info(cubeb_stream * stm)
int r = -1;
pa_operation * o = NULL;
if (stm->output_stream) {
o = WRAP(pa_stream_update_timing_info)(stm->output_stream, stream_success_callback, stm);
o = WRAP(pa_stream_update_timing_info)(stm->output_stream,
stream_success_callback, stm);
if (o) {
r = operation_wait(stm->context, stm->output_stream, o);
WRAP(pa_operation_unref)(o);
@ -502,7 +519,8 @@ stream_update_timing_info(cubeb_stream * stm)
}
if (stm->input_stream) {
o = WRAP(pa_stream_update_timing_info)(stm->input_stream, stream_success_callback, stm);
o = WRAP(pa_stream_update_timing_info)(stm->input_stream,
stream_success_callback, stm);
if (o) {
r = operation_wait(stm->context, stm->input_stream, o);
WRAP(pa_operation_unref)(o);
@ -584,8 +602,10 @@ layout_to_channel_map(cubeb_channel_layout layout, pa_channel_map * cm)
}
}
static void pulse_context_destroy(cubeb * ctx);
static void pulse_destroy(cubeb * ctx);
static void
pulse_context_destroy(cubeb * ctx);
static void
pulse_destroy(cubeb * ctx);
static int
pulse_context_init(cubeb * ctx)
@ -597,12 +617,13 @@ pulse_context_init(cubeb * ctx)
pulse_context_destroy(ctx);
}
ctx->context = WRAP(pa_context_new)(WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop),
ctx->context_name);
ctx->context = WRAP(pa_context_new)(
WRAP(pa_threaded_mainloop_get_api)(ctx->mainloop), ctx->context_name);
if (!ctx->context) {
return -1;
}
WRAP(pa_context_set_state_callback)(ctx->context, context_state_callback, ctx);
WRAP(pa_context_set_state_callback)
(ctx->context, context_state_callback, ctx);
WRAP(pa_threaded_mainloop_lock)(ctx->mainloop);
r = WRAP(pa_context_connect)(ctx->context, NULL, 0, NULL);
@ -621,8 +642,8 @@ pulse_context_init(cubeb * ctx)
return 0;
}
static int pulse_subscribe_notifications(cubeb * context,
pa_subscription_mask_t mask);
static int
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask);
/*static*/ int
pulse_init(cubeb ** context, char const * context_name)
@ -642,7 +663,8 @@ pulse_init(cubeb ** context, char const * context_name)
}
}
#define LOAD(x) { \
#define LOAD(x) \
{ \
cubeb_##x = dlsym(libpulse, #x); \
if (!cubeb_##x) { \
dlclose(libpulse); \
@ -735,7 +757,8 @@ pulse_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
}
static int
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
pulse_get_min_latency(cubeb * ctx, cubeb_stream_params params,
uint32_t * latency_frames)
{
(void)ctx;
// According to PulseAudio developers, this is a safe minimum.
@ -764,6 +787,10 @@ pulse_context_destroy(cubeb * ctx)
static void
pulse_destroy(cubeb * ctx)
{
assert(!ctx->input_collection_changed_callback &&
!ctx->input_collection_changed_user_ptr &&
!ctx->output_collection_changed_callback &&
!ctx->output_collection_changed_user_ptr);
free(ctx->context_name);
if (ctx->context) {
pulse_context_destroy(ctx);
@ -785,7 +812,8 @@ pulse_destroy(cubeb * ctx)
free(ctx);
}
static void pulse_stream_destroy(cubeb_stream * stm);
static void
pulse_stream_destroy(cubeb_stream * stm);
static pa_sample_format_t
to_pulse_format(cubeb_sample_format format)
@ -809,31 +837,38 @@ pulse_default_layout_for_channels(uint32_t ch)
{
assert(ch > 0 && ch <= 8);
switch (ch) {
case 1: return CUBEB_LAYOUT_MONO;
case 2: return CUBEB_LAYOUT_STEREO;
case 3: return CUBEB_LAYOUT_3F;
case 4: return CUBEB_LAYOUT_QUAD;
case 5: return CUBEB_LAYOUT_3F2;
case 6: return CUBEB_LAYOUT_3F_LFE |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT;
case 7: return CUBEB_LAYOUT_3F3R_LFE;
case 8: return CUBEB_LAYOUT_3F4_LFE;
case 1:
return CUBEB_LAYOUT_MONO;
case 2:
return CUBEB_LAYOUT_STEREO;
case 3:
return CUBEB_LAYOUT_3F;
case 4:
return CUBEB_LAYOUT_QUAD;
case 5:
return CUBEB_LAYOUT_3F2;
case 6:
return CUBEB_LAYOUT_3F_LFE | CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT;
case 7:
return CUBEB_LAYOUT_3F3R_LFE;
case 8:
return CUBEB_LAYOUT_3F4_LFE;
}
// Never get here!
return CUBEB_LAYOUT_UNDEFINED;
}
static int
create_pa_stream(cubeb_stream * stm,
pa_stream ** pa_stm,
cubeb_stream_params * stream_params,
char const * stream_name)
create_pa_stream(cubeb_stream * stm, pa_stream ** pa_stm,
cubeb_stream_params * stream_params, char const * stream_name)
{
assert(stm && stream_params);
assert(&stm->input_stream == pa_stm || (&stm->output_stream == pa_stm &&
assert(&stm->input_stream == pa_stm ||
(&stm->output_stream == pa_stm &&
(stream_params->layout == CUBEB_LAYOUT_UNDEFINED ||
(stream_params->layout != CUBEB_LAYOUT_UNDEFINED &&
cubeb_channel_layout_nb_channels(stream_params->layout) == stream_params->channels))));
cubeb_channel_layout_nb_channels(stream_params->layout) ==
stream_params->channels))));
if (stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
@ -850,13 +885,18 @@ create_pa_stream(cubeb_stream * stm,
if (stream_params->layout == CUBEB_LAYOUT_UNDEFINED) {
pa_channel_map cm;
if (stream_params->channels <= 8 &&
!WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels, PA_CHANNEL_MAP_DEFAULT)) {
LOG("Layout undefined and PulseAudio's default layout has not been configured, guess one.");
layout_to_channel_map(pulse_default_layout_for_channels(stream_params->channels), &cm);
*pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
!WRAP(pa_channel_map_init_auto)(&cm, stream_params->channels,
PA_CHANNEL_MAP_DEFAULT)) {
LOG("Layout undefined and PulseAudio's default layout has not been "
"configured, guess one.");
layout_to_channel_map(
pulse_default_layout_for_channels(stream_params->channels), &cm);
*pa_stm =
WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, &cm);
} else {
LOG("Layout undefined, PulseAudio will use its default.");
*pa_stm = WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
*pa_stm =
WRAP(pa_stream_new)(stm->context->context, stream_name, &ss, NULL);
}
} else {
pa_channel_map cm;
@ -867,7 +907,8 @@ create_pa_stream(cubeb_stream * stm,
}
static pa_buffer_attr
set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spec)
set_buffering_attribute(unsigned int latency_frames,
pa_sample_spec * sample_spec)
{
pa_buffer_attr battr;
battr.maxlength = -1;
@ -876,24 +917,23 @@ set_buffering_attribute(unsigned int latency_frames, pa_sample_spec * sample_spe
battr.minreq = battr.tlength / 4;
battr.fragsize = battr.minreq;
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",
battr.maxlength, battr.tlength, battr.prebuf, battr.minreq, battr.fragsize);
LOG("Requested buffer attributes maxlength %u, tlength %u, prebuf %u, minreq "
"%u, fragsize %u",
battr.maxlength, battr.tlength, battr.prebuf, battr.minreq,
battr.fragsize);
return battr;
}
static int
pulse_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
pulse_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name, cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
cubeb_state_callback state_callback, void * user_ptr)
{
cubeb_stream * stm;
pa_buffer_attr battr;
@ -921,22 +961,25 @@ pulse_stream_init(cubeb * context,
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
if (output_stream_params) {
r = create_pa_stream(stm, &stm->output_stream, output_stream_params, stream_name);
r = create_pa_stream(stm, &stm->output_stream, output_stream_params,
stream_name);
if (r != CUBEB_OK) {
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
pulse_stream_destroy(stm);
return r;
}
stm->output_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
stm->output_sample_spec =
*(WRAP(pa_stream_get_sample_spec)(stm->output_stream));
WRAP(pa_stream_set_state_callback)(stm->output_stream, stream_state_callback, stm);
WRAP(pa_stream_set_write_callback)(stm->output_stream, stream_write_callback, stm);
WRAP(pa_stream_set_state_callback)
(stm->output_stream, stream_state_callback, stm);
WRAP(pa_stream_set_write_callback)
(stm->output_stream, stream_write_callback, stm);
battr = set_buffering_attribute(latency_frames, &stm->output_sample_spec);
WRAP(pa_stream_connect_playback)(stm->output_stream,
(char const *) output_device,
&battr,
WRAP(pa_stream_connect_playback)
(stm->output_stream, (char const *)output_device, &battr,
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY,
NULL, NULL);
@ -944,22 +987,25 @@ pulse_stream_init(cubeb * context,
// Set up input stream
if (input_stream_params) {
r = create_pa_stream(stm, &stm->input_stream, input_stream_params, stream_name);
r = create_pa_stream(stm, &stm->input_stream, input_stream_params,
stream_name);
if (r != CUBEB_OK) {
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
pulse_stream_destroy(stm);
return r;
}
stm->input_sample_spec = *(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
stm->input_sample_spec =
*(WRAP(pa_stream_get_sample_spec)(stm->input_stream));
WRAP(pa_stream_set_state_callback)(stm->input_stream, stream_state_callback, stm);
WRAP(pa_stream_set_read_callback)(stm->input_stream, stream_read_callback, stm);
WRAP(pa_stream_set_state_callback)
(stm->input_stream, stream_state_callback, stm);
WRAP(pa_stream_set_read_callback)
(stm->input_stream, stream_read_callback, stm);
battr = set_buffering_attribute(latency_frames, &stm->input_sample_spec);
WRAP(pa_stream_connect_record)(stm->input_stream,
(char const *) input_device,
&battr,
WRAP(pa_stream_connect_record)
(stm->input_stream, (char const *)input_device, &battr,
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY);
}
@ -982,15 +1028,19 @@ pulse_stream_init(cubeb * context,
if (output_stream_params) {
const pa_buffer_attr * output_att;
output_att = WRAP(pa_stream_get_buffer_attr)(stm->output_stream);
LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",output_att->maxlength, output_att->tlength,
output_att->prebuf, output_att->minreq, output_att->fragsize);
LOG("Output buffer attributes maxlength %u, tlength %u, prebuf %u, "
"minreq %u, fragsize %u",
output_att->maxlength, output_att->tlength, output_att->prebuf,
output_att->minreq, output_att->fragsize);
}
if (input_stream_params) {
const pa_buffer_attr * input_att;
input_att = WRAP(pa_stream_get_buffer_attr)(stm->input_stream);
LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq %u, fragsize %u",input_att->maxlength, input_att->tlength,
input_att->prebuf, input_att->minreq, input_att->fragsize);
LOG("Input buffer attributes maxlength %u, tlength %u, prebuf %u, minreq "
"%u, fragsize %u",
input_att->maxlength, input_att->tlength, input_att->prebuf,
input_att->minreq, input_att->fragsize);
}
}
@ -1010,7 +1060,8 @@ pulse_stream_destroy(cubeb_stream * stm)
if (stm->drain_timer) {
/* there's no pa_rttime_free, so use this instead. */
WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop)->time_free(stm->drain_timer);
WRAP(pa_threaded_mainloop_get_api)
(stm->context->mainloop)->time_free(stm->drain_timer);
}
WRAP(pa_stream_set_state_callback)(stm->output_stream, NULL, NULL);
@ -1054,7 +1105,8 @@ pulse_stream_start(cubeb_stream * stm)
* things roll. This is done via a defer event in order to execute it
* from PA server thread. */
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
WRAP(pa_mainloop_api_once)(WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop),
WRAP(pa_mainloop_api_once)
(WRAP(pa_threaded_mainloop_get_api)(stm->context->mainloop),
pulse_defer_event_cb, stm);
WRAP(pa_threaded_mainloop_unlock)(stm->context->mainloop);
}
@ -1178,9 +1230,8 @@ pulse_stream_set_volume(cubeb_stream * stm, float volume)
index = WRAP(pa_stream_get_index)(stm->output_stream);
op = WRAP(pa_context_set_sink_input_volume)(ctx->context,
index, &cvol, volume_success,
stm);
op = WRAP(pa_context_set_sink_input_volume)(ctx->context, index, &cvol,
volume_success, stm);
if (op) {
operation_wait(ctx, stm->output_stream, op);
WRAP(pa_operation_unref)(op);
@ -1201,8 +1252,8 @@ pulse_stream_set_name(cubeb_stream * stm, char const * stream_name)
WRAP(pa_threaded_mainloop_lock)(stm->context->mainloop);
pa_operation * op =
WRAP(pa_stream_set_name)(stm->output_stream, stream_name, rename_success, stm);
pa_operation * op = WRAP(pa_stream_set_name)(stm->output_stream, stream_name,
rename_success, stm);
if (op) {
operation_wait(stm->context, stm->output_stream, op);
@ -1246,8 +1297,8 @@ pulse_ensure_dev_list_data_list_size (pulse_dev_list_data * list_data)
{
if (list_data->count == list_data->max) {
list_data->max += 8;
list_data->devinfo = realloc(list_data->devinfo,
sizeof(cubeb_device_info) * list_data->max);
list_data->devinfo =
realloc(list_data->devinfo, sizeof(cubeb_device_info) * list_data->max);
}
}
@ -1267,8 +1318,8 @@ pulse_get_state_from_sink_port(pa_sink_port_info * info)
}
static void
pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
int eol, void * user_data)
pulse_sink_info_cb(pa_context * context, const pa_sink_info * info, int eol,
void * user_data)
{
pulse_dev_list_data * list_data = user_data;
cubeb_device_info * devinfo;
@ -1307,11 +1358,13 @@ pulse_sink_info_cb(pa_context * context, const pa_sink_info * info,
devinfo->type = CUBEB_DEVICE_TYPE_OUTPUT;
devinfo->state = pulse_get_state_from_sink_port(info->active_port);
devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0) ?
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
devinfo->preferred = (strcmp(info->name, list_data->default_sink_name) == 0)
? CUBEB_DEVICE_PREF_ALL
: CUBEB_DEVICE_PREF_NONE;
devinfo->format = CUBEB_DEVICE_FMT_ALL;
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
devinfo->default_format =
pulse_format_to_cubeb_format(info->sample_spec.format);
devinfo->max_channels = info->channel_map.channels;
devinfo->min_rate = 1;
devinfo->max_rate = PA_RATE_MAX;
@ -1339,8 +1392,8 @@ pulse_get_state_from_source_port(pa_source_port_info * info)
}
static void
pulse_source_info_cb(pa_context * context, const pa_source_info * info,
int eol, void * user_data)
pulse_source_info_cb(pa_context * context, const pa_source_info * info, int eol,
void * user_data)
{
pulse_dev_list_data * list_data = user_data;
cubeb_device_info * devinfo;
@ -1376,11 +1429,13 @@ pulse_source_info_cb(pa_context * context, const pa_source_info * info,
devinfo->type = CUBEB_DEVICE_TYPE_INPUT;
devinfo->state = pulse_get_state_from_source_port(info->active_port);
devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0) ?
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
devinfo->preferred = (strcmp(info->name, list_data->default_source_name) == 0)
? CUBEB_DEVICE_PREF_ALL
: CUBEB_DEVICE_PREF_NONE;
devinfo->format = CUBEB_DEVICE_FMT_ALL;
devinfo->default_format = pulse_format_to_cubeb_format(info->sample_spec.format);
devinfo->default_format =
pulse_format_to_cubeb_format(info->sample_spec.format);
devinfo->max_channels = info->channel_map.channels;
devinfo->min_rate = 1;
devinfo->max_rate = PA_RATE_MAX;
@ -1418,8 +1473,8 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
o = WRAP(pa_context_get_server_info)(context->context,
pulse_server_info_cb, &user_data);
o = WRAP(pa_context_get_server_info)(context->context, pulse_server_info_cb,
&user_data);
if (o) {
operation_wait(context, NULL, o);
WRAP(pa_operation_unref)(o);
@ -1454,7 +1509,8 @@ pulse_enumerate_devices(cubeb * context, cubeb_device_type type,
}
static int
pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collection)
pulse_device_collection_destroy(cubeb * ctx,
cubeb_device_collection * collection)
{
size_t n;
@ -1469,7 +1525,8 @@ pulse_device_collection_destroy(cubeb * ctx, cubeb_device_collection * collectio
}
static int
pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device)
pulse_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device)
{
#if PA_CHECK_VERSION(0, 9, 8)
*device = calloc(1, sizeof(cubeb_device));
@ -1493,8 +1550,7 @@ pulse_stream_get_current_device(cubeb_stream * stm, cubeb_device ** const device
}
static int
pulse_stream_device_destroy(cubeb_stream * stream,
cubeb_device * device)
pulse_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
{
(void)stream;
free(device->input_name);
@ -1504,8 +1560,7 @@ pulse_stream_device_destroy(cubeb_stream * stream,
}
static void
pulse_subscribe_callback(pa_context * ctx,
pa_subscription_event_type_t t,
pulse_subscribe_callback(pa_context * ctx, pa_subscription_event_type_t t,
uint32_t index, void * userdata)
{
(void)ctx;
@ -1515,36 +1570,49 @@ pulse_subscribe_callback(pa_context * ctx,
case PA_SUBSCRIPTION_EVENT_SERVER:
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_CHANGE) {
LOG("Server changed %d", index);
WRAP(pa_context_get_server_info)(context->context, server_info_callback, context);
WRAP(pa_context_get_server_info)
(context->context, server_info_callback, context);
}
break;
case PA_SUBSCRIPTION_EVENT_SOURCE:
case PA_SUBSCRIPTION_EVENT_SINK:
if (g_cubeb_log_level) {
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_REMOVE) {
LOG("Removing source index %d", index);
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
PA_SUBSCRIPTION_EVENT_SOURCE &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_NEW) {
LOG("Adding source index %d", index);
}
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE) {
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_REMOVE) {
LOG("Removing sink index %d", index);
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
} else if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
PA_SUBSCRIPTION_EVENT_SINK &&
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) ==
PA_SUBSCRIPTION_EVENT_NEW) {
LOG("Adding sink index %d", index);
}
}
if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_REMOVE ||
(t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) == PA_SUBSCRIPTION_EVENT_NEW) {
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE) {
context->input_collection_changed_callback(context, context->input_collection_changed_user_ptr);
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
PA_SUBSCRIPTION_EVENT_SOURCE) {
context->input_collection_changed_callback(
context, context->input_collection_changed_user_ptr);
}
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK) {
context->output_collection_changed_callback(context, context->output_collection_changed_user_ptr);
if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) ==
PA_SUBSCRIPTION_EVENT_SINK) {
context->output_collection_changed_callback(
context, context->output_collection_changed_user_ptr);
}
}
break;
@ -1561,13 +1629,16 @@ subscribe_success(pa_context *c, int success, void *userdata)
}
static int
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) {
pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask)
{
WRAP(pa_threaded_mainloop_lock)(context->mainloop);
WRAP(pa_context_set_subscribe_callback)(context->context, pulse_subscribe_callback, context);
WRAP(pa_context_set_subscribe_callback)
(context->context, pulse_subscribe_callback, context);
pa_operation * o;
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success, context);
o = WRAP(pa_context_subscribe)(context->context, mask, subscribe_success,
context);
if (o == NULL) {
WRAP(pa_threaded_mainloop_unlock)(context->mainloop);
LOG("Context subscribe failed");
@ -1582,8 +1653,8 @@ pulse_subscribe_notifications(cubeb * context, pa_subscription_mask_t mask) {
}
static int
pulse_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
pulse_register_device_collection_changed(
cubeb * context, cubeb_device_type devtype,
cubeb_device_collection_changed_callback collection_changed_callback,
void * user_ptr)
{
@ -1625,7 +1696,6 @@ static struct cubeb_ops const pulse_ops = {
.stream_destroy = pulse_stream_destroy,
.stream_start = pulse_stream_start,
.stream_stop = pulse_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = pulse_stream_get_position,
.stream_get_latency = pulse_stream_get_latency,
.stream_get_input_latency = NULL,
@ -1634,5 +1704,5 @@ static struct cubeb_ops const pulse_ops = {
.stream_get_current_device = pulse_stream_get_current_device,
.stream_device_destroy = pulse_stream_device_destroy,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = pulse_register_device_collection_changed
};
.register_device_collection_changed =
pulse_register_device_collection_changed};

View file

@ -8,16 +8,16 @@
#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)
@ -35,7 +35,8 @@ to_speex_quality(cubeb_resampler_quality q)
}
}
uint32_t min_buffered_audio_frame(uint32_t sample_rate)
uint32_t
min_buffered_audio_frame(uint32_t sample_rate)
{
return sample_rate / 20;
}
@ -46,23 +47,22 @@ passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
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,
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
@ -79,7 +79,8 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
// so we can pass it as one pointer to the callback. Or this is a glitch.
// 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.
// 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)) {
@ -87,8 +88,8 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
// buffer with silence. First keep the actual number of input samples
// 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);
}
@ -100,12 +101,14 @@ long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_cou
// pass the current input data directly to the callback
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,
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) {
@ -125,17 +128,12 @@ 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)
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;
@ -147,28 +145,29 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
}
template <typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::~cubeb_resampler_speex()
{ }
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)
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);
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)
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);
@ -185,8 +184,7 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
out_unprocessed =
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) {
@ -207,27 +205,36 @@ cubeb_resampler_speex<T, InputProcessor, 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. */
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
@ -237,16 +244,17 @@ cubeb_resampler_speex<T, InputProcessor, 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;
@ -277,15 +285,14 @@ cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
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;
@ -332,21 +338,13 @@ cubeb_resampler_create(cubeb_stream * stream,
switch (format) {
case CUBEB_SAMPLE_S16NE:
return cubeb_resampler_create_internal<short>(stream,
input_params,
output_params,
target_rate,
callback,
user_ptr,
quality);
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);
return cubeb_resampler_create_internal<float>(
stream, input_params, output_params, target_rate, callback, user_ptr,
quality, reclock);
default:
assert(false);
return nullptr;
@ -354,14 +352,12 @@ cubeb_resampler_create(cubeb_stream * stream,
}
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_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);
/**
* 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
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"
@ -31,23 +31,26 @@ MOZ_BEGIN_STD_NAMESPACE
#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));
@ -77,29 +76,24 @@ protected:
};
template <typename T>
class passthrough_resampler : public cubeb_resampler
, public processor {
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));
}
}
@ -120,10 +114,8 @@ 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,10 +189,7 @@ 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,
speex_resample(input_buffer, &input_frame_count, output_buffer,
&output_frame_count);
}
@ -227,8 +213,8 @@ public:
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. */
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++) {
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,9 +326,11 @@ 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. */
@ -347,10 +341,8 @@ private:
int rv;
rv =
#endif
speex_resampler_process_interleaved_float(speex_resampler,
input_buffer,
input_frame_count,
output_buffer,
speex_resampler_process_interleaved_float(
speex_resampler, input_buffer, input_frame_count, output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
@ -362,10 +354,8 @@ private:
int rv;
rv =
#endif
speex_resampler_process_interleaved_int(speex_resampler,
input_buffer,
input_frame_count,
output_buffer,
speex_resampler_process_interleaved_int(
speex_resampler, input_buffer, input_frame_count, output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
@ -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;
@ -512,9 +498,9 @@ 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;
@ -530,21 +516,19 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
(output_params && !input_params && (output_params->rate == target_rate))) {
(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,
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,
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;
@ -552,10 +536,8 @@ cubeb_resampler_create_internal(cubeb_stream * stream,
}
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,
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(),
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. */
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);
@ -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;
@ -100,7 +95,8 @@ ring_array_destroy(ring_array * ra)
/** 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)
{

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.
@ -64,8 +62,7 @@ public:
/* 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();
@ -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,8 +456,7 @@ 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),

View file

@ -4,29 +4,31 @@
* 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 WRAP(x) (*cubeb_##x)
#define LIBSNDIO_API_VISIT(X) \
X(sio_close) \
X(sio_eof) \
@ -41,7 +43,7 @@
X(sio_setpar) \
X(sio_start) \
X(sio_stop) \
X(sio_write) \
X(sio_write)
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
@ -319,7 +321,8 @@ sndio_init(cubeb **context, char const *context_name)
}
}
#define LOAD(x) { \
#define LOAD(x) \
{ \
cubeb_##x = dlsym(libsndio, #x); \
if (!cubeb_##x) { \
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
@ -365,17 +368,14 @@ 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;
struct sio_par wpar, rpar;
@ -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.
@ -626,7 +627,7 @@ sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
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;
@ -658,7 +659,6 @@ static struct cubeb_ops const sndio_ops = {
.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,
@ -666,5 +666,4 @@ static struct cubeb_ops const sndio_ops = {
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};
.register_device_collection_changed = NULL};

View file

@ -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
@ -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;
@ -183,7 +183,8 @@ 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;
prinfo->sample_rate < SUN_MAX_RATE &&
prinfo->sample_rate > SUN_MIN_RATE;
}
static int
@ -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;
@ -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,17 +509,13 @@ 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;
@ -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) {
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;
}
@ -718,7 +722,6 @@ static struct cubeb_ops const sun_ops = {
.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,
@ -727,5 +730,4 @@ static struct cubeb_ops const sun_ops = {
.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
};
.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,7 +7,8 @@
#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:

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"
@ -25,7 +25,8 @@
/** Similar to memcpy, but accounts for the size of an element. */
template <typename T>
void PodCopy(T * destination, const T * source, size_t count)
void
PodCopy(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination && source);
@ -34,7 +35,8 @@ void PodCopy(T * destination, const T * source, size_t count)
/** Similar to memmove, but accounts for the size of an element. */
template <typename T>
void PodMove(T * destination, const T * source, size_t count)
void
PodMove(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination && source);
@ -43,7 +45,8 @@ void PodMove(T * destination, const T * source, size_t count)
/** Similar to a memset to zero, but accounts for the size of an element. */
template <typename T>
void PodZero(T * destination, size_t count)
void
PodZero(T * destination, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination);
@ -52,7 +55,8 @@ void PodZero(T * destination, size_t count)
namespace {
template <typename T, typename Trait>
void Copy(T * destination, const T * source, size_t count, Trait)
void
Copy(T * destination, const T * source, size_t count, Trait)
{
for (size_t i = 0; i < count; i++) {
destination[i] = source[i];
@ -60,11 +64,12 @@ void Copy(T * destination, const T * source, size_t count, Trait)
}
template <typename T>
void Copy(T * destination, const T * source, size_t count, std::true_type)
void
Copy(T * destination, const T * source, size_t count, std::true_type)
{
PodCopy(destination, source, count);
}
}
} // namespace
/**
* This allows copying a number of elements from a `source` pointer to a
@ -72,7 +77,8 @@ void Copy(T * destination, const T * source, size_t count, std::true_type)
* calls the constructors and destructors otherwise.
*/
template <typename T>
void Copy(T * destination, const T * source, size_t count)
void
Copy(T * destination, const T * source, size_t count)
{
assert(destination && source);
Copy(destination, source, count, typename std::is_trivial<T>::type());
@ -80,7 +86,8 @@ void Copy(T * destination, const T * source, size_t count)
namespace {
template <typename T, typename Trait>
void ConstructDefault(T * destination, size_t count, Trait)
void
ConstructDefault(T * destination, size_t count, Trait)
{
for (size_t i = 0; i < count; i++) {
destination[i] = T();
@ -88,50 +95,39 @@ void ConstructDefault(T * destination, size_t count, Trait)
}
template <typename T>
void ConstructDefault(T * destination,
size_t count, std::true_type)
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)
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
{
@ -146,22 +142,13 @@ public:
}
/** 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.
@ -227,10 +214,7 @@ 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.
@ -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()
{

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,12 +54,12 @@ public:
}
private:
CRITICAL_SECTION critical_section;
SRWLOCK srwlock;
#ifndef NDEBUG
DWORD owner;
#endif
// Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
// Disallow copy and assignment because SRWLock cannot be copied.
owned_critical_section(const owned_critical_section &);
owned_critical_section & operator=(const owned_critical_section &);
};

File diff suppressed because it is too large Load diff

View file

@ -8,23 +8,28 @@
#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 */
#endif
@ -68,11 +73,6 @@
#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
@ -223,8 +227,7 @@ 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;
XASSERT(ctx);
@ -256,7 +259,8 @@ 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;
struct cubeb_stream_item * item;
@ -265,7 +269,8 @@ winmm_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR
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);
@ -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;
}
@ -827,12 +921,14 @@ winmm_query_preferred_out_device(UINT devid)
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 &&
(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 &&
(DWORD_PTR)&compref,
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
devid == compref)
ret |= CUBEB_DEVICE_PREF_VOICE;
@ -851,7 +947,8 @@ 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;
@ -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,7 +972,8 @@ 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;
@ -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;
@ -905,12 +1003,14 @@ winmm_query_preferred_in_device(UINT devid)
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 &&
(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 &&
(DWORD_PTR)&compref,
(DWORD_PTR)&status) == MMSYSERR_NOERROR &&
devid == compref)
ret |= CUBEB_DEVICE_PREF_VOICE;
@ -918,7 +1018,8 @@ 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;
@ -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,7 +1043,8 @@ 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;
@ -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) {
@ -1056,7 +1160,6 @@ static struct cubeb_ops const winmm_ops = {
/*.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,
@ -1065,5 +1168,4 @@ static struct cubeb_ops const winmm_ops = {
/*.stream_get_current_device =*/NULL,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback=*/NULL,
/*.register_device_collection_changed =*/ NULL
};
/*.register_device_collection_changed =*/NULL};