Merge branch 'cubeb'

This commit is contained in:
Connor McLaughlin 2020-01-11 13:52:02 +10:00
commit 835bbfaea5
72 changed files with 24728 additions and 24 deletions

16
dep/cubeb/AUTHORS Normal file
View file

@ -0,0 +1,16 @@
Matthew Gregan <kinetik@flim.org>
Alexandre Ratchov <alex@caoua.org>
Michael Wu <mwu@mozilla.com>
Paul Adenot <paul@paul.cx>
David Richards <drichards@mozilla.com>
Sebastien Alaiwan <sebastien.alaiwan@gmail.com>
KO Myung-Hun <komh@chollian.net>
Haakon Sporsheim <haakon.sporsheim@telenordigital.com>
Alex Chronopoulos <achronop@gmail.com>
Jan Beich <jbeich@FreeBSD.org>
Vito Caputo <vito.caputo@coreos.com>
Landry Breuil <landry@openbsd.org>
Jacek Caban <jacek@codeweavers.com>
Paul Hancock <Paul.Hancock.17041993@live.com>
Ted Mielczarek <ted@mielczarek.org>
Chun-Min Chang <chun.m.chang@gmail.com>

332
dep/cubeb/CMakeLists.txt Normal file
View file

@ -0,0 +1,332 @@
# TODO
# - backend selection via command line, rather than simply detecting headers.
cmake_minimum_required(VERSION 3.1 FATAL_ERROR)
project(cubeb
VERSION 0.0.0)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
option(BUILD_TESTS "Build tests" ON)
option(BUILD_RUST_LIBS "Build rust backends" OFF)
option(BUILD_TOOLS "Build tools" ON)
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING
"Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif()
if(POLICY CMP0063)
cmake_policy(SET CMP0063 NEW)
endif()
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT COMMAND add_sanitizers)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/sanitizers-cmake/cmake")
find_package(Sanitizers)
if(NOT COMMAND add_sanitizers)
message(FATAL_ERROR "Could not find sanitizers-cmake: run\n\tgit submodule update --init --recursive\nin base git checkout")
endif()
endif()
if(BUILD_TESTS)
if(NOT TARGET gtest_main)
if(NOT EXISTS "${PROJECT_SOURCE_DIR}/googletest/CMakeLists.txt")
message(FATAL_ERROR "Could not find googletest: run\n\tgit submodule update --init --recursive\nin base git checkout")
endif()
add_definitions(-DGTEST_HAS_TR1_TUPLE=0)
set(gtest_force_shared_crt ON CACHE BOOL "")
add_subdirectory(googletest)
endif()
endif()
if (BUILD_RUST_LIBS)
if(EXISTS "${PROJECT_SOURCE_DIR}/src/cubeb-pulse-rs")
set(USE_PULSE_RUST 1)
endif()
if(EXISTS "${PROJECT_SOURCE_DIR}/src/cubeb-coreaudio-rs")
set(USE_AUDIOUNIT_RUST 1)
endif()
endif()
# On OS/2, visibility attribute is not supported.
if(NOT OS2)
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
endif()
set(CMAKE_CXX_WARNING_LEVEL 4)
if(NOT MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
endif()
add_library(cubeb
src/cubeb.c
src/cubeb_mixer.cpp
src/cubeb_resampler.cpp
src/cubeb_log.cpp
src/cubeb_strings.c
src/cubeb_utils.cpp
$<TARGET_OBJECTS:speex>)
target_include_directories(cubeb
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<INSTALL_INTERFACE:include>
)
target_include_directories(cubeb PRIVATE src)
target_compile_definitions(cubeb PRIVATE OUTSIDE_SPEEX)
target_compile_definitions(cubeb PRIVATE FLOATING_POINT)
target_compile_definitions(cubeb PRIVATE EXPORT=)
target_compile_definitions(cubeb PRIVATE RANDOM_PREFIX=speex)
add_sanitizers(cubeb)
include(GenerateExportHeader)
generate_export_header(cubeb EXPORT_FILE_NAME ${CMAKE_BINARY_DIR}/exports/cubeb_export.h)
target_include_directories(cubeb
PUBLIC $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/exports>
)
if(UNIX)
include(GNUInstallDirs)
else()
set(CMAKE_INSTALL_LIBDIR "lib")
set(CMAKE_INSTALL_BINDIR "bin")
set(CMAKE_INSTALL_DATADIR "share")
set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DATADIR}/doc")
set(CMAKE_INSTALL_INCLUDEDIR "include")
endif()
install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
install(DIRECTORY ${CMAKE_BINARY_DIR}/exports/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
include(CMakePackageConfigHelpers)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
COMPATIBILITY SameMajorVersion
)
configure_package_config_file(
"Config.cmake.in"
"${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
install(TARGETS cubeb
EXPORT "${PROJECT_NAME}Targets"
DESTINATION ${CMAKE_INSTALL_PREFIX}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)
install(
FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
install(
EXPORT "${PROJECT_NAME}Targets"
NAMESPACE "${PROJECT_NAME}::"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}"
)
add_library(speex OBJECT
src/speex/resample.c)
set_target_properties(speex PROPERTIES POSITION_INDEPENDENT_CODE TRUE)
target_compile_definitions(speex PRIVATE OUTSIDE_SPEEX)
target_compile_definitions(speex PRIVATE FLOATING_POINT)
target_compile_definitions(speex PRIVATE EXPORT=)
target_compile_definitions(speex PRIVATE RANDOM_PREFIX=speex)
include(CheckIncludeFiles)
check_include_files(AudioUnit/AudioUnit.h USE_AUDIOUNIT)
if(USE_AUDIOUNIT)
target_sources(cubeb PRIVATE
src/cubeb_audiounit.cpp
src/cubeb_osx_run_loop.cpp)
target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT)
target_link_libraries(cubeb PRIVATE "-framework AudioUnit" "-framework CoreAudio" "-framework CoreServices")
endif()
check_include_files(pulse/pulseaudio.h USE_PULSE)
if(USE_PULSE)
target_sources(cubeb PRIVATE
src/cubeb_pulse.c)
target_compile_definitions(cubeb PRIVATE USE_PULSE)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(alsa/asoundlib.h USE_ALSA)
if(USE_ALSA)
target_sources(cubeb PRIVATE
src/cubeb_alsa.c)
target_compile_definitions(cubeb PRIVATE USE_ALSA)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(jack/jack.h USE_JACK)
if(USE_JACK)
target_sources(cubeb PRIVATE
src/cubeb_jack.cpp)
target_compile_definitions(cubeb PRIVATE USE_JACK)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(audioclient.h USE_WASAPI)
if(USE_WASAPI)
target_sources(cubeb PRIVATE
src/cubeb_wasapi.cpp)
target_compile_definitions(cubeb PRIVATE USE_WASAPI)
target_link_libraries(cubeb PRIVATE avrt ole32)
endif()
check_include_files("windows.h;mmsystem.h" USE_WINMM)
if(USE_WINMM)
target_sources(cubeb PRIVATE
src/cubeb_winmm.c)
target_compile_definitions(cubeb PRIVATE USE_WINMM)
target_link_libraries(cubeb PRIVATE winmm)
endif()
check_include_files(SLES/OpenSLES.h USE_OPENSL)
if(USE_OPENSL)
target_sources(cubeb PRIVATE
src/cubeb_opensl.c
src/cubeb-jni.cpp)
target_compile_definitions(cubeb PRIVATE USE_OPENSL)
target_link_libraries(cubeb PRIVATE OpenSLES)
endif()
check_include_files(android/log.h USE_AUDIOTRACK)
if(USE_AUDIOTRACK)
target_sources(cubeb PRIVATE
src/cubeb_audiotrack.c)
target_compile_definitions(cubeb PRIVATE USE_AUDIOTRACK)
target_link_libraries(cubeb PRIVATE log)
endif()
check_include_files(sndio.h USE_SNDIO)
if(USE_SNDIO)
target_sources(cubeb PRIVATE
src/cubeb_sndio.c)
target_compile_definitions(cubeb PRIVATE USE_SNDIO)
target_link_libraries(cubeb PRIVATE pthread ${CMAKE_DL_LIBS})
endif()
check_include_files(sys/audioio.h USE_SUN)
if(USE_SUN)
target_sources(cubeb PRIVATE
src/cubeb_sun.c)
target_compile_definitions(cubeb PRIVATE USE_SUN)
target_link_libraries(cubeb PRIVATE pthread)
endif()
check_include_files(kai.h USE_KAI)
if(USE_KAI)
target_sources(cubeb PRIVATE
src/cubeb_kai.c)
target_compile_definitions(cubeb PRIVATE USE_KAI)
target_link_libraries(cubeb PRIVATE kai)
endif()
if(USE_PULSE_RUST)
include(ExternalProject)
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
ExternalProject_Add(
cubeb_pulse_rs
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND cargo build COMMAND cargo build --release
BINARY_DIR "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs"
INSTALL_COMMAND ""
LOG_BUILD ON)
add_dependencies(cubeb cubeb_pulse_rs)
target_compile_definitions(cubeb PRIVATE USE_PULSE_RUST)
target_link_libraries(cubeb PRIVATE
debug "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs/target/debug/libcubeb_pulse.a"
optimized "${CMAKE_SOURCE_DIR}/src/cubeb-pulse-rs/target/release/libcubeb_pulse.a" pulse)
endif()
if(USE_AUDIOUNIT_RUST)
include(ExternalProject)
set_directory_properties(PROPERTIES EP_PREFIX ${CMAKE_BINARY_DIR}/rust)
ExternalProject_Add(
cubeb_coreaudio_rs
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ""
BUILD_COMMAND cargo build COMMAND cargo build --release
BINARY_DIR "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs"
INSTALL_COMMAND ""
LOG_BUILD ON)
add_dependencies(cubeb cubeb_coreaudio_rs)
target_compile_definitions(cubeb PRIVATE USE_AUDIOUNIT_RUST)
target_link_libraries(cubeb PRIVATE
debug "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs/target/debug/libcubeb_coreaudio.a"
optimized "${CMAKE_SOURCE_DIR}/src/cubeb-coreaudio-rs/target/release/libcubeb_coreaudio.a")
endif()
find_package(Doxygen)
if(DOXYGEN_FOUND)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile @ONLY)
add_custom_target(doc ALL
${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/docs/Doxyfile
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/docs
COMMENT "Generating API documentation with Doxygen" VERBATIM)
endif()
if(BUILD_TESTS)
enable_testing()
macro(cubeb_add_test NAME)
add_executable(test_${NAME} test/test_${NAME}.cpp)
target_include_directories(test_${NAME} PRIVATE ${gtest_SOURCE_DIR}/include)
target_include_directories(test_${NAME} PRIVATE src)
target_link_libraries(test_${NAME} PRIVATE cubeb gtest_main)
add_test(${NAME} test_${NAME})
add_sanitizers(test_${NAME})
install(TARGETS test_${NAME} DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
endmacro(cubeb_add_test)
cubeb_add_test(sanity)
cubeb_add_test(tone)
cubeb_add_test(audio)
cubeb_add_test(record)
cubeb_add_test(devices)
cubeb_add_test(callback_ret)
add_executable(test_resampler test/test_resampler.cpp src/cubeb_resampler.cpp $<TARGET_OBJECTS:speex>)
target_include_directories(test_resampler PRIVATE ${gtest_SOURCE_DIR}/include)
target_include_directories(test_resampler PRIVATE src)
target_compile_definitions(test_resampler PRIVATE OUTSIDE_SPEEX)
target_compile_definitions(test_resampler PRIVATE FLOATING_POINT)
target_compile_definitions(test_resampler PRIVATE EXPORT=)
target_compile_definitions(test_resampler PRIVATE RANDOM_PREFIX=speex)
target_link_libraries(test_resampler PRIVATE cubeb gtest_main)
add_test(resampler test_resampler)
add_sanitizers(test_resampler)
install(TARGETS test_resampler DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
cubeb_add_test(duplex)
if (USE_WASAPI)
cubeb_add_test(overload_callback)
cubeb_add_test(loopback)
endif()
cubeb_add_test(latency test_latency)
cubeb_add_test(ring_array)
cubeb_add_test(utils)
cubeb_add_test(ring_buffer)
cubeb_add_test(device_changed_callback)
endif()
if(BUILD_TOOLS)
add_executable(cubeb-test tools/cubeb-test.cpp)
target_include_directories(cubeb-test PRIVATE src)
target_link_libraries(cubeb-test PRIVATE cubeb)
add_sanitizers(cubeb-test)
install(TARGETS cubeb-test DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR})
endif()

View file

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

46
dep/cubeb/INSTALL.md Normal file
View file

@ -0,0 +1,46 @@
# Build instructions for libcubeb
You must have CMake v3.1 or later installed.
1. `git clone --recursive https://github.com/kinetiknz/cubeb.git`
2. `mkdir cubeb-build`
3. `cd cubeb-build`
3. `cmake ../cubeb`
4. `cmake --build .`
5. `ctest`
# Windows build notes
Windows builds can use Microsoft Visual Studio 2015, Microsoft Visual Studio
2017, or MinGW-w64 with Win32 threads (by passing `cmake -G` to generate the
appropriate build configuration).
## Microsoft Visual Studio 2015 or 2017 Command Line
CMake can be used from the command line by following the build steps at the top
of this file. CMake will select a default generator based on the environment,
or one can be specified with the `-G` argument.
## Microsoft Visual Studio 2017 IDE
Visual Studio 2017 adds in built support for CMake. CMake can be used from
within the IDE via the following steps:
- Navigate to `File -> Open -> Cmake...`
- Open `CMakeLists.txt` file in the root of the project.
Note, to generate the build in the cubeb dir CMake settings need to be updated
via: `CMake -> Change CMake Settings -> CMakeLists.txt`. The default
configuration used by Visual Studio will place the build in a different location
than the steps detailed at the top of this file.
## MinGW-w64
To build with MinGW-w64, install the following items:
- Download and install MinGW-w64 with Win32 threads.
- Download and install CMake.
- Run MinGW-w64 Terminal from the Start Menu.
- Follow the build steps at the top of this file, but at step 3 run:
`cmake -G "MinGW Makefiles" ..`
- Continue the build steps at the top of this file.

13
dep/cubeb/LICENSE Normal file
View file

@ -0,0 +1,13 @@
Copyright © 2011 Mozilla Foundation
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

6
dep/cubeb/README.md Normal file
View file

@ -0,0 +1,6 @@
[![Build Status](https://travis-ci.org/kinetiknz/cubeb.svg?branch=master)](https://travis-ci.org/kinetiknz/cubeb)
[![Build status](https://ci.appveyor.com/api/projects/status/osv2r0m1j1nt9csr/branch/master?svg=true)](https://ci.appveyor.com/project/kinetiknz/cubeb/branch/master)
See INSTALL.md for build instructions.
Licensed under an ISC-style license. See LICENSE for details.

41
dep/cubeb/TODO Normal file
View file

@ -0,0 +1,41 @@
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

36
dep/cubeb/cubeb.supp Normal file
View file

@ -0,0 +1,36 @@
{
snd_config_update-malloc
Memcheck:Leak
fun:malloc
...
fun:snd_config_update_r
}
{
snd1_dlobj_cache_get-malloc
Memcheck:Leak
fun:malloc
...
fun:snd1_dlobj_cache_get
}
{
parse_defs-malloc
Memcheck:Leak
fun:malloc
...
fun:parse_defs
}
{
parse_defs-calloc
Memcheck:Leak
fun:calloc
...
fun:parse_defs
}
{
pa_client_conf_from_x11-malloc
Memcheck:Leak
fun:malloc
...
fun:pa_client_conf_from_x11
}

406
dep/cubeb/cubeb.vcxproj Normal file
View file

@ -0,0 +1,406 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugFast|Win32">
<Configuration>DebugFast</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="DebugFast|x64">
<Configuration>DebugFast</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseLTCG|Win32">
<Configuration>ReleaseLTCG</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="ReleaseLTCG|x64">
<Configuration>ReleaseLTCG</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\cubeb\cubeb.h" />
<ClInclude Include="include\cubeb\cubeb_export.h" />
<ClInclude Include="src\cubeb-internal.h" />
<ClInclude Include="src\cubeb-speex-resampler.h" />
<ClInclude Include="src\cubeb_array_queue.h" />
<ClInclude Include="src\cubeb_assert.h" />
<ClInclude Include="src\cubeb_log.h" />
<ClInclude Include="src\cubeb_mixer.h" />
<ClInclude Include="src\cubeb_resampler.h" />
<ClInclude Include="src\cubeb_resampler_internal.h" />
<ClInclude Include="src\cubeb_ringbuffer.h" />
<ClInclude Include="src\cubeb_ring_array.h" />
<ClInclude Include="src\cubeb_strings.h" />
<ClInclude Include="src\cubeb_utils.h" />
<ClInclude Include="src\cubeb_utils_win.h" />
<ClInclude Include="src\speex\arch.h" />
<ClInclude Include="src\speex\fixed_generic.h" />
<ClInclude Include="src\speex\resample_neon.h" />
<ClInclude Include="src\speex\resample_sse.h" />
<ClInclude Include="src\speex\speex_config_types.h" />
<ClInclude Include="src\speex\speex_resampler.h" />
<ClInclude Include="src\speex\stack_alloc.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\cubeb.c" />
<ClCompile Include="src\cubeb_log.cpp" />
<ClCompile Include="src\cubeb_mixer.cpp" />
<ClCompile Include="src\cubeb_resampler.cpp" />
<ClCompile Include="src\cubeb_strings.c" />
<ClCompile Include="src\cubeb_utils.cpp" />
<ClCompile Include="src\cubeb_wasapi.cpp" />
<ClCompile Include="src\cubeb_winmm.c" />
<ClCompile Include="src\speex\resample.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{72F9423C-91EE-4487-AAC6-555ED6F61AA1}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>inih</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
<IntDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</IntDir>
<TargetName>$(ProjectName)-$(Platform)-$(Configuration)</TargetName>
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)build\$(ProjectName)-$(Platform)-$(Configuration)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<SupportJustMyCode>false</SupportJustMyCode>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='DebugFast|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<MinimalRebuild>false</MinimalRebuild>
<LanguageStandard>stdcpp17</LanguageStandard>
<SupportJustMyCode>false</SupportJustMyCode>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64-debug;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<WholeProgramOptimization>false</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|Win32'">
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib32;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<WholeProgramOptimization>false</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='ReleaseLTCG|x64'">
<ClCompile>
<WarningLevel>TurnOffAllWarnings</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>USE_WASAPI;USE_WINMM;OUTSIDE_SPEEX;FLOATING_POINT;RANDOM_PREFIX=speex;EXPORT=;_CRT_NONSTDC_NO_DEPRECATE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)include;$(ProjectDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<WholeProgramOptimization>true</WholeProgramOptimization>
<LanguageStandard>stdcpp17</LanguageStandard>
<OmitFramePointers>true</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>$(SolutionDir)dep\lib64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Lib />
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClInclude Include="include\cubeb\cubeb.h" />
<ClInclude Include="src\speex\fixed_generic.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="src\speex\resample_neon.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="src\speex\resample_sse.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="src\speex\speex_config_types.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="src\speex\speex_resampler.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="src\speex\stack_alloc.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="src\speex\arch.h">
<Filter>speex</Filter>
</ClInclude>
<ClInclude Include="include\cubeb\cubeb_export.h" />
<ClInclude Include="src\cubeb-internal.h" />
<ClInclude Include="src\cubeb-speex-resampler.h" />
<ClInclude Include="src\cubeb_array_queue.h" />
<ClInclude Include="src\cubeb_assert.h" />
<ClInclude Include="src\cubeb_log.h" />
<ClInclude Include="src\cubeb_mixer.h" />
<ClInclude Include="src\cubeb_resampler.h" />
<ClInclude Include="src\cubeb_resampler_internal.h" />
<ClInclude Include="src\cubeb_ring_array.h" />
<ClInclude Include="src\cubeb_ringbuffer.h" />
<ClInclude Include="src\cubeb_strings.h" />
<ClInclude Include="src\cubeb_utils.h" />
<ClInclude Include="src\cubeb_utils_win.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="speex">
<UniqueIdentifier>{a22ed8dc-2384-4a96-bd3b-2370d6d7cd62}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\speex\resample.c">
<Filter>speex</Filter>
</ClCompile>
<ClCompile Include="src\cubeb_wasapi.cpp" />
<ClCompile Include="src\cubeb_winmm.c" />
<ClCompile Include="src\cubeb.c" />
<ClCompile Include="src\cubeb_log.cpp" />
<ClCompile Include="src\cubeb_mixer.cpp" />
<ClCompile Include="src\cubeb_resampler.cpp" />
<ClCompile Include="src\cubeb_strings.c" />
<ClCompile Include="src\cubeb_utils.cpp" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,658 @@
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382)
#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382
#include <stdint.h>
#include <stdlib.h>
#include "cubeb_export.h"
#if defined(__cplusplus)
extern "C" {
#endif
/** @mainpage
@section intro Introduction
This is the documentation for the <tt>libcubeb</tt> C API.
<tt>libcubeb</tt> is a callback-based audio API library allowing the
authoring of portable multiplatform audio playback and recording.
@section example Example code
This example shows how to create a duplex stream that pipes the microphone
to the speakers, with minimal latency and the proper sample-rate for the
platform.
@code
cubeb * app_ctx;
cubeb_init(&app_ctx, "Example Application", NULL);
int rv;
uint32_t rate;
uint32_t latency_frames;
uint64_t ts;
rv = cubeb_get_preferred_sample_rate(app_ctx, &rate);
if (rv != CUBEB_OK) {
fprintf(stderr, "Could not get preferred sample-rate");
return rv;
}
cubeb_stream_params output_params;
output_params.format = CUBEB_SAMPLE_FLOAT32NE;
output_params.rate = rate;
output_params.channels = 2;
output_params.layout = CUBEB_LAYOUT_UNDEFINED;
output_params.prefs = CUBEB_STREAM_PREF_NONE;
rv = cubeb_get_min_latency(app_ctx, &output_params, &latency_frames);
if (rv != CUBEB_OK) {
fprintf(stderr, "Could not get minimum latency");
return rv;
}
cubeb_stream_params input_params;
input_params.format = CUBEB_SAMPLE_FLOAT32NE;
input_params.rate = rate;
input_params.channels = 1;
input_params.layout = CUBEB_LAYOUT_UNDEFINED;
input_params.prefs = CUBEB_STREAM_PREF_NONE;
cubeb_stream * stm;
rv = cubeb_stream_init(app_ctx, &stm, "Example Stream 1",
NULL, &input_params,
NULL, &output_params,
latency_frames,
data_cb, state_cb,
NULL);
if (rv != CUBEB_OK) {
fprintf(stderr, "Could not open the stream");
return rv;
}
rv = cubeb_stream_start(stm);
if (rv != CUBEB_OK) {
fprintf(stderr, "Could not start the stream");
return rv;
}
for (;;) {
cubeb_stream_get_position(stm, &ts);
printf("time=%llu\n", ts);
sleep(1);
}
rv = cubeb_stream_stop(stm);
if (rv != CUBEB_OK) {
fprintf(stderr, "Could not stop the stream");
return rv;
}
cubeb_stream_destroy(stm);
cubeb_destroy(app_ctx);
@endcode
@code
long data_cb(cubeb_stream * stm, void * user,
const void * input_buffer, void * output_buffer, long nframes)
{
const float * in = input_buffer;
float * out = output_buffer;
for (int i = 0; i < nframes; ++i) {
for (int c = 0; c < 2; ++c) {
out[2 * i + c] = in[i];
}
}
return nframes;
}
@endcode
@code
void state_cb(cubeb_stream * stm, void * user, cubeb_state state)
{
printf("state=%d\n", state);
}
@endcode
*/
/** @file
The <tt>libcubeb</tt> C API. */
typedef struct cubeb cubeb; /**< Opaque handle referencing the application state. */
typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */
/** Sample format enumeration. */
typedef enum {
/**< Little endian 16-bit signed PCM. */
CUBEB_SAMPLE_S16LE,
/**< Big endian 16-bit signed PCM. */
CUBEB_SAMPLE_S16BE,
/**< Little endian 32-bit IEEE floating point PCM. */
CUBEB_SAMPLE_FLOAT32LE,
/**< Big endian 32-bit IEEE floating point PCM. */
CUBEB_SAMPLE_FLOAT32BE,
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
/**< Native endian 16-bit signed PCM. */
CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16BE,
/**< Native endian 32-bit IEEE floating point PCM. */
CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32BE
#else
/**< Native endian 16-bit signed PCM. */
CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16LE,
/**< Native endian 32-bit IEEE floating point PCM. */
CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32LE
#endif
} cubeb_sample_format;
/** An opaque handle used to refer a particular input or output device
* across calls. */
typedef void const * cubeb_devid;
/** Level (verbosity) of logging for a particular cubeb context. */
typedef enum {
CUBEB_LOG_DISABLED = 0, /** < Logging disabled */
CUBEB_LOG_NORMAL = 1, /**< Logging lifetime operation (creation/destruction). */
CUBEB_LOG_VERBOSE = 2, /**< Verbose logging of callbacks, can have performance implications. */
} cubeb_log_level;
typedef enum {
CHANNEL_UNKNOWN = 0,
CHANNEL_FRONT_LEFT = 1 << 0,
CHANNEL_FRONT_RIGHT = 1 << 1,
CHANNEL_FRONT_CENTER = 1 << 2,
CHANNEL_LOW_FREQUENCY = 1 << 3,
CHANNEL_BACK_LEFT = 1 << 4,
CHANNEL_BACK_RIGHT = 1 << 5,
CHANNEL_FRONT_LEFT_OF_CENTER = 1 << 6,
CHANNEL_FRONT_RIGHT_OF_CENTER = 1 << 7,
CHANNEL_BACK_CENTER = 1 << 8,
CHANNEL_SIDE_LEFT = 1 << 9,
CHANNEL_SIDE_RIGHT = 1 << 10,
CHANNEL_TOP_CENTER = 1 << 11,
CHANNEL_TOP_FRONT_LEFT = 1 << 12,
CHANNEL_TOP_FRONT_CENTER = 1 << 13,
CHANNEL_TOP_FRONT_RIGHT = 1 << 14,
CHANNEL_TOP_BACK_LEFT = 1 << 15,
CHANNEL_TOP_BACK_CENTER = 1 << 16,
CHANNEL_TOP_BACK_RIGHT = 1 << 17
} cubeb_channel;
typedef uint32_t cubeb_channel_layout;
// Some common layout definitions.
enum {
CUBEB_LAYOUT_UNDEFINED = 0, // Indicate the speaker's layout is undefined.
CUBEB_LAYOUT_MONO = CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_MONO_LFE = CUBEB_LAYOUT_MONO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_STEREO = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT,
CUBEB_LAYOUT_STEREO_LFE = CUBEB_LAYOUT_STEREO | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F =
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F_LFE = CUBEB_LAYOUT_3F | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F1 =
CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_2F1_LFE = CUBEB_LAYOUT_2F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F1 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_BACK_CENTER,
CUBEB_LAYOUT_3F1_LFE = CUBEB_LAYOUT_3F1 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_2F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_2F2_LFE = CUBEB_LAYOUT_2F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_QUAD = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT,
CUBEB_LAYOUT_QUAD_LFE = CUBEB_LAYOUT_QUAD | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2 = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F2_LFE = CUBEB_LAYOUT_3F2 | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F2_BACK = CUBEB_LAYOUT_QUAD | CHANNEL_FRONT_CENTER,
CUBEB_LAYOUT_3F2_LFE_BACK = CUBEB_LAYOUT_3F2_BACK | CHANNEL_LOW_FREQUENCY,
CUBEB_LAYOUT_3F3R_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_CENTER | CHANNEL_SIDE_LEFT |
CHANNEL_SIDE_RIGHT,
CUBEB_LAYOUT_3F4_LFE = CHANNEL_FRONT_LEFT | CHANNEL_FRONT_RIGHT |
CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
};
/** Miscellaneous stream preferences. */
typedef enum {
CUBEB_STREAM_PREF_NONE = 0x00, /**< No stream preferences are requested. */
CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
specified on the input params and an
output device to loopback from should
be passed in place of an input device. */
CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
default device on OS
changes. */
CUBEB_STREAM_PREF_VOICE = 0x04 /**< This stream is going to transport voice data.
Depending on the backend and platform, this can
change the audio input or output devices
selected, as well as the quality of the stream,
for example to accomodate bluetooth SCO modes on
bluetooth devices. */
} cubeb_stream_prefs;
/** Stream format initialization parameters. */
typedef struct {
cubeb_sample_format format; /**< Requested sample format. One of
#cubeb_sample_format. */
uint32_t rate; /**< Requested sample rate. Valid range is [1000, 192000]. */
uint32_t channels; /**< Requested channel count. Valid range is [1, 8]. */
cubeb_channel_layout layout; /**< Requested channel layout. This must be consistent with the provided channels. CUBEB_LAYOUT_UNDEFINED if unknown */
cubeb_stream_prefs prefs; /**< Requested preferences. */
} cubeb_stream_params;
/** Audio device description */
typedef struct {
char * output_name; /**< The name of the output device */
char * input_name; /**< The name of the input device */
} cubeb_device;
/** Stream states signaled via state_callback. */
typedef enum {
CUBEB_STATE_STARTED, /**< Stream started. */
CUBEB_STATE_STOPPED, /**< Stream stopped. */
CUBEB_STATE_DRAINED, /**< Stream drained. */
CUBEB_STATE_ERROR /**< Stream disabled due to error. */
} cubeb_state;
/** Result code enumeration. */
enum {
CUBEB_OK = 0, /**< Success. */
CUBEB_ERROR = -1, /**< Unclassified error. */
CUBEB_ERROR_INVALID_FORMAT = -2, /**< Unsupported #cubeb_stream_params requested. */
CUBEB_ERROR_INVALID_PARAMETER = -3, /**< Invalid parameter specified. */
CUBEB_ERROR_NOT_SUPPORTED = -4, /**< Optional function not implemented in current backend. */
CUBEB_ERROR_DEVICE_UNAVAILABLE = -5 /**< Device specified by #cubeb_devid not available. */
};
/**
* Whether a particular device is an input device (e.g. a microphone), or an
* output device (e.g. headphones). */
typedef enum {
CUBEB_DEVICE_TYPE_UNKNOWN,
CUBEB_DEVICE_TYPE_INPUT,
CUBEB_DEVICE_TYPE_OUTPUT
} cubeb_device_type;
/**
* The state of a device.
*/
typedef enum {
CUBEB_DEVICE_STATE_DISABLED, /**< The device has been disabled at the system level. */
CUBEB_DEVICE_STATE_UNPLUGGED, /**< The device is enabled, but nothing is plugged into it. */
CUBEB_DEVICE_STATE_ENABLED /**< The device is enabled. */
} cubeb_device_state;
/**
* Architecture specific sample type.
*/
typedef enum {
CUBEB_DEVICE_FMT_S16LE = 0x0010, /**< 16-bit integers, Little Endian. */
CUBEB_DEVICE_FMT_S16BE = 0x0020, /**< 16-bit integers, Big Endian. */
CUBEB_DEVICE_FMT_F32LE = 0x1000, /**< 32-bit floating point, Little Endian. */
CUBEB_DEVICE_FMT_F32BE = 0x2000 /**< 32-bit floating point, Big Endian. */
} cubeb_device_fmt;
#if defined(WORDS_BIGENDIAN) || defined(__BIG_ENDIAN__)
/** 16-bit integers, native endianess, when on a Big Endian environment. */
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16BE
/** 32-bit floating points, native endianess, when on a Big Endian environment. */
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32BE
#else
/** 16-bit integers, native endianess, when on a Little Endian environment. */
#define CUBEB_DEVICE_FMT_S16NE CUBEB_DEVICE_FMT_S16LE
/** 32-bit floating points, native endianess, when on a Little Endian
* environment. */
#define CUBEB_DEVICE_FMT_F32NE CUBEB_DEVICE_FMT_F32LE
#endif
/** All the 16-bit integers types. */
#define CUBEB_DEVICE_FMT_S16_MASK (CUBEB_DEVICE_FMT_S16LE | CUBEB_DEVICE_FMT_S16BE)
/** All the 32-bit floating points types. */
#define CUBEB_DEVICE_FMT_F32_MASK (CUBEB_DEVICE_FMT_F32LE | CUBEB_DEVICE_FMT_F32BE)
/** All the device formats types. */
#define CUBEB_DEVICE_FMT_ALL (CUBEB_DEVICE_FMT_S16_MASK | CUBEB_DEVICE_FMT_F32_MASK)
/** Channel type for a `cubeb_stream`. Depending on the backend and platform
* used, this can control inter-stream interruption, ducking, and volume
* control.
*/
typedef enum {
CUBEB_DEVICE_PREF_NONE = 0x00,
CUBEB_DEVICE_PREF_MULTIMEDIA = 0x01,
CUBEB_DEVICE_PREF_VOICE = 0x02,
CUBEB_DEVICE_PREF_NOTIFICATION = 0x04,
CUBEB_DEVICE_PREF_ALL = 0x0F
} cubeb_device_pref;
/** This structure holds the characteristics
* of an input or output audio device. It is obtained using
* `cubeb_enumerate_devices`, which returns these structures via
* `cubeb_device_collection` and must be destroyed via
* `cubeb_device_collection_destroy`. */
typedef struct {
cubeb_devid devid; /**< Device identifier handle. */
char const * device_id; /**< Device identifier which might be presented in a UI. */
char const * friendly_name; /**< Friendly device name which might be presented in a UI. */
char const * group_id; /**< Two devices have the same group identifier if they belong to the same physical device; for example a headset and microphone. */
char const * vendor_name; /**< Optional vendor name, may be NULL. */
cubeb_device_type type; /**< Type of device (Input/Output). */
cubeb_device_state state; /**< State of device disabled/enabled/unplugged. */
cubeb_device_pref preferred;/**< Preferred device. */
cubeb_device_fmt format; /**< Sample format supported. */
cubeb_device_fmt default_format; /**< The default sample format for this device. */
uint32_t max_channels; /**< Channels. */
uint32_t default_rate; /**< Default/Preferred sample rate. */
uint32_t max_rate; /**< Maximum sample rate supported. */
uint32_t min_rate; /**< Minimum sample rate supported. */
uint32_t latency_lo; /**< Lowest possible latency in frames. */
uint32_t latency_hi; /**< Higest possible latency in frames. */
} cubeb_device_info;
/** Device collection.
* Returned by `cubeb_enumerate_devices` and destroyed by
* `cubeb_device_collection_destroy`. */
typedef struct {
cubeb_device_info * device; /**< Array of pointers to device info. */
size_t count; /**< Device count in collection. */
} cubeb_device_collection;
/** User supplied data callback.
- Calling other cubeb functions from this callback is unsafe.
- The code in the callback should be non-blocking.
- Returning less than the number of frames this callback asks for or
provides puts the stream in drain mode. This callback will not be called
again, and the state callback will be called with CUBEB_STATE_DRAINED when
all the frames have been output.
@param stream The stream for which this callback fired.
@param user_ptr The pointer passed to cubeb_stream_init.
@param input_buffer A pointer containing the input data, or nullptr
if this is an output-only stream.
@param output_buffer A pointer to a buffer to be filled with audio samples,
or nullptr if this is an input-only stream.
@param nframes The number of frames of the two buffer.
@retval If the stream has output, this is the number of frames written to
the output buffer. In this case, if this number is less than
nframes then the stream will start to drain. If the stream is
input only, then returning nframes indicates data has been read.
In this case, a value less than nframes will result in the stream
being stopped.
@retval CUBEB_ERROR on error, in which case the data callback will stop
and the stream will enter a shutdown state. */
typedef long (* cubeb_data_callback)(cubeb_stream * stream,
void * user_ptr,
void const * input_buffer,
void * output_buffer,
long nframes);
/** User supplied state callback.
@param stream The stream for this this callback fired.
@param user_ptr The pointer passed to cubeb_stream_init.
@param state The new state of the stream. */
typedef void (* cubeb_state_callback)(cubeb_stream * stream,
void * user_ptr,
cubeb_state state);
/**
* User supplied callback called when the underlying device changed.
* @param user The pointer passed to cubeb_stream_init. */
typedef void (* cubeb_device_changed_callback)(void * user_ptr);
/**
* User supplied callback called when the underlying device collection changed.
* @param context A pointer to the cubeb context.
* @param user_ptr The pointer passed to cubeb_register_device_collection_changed. */
typedef void (* cubeb_device_collection_changed_callback)(cubeb * context,
void * user_ptr);
/** User supplied callback called when a message needs logging. */
typedef void (* cubeb_log_callback)(char const * fmt, ...);
/** Initialize an application context. This will perform any library or
application scoped initialization.
Note: On Windows platforms, COM must be initialized in MTA mode on
any thread that will call the cubeb API.
@param context A out param where an opaque pointer to the application
context will be returned.
@param context_name A name for the context. Depending on the platform this
can appear in different locations.
@param backend_name The name of the cubeb backend user desires to select.
Accepted values self-documented in cubeb.c: init_oneshot
If NULL, a default ordering is used for backend choice.
A valid choice overrides all other possible backends,
so long as the backend was included at compile time.
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR in case of error, for example because the host
has no audio hardware. */
CUBEB_EXPORT int cubeb_init(cubeb ** context, char const * context_name,
char const * backend_name);
/** Get a read-only string identifying this context's current backend.
@param context A pointer to the cubeb context.
@retval Read-only string identifying current backend. */
CUBEB_EXPORT char const * cubeb_get_backend_id(cubeb * context);
/** Get the maximum possible number of channels.
@param context A pointer to the cubeb context.
@param max_channels The maximum number of channels.
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels);
/** Get the minimal latency value, in frames, that is guaranteed to work
when creating a stream for the specified sample rate. This is platform,
hardware and backend dependent.
@param context A pointer to the cubeb context.
@param params On some backends, the minimum achievable latency depends on
the characteristics of the stream.
@param latency_frames The latency value, in frames, to pass to
cubeb_stream_init.
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_get_min_latency(cubeb * context,
cubeb_stream_params * params,
uint32_t * latency_frames);
/** Get the preferred sample rate for this backend: this is hardware and
platform dependent, and can avoid resampling, and/or trigger fastpaths.
@param context A pointer to the cubeb context.
@param rate The samplerate (in Hz) the current configuration prefers.
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate);
/** Destroy an application context. This must be called after all stream have
* been destroyed.
@param context A pointer to the cubeb context.*/
CUBEB_EXPORT void cubeb_destroy(cubeb * context);
/** Initialize a stream associated with the supplied application context.
@param context A pointer to the cubeb context.
@param stream An out parameter to be filled with the an opaque pointer to a
cubeb stream.
@param stream_name A name for this stream.
@param input_device Device for the input side of the stream. If NULL the
default input device is used.
@param input_stream_params Parameters for the input side of the stream, or
NULL if this stream is output only.
@param output_device Device for the output side of the stream. If NULL the
default output device is used.
@param output_stream_params Parameters for the output side of the stream, or
NULL if this stream is input only.
@param latency_frames Stream latency in frames. Valid range
is [1, 96000].
@param data_callback Will be called to preroll data before playback is
started by cubeb_stream_start.
@param state_callback A pointer to a state callback.
@param user_ptr A pointer that will be passed to the callbacks. This pointer
must outlive the life time of the stream.
@retval CUBEB_OK
@retval CUBEB_ERROR
@retval CUBEB_ERROR_INVALID_FORMAT
@retval CUBEB_ERROR_DEVICE_UNAVAILABLE */
CUBEB_EXPORT int cubeb_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
uint32_t latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
/** Destroy a stream. `cubeb_stream_stop` MUST be called before destroying a
stream.
@param stream The stream to destroy. */
CUBEB_EXPORT void cubeb_stream_destroy(cubeb_stream * stream);
/** Start playback.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_start(cubeb_stream * stream);
/** Stop playback.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_stop(cubeb_stream * stream);
/** Reset stream to the default device.
@param stream
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_reset_default_device(cubeb_stream * stream);
/** Get the current stream playback position.
@param stream
@param position Playback position in frames.
@retval CUBEB_OK
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position);
/** Get the latency for this stream, in frames. This is the number of frames
between the time cubeb acquires the data in the callback and the listener
can hear the sound.
@param stream
@param latency Current approximate stream latency in frames.
@retval CUBEB_OK
@retval CUBEB_ERROR_NOT_SUPPORTED
@retval CUBEB_ERROR */
CUBEB_EXPORT int cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency);
/** Set the volume for a stream.
@param stream the stream for which to adjust the volume.
@param volume a float between 0.0 (muted) and 1.0 (maximum volume)
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER volume is outside [0.0, 1.0] or
stream is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_set_volume(cubeb_stream * stream, float volume);
/** Get the current output device for this stream.
@param stm the stream for which to query the current output device
@param device a pointer in which the current output device will be stored.
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if either stm, device or count are
invalid pointers
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_get_current_device(cubeb_stream * stm,
cubeb_device ** const device);
/** Destroy a cubeb_device structure.
@param stream the stream passed in cubeb_stream_get_current_device
@param devices the devices to destroy
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if devices is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_device_destroy(cubeb_stream * stream,
cubeb_device * devices);
/** Set a callback to be notified when the output device changes.
@param stream the stream for which to set the callback.
@param device_changed_callback a function called whenever the device has
changed. Passing NULL allow to unregister a function
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if either stream or
device_changed_callback are invalid pointers.
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
/** Return the user data pointer registered with the stream with cubeb_stream_init.
@param stream the stream for which to retrieve user data pointer.
@retval user data pointer */
CUBEB_EXPORT void * cubeb_stream_user_ptr(cubeb_stream * stream);
/** Returns enumerated devices.
@param context
@param devtype device type to include
@param collection output collection. Must be destroyed with cubeb_device_collection_destroy
@retval CUBEB_OK in case of success
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection * collection);
/** Destroy a cubeb_device_collection, and its `cubeb_device_info`.
@param context
@param collection collection to destroy
@retval CUBEB_OK
@retval CUBEB_ERROR_INVALID_PARAMETER if collection is an invalid pointer */
CUBEB_EXPORT int cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection);
/** Registers a callback which is called when the system detects
a new device or a device is removed.
@param context
@param devtype device type to include. Different callbacks and user pointers
can be registered for each devtype. The hybrid devtype
`CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT` is also valid
and will register the provided callback and user pointer in both sides.
@param callback a function called whenever the system device list changes.
Passing NULL allow to unregister a function. You have to unregister
first before you register a new callback.
@param user_ptr pointer to user specified data which will be present in
subsequent callbacks.
@retval CUBEB_ERROR_NOT_SUPPORTED */
CUBEB_EXPORT int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr);
/** Set a callback to be called with a message.
@param log_level CUBEB_LOG_VERBOSE, CUBEB_LOG_NORMAL.
@param log_callback A function called with a message when there is
something to log. Pass NULL to unregister.
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR_INVALID_PARAMETER if either context or log_callback are
invalid pointers, or if level is not
in cubeb_log_level. */
CUBEB_EXPORT int cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback);
#if defined(__cplusplus)
}
#endif
#endif /* CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 */

View file

@ -0,0 +1,12 @@
#ifndef CUBEB_EXPORT_H
#define CUBEB_EXPORT_H
#define CUBEB_EXPORT
#define CUBEB_NO_EXPORT
#ifdef WIN32
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "avrt.lib")
#endif
#endif

View file

@ -0,0 +1,81 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdint.h>
/*
* The following definitions are copied from the android sources. Only the
* relevant enum member and values needed are copied.
*/
/*
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/utils/Errors.h
*/
typedef int32_t status_t;
/*
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioTrack.h
*/
struct Buffer {
uint32_t flags;
int channelCount;
int format;
size_t frameCount;
size_t size;
union {
void* raw;
short* i16;
int8_t* i8;
};
};
enum event_type {
EVENT_MORE_DATA = 0,
EVENT_UNDERRUN = 1,
EVENT_LOOP_END = 2,
EVENT_MARKER = 3,
EVENT_NEW_POS = 4,
EVENT_BUFFER_END = 5
};
/**
* From https://android.googlesource.com/platform/frameworks/base/+/android-2.2.3_r2.1/include/media/AudioSystem.h
* and
* https://android.googlesource.com/platform/system/core/+/android-4.2.2_r1/include/system/audio.h
*/
#define AUDIO_STREAM_TYPE_MUSIC 3
enum {
AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS = 0x1,
AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS = 0x2,
AUDIO_CHANNEL_OUT_MONO_ICS = AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS,
AUDIO_CHANNEL_OUT_STEREO_ICS = (AUDIO_CHANNEL_OUT_FRONT_LEFT_ICS | AUDIO_CHANNEL_OUT_FRONT_RIGHT_ICS)
} AudioTrack_ChannelMapping_ICS;
enum {
AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy = 0x4,
AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy = 0x8,
AUDIO_CHANNEL_OUT_MONO_Legacy = AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy,
AUDIO_CHANNEL_OUT_STEREO_Legacy = (AUDIO_CHANNEL_OUT_FRONT_LEFT_Legacy | AUDIO_CHANNEL_OUT_FRONT_RIGHT_Legacy)
} AudioTrack_ChannelMapping_Legacy;
typedef enum {
AUDIO_FORMAT_PCM = 0x00000000,
AUDIO_FORMAT_PCM_SUB_16_BIT = 0x1,
AUDIO_FORMAT_PCM_16_BIT = (AUDIO_FORMAT_PCM | AUDIO_FORMAT_PCM_SUB_16_BIT),
} AudioTrack_SampleType;

View file

@ -0,0 +1,76 @@
#ifndef _CUBEB_OUTPUT_LATENCY_H_
#define _CUBEB_OUTPUT_LATENCY_H_
#include <stdbool.h>
#include "cubeb_media_library.h"
#include "../cubeb-jni.h"
struct output_latency_function {
media_lib * from_lib;
cubeb_jni * from_jni;
int version;
};
typedef struct output_latency_function output_latency_function;
const int ANDROID_JELLY_BEAN_MR1_4_2 = 17;
output_latency_function *
cubeb_output_latency_load_method(int version)
{
output_latency_function * ol = NULL;
ol = calloc(1, sizeof(output_latency_function));
ol->version = version;
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
ol->from_jni = cubeb_jni_init();
return ol;
}
ol->from_lib = cubeb_load_media_library();
return ol;
}
bool
cubeb_output_latency_method_is_loaded(output_latency_function * ol)
{
assert(ol);
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
return !!ol->from_jni;
}
return !!ol->from_lib;
}
void
cubeb_output_latency_unload_method(output_latency_function * ol)
{
if (!ol) {
return;
}
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_jni) {
cubeb_jni_destroy(ol->from_jni);
}
if (ol->version <= ANDROID_JELLY_BEAN_MR1_4_2 && ol->from_lib) {
cubeb_close_media_library(ol->from_lib);
}
free(ol);
}
uint32_t
cubeb_get_output_latency(output_latency_function * ol)
{
assert(cubeb_output_latency_method_is_loaded(ol));
if (ol->version > ANDROID_JELLY_BEAN_MR1_4_2){
return cubeb_get_output_latency_from_jni(ol->from_jni);
}
return cubeb_get_output_latency_from_media_library(ol->from_lib);
}
#endif // _CUBEB_OUTPUT_LATENCY_H_

View file

@ -0,0 +1,62 @@
#ifndef _CUBEB_MEDIA_LIBRARY_H_
#define _CUBEB_MEDIA_LIBRARY_H_
struct media_lib {
void * libmedia;
int32_t (* get_output_latency)(uint32_t * latency, int stream_type);
};
typedef struct media_lib media_lib;
media_lib *
cubeb_load_media_library()
{
media_lib ml = {0};
ml.libmedia = dlopen("libmedia.so", RTLD_LAZY);
if (!ml.libmedia) {
return NULL;
}
// Get the latency, in ms, from AudioFlinger. First, try the most recent signature.
// status_t AudioSystem::getOutputLatency(uint32_t* latency, audio_stream_type_t streamType)
ml.get_output_latency =
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t");
if (!ml.get_output_latency) {
// In case of failure, try the signature from legacy version.
// status_t AudioSystem::getOutputLatency(uint32_t* latency, int streamType)
ml.get_output_latency =
dlsym(ml.libmedia, "_ZN7android11AudioSystem16getOutputLatencyEPji");
if (!ml.get_output_latency) {
return NULL;
}
}
media_lib * rv = NULL;
rv = calloc(1, sizeof(media_lib));
assert(rv);
*rv = ml;
return rv;
}
void
cubeb_close_media_library(media_lib * ml)
{
dlclose(ml->libmedia);
ml->libmedia = NULL;
ml->get_output_latency = NULL;
free(ml);
}
uint32_t
cubeb_get_output_latency_from_media_library(media_lib * ml)
{
uint32_t latency = 0;
const int audio_stream_type_music = 3;
int32_t r = ml->get_output_latency(&latency, audio_stream_type_music);
if (r) {
return 0;
}
return latency;
}
#endif // _CUBEB_MEDIA_LIBRARY_H_

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This file is similar to the file "OpenSLES_AndroidConfiguration.h" found in
* the Android NDK, but removes the #ifdef __cplusplus defines, so we can keep
* using a C compiler in cubeb.
*/
#ifndef OPENSL_ES_ANDROIDCONFIGURATION_H_
#define OPENSL_ES_ANDROIDCONFIGURATION_H_
/*---------------------------------------------------------------------------*/
/* Android AudioRecorder configuration */
/*---------------------------------------------------------------------------*/
/** Audio recording preset */
/** Audio recording preset key */
#define SL_ANDROID_KEY_RECORDING_PRESET ((const SLchar*) "androidRecordingPreset")
/** Audio recording preset values */
/** preset "none" cannot be set, it is used to indicate the current settings
* do not match any of the presets. */
#define SL_ANDROID_RECORDING_PRESET_NONE ((SLuint32) 0x00000000)
/** generic recording configuration on the platform */
#define SL_ANDROID_RECORDING_PRESET_GENERIC ((SLuint32) 0x00000001)
/** uses the microphone audio source with the same orientation as the camera
* if available, the main device microphone otherwise */
#define SL_ANDROID_RECORDING_PRESET_CAMCORDER ((SLuint32) 0x00000002)
/** uses the main microphone tuned for voice recognition */
#define SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION ((SLuint32) 0x00000003)
/** uses the main microphone tuned for audio communications */
#define SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION ((SLuint32) 0x00000004)
/** uses the main microphone unprocessed */
#define SL_ANDROID_RECORDING_PRESET_UNPROCESSED ((SLuint32) 0x00000005)
/*---------------------------------------------------------------------------*/
/* Android AudioPlayer configuration */
/*---------------------------------------------------------------------------*/
/** Audio playback stream type */
/** Audio playback stream type key */
#define SL_ANDROID_KEY_STREAM_TYPE ((const SLchar*) "androidPlaybackStreamType")
/** Audio playback stream type values */
/* same as android.media.AudioManager.STREAM_VOICE_CALL */
#define SL_ANDROID_STREAM_VOICE ((SLint32) 0x00000000)
/* same as android.media.AudioManager.STREAM_SYSTEM */
#define SL_ANDROID_STREAM_SYSTEM ((SLint32) 0x00000001)
/* same as android.media.AudioManager.STREAM_RING */
#define SL_ANDROID_STREAM_RING ((SLint32) 0x00000002)
/* same as android.media.AudioManager.STREAM_MUSIC */
#define SL_ANDROID_STREAM_MEDIA ((SLint32) 0x00000003)
/* same as android.media.AudioManager.STREAM_ALARM */
#define SL_ANDROID_STREAM_ALARM ((SLint32) 0x00000004)
/* same as android.media.AudioManager.STREAM_NOTIFICATION */
#define SL_ANDROID_STREAM_NOTIFICATION ((SLint32) 0x00000005)
/*---------------------------------------------------------------------------*/
/* Android AudioPlayer and AudioRecorder configuration */
/*---------------------------------------------------------------------------*/
/** Audio Performance mode.
* Performance mode tells the framework how to configure the audio path
* for a player or recorder according to application performance and
* functional requirements.
* It affects the output or input latency based on acceptable tradeoffs on
* battery drain and use of pre or post processing effects.
* Performance mode should be set before realizing the object and should be
* read after realizing the object to check if the requested mode could be
* granted or not.
*/
/** Audio Performance mode key */
#define SL_ANDROID_KEY_PERFORMANCE_MODE ((const SLchar*) "androidPerformanceMode")
/** Audio performance values */
/* No specific performance requirement. Allows HW and SW pre/post processing. */
#define SL_ANDROID_PERFORMANCE_NONE ((SLuint32) 0x00000000)
/* Priority given to latency. No HW or software pre/post processing.
* This is the default if no performance mode is specified. */
#define SL_ANDROID_PERFORMANCE_LATENCY ((SLuint32) 0x00000001)
/* Priority given to latency while still allowing HW pre and post processing. */
#define SL_ANDROID_PERFORMANCE_LATENCY_EFFECTS ((SLuint32) 0x00000002)
/* Priority given to power saving if latency is not a concern.
* Allows HW and SW pre/post processing. */
#define SL_ANDROID_PERFORMANCE_POWER_SAVING ((SLuint32) 0x00000003)
#endif /* OPENSL_ES_ANDROIDCONFIGURATION_H_ */

View file

@ -0,0 +1,79 @@
/*
* Copyright © 2013 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5)
#define CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5
#include "cubeb/cubeb.h"
#include "cubeb_log.h"
#include "cubeb_assert.h"
#include <stdio.h>
#include <string.h>
#ifdef __clang__
#ifndef CLANG_ANALYZER_NORETURN
#if __has_feature(attribute_analyzer_noreturn)
#define CLANG_ANALYZER_NORETURN __attribute__((analyzer_noreturn))
#else
#define CLANG_ANALYZER_NORETURN
#endif // ifndef CLANG_ANALYZER_NORETURN
#endif // __has_feature(attribute_analyzer_noreturn)
#else // __clang__
#define CLANG_ANALYZER_NORETURN
#endif
#if defined(__cplusplus)
extern "C" {
#endif
#if defined(__cplusplus)
}
#endif
struct cubeb_ops {
int (* init)(cubeb ** context, char const * context_name);
char const * (* get_backend_id)(cubeb * context);
int (* get_max_channel_count)(cubeb * context, uint32_t * max_channels);
int (* get_min_latency)(cubeb * context,
cubeb_stream_params params,
uint32_t * latency_ms);
int (* get_preferred_sample_rate)(cubeb * context, uint32_t * rate);
int (* enumerate_devices)(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection);
int (* device_collection_destroy)(cubeb * context,
cubeb_device_collection * collection);
void (* destroy)(cubeb * context);
int (* stream_init)(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr);
void (* stream_destroy)(cubeb_stream * stream);
int (* stream_start)(cubeb_stream * stream);
int (* stream_stop)(cubeb_stream * stream);
int (* stream_reset_default_device)(cubeb_stream * stream);
int (* stream_get_position)(cubeb_stream * stream, uint64_t * position);
int (* stream_get_latency)(cubeb_stream * stream, uint32_t * latency);
int (* stream_set_volume)(cubeb_stream * stream, float volumes);
int (* stream_get_current_device)(cubeb_stream * stream,
cubeb_device ** const device);
int (* stream_device_destroy)(cubeb_stream * stream,
cubeb_device * device);
int (* stream_register_device_changed_callback)(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback);
int (* register_device_collection_changed)(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr);
};
#endif /* CUBEB_INTERNAL_0eb56756_4e20_4404_a76d_42bf88cd15a5 */

View file

@ -0,0 +1,30 @@
#ifndef _CUBEB_JNI_INSTANCES_H_
#define _CUBEB_JNI_INSTANCES_H_
/*
* The methods in this file offer a way to pass in the required
* JNI instances in the cubeb library. By default they return NULL.
* In this case part of the cubeb API that depends on JNI
* will return CUBEB_ERROR_NOT_SUPPORTED. Currently only one
* method depends on that:
*
* cubeb_stream_get_position()
*
* Users that want to use that cubeb API method must "override"
* the methods bellow to return a valid instance of JavaVM
* and application's Context object.
* */
JNIEnv *
cubeb_get_jni_env_for_thread()
{
return nullptr;
}
jobject
cubeb_jni_get_context_instance()
{
return nullptr;
}
#endif //_CUBEB_JNI_INSTANCES_H_

View file

@ -0,0 +1,68 @@
#include "jni.h"
#include <assert.h>
#include "cubeb-jni-instances.h"
#define AUDIO_STREAM_TYPE_MUSIC 3
struct cubeb_jni {
jobject s_audio_manager_obj = nullptr;
jclass s_audio_manager_class = nullptr;
jmethodID s_get_output_latency_id = nullptr;
};
extern "C"
cubeb_jni *
cubeb_jni_init()
{
jobject ctx_obj = cubeb_jni_get_context_instance();
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
if (!jni_env || !ctx_obj) {
return nullptr;
}
cubeb_jni * cubeb_jni_ptr = new cubeb_jni;
assert(cubeb_jni_ptr);
// Find the audio manager object and make it global to call it from another method
jclass context_class = jni_env->FindClass("android/content/Context");
jfieldID audio_service_field = jni_env->GetStaticFieldID(context_class, "AUDIO_SERVICE", "Ljava/lang/String;");
jstring jstr = (jstring)jni_env->GetStaticObjectField(context_class, audio_service_field);
jmethodID get_system_service_id = jni_env->GetMethodID(context_class, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;");
jobject audio_manager_obj = jni_env->CallObjectMethod(ctx_obj, get_system_service_id, jstr);
cubeb_jni_ptr->s_audio_manager_obj = reinterpret_cast<jobject>(jni_env->NewGlobalRef(audio_manager_obj));
// Make the audio manager class a global reference in order to preserve method id
jclass audio_manager_class = jni_env->FindClass("android/media/AudioManager");
cubeb_jni_ptr->s_audio_manager_class = reinterpret_cast<jclass>(jni_env->NewGlobalRef(audio_manager_class));
cubeb_jni_ptr->s_get_output_latency_id = jni_env->GetMethodID (audio_manager_class, "getOutputLatency", "(I)I");
jni_env->DeleteLocalRef(ctx_obj);
jni_env->DeleteLocalRef(context_class);
jni_env->DeleteLocalRef(jstr);
jni_env->DeleteLocalRef(audio_manager_obj);
jni_env->DeleteLocalRef(audio_manager_class);
return cubeb_jni_ptr;
}
extern "C"
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr)
{
assert(cubeb_jni_ptr);
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
return jni_env->CallIntMethod(cubeb_jni_ptr->s_audio_manager_obj, cubeb_jni_ptr->s_get_output_latency_id, AUDIO_STREAM_TYPE_MUSIC); //param: AudioManager.STREAM_MUSIC
}
extern "C"
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr)
{
assert(cubeb_jni_ptr);
JNIEnv * jni_env = cubeb_get_jni_env_for_thread();
assert(jni_env);
jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_obj);
jni_env->DeleteGlobalRef(cubeb_jni_ptr->s_audio_manager_class);
delete cubeb_jni_ptr;
}

10
dep/cubeb/src/cubeb-jni.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef _CUBEB_JNI_H_
#define _CUBEB_JNI_H_
typedef struct cubeb_jni cubeb_jni;
cubeb_jni * cubeb_jni_init();
int cubeb_get_output_latency_from_jni(cubeb_jni * cubeb_jni_ptr);
void cubeb_jni_destroy(cubeb_jni * cubeb_jni_ptr);
#endif // _CUBEB_JNI_H_

View file

@ -0,0 +1,43 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef _CUBEB_SLES_H_
#define _CUBEB_SLES_H_
#include <SLES/OpenSLES.h>
static SLresult
cubeb_get_sles_engine(SLObjectItf * pEngine,
SLuint32 numOptions,
const SLEngineOption * pEngineOptions,
SLuint32 numInterfaces,
const SLInterfaceID * pInterfaceIds,
const SLboolean * pInterfaceRequired)
{
return slCreateEngine(pEngine,
numOptions,
pEngineOptions,
numInterfaces,
pInterfaceIds,
pInterfaceRequired);
}
static void
cubeb_destroy_sles_engine(SLObjectItf * self)
{
if (*self != NULL) {
(**self)->Destroy(*self);
*self = NULL;
}
}
static SLresult
cubeb_realize_sles_engine(SLObjectItf self)
{
return (*self)->Realize(self, SL_BOOLEAN_FALSE);
}
#endif

View file

@ -0,0 +1 @@
#include <speex/speex_resampler.h>

665
dep/cubeb/src/cubeb.c Normal file
View file

@ -0,0 +1,665 @@
/*
* Copyright © 2013 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#undef NDEBUG
#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#define NELEMS(x) ((int) (sizeof(x) / sizeof(x[0])))
struct cubeb {
struct cubeb_ops * ops;
};
struct cubeb_stream {
/*
* Note: All implementations of cubeb_stream must keep the following
* layout.
*/
struct cubeb * context;
void * user_ptr;
};
#if defined(USE_PULSE)
int pulse_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_PULSE_RUST)
int pulse_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_JACK)
int jack_init (cubeb ** context, char const * context_name);
#endif
#if defined(USE_ALSA)
int alsa_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT)
int audiounit_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOUNIT_RUST)
int audiounit_rust_init(cubeb ** contet, char const * context_name);
#endif
#if defined(USE_WINMM)
int winmm_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_WASAPI)
int wasapi_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SNDIO)
int sndio_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_SUN)
int sun_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_OPENSL)
int opensl_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_AUDIOTRACK)
int audiotrack_init(cubeb ** context, char const * context_name);
#endif
#if defined(USE_KAI)
int kai_init(cubeb ** context, char const * context_name);
#endif
static int
validate_stream_params(cubeb_stream_params * input_stream_params,
cubeb_stream_params * output_stream_params)
{
XASSERT(input_stream_params || output_stream_params);
if (output_stream_params) {
if (output_stream_params->rate < 1000 || output_stream_params->rate > 192000 ||
output_stream_params->channels < 1 || output_stream_params->channels > UINT8_MAX) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
if (input_stream_params) {
if (input_stream_params->rate < 1000 || input_stream_params->rate > 192000 ||
input_stream_params->channels < 1 || input_stream_params->channels > 8) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
// Rate and sample format must be the same for input and output, if using a
// duplex stream
if (input_stream_params && output_stream_params) {
if (input_stream_params->rate != output_stream_params->rate ||
input_stream_params->format != output_stream_params->format) {
return CUBEB_ERROR_INVALID_FORMAT;
}
}
cubeb_stream_params * params = input_stream_params ?
input_stream_params : output_stream_params;
switch (params->format) {
case CUBEB_SAMPLE_S16LE:
case CUBEB_SAMPLE_S16BE:
case CUBEB_SAMPLE_FLOAT32LE:
case CUBEB_SAMPLE_FLOAT32BE:
return CUBEB_OK;
}
return CUBEB_ERROR_INVALID_FORMAT;
}
static int
validate_latency(int latency)
{
if (latency < 1 || latency > 96000) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
return CUBEB_OK;
}
int
cubeb_init(cubeb ** context, char const * context_name, char const * backend_name)
{
int (* init_oneshot)(cubeb **, char const *) = NULL;
if (backend_name != NULL) {
if (!strcmp(backend_name, "pulse")) {
#if defined(USE_PULSE)
init_oneshot = pulse_init;
#endif
} else if (!strcmp(backend_name, "pulse-rust")) {
#if defined(USE_PULSE_RUST)
init_oneshot = pulse_rust_init;
#endif
} else if (!strcmp(backend_name, "jack")) {
#if defined(USE_JACK)
init_oneshot = jack_init;
#endif
} else if (!strcmp(backend_name, "alsa")) {
#if defined(USE_ALSA)
init_oneshot = alsa_init;
#endif
} else if (!strcmp(backend_name, "audiounit")) {
#if defined(USE_AUDIOUNIT)
init_oneshot = audiounit_init;
#endif
} else if (!strcmp(backend_name, "audiounit-rust")) {
#if defined(USE_AUDIOUNIT_RUST)
init_oneshot = audiounit_rust_init;
#endif
} else if (!strcmp(backend_name, "wasapi")) {
#if defined(USE_WASAPI)
init_oneshot = wasapi_init;
#endif
} else if (!strcmp(backend_name, "winmm")) {
#if defined(USE_WINMM)
init_oneshot = winmm_init;
#endif
} else if (!strcmp(backend_name, "sndio")) {
#if defined(USE_SNDIO)
init_oneshot = sndio_init;
#endif
} else if (!strcmp(backend_name, "sun")) {
#if defined(USE_SUN)
init_oneshot = sun_init;
#endif
} else if (!strcmp(backend_name, "opensl")) {
#if defined(USE_OPENSL)
init_oneshot = opensl_init;
#endif
} else if (!strcmp(backend_name, "audiotrack")) {
#if defined(USE_AUDIOTRACK)
init_oneshot = audiotrack_init;
#endif
} else if (!strcmp(backend_name, "kai")) {
#if defined(USE_KAI)
init_oneshot = kai_init;
#endif
} else {
/* Already set */
}
}
int (* default_init[])(cubeb **, char const *) = {
/*
* init_oneshot must be at the top to allow user
* to override all other choices
*/
init_oneshot,
#if defined(USE_PULSE_RUST)
pulse_rust_init,
#endif
#if defined(USE_PULSE)
pulse_init,
#endif
#if defined(USE_JACK)
jack_init,
#endif
#if defined(USE_SNDIO)
sndio_init,
#endif
#if defined(USE_ALSA)
alsa_init,
#endif
#if defined(USE_AUDIOUNIT)
audiounit_init,
#endif
#if defined(USE_AUDIOUNIT_RUST)
audiounit_rust_init,
#endif
#if defined(USE_WASAPI)
wasapi_init,
#endif
#if defined(USE_WINMM)
winmm_init,
#endif
#if defined(USE_SUN)
sun_init,
#endif
#if defined(USE_OPENSL)
opensl_init,
#endif
#if defined(USE_AUDIOTRACK)
audiotrack_init,
#endif
#if defined(USE_KAI)
kai_init,
#endif
};
int i;
if (!context) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
#define OK(fn) assert((* context)->ops->fn)
for (i = 0; i < NELEMS(default_init); ++i) {
if (default_init[i] && default_init[i](context, context_name) == CUBEB_OK) {
/* Assert that the minimal API is implemented. */
OK(get_backend_id);
OK(destroy);
OK(stream_init);
OK(stream_destroy);
OK(stream_start);
OK(stream_stop);
OK(stream_get_position);
return CUBEB_OK;
}
}
return CUBEB_ERROR;
}
char const *
cubeb_get_backend_id(cubeb * context)
{
if (!context) {
return NULL;
}
return context->ops->get_backend_id(context);
}
int
cubeb_get_max_channel_count(cubeb * context, uint32_t * max_channels)
{
if (!context || !max_channels) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!context->ops->get_max_channel_count) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return context->ops->get_max_channel_count(context, max_channels);
}
int
cubeb_get_min_latency(cubeb * context, cubeb_stream_params * params, uint32_t * latency_ms)
{
if (!context || !params || !latency_ms) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!context->ops->get_min_latency) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return context->ops->get_min_latency(context, *params, latency_ms);
}
int
cubeb_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
{
if (!context || !rate) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!context->ops->get_preferred_sample_rate) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return context->ops->get_preferred_sample_rate(context, rate);
}
void
cubeb_destroy(cubeb * context)
{
if (!context) {
return;
}
context->ops->destroy(context);
}
int
cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
{
int r;
if (!context || !stream || !data_callback || !state_callback) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if ((r = validate_stream_params(input_stream_params, output_stream_params)) != CUBEB_OK ||
(r = validate_latency(latency)) != CUBEB_OK) {
return r;
}
r = context->ops->stream_init(context, stream, stream_name,
input_device,
input_stream_params,
output_device,
output_stream_params,
latency,
data_callback,
state_callback,
user_ptr);
if (r == CUBEB_ERROR_INVALID_FORMAT) {
LOG("Invalid format, %p %p %d %d",
output_stream_params, input_stream_params,
output_stream_params && output_stream_params->format,
input_stream_params && input_stream_params->format);
}
return r;
}
void
cubeb_stream_destroy(cubeb_stream * stream)
{
if (!stream) {
return;
}
stream->context->ops->stream_destroy(stream);
}
int
cubeb_stream_start(cubeb_stream * stream)
{
if (!stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
return stream->context->ops->stream_start(stream);
}
int
cubeb_stream_stop(cubeb_stream * stream)
{
if (!stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
return stream->context->ops->stream_stop(stream);
}
int
cubeb_stream_reset_default_device(cubeb_stream * stream)
{
if (!stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_reset_default_device) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_reset_default_device(stream);
}
int
cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position)
{
if (!stream || !position) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
return stream->context->ops->stream_get_position(stream, position);
}
int
cubeb_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
{
if (!stream || !latency) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_get_latency) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_get_latency(stream, latency);
}
int
cubeb_stream_set_volume(cubeb_stream * stream, float volume)
{
if (!stream || volume > 1.0 || volume < 0.0) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_set_volume) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_set_volume(stream, volume);
}
int cubeb_stream_get_current_device(cubeb_stream * stream,
cubeb_device ** const device)
{
if (!stream || !device) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_get_current_device) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_get_current_device(stream, device);
}
int cubeb_stream_device_destroy(cubeb_stream * stream,
cubeb_device * device)
{
if (!stream || !device) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_device_destroy) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_device_destroy(stream, device);
}
int cubeb_stream_register_device_changed_callback(cubeb_stream * stream,
cubeb_device_changed_callback device_changed_callback)
{
if (!stream) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (!stream->context->ops->stream_register_device_changed_callback) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return stream->context->ops->stream_register_device_changed_callback(stream, device_changed_callback);
}
void * cubeb_stream_user_ptr(cubeb_stream * stream)
{
if (!stream) {
return NULL;
}
return stream->user_ptr;
}
static
void log_device(cubeb_device_info * device_info)
{
char devfmts[128] = "";
const char * devtype, * devstate, * devdeffmt;
switch (device_info->type) {
case CUBEB_DEVICE_TYPE_INPUT:
devtype = "input";
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
devtype = "output";
break;
case CUBEB_DEVICE_TYPE_UNKNOWN:
default:
devtype = "unknown?";
break;
};
switch (device_info->state) {
case CUBEB_DEVICE_STATE_DISABLED:
devstate = "disabled";
break;
case CUBEB_DEVICE_STATE_UNPLUGGED:
devstate = "unplugged";
break;
case CUBEB_DEVICE_STATE_ENABLED:
devstate = "enabled";
break;
default:
devstate = "unknown?";
break;
};
switch (device_info->default_format) {
case CUBEB_DEVICE_FMT_S16LE:
devdeffmt = "S16LE";
break;
case CUBEB_DEVICE_FMT_S16BE:
devdeffmt = "S16BE";
break;
case CUBEB_DEVICE_FMT_F32LE:
devdeffmt = "F32LE";
break;
case CUBEB_DEVICE_FMT_F32BE:
devdeffmt = "F32BE";
break;
default:
devdeffmt = "unknown?";
break;
};
if (device_info->format & CUBEB_DEVICE_FMT_S16LE) {
strcat(devfmts, " S16LE");
}
if (device_info->format & CUBEB_DEVICE_FMT_S16BE) {
strcat(devfmts, " S16BE");
}
if (device_info->format & CUBEB_DEVICE_FMT_F32LE) {
strcat(devfmts, " F32LE");
}
if (device_info->format & CUBEB_DEVICE_FMT_F32BE) {
strcat(devfmts, " F32BE");
}
LOG("DeviceID: \"%s\"%s\n"
"\tName:\t\"%s\"\n"
"\tGroup:\t\"%s\"\n"
"\tVendor:\t\"%s\"\n"
"\tType:\t%s\n"
"\tState:\t%s\n"
"\tMaximum channels:\t%u\n"
"\tFormat:\t%s (0x%x) (default: %s)\n"
"\tRate:\t[%u, %u] (default: %u)\n"
"\tLatency: lo %u frames, hi %u frames",
device_info->device_id, device_info->preferred ? " (PREFERRED)" : "",
device_info->friendly_name,
device_info->group_id,
device_info->vendor_name,
devtype,
devstate,
device_info->max_channels,
(devfmts[0] == '\0') ? devfmts : devfmts + 1, (unsigned int)device_info->format, devdeffmt,
device_info->min_rate, device_info->max_rate, device_info->default_rate,
device_info->latency_lo, device_info->latency_hi);
}
int cubeb_enumerate_devices(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection * collection)
{
int rv;
if ((devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
return CUBEB_ERROR_INVALID_PARAMETER;
if (collection == NULL)
return CUBEB_ERROR_INVALID_PARAMETER;
if (!context->ops->enumerate_devices)
return CUBEB_ERROR_NOT_SUPPORTED;
rv = context->ops->enumerate_devices(context, devtype, collection);
if (g_cubeb_log_callback) {
for (size_t i = 0; i < collection->count; i++) {
log_device(&collection->device[i]);
}
}
return rv;
}
int cubeb_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
int r;
if (context == NULL || collection == NULL)
return CUBEB_ERROR_INVALID_PARAMETER;
if (!context->ops->device_collection_destroy)
return CUBEB_ERROR_NOT_SUPPORTED;
if (!collection->device)
return CUBEB_OK;
r = context->ops->device_collection_destroy(context, collection);
if (r == CUBEB_OK) {
collection->device = NULL;
collection->count = 0;
}
return r;
}
int cubeb_register_device_collection_changed(cubeb * context,
cubeb_device_type devtype,
cubeb_device_collection_changed_callback callback,
void * user_ptr)
{
if (context == NULL || (devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) == 0)
return CUBEB_ERROR_INVALID_PARAMETER;
if (!context->ops->register_device_collection_changed) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
return context->ops->register_device_collection_changed(context, devtype, callback, user_ptr);
}
int cubeb_set_log_callback(cubeb_log_level log_level,
cubeb_log_callback log_callback)
{
if (log_level < CUBEB_LOG_DISABLED || log_level > CUBEB_LOG_VERBOSE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if (!log_callback && log_level != CUBEB_LOG_DISABLED) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
if (g_cubeb_log_callback && log_callback) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
g_cubeb_log_callback = log_callback;
g_cubeb_log_level = log_level;
// Logging a message here allows to initialize the asynchronous logger from a
// thread that is not the audio rendering thread, and especially to not
// initialize it the first time we find a verbose log, which is often in the
// audio rendering callback, that runs from the audio rendering thread, and
// that is high priority, and that we don't want to block.
if (log_level >= CUBEB_LOG_VERBOSE) {
ALOGV("Starting cubeb log");
}
return CUBEB_OK;
}

1452
dep/cubeb/src/cubeb_alsa.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,97 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_ARRAY_QUEUE_H
#define CUBEB_ARRAY_QUEUE_H
#include <assert.h>
#include <pthread.h>
#include <unistd.h>
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct
{
void ** buf;
size_t num;
size_t writePos;
size_t readPos;
pthread_mutex_t mutex;
} array_queue;
array_queue * array_queue_create(size_t num)
{
assert(num != 0);
array_queue * new_queue = (array_queue*)calloc(1, sizeof(array_queue));
new_queue->buf = (void **)calloc(1, sizeof(void *) * num);
new_queue->readPos = 0;
new_queue->writePos = 0;
new_queue->num = num;
pthread_mutex_init(&new_queue->mutex, NULL);
return new_queue;
}
void array_queue_destroy(array_queue * aq)
{
assert(aq);
free(aq->buf);
pthread_mutex_destroy(&aq->mutex);
free(aq);
}
int array_queue_push(array_queue * aq, void * item)
{
assert(item);
pthread_mutex_lock(&aq->mutex);
int ret = -1;
if(aq->buf[aq->writePos % aq->num] == NULL)
{
aq->buf[aq->writePos % aq->num] = item;
aq->writePos = (aq->writePos + 1) % aq->num;
ret = 0;
}
// else queue is full
pthread_mutex_unlock(&aq->mutex);
return ret;
}
void* array_queue_pop(array_queue * aq)
{
pthread_mutex_lock(&aq->mutex);
void * value = aq->buf[aq->readPos % aq->num];
if(value)
{
aq->buf[aq->readPos % aq->num] = NULL;
aq->readPos = (aq->readPos + 1) % aq->num;
}
pthread_mutex_unlock(&aq->mutex);
return value;
}
size_t array_queue_get_size(array_queue * aq)
{
pthread_mutex_lock(&aq->mutex);
ssize_t r = aq->writePos - aq->readPos;
if (r < 0) {
r = aq->num + r;
assert(r >= 0);
}
pthread_mutex_unlock(&aq->mutex);
return (size_t)r;
}
#if defined(__cplusplus)
}
#endif
#endif //CUBE_ARRAY_QUEUE_H

View file

@ -0,0 +1,26 @@
/*
* Copyright © 2017 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_ASSERT
#define CUBEB_ASSERT
#include <stdio.h>
#include <stdlib.h>
/**
* This allow using an external release assert method. This file should only
* export a function or macro called XASSERT that aborts the program.
*/
#define XASSERT(expr) do { \
if (!(expr)) { \
fprintf(stderr, "%s:%d - fatal error: %s\n", __FILE__, __LINE__, #expr); \
abort(); \
} \
} while (0)
#endif

View file

@ -0,0 +1,441 @@
/*
* Copyright © 2013 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(NDEBUG)
#define NDEBUG
#endif
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <time.h>
#include <dlfcn.h>
#include <android/log.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include "android/audiotrack_definitions.h"
#ifndef ALOG
#if defined(DEBUG) || defined(FORCE_ALOG)
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko - Cubeb" , ## args)
#else
#define ALOG(args...)
#endif
#endif
/**
* A lot of bytes for safety. It should be possible to bring this down a bit. */
#define SIZE_AUDIOTRACK_INSTANCE 256
/**
* call dlsym to get the symbol |mangled_name|, handle the error and store the
* pointer in |pointer|. Because depending on Android version, we want different
* symbols, not finding a symbol is not an error. */
#define DLSYM_DLERROR(mangled_name, pointer, lib) \
do { \
pointer = dlsym(lib, mangled_name); \
if (!pointer) { \
ALOG("error while loading %stm: %stm\n", mangled_name, dlerror()); \
} else { \
ALOG("%stm: OK", mangled_name); \
} \
} while(0);
static struct cubeb_ops const audiotrack_ops;
void audiotrack_destroy(cubeb * context);
void audiotrack_stream_destroy(cubeb_stream * stream);
struct AudioTrack {
/* only available on ICS and later. The second int paramter is in fact of type audio_stream_type_t. */
/* static */ status_t (*get_min_frame_count)(int* frame_count, int stream_type, uint32_t rate);
/* if we have a recent ctor, but can't find the above symbol, we
* can get the minimum frame count with this signature, and we are
* running gingerbread. */
/* static */ status_t (*get_min_frame_count_gingerbread)(int* frame_count, int stream_type, uint32_t rate);
void* (*ctor)(void* instance, int, unsigned int, int, int, int, unsigned int, void (*)(int, void*, void*), void*, int, int);
void* (*dtor)(void* instance);
void (*start)(void* instance);
void (*pause)(void* instance);
uint32_t (*latency)(void* instance);
status_t (*check)(void* instance);
status_t (*get_position)(void* instance, uint32_t* position);
/* static */ int (*get_output_samplingrate)(int* samplerate, int stream);
status_t (*set_marker_position)(void* instance, unsigned int);
status_t (*set_volume)(void* instance, float left, float right);
};
struct cubeb {
struct cubeb_ops const * ops;
void * library;
struct AudioTrack klass;
};
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context;
void * user_ptr;
/**/
cubeb_stream_params params;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
void * instance;
/* Number of frames that have been passed to the AudioTrack callback */
long unsigned written;
int draining;
};
static void
audiotrack_refill(int event, void* user, void* info)
{
cubeb_stream * stream = user;
switch (event) {
case EVENT_MORE_DATA: {
long got = 0;
struct Buffer * b = (struct Buffer*)info;
if (stream->draining) {
return;
}
got = stream->data_callback(stream, stream->user_ptr, NULL, b->raw, b->frameCount);
stream->written += got;
if (got != (long)b->frameCount) {
stream->draining = 1;
/* set a marker so we are notified when the are done draining, that is,
* when every frame has been played by android. */
stream->context->klass.set_marker_position(stream->instance, stream->written);
}
break;
}
case EVENT_UNDERRUN:
ALOG("underrun in cubeb backend.");
break;
case EVENT_LOOP_END:
assert(0 && "We don't support the loop feature of audiotrack.");
break;
case EVENT_MARKER:
assert(stream->draining);
stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
break;
case EVENT_NEW_POS:
assert(0 && "We don't support the setPositionUpdatePeriod feature of audiotrack.");
break;
case EVENT_BUFFER_END:
assert(0 && "Should not happen.");
break;
}
}
/* We are running on gingerbread if we found the gingerbread signature for
* getMinFrameCount */
static int
audiotrack_version_is_gingerbread(cubeb * ctx)
{
return ctx->klass.get_min_frame_count_gingerbread != NULL;
}
int
audiotrack_get_min_frame_count(cubeb * ctx, cubeb_stream_params * params, int * min_frame_count)
{
status_t status;
/* Recent Android have a getMinFrameCount method. */
if (!audiotrack_version_is_gingerbread(ctx)) {
status = ctx->klass.get_min_frame_count(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
} else {
status = ctx->klass.get_min_frame_count_gingerbread(min_frame_count, AUDIO_STREAM_TYPE_MUSIC, params->rate);
}
if (status != 0) {
ALOG("error getting the min frame count");
return CUBEB_ERROR;
}
return CUBEB_OK;
}
int
audiotrack_init(cubeb ** context, char const * context_name)
{
cubeb * ctx;
struct AudioTrack* c;
assert(context);
*context = NULL;
ctx = calloc(1, sizeof(*ctx));
assert(ctx);
/* If we use an absolute path here ("/system/lib/libmedia.so"), and on Android
* 2.2, the dlopen succeeds, all the dlsym succeed, but a segfault happens on
* the first call to a dlsym'ed function. Somehow this does not happen when
* using only the name of the library. */
ctx->library = dlopen("libmedia.so", RTLD_LAZY);
if (!ctx->library) {
ALOG("dlopen error: %s.", dlerror());
free(ctx);
return CUBEB_ERROR;
}
/* Recent Android first, then Gingerbread. */
DLSYM_DLERROR("_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii", ctx->klass.ctor, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrackD1Ev", ctx->klass.dtor, ctx->library);
DLSYM_DLERROR("_ZNK7android10AudioTrack7latencyEv", ctx->klass.latency, ctx->library);
DLSYM_DLERROR("_ZNK7android10AudioTrack9initCheckEv", ctx->klass.check, ctx->library);
DLSYM_DLERROR("_ZN7android11AudioSystem21getOutputSamplingRateEPii", ctx->klass.get_output_samplingrate, ctx->library);
/* |getMinFrameCount| is available on gingerbread and ICS with different signatures. */
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPi19audio_stream_type_tj", ctx->klass.get_min_frame_count, ctx->library);
if (!ctx->klass.get_min_frame_count) {
DLSYM_DLERROR("_ZN7android10AudioTrack16getMinFrameCountEPiij", ctx->klass.get_min_frame_count_gingerbread, ctx->library);
}
DLSYM_DLERROR("_ZN7android10AudioTrack5startEv", ctx->klass.start, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack5pauseEv", ctx->klass.pause, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack11getPositionEPj", ctx->klass.get_position, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack17setMarkerPositionEj", ctx->klass.set_marker_position, ctx->library);
DLSYM_DLERROR("_ZN7android10AudioTrack9setVolumeEff", ctx->klass.set_volume, ctx->library);
/* check that we have a combination of symbol that makes sense */
c = &ctx->klass;
if(!(c->ctor &&
c->dtor && c->latency && c->check &&
/* at least one way to get the minimum frame count to request. */
(c->get_min_frame_count ||
c->get_min_frame_count_gingerbread) &&
c->start && c->pause && c->get_position && c->set_marker_position)) {
ALOG("Could not find all the symbols we need.");
audiotrack_destroy(ctx);
return CUBEB_ERROR;
}
ctx->ops = &audiotrack_ops;
*context = ctx;
return CUBEB_OK;
}
char const *
audiotrack_get_backend_id(cubeb * context)
{
return "audiotrack";
}
static int
audiotrack_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
assert(ctx && max_channels);
/* The android mixer handles up to two channels, see
http://androidxref.com/4.2.2_r1/xref/frameworks/av/services/audioflinger/AudioFlinger.h#67 */
*max_channels = 2;
return CUBEB_OK;
}
static int
audiotrack_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_ms)
{
/* We always use the lowest latency possible when using this backend (see
* audiotrack_stream_init), so this value is not going to be used. */
int r;
r = audiotrack_get_min_frame_count(ctx, &params, (int *)latency_ms);
if (r != CUBEB_OK) {
return CUBEB_ERROR;
}
return CUBEB_OK;
}
static int
audiotrack_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
status_t r;
r = ctx->klass.get_output_samplingrate((int32_t *)rate, 3 /* MUSIC */);
return r == 0 ? CUBEB_OK : CUBEB_ERROR;
}
void
audiotrack_destroy(cubeb * context)
{
assert(context);
dlclose(context->library);
free(context);
}
int
audiotrack_stream_init(cubeb * ctx, cubeb_stream ** stream, char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
{
cubeb_stream * stm;
int32_t channels;
uint32_t min_frame_count;
assert(ctx && stream);
assert(!input_stream_params && "not supported");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
if (output_stream_params->format == CUBEB_SAMPLE_FLOAT32LE ||
output_stream_params->format == CUBEB_SAMPLE_FLOAT32BE) {
return CUBEB_ERROR_INVALID_FORMAT;
}
if (audiotrack_get_min_frame_count(ctx, output_stream_params, (int *)&min_frame_count)) {
return CUBEB_ERROR;
}
stm = calloc(1, sizeof(*stm));
assert(stm);
stm->context = ctx;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->params = *output_stream_params;
stm->instance = calloc(SIZE_AUDIOTRACK_INSTANCE, 1);
(*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) = 0xbaadbaad;
assert(stm->instance && "cubeb: EOM");
/* gingerbread uses old channel layout enum */
if (audiotrack_version_is_gingerbread(ctx)) {
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_Legacy : AUDIO_CHANNEL_OUT_MONO_Legacy;
} else {
channels = stm->params.channels == 2 ? AUDIO_CHANNEL_OUT_STEREO_ICS : AUDIO_CHANNEL_OUT_MONO_ICS;
}
ctx->klass.ctor(stm->instance, AUDIO_STREAM_TYPE_MUSIC, stm->params.rate,
AUDIO_FORMAT_PCM_16_BIT, channels, min_frame_count, 0,
audiotrack_refill, stm, 0, 0);
assert((*(uint32_t*)((intptr_t)stm->instance + SIZE_AUDIOTRACK_INSTANCE - 4)) == 0xbaadbaad);
if (ctx->klass.check(stm->instance)) {
ALOG("stream not initialized properly.");
audiotrack_stream_destroy(stm);
return CUBEB_ERROR;
}
*stream = stm;
return CUBEB_OK;
}
void
audiotrack_stream_destroy(cubeb_stream * stream)
{
assert(stream->context);
stream->context->klass.dtor(stream->instance);
free(stream->instance);
stream->instance = NULL;
free(stream);
}
int
audiotrack_stream_start(cubeb_stream * stream)
{
assert(stream->instance);
stream->context->klass.start(stream->instance);
stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STARTED);
return CUBEB_OK;
}
int
audiotrack_stream_stop(cubeb_stream * stream)
{
assert(stream->instance);
stream->context->klass.pause(stream->instance);
stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_STOPPED);
return CUBEB_OK;
}
int
audiotrack_stream_get_position(cubeb_stream * stream, uint64_t * position)
{
uint32_t p;
assert(stream->instance && position);
stream->context->klass.get_position(stream->instance, &p);
*position = p;
return CUBEB_OK;
}
int
audiotrack_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
{
assert(stream->instance && latency);
/* Android returns the latency in ms, we want it in frames. */
*latency = stream->context->klass.latency(stream->instance);
/* with rate <= 96000, we won't overflow until 44.739 seconds of latency */
*latency = (*latency * stream->params.rate) / 1000;
return 0;
}
int
audiotrack_stream_set_volume(cubeb_stream * stream, float volume)
{
status_t status;
status = stream->context->klass.set_volume(stream->instance, volume, volume);
if (status) {
return CUBEB_ERROR;
}
return CUBEB_OK;
}
static struct cubeb_ops const audiotrack_ops = {
.init = audiotrack_init,
.get_backend_id = audiotrack_get_backend_id,
.get_max_channel_count = audiotrack_get_max_channel_count,
.get_min_latency = audiotrack_get_min_latency,
.get_preferred_sample_rate = audiotrack_get_preferred_sample_rate,
.enumerate_devices = NULL,
.device_collection_destroy = NULL,
.destroy = audiotrack_destroy,
.stream_init = audiotrack_stream_init,
.stream_destroy = audiotrack_stream_destroy,
.stream_start = audiotrack_stream_start,
.stream_stop = audiotrack_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = audiotrack_stream_get_position,
.stream_get_latency = audiotrack_stream_get_latency,
.stream_set_volume = audiotrack_stream_set_volume,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};

File diff suppressed because it is too large Load diff

1075
dep/cubeb/src/cubeb_jack.cpp Normal file

File diff suppressed because it is too large Load diff

369
dep/cubeb/src/cubeb_kai.c Normal file
View file

@ -0,0 +1,369 @@
/*
* Copyright © 2015 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <sys/fmutex.h>
#include <kai.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
/* We don't support more than 2 channels in KAI */
#define MAX_CHANNELS 2
#define NBUFS 2
#define FRAME_SIZE 2048
struct cubeb_stream_item {
cubeb_stream * stream;
};
static struct cubeb_ops const kai_ops;
struct cubeb {
struct cubeb_ops const * ops;
};
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context;
void * user_ptr;
/**/
cubeb_stream_params params;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
HKAI hkai;
KAISPEC spec;
uint64_t total_frames;
float soft_volume;
_fmutex mutex;
float float_buffer[FRAME_SIZE * MAX_CHANNELS];
};
static inline long
frames_to_bytes(long frames, cubeb_stream_params params)
{
return frames * 2 * params.channels; /* 2 bytes per frame */
}
static inline long
bytes_to_frames(long bytes, cubeb_stream_params params)
{
return bytes / 2 / params.channels; /* 2 bytes per frame */
}
static void kai_destroy(cubeb * ctx);
/*static*/ int
kai_init(cubeb ** context, char const * context_name)
{
cubeb * ctx;
XASSERT(context);
*context = NULL;
if (kaiInit(KAIM_AUTO))
return CUBEB_ERROR;
ctx = calloc(1, sizeof(*ctx));
XASSERT(ctx);
ctx->ops = &kai_ops;
*context = ctx;
return CUBEB_OK;
}
static char const *
kai_get_backend_id(cubeb * ctx)
{
return "kai";
}
static void
kai_destroy(cubeb * ctx)
{
kaiDone();
free(ctx);
}
static void
float_to_s16ne(int16_t *dst, float *src, size_t n)
{
long l;
while (n--) {
l = lrintf(*src++ * 0x8000);
if (l > 32767)
l = 32767;
if (l < -32768)
l = -32768;
*dst++ = (int16_t)l;
}
}
static ULONG APIENTRY
kai_callback(PVOID cbdata, PVOID buffer, ULONG len)
{
cubeb_stream * stm = cbdata;
void *p;
long wanted_frames;
long frames;
float soft_volume;
int elements = len / sizeof(int16_t);
p = stm->params.format == CUBEB_SAMPLE_FLOAT32NE
? stm->float_buffer : buffer;
wanted_frames = bytes_to_frames(len, stm->params);
frames = stm->data_callback(stm, stm->user_ptr, NULL, p, wanted_frames);
_fmutex_request(&stm->mutex, 0);
stm->total_frames += frames;
soft_volume = stm->soft_volume;
_fmutex_release(&stm->mutex);
if (frames < wanted_frames)
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
if (stm->params.format == CUBEB_SAMPLE_FLOAT32NE)
float_to_s16ne(buffer, p, elements);
if (soft_volume != -1.0f) {
int16_t *b = buffer;
int i;
for (i = 0; i < elements; i++)
*b++ *= soft_volume;
}
return frames_to_bytes(frames, stm->params);
}
static void kai_stream_destroy(cubeb_stream * stm);
static int
kai_stream_init(cubeb * context, cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency, cubeb_data_callback data_callback,
cubeb_state_callback state_callback, void * user_ptr)
{
cubeb_stream * stm;
KAISPEC wanted_spec;
XASSERT(!input_stream_params && "not supported.");
if (input_device || output_device) {
/* Device selection not yet implemented. */
return CUBEB_ERROR_DEVICE_UNAVAILABLE;
}
if (!output_stream_params)
return CUBEB_ERROR_INVALID_PARAMETER;
// Loopback is unsupported
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
return CUBEB_ERROR_NOT_SUPPORTED;
}
if (output_stream_params->channels < 1 ||
output_stream_params->channels > MAX_CHANNELS)
return CUBEB_ERROR_INVALID_FORMAT;
XASSERT(context);
XASSERT(stream);
*stream = NULL;
stm = calloc(1, sizeof(*stm));
XASSERT(stm);
stm->context = context;
stm->params = *output_stream_params;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->soft_volume = -1.0f;
if (_fmutex_create(&stm->mutex, 0)) {
free(stm);
return CUBEB_ERROR;
}
wanted_spec.usDeviceIndex = 0;
wanted_spec.ulType = KAIT_PLAY;
wanted_spec.ulBitsPerSample = BPS_16;
wanted_spec.ulSamplingRate = stm->params.rate;
wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM;
wanted_spec.ulChannels = stm->params.channels;
wanted_spec.ulNumBuffers = NBUFS;
wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, stm->params);
wanted_spec.fShareable = TRUE;
wanted_spec.pfnCallBack = kai_callback;
wanted_spec.pCallBackData = stm;
if (kaiOpen(&wanted_spec, &stm->spec, &stm->hkai)) {
_fmutex_close(&stm->mutex);
free(stm);
return CUBEB_ERROR;
}
*stream = stm;
return CUBEB_OK;
}
static void
kai_stream_destroy(cubeb_stream * stm)
{
kaiClose(stm->hkai);
_fmutex_close(&stm->mutex);
free(stm);
}
static int
kai_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
XASSERT(ctx && max_channels);
*max_channels = MAX_CHANNELS;
return CUBEB_OK;
}
static int
kai_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency)
{
/* We have at least two buffers. One is being played, the other one is being
filled. So there is as much latency as one buffer. */
*latency = FRAME_SIZE;
return CUBEB_OK;
}
static int
kai_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
cubeb_stream_params params;
KAISPEC wanted_spec;
KAISPEC spec;
HKAI hkai;
params.format = CUBEB_SAMPLE_S16NE;
params.rate = 48000;
params.channels = 2;
wanted_spec.usDeviceIndex = 0;
wanted_spec.ulType = KAIT_PLAY;
wanted_spec.ulBitsPerSample = BPS_16;
wanted_spec.ulSamplingRate = params.rate;
wanted_spec.ulDataFormat = MCI_WAVE_FORMAT_PCM;
wanted_spec.ulChannels = params.channels;
wanted_spec.ulNumBuffers = NBUFS;
wanted_spec.ulBufferSize = frames_to_bytes(FRAME_SIZE, params);
wanted_spec.fShareable = TRUE;
wanted_spec.pfnCallBack = kai_callback;
wanted_spec.pCallBackData = NULL;
/* Test 48KHz */
if (kaiOpen(&wanted_spec, &spec, &hkai)) {
/* Not supported. Fall back to 44.1KHz */
params.rate = 44100;
} else {
/* Supported. Use 48KHz */
kaiClose(hkai);
}
*rate = params.rate;
return CUBEB_OK;
}
static int
kai_stream_start(cubeb_stream * stm)
{
if (kaiPlay(stm->hkai))
return CUBEB_ERROR;
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
return CUBEB_OK;
}
static int
kai_stream_stop(cubeb_stream * stm)
{
if (kaiStop(stm->hkai))
return CUBEB_ERROR;
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
return CUBEB_OK;
}
static int
kai_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
_fmutex_request(&stm->mutex, 0);
*position = stm->total_frames;
_fmutex_release(&stm->mutex);
return CUBEB_OK;
}
static int
kai_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
/* Out of buffers, one is being played, the others are being filled.
So there is as much latency as total buffers - 1. */
*latency = bytes_to_frames(stm->spec.ulBufferSize, stm->params)
* (stm->spec.ulNumBuffers - 1);
return CUBEB_OK;
}
static int
kai_stream_set_volume(cubeb_stream * stm, float volume)
{
_fmutex_request(&stm->mutex, 0);
stm->soft_volume = volume;
_fmutex_release(&stm->mutex);
return CUBEB_OK;
}
static struct cubeb_ops const kai_ops = {
/*.init =*/ kai_init,
/*.get_backend_id =*/ kai_get_backend_id,
/*.get_max_channel_count=*/ kai_get_max_channel_count,
/*.get_min_latency=*/ kai_get_min_latency,
/*.get_preferred_sample_rate =*/ kai_get_preferred_sample_rate,
/*.get_preferred_channel_layout =*/ NULL,
/*.enumerate_devices =*/ NULL,
/*.device_collection_destroy =*/ NULL,
/*.destroy =*/ kai_destroy,
/*.stream_init =*/ kai_stream_init,
/*.stream_destroy =*/ kai_stream_destroy,
/*.stream_start =*/ kai_stream_start,
/*.stream_stop =*/ kai_stream_stop,
/*.stream_reset_default_device =*/ NULL,
/*.stream_get_position =*/ kai_stream_get_position,
/*.stream_get_latency = */ kai_stream_get_latency,
/*.stream_set_volume =*/ kai_stream_set_volume,
/*.stream_get_current_device =*/ NULL,
/*.stream_device_destroy =*/ NULL,
/*.stream_register_device_changed_callback=*/ NULL,
/*.register_device_collection_changed=*/ NULL
};

144
dep/cubeb/src/cubeb_log.cpp Normal file
View file

@ -0,0 +1,144 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#define NOMINMAX
#include "cubeb_log.h"
#include "cubeb_ringbuffer.h"
#include <cstdarg>
#ifdef _WIN32
#include <windows.h>
#else
#include <time.h>
#endif
cubeb_log_level g_cubeb_log_level;
cubeb_log_callback g_cubeb_log_callback;
/** The maximum size of a log message, after having been formatted. */
const size_t CUBEB_LOG_MESSAGE_MAX_SIZE = 256;
/** The maximum number of log messages that can be queued before dropping
* messages. */
const size_t CUBEB_LOG_MESSAGE_QUEUE_DEPTH = 40;
/** Number of milliseconds to wait before dequeuing log messages. */
#define CUBEB_LOG_BATCH_PRINT_INTERVAL_MS 10
/**
* This wraps an inline buffer, that represents a log message, that must be
* null-terminated.
* This class should not use system calls or other potentially blocking code.
*/
class cubeb_log_message
{
public:
cubeb_log_message()
{
*storage = '\0';
}
cubeb_log_message(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
size_t length = strlen(str);
/* paranoia against malformed message */
assert(length < CUBEB_LOG_MESSAGE_MAX_SIZE);
if (length > CUBEB_LOG_MESSAGE_MAX_SIZE - 1) {
return;
}
PodCopy(storage, str, length);
storage[length] = '\0';
}
char const * get() {
return storage;
}
private:
char storage[CUBEB_LOG_MESSAGE_MAX_SIZE];
};
/** Lock-free asynchronous logger, made so that logging from a
* real-time audio callback does not block the audio thread. */
class cubeb_async_logger
{
public:
/* This is thread-safe since C++11 */
static cubeb_async_logger & get() {
static cubeb_async_logger instance;
return instance;
}
void push(char const str[CUBEB_LOG_MESSAGE_MAX_SIZE])
{
cubeb_log_message msg(str);
msg_queue.enqueue(msg);
}
void run()
{
std::thread([this]() {
while (true) {
cubeb_log_message msg;
while (msg_queue.dequeue(&msg, 1)) {
LOGV("%s", msg.get());
}
#ifdef _WIN32
Sleep(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS);
#else
timespec sleep_duration = sleep_for;
timespec remainder;
do {
if (nanosleep(&sleep_duration, &remainder) == 0 ||
errno != EINTR) {
break;
}
sleep_duration = remainder;
} while (remainder.tv_sec || remainder.tv_nsec);
#endif
}
}).detach();
}
// Tell the underlying queue the producer thread has changed, so it does not
// assert in debug. This should be called with the thread stopped.
void reset_producer_thread()
{
msg_queue.reset_thread_ids();
}
private:
#ifndef _WIN32
const struct timespec sleep_for = {
CUBEB_LOG_BATCH_PRINT_INTERVAL_MS/1000,
(CUBEB_LOG_BATCH_PRINT_INTERVAL_MS%1000)*1000*1000
};
#endif
cubeb_async_logger()
: msg_queue(CUBEB_LOG_MESSAGE_QUEUE_DEPTH)
{
run();
}
/** This is quite a big data structure, but is only instantiated if the
* asynchronous logger is used.*/
lock_free_queue<cubeb_log_message> msg_queue;
};
void cubeb_async_log(char const * fmt, ...)
{
if (!g_cubeb_log_callback) {
return;
}
// This is going to copy a 256 bytes array around, which is fine.
// We don't want to allocate memory here, because this is made to
// be called from a real-time callback.
va_list args;
va_start(args, fmt);
char msg[CUBEB_LOG_MESSAGE_MAX_SIZE];
vsnprintf(msg, CUBEB_LOG_MESSAGE_MAX_SIZE, fmt, args);
cubeb_async_logger::get().push(msg);
va_end(args);
}
void cubeb_async_log_reset_threads()
{
if (!g_cubeb_log_callback) {
return;
}
cubeb_async_logger::get().reset_producer_thread();
}

47
dep/cubeb/src/cubeb_log.h Normal file
View file

@ -0,0 +1,47 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_LOG
#define CUBEB_LOG
#include "cubeb/cubeb.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(__GNUC__) || defined(__clang__)
#define PRINTF_FORMAT(fmt, args) __attribute__((format(printf, fmt, args)))
#else
#define PRINTF_FORMAT(fmt, args)
#endif
extern cubeb_log_level g_cubeb_log_level;
extern cubeb_log_callback g_cubeb_log_callback PRINTF_FORMAT(1, 2);
void cubeb_async_log(const char * fmt, ...);
void cubeb_async_log_reset_threads();
#ifdef __cplusplus
}
#endif
#define LOGV(msg, ...) LOG_INTERNAL(CUBEB_LOG_VERBOSE, msg, ##__VA_ARGS__)
#define LOG(msg, ...) LOG_INTERNAL(CUBEB_LOG_NORMAL, msg, ##__VA_ARGS__)
#define LOG_INTERNAL(level, fmt, ...) do { \
if (g_cubeb_log_callback && level <= g_cubeb_log_level) { \
g_cubeb_log_callback("%s:%d: " fmt "\n", __FILE__, __LINE__, ##__VA_ARGS__); \
} \
} while(0)
/* Asynchronous verbose logging, to log in real-time callbacks. */
#define ALOGV(fmt, ...) \
do { \
cubeb_async_log(fmt, ##__VA_ARGS__); \
} while(0)
#endif // CUBEB_LOG

View file

@ -0,0 +1,663 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*
* Adapted from code based on libswresample's rematrix.c
*/
#define NOMINMAX
#include <algorithm>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstdlib>
#include <memory>
#include <type_traits>
#include "cubeb-internal.h"
#include "cubeb_mixer.h"
#include "cubeb_utils.h"
#ifndef FF_ARRAY_ELEMS
#define FF_ARRAY_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
#endif
#define CHANNELS_MAX 32
#define FRONT_LEFT 0
#define FRONT_RIGHT 1
#define FRONT_CENTER 2
#define LOW_FREQUENCY 3
#define BACK_LEFT 4
#define BACK_RIGHT 5
#define FRONT_LEFT_OF_CENTER 6
#define FRONT_RIGHT_OF_CENTER 7
#define BACK_CENTER 8
#define SIDE_LEFT 9
#define SIDE_RIGHT 10
#define TOP_CENTER 11
#define TOP_FRONT_LEFT 12
#define TOP_FRONT_CENTER 13
#define TOP_FRONT_RIGHT 14
#define TOP_BACK_LEFT 15
#define TOP_BACK_CENTER 16
#define TOP_BACK_RIGHT 17
#define NUM_NAMED_CHANNELS 18
#ifndef M_SQRT1_2
#define M_SQRT1_2 0.70710678118654752440 /* 1/sqrt(2) */
#endif
#ifndef M_SQRT2
#define M_SQRT2 1.41421356237309504880 /* sqrt(2) */
#endif
#define SQRT3_2 1.22474487139158904909 /* sqrt(3/2) */
#define C30DB M_SQRT2
#define C15DB 1.189207115
#define C__0DB 1.0
#define C_15DB 0.840896415
#define C_30DB M_SQRT1_2
#define C_45DB 0.594603558
#define C_60DB 0.5
static cubeb_channel_layout
cubeb_channel_layout_check(cubeb_channel_layout l, uint32_t c)
{
if (l == CUBEB_LAYOUT_UNDEFINED) {
switch (c) {
case 1: return CUBEB_LAYOUT_MONO;
case 2: return CUBEB_LAYOUT_STEREO;
}
}
return l;
}
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout x)
{
#if __GNUC__ || __clang__
return __builtin_popcount (x);
#else
x -= (x >> 1) & 0x55555555;
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
x = (x + (x >> 4)) & 0x0F0F0F0F;
x += x >> 8;
return (x + (x >> 16)) & 0x3F;
#endif
}
struct MixerContext {
MixerContext(cubeb_sample_format f,
uint32_t in_channels,
cubeb_channel_layout in,
uint32_t out_channels,
cubeb_channel_layout out)
: _format(f)
, _in_ch_layout(cubeb_channel_layout_check(in, in_channels))
, _out_ch_layout(cubeb_channel_layout_check(out, out_channels))
, _in_ch_count(in_channels)
, _out_ch_count(out_channels)
{
if (in_channels != cubeb_channel_layout_nb_channels(in) ||
out_channels != cubeb_channel_layout_nb_channels(out)) {
// Mismatch between channels and layout, aborting.
return;
}
_valid = init() >= 0;
}
static bool even(cubeb_channel_layout layout)
{
if (!layout) {
return true;
}
if (layout & (layout - 1)) {
return true;
}
return false;
}
// Ensure that the layout is sane (that is have symmetrical left/right
// channels), if not, layout will be treated as mono.
static cubeb_channel_layout clean_layout(cubeb_channel_layout layout)
{
if (layout && layout != CHANNEL_FRONT_LEFT && !(layout & (layout - 1))) {
LOG("Treating layout as mono");
return CHANNEL_FRONT_CENTER;
}
return layout;
}
static bool sane_layout(cubeb_channel_layout layout)
{
if (!(layout & CUBEB_LAYOUT_3F)) { // at least 1 front speaker
return false;
}
if (!even(layout & (CHANNEL_FRONT_LEFT |
CHANNEL_FRONT_RIGHT))) { // no asymetric front
return false;
}
if (!even(layout &
(CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT))) { // no asymetric side
return false;
}
if (!even(layout & (CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT))) {
return false;
}
if (!even(layout &
(CHANNEL_FRONT_LEFT_OF_CENTER | CHANNEL_FRONT_RIGHT_OF_CENTER))) {
return false;
}
if (cubeb_channel_layout_nb_channels(layout) >= CHANNELS_MAX) {
return false;
}
return true;
}
int auto_matrix();
int init();
const cubeb_sample_format _format;
const cubeb_channel_layout _in_ch_layout; ///< input channel layout
const cubeb_channel_layout _out_ch_layout; ///< output channel layout
const uint32_t _in_ch_count; ///< input channel count
const uint32_t _out_ch_count; ///< output channel count
const float _surround_mix_level = C_30DB; ///< surround mixing level
const float _center_mix_level = C_30DB; ///< center mixing level
const float _lfe_mix_level = 1; ///< LFE mixing level
double _matrix[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< floating point rematrixing coefficients
float _matrix_flt[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< single precision floating point rematrixing coefficients
int32_t _matrix32[CHANNELS_MAX][CHANNELS_MAX] = {{ 0 }}; ///< 17.15 fixed point rematrixing coefficients
uint8_t _matrix_ch[CHANNELS_MAX][CHANNELS_MAX+1] = {{ 0 }}; ///< Lists of input channels per output channel that have non zero rematrixing coefficients
bool _clipping = false; ///< Set to true if clipping detection is required
bool _valid = false; ///< Set to true if context is valid.
};
int MixerContext::auto_matrix()
{
double matrix[NUM_NAMED_CHANNELS][NUM_NAMED_CHANNELS] = { { 0 } };
double maxcoef = 0;
float maxval;
cubeb_channel_layout in_ch_layout = clean_layout(_in_ch_layout);
cubeb_channel_layout out_ch_layout = clean_layout(_out_ch_layout);
if (!sane_layout(in_ch_layout)) {
// Channel Not Supported
LOG("Input Layout %x is not supported", _in_ch_layout);
return -1;
}
if (!sane_layout(out_ch_layout)) {
LOG("Output Layout %x is not supported", _out_ch_layout);
return -1;
}
for (uint32_t i = 0; i < FF_ARRAY_ELEMS(matrix); i++) {
if (in_ch_layout & out_ch_layout & (1U << i)) {
matrix[i][i] = 1.0;
}
}
cubeb_channel_layout unaccounted = in_ch_layout & ~out_ch_layout;
// Rematrixing is done via a matrix of coefficient that should be applied to
// all channels. Channels are treated as pair and must be symmetrical (if a
// left channel exists, the corresponding right should exist too) unless the
// output layout has similar layout. Channels are then mixed toward the front
// center or back center if they exist with a slight bias toward the front.
if (unaccounted & CHANNEL_FRONT_CENTER) {
if ((out_ch_layout & CUBEB_LAYOUT_STEREO) == CUBEB_LAYOUT_STEREO) {
if (in_ch_layout & CUBEB_LAYOUT_STEREO) {
matrix[FRONT_LEFT][FRONT_CENTER] += _center_mix_level;
matrix[FRONT_RIGHT][FRONT_CENTER] += _center_mix_level;
} else {
matrix[FRONT_LEFT][FRONT_CENTER] += M_SQRT1_2;
matrix[FRONT_RIGHT][FRONT_CENTER] += M_SQRT1_2;
}
}
}
if (unaccounted & CUBEB_LAYOUT_STEREO) {
if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][FRONT_LEFT] += M_SQRT1_2;
matrix[FRONT_CENTER][FRONT_RIGHT] += M_SQRT1_2;
if (in_ch_layout & CHANNEL_FRONT_CENTER)
matrix[FRONT_CENTER][FRONT_CENTER] = _center_mix_level * M_SQRT2;
}
}
if (unaccounted & CHANNEL_BACK_CENTER) {
if (out_ch_layout & CHANNEL_BACK_LEFT) {
matrix[BACK_LEFT][BACK_CENTER] += M_SQRT1_2;
matrix[BACK_RIGHT][BACK_CENTER] += M_SQRT1_2;
} else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
matrix[SIDE_LEFT][BACK_CENTER] += M_SQRT1_2;
matrix[SIDE_RIGHT][BACK_CENTER] += M_SQRT1_2;
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
matrix[FRONT_LEFT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
matrix[FRONT_RIGHT][BACK_CENTER] += _surround_mix_level * M_SQRT1_2;
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][BACK_CENTER] +=
_surround_mix_level * M_SQRT1_2;
}
}
if (unaccounted & CHANNEL_BACK_LEFT) {
if (out_ch_layout & CHANNEL_BACK_CENTER) {
matrix[BACK_CENTER][BACK_LEFT] += M_SQRT1_2;
matrix[BACK_CENTER][BACK_RIGHT] += M_SQRT1_2;
} else if (out_ch_layout & CHANNEL_SIDE_LEFT) {
if (in_ch_layout & CHANNEL_SIDE_LEFT) {
matrix[SIDE_LEFT][BACK_LEFT] += M_SQRT1_2;
matrix[SIDE_RIGHT][BACK_RIGHT] += M_SQRT1_2;
} else {
matrix[SIDE_LEFT][BACK_LEFT] += 1.0;
matrix[SIDE_RIGHT][BACK_RIGHT] += 1.0;
}
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
matrix[FRONT_LEFT][BACK_LEFT] += _surround_mix_level;
matrix[FRONT_RIGHT][BACK_RIGHT] += _surround_mix_level;
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][BACK_LEFT] += _surround_mix_level * M_SQRT1_2;
matrix[FRONT_CENTER][BACK_RIGHT] += _surround_mix_level * M_SQRT1_2;
}
}
if (unaccounted & CHANNEL_SIDE_LEFT) {
if (out_ch_layout & CHANNEL_BACK_LEFT) {
/* if back channels do not exist in the input, just copy side
channels to back channels, otherwise mix side into back */
if (in_ch_layout & CHANNEL_BACK_LEFT) {
matrix[BACK_LEFT][SIDE_LEFT] += M_SQRT1_2;
matrix[BACK_RIGHT][SIDE_RIGHT] += M_SQRT1_2;
} else {
matrix[BACK_LEFT][SIDE_LEFT] += 1.0;
matrix[BACK_RIGHT][SIDE_RIGHT] += 1.0;
}
} else if (out_ch_layout & CHANNEL_BACK_CENTER) {
matrix[BACK_CENTER][SIDE_LEFT] += M_SQRT1_2;
matrix[BACK_CENTER][SIDE_RIGHT] += M_SQRT1_2;
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
matrix[FRONT_LEFT][SIDE_LEFT] += _surround_mix_level;
matrix[FRONT_RIGHT][SIDE_RIGHT] += _surround_mix_level;
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][SIDE_LEFT] += _surround_mix_level * M_SQRT1_2;
matrix[FRONT_CENTER][SIDE_RIGHT] += _surround_mix_level * M_SQRT1_2;
}
}
if (unaccounted & CHANNEL_FRONT_LEFT_OF_CENTER) {
if (out_ch_layout & CHANNEL_FRONT_LEFT) {
matrix[FRONT_LEFT][FRONT_LEFT_OF_CENTER] += 1.0;
matrix[FRONT_RIGHT][FRONT_RIGHT_OF_CENTER] += 1.0;
} else if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][FRONT_LEFT_OF_CENTER] += M_SQRT1_2;
matrix[FRONT_CENTER][FRONT_RIGHT_OF_CENTER] += M_SQRT1_2;
}
}
/* mix LFE into front left/right or center */
if (unaccounted & CHANNEL_LOW_FREQUENCY) {
if (out_ch_layout & CHANNEL_FRONT_CENTER) {
matrix[FRONT_CENTER][LOW_FREQUENCY] += _lfe_mix_level;
} else if (out_ch_layout & CHANNEL_FRONT_LEFT) {
matrix[FRONT_LEFT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
matrix[FRONT_RIGHT][LOW_FREQUENCY] += _lfe_mix_level * M_SQRT1_2;
}
}
// Normalize the conversion matrix.
for (uint32_t out_i = 0, i = 0; i < CHANNELS_MAX; i++) {
double sum = 0;
int in_i = 0;
if ((out_ch_layout & (1U << i)) == 0) {
continue;
}
for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
if ((in_ch_layout & (1U << j)) == 0) {
continue;
}
if (i < FF_ARRAY_ELEMS(matrix) && j < FF_ARRAY_ELEMS(matrix[0])) {
_matrix[out_i][in_i] = matrix[i][j];
} else {
_matrix[out_i][in_i] =
i == j && (in_ch_layout & out_ch_layout & (1U << i));
}
sum += fabs(_matrix[out_i][in_i]);
in_i++;
}
maxcoef = std::max(maxcoef, sum);
out_i++;
}
if (_format == CUBEB_SAMPLE_S16NE) {
maxval = 1.0;
} else {
maxval = INT_MAX;
}
// Normalize matrix if needed.
if (maxcoef > maxval) {
maxcoef /= maxval;
for (uint32_t i = 0; i < CHANNELS_MAX; i++)
for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
_matrix[i][j] /= maxcoef;
}
}
if (_format == CUBEB_SAMPLE_FLOAT32NE) {
for (uint32_t i = 0; i < FF_ARRAY_ELEMS(_matrix); i++) {
for (uint32_t j = 0; j < FF_ARRAY_ELEMS(_matrix[0]); j++) {
_matrix_flt[i][j] = _matrix[i][j];
}
}
}
return 0;
}
int MixerContext::init()
{
int r = auto_matrix();
if (r) {
return r;
}
// Determine if matrix operation would overflow
if (_format == CUBEB_SAMPLE_S16NE) {
int maxsum = 0;
for (uint32_t i = 0; i < _out_ch_count; i++) {
double rem = 0;
int sum = 0;
for (uint32_t j = 0; j < _in_ch_count; j++) {
double target = _matrix[i][j] * 32768 + rem;
int value = lrintf(target);
rem += target - value;
sum += std::abs(value);
}
maxsum = std::max(maxsum, sum);
}
if (maxsum > 32768) {
_clipping = true;
}
}
// FIXME quantize for integers
for (uint32_t i = 0; i < CHANNELS_MAX; i++) {
int ch_in = 0;
for (uint32_t j = 0; j < CHANNELS_MAX; j++) {
_matrix32[i][j] = lrintf(_matrix[i][j] * 32768);
if (_matrix[i][j]) {
_matrix_ch[i][++ch_in] = j;
}
}
_matrix_ch[i][0] = ch_in;
}
return 0;
}
template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
void
sum2(TYPE_SAMPLE * out,
uint32_t stride_out,
const TYPE_SAMPLE * in1,
const TYPE_SAMPLE * in2,
uint32_t stride_in,
TYPE_COEFF coeff1,
TYPE_COEFF coeff2,
F&& operand,
uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF,
typename std::result_of<F(TYPE_COEFF)>::type>::value,
"function must return the same type as used by matrix_coeff");
for (uint32_t i = 0; i < frames; i++) {
*out = operand(coeff1 * *in1 + coeff2 * *in2);
out += stride_out;
in1 += stride_in;
in2 += stride_in;
}
}
template<typename TYPE_SAMPLE, typename TYPE_COEFF, typename F>
void
copy(TYPE_SAMPLE * out,
uint32_t stride_out,
const TYPE_SAMPLE * in,
uint32_t stride_in,
TYPE_COEFF coeff,
F&& operand,
uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF,
typename std::result_of<F(TYPE_COEFF)>::type>::value,
"function must return the same type as used by matrix_coeff");
for (uint32_t i = 0; i < frames; i++) {
*out = operand(coeff * *in);
out += stride_out;
in += stride_in;
}
}
template <typename TYPE, typename TYPE_COEFF, size_t COLS, typename F>
static int rematrix(const MixerContext * s, TYPE * aOut, const TYPE * aIn,
const TYPE_COEFF (&matrix_coeff)[COLS][COLS],
F&& aF, uint32_t frames)
{
static_assert(
std::is_same<TYPE_COEFF,
typename std::result_of<F(TYPE_COEFF)>::type>::value,
"function must return the same type as used by matrix_coeff");
for (uint32_t out_i = 0; out_i < s->_out_ch_count; out_i++) {
TYPE* out = aOut + out_i;
switch (s->_matrix_ch[out_i][0]) {
case 0:
for (uint32_t i = 0; i < frames; i++) {
out[i * s->_out_ch_count] = 0;
}
break;
case 1: {
int in_i = s->_matrix_ch[out_i][1];
copy(out,
s->_out_ch_count,
aIn + in_i,
s->_in_ch_count,
matrix_coeff[out_i][in_i],
aF,
frames);
} break;
case 2:
sum2(out,
s->_out_ch_count,
aIn + s->_matrix_ch[out_i][1],
aIn + s->_matrix_ch[out_i][2],
s->_in_ch_count,
matrix_coeff[out_i][s->_matrix_ch[out_i][1]],
matrix_coeff[out_i][s->_matrix_ch[out_i][2]],
aF,
frames);
break;
default:
for (uint32_t i = 0; i < frames; i++) {
TYPE_COEFF v = 0;
for (uint32_t j = 0; j < s->_matrix_ch[out_i][0]; j++) {
uint32_t in_i = s->_matrix_ch[out_i][1 + j];
v +=
*(aIn + in_i + i * s->_in_ch_count) * matrix_coeff[out_i][in_i];
}
out[i * s->_out_ch_count] = aF(v);
}
break;
}
}
return 0;
}
struct cubeb_mixer
{
cubeb_mixer(cubeb_sample_format format,
uint32_t in_channels,
cubeb_channel_layout in_layout,
uint32_t out_channels,
cubeb_channel_layout out_layout)
: _context(format, in_channels, in_layout, out_channels, out_layout)
{
}
template<typename T>
void copy_and_trunc(size_t frames,
const T * input_buffer,
T * output_buffer) const
{
if (_context._in_ch_count <= _context._out_ch_count) {
// Not enough channels to copy, fill the gaps with silence.
if (_context._in_ch_count == 1 && _context._out_ch_count >= 2) {
// Special case for upmixing mono input to stereo and more. We will
// duplicate the mono channel to the first two channels. On most system,
// the first two channels are for left and right. It is commonly
// expected that mono will on both left+right channels
for (uint32_t i = 0; i < frames; i++) {
output_buffer[0] = output_buffer[1] = *input_buffer;
PodZero(output_buffer + 2, _context._out_ch_count - 2);
output_buffer += _context._out_ch_count;
input_buffer++;
}
return;
}
for (uint32_t i = 0; i < frames; i++) {
PodCopy(output_buffer, input_buffer, _context._in_ch_count);
output_buffer += _context._in_ch_count;
input_buffer += _context._in_ch_count;
PodZero(output_buffer, _context._out_ch_count - _context._in_ch_count);
output_buffer += _context._out_ch_count - _context._in_ch_count;
}
} else {
for (uint32_t i = 0; i < frames; i++) {
PodCopy(output_buffer, input_buffer, _context._out_ch_count);
output_buffer += _context._out_ch_count;
input_buffer += _context._in_ch_count;
}
}
}
int mix(size_t frames,
const void * input_buffer,
size_t input_buffer_size,
void * output_buffer,
size_t output_buffer_size) const
{
if (frames <= 0 || _context._out_ch_count == 0) {
return 0;
}
// Check if output buffer is of sufficient size.
size_t size_read_needed =
frames * _context._in_ch_count * cubeb_sample_size(_context._format);
if (input_buffer_size < size_read_needed) {
// We don't have enough data to read!
return -1;
}
if (output_buffer_size * _context._in_ch_count <
size_read_needed * _context._out_ch_count) {
return -1;
}
if (!valid()) {
// The channel layouts were invalid or unsupported, instead we will simply
// either drop the extra channels, or fill with silence the missing ones
if (_context._format == CUBEB_SAMPLE_FLOAT32NE) {
copy_and_trunc(frames,
static_cast<const float*>(input_buffer),
static_cast<float*>(output_buffer));
} else {
assert(_context._format == CUBEB_SAMPLE_S16NE);
copy_and_trunc(frames,
static_cast<const int16_t*>(input_buffer),
reinterpret_cast<int16_t*>(output_buffer));
}
return 0;
}
switch (_context._format)
{
case CUBEB_SAMPLE_FLOAT32NE: {
auto f = [](float x) { return x; };
return rematrix(&_context,
static_cast<float*>(output_buffer),
static_cast<const float*>(input_buffer),
_context._matrix_flt,
f,
frames);
}
case CUBEB_SAMPLE_S16NE:
if (_context._clipping) {
auto f = [](int x) {
int y = (x + 16384) >> 15;
// clip the signed integer value into the -32768,32767 range.
if ((y + 0x8000U) & ~0xFFFF) {
return (y >> 31) ^ 0x7FFF;
}
return y;
};
return rematrix(&_context,
static_cast<int16_t*>(output_buffer),
static_cast<const int16_t*>(input_buffer),
_context._matrix32,
f,
frames);
} else {
auto f = [](int x) { return (x + 16384) >> 15; };
return rematrix(&_context,
static_cast<int16_t*>(output_buffer),
static_cast<const int16_t*>(input_buffer),
_context._matrix32,
f,
frames);
}
break;
default:
assert(false);
break;
}
return -1;
}
// Return false if any of the input or ouput layout were invalid.
bool valid() const { return _context._valid; }
virtual ~cubeb_mixer(){};
MixerContext _context;
};
cubeb_mixer* cubeb_mixer_create(cubeb_sample_format format,
uint32_t in_channels,
cubeb_channel_layout in_layout,
uint32_t out_channels,
cubeb_channel_layout out_layout)
{
return new cubeb_mixer(
format, in_channels, in_layout, out_channels, out_layout);
}
void cubeb_mixer_destroy(cubeb_mixer * mixer)
{
delete mixer;
}
int cubeb_mixer_mix(cubeb_mixer * mixer,
size_t frames,
const void * input_buffer,
size_t input_buffer_size,
void * output_buffer,
size_t output_buffer_size)
{
return mixer->mix(
frames, input_buffer, input_buffer_size, output_buffer, output_buffer_size);
}

View file

@ -0,0 +1,37 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_MIXER
#define CUBEB_MIXER
#include "cubeb/cubeb.h" // for cubeb_channel_layout and cubeb_stream_params.
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct cubeb_mixer cubeb_mixer;
cubeb_mixer * cubeb_mixer_create(cubeb_sample_format format,
uint32_t in_channels,
cubeb_channel_layout in_layout,
uint32_t out_channels,
cubeb_channel_layout out_layout);
void cubeb_mixer_destroy(cubeb_mixer * mixer);
int cubeb_mixer_mix(cubeb_mixer * mixer,
size_t frames,
const void * input_buffer,
size_t input_buffer_size,
void * output_buffer,
size_t output_buffer_size);
unsigned int cubeb_channel_layout_nb_channels(cubeb_channel_layout channel_layout);
#if defined(__cplusplus)
}
#endif
#endif // CUBEB_MIXER

1760
dep/cubeb/src/cubeb_opensl.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include <cubeb/cubeb.h>
#include "cubeb_osx_run_loop.h"
#include "cubeb_log.h"
#include <AudioUnit/AudioUnit.h>
#include <CoreAudio/AudioHardware.h>
#include <CoreAudio/HostTime.h>
#include <CoreFoundation/CoreFoundation.h>
void cubeb_set_coreaudio_notification_runloop()
{
/* This is needed so that AudioUnit listeners get called on this thread, and
* not the main thread. If we don't do that, they are not called, or a crash
* occur, depending on the OSX version. */
AudioObjectPropertyAddress runloop_address = {
kAudioHardwarePropertyRunLoop,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster
};
CFRunLoopRef run_loop = nullptr;
OSStatus r;
r = AudioObjectSetPropertyData(kAudioObjectSystemObject,
&runloop_address,
0, NULL, sizeof(CFRunLoopRef), &run_loop);
if (r != noErr) {
LOG("Could not make global CoreAudio notifications use their own thread.");
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright © 2014 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
/* On OSX 10.6 and after, the notification callbacks from the audio hardware are
* called on the main thread. Setting the kAudioHardwarePropertyRunLoop property
* to null tells the OSX to use a separate thread for that.
*
* This has to be called only once per process, so it is in a separate header
* for easy integration in other code bases. */
#if defined(__cplusplus)
extern "C" {
#endif
void cubeb_set_coreaudio_notification_runloop();
#if defined(__cplusplus)
}
#endif

1605
dep/cubeb/src/cubeb_pulse.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,372 @@
/*
* Copyright © 2014 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX
#include <algorithm>
#include <cmath>
#include <cassert>
#include <cstring>
#include <cstddef>
#include <cstdio>
#include "cubeb_resampler.h"
#include "cubeb-speex-resampler.h"
#include "cubeb_resampler_internal.h"
#include "cubeb_utils.h"
int
to_speex_quality(cubeb_resampler_quality q)
{
switch(q) {
case CUBEB_RESAMPLER_QUALITY_VOIP:
return SPEEX_RESAMPLER_QUALITY_VOIP;
case CUBEB_RESAMPLER_QUALITY_DEFAULT:
return SPEEX_RESAMPLER_QUALITY_DEFAULT;
case CUBEB_RESAMPLER_QUALITY_DESKTOP:
return SPEEX_RESAMPLER_QUALITY_DESKTOP;
default:
assert(false);
return 0XFFFFFFFF;
}
}
uint32_t min_buffered_audio_frame(uint32_t sample_rate)
{
return sample_rate / 20;
}
template<typename T>
passthrough_resampler<T>::passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels,
uint32_t sample_rate)
: processor(input_channels)
, stream(s)
, data_callback(cb)
, user_ptr(ptr)
, sample_rate(sample_rate)
{
}
template<typename T>
long passthrough_resampler<T>::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames)
{
if (input_buffer) {
assert(input_frames_count);
}
assert((input_buffer && output_buffer &&
*input_frames_count + static_cast<int>(samples_to_frames(internal_input_buffer.length())) >= output_frames) ||
(output_buffer && !input_buffer && (!input_frames_count || *input_frames_count == 0)) ||
(input_buffer && !output_buffer && output_frames == 0));
// When we have no pending input data and exactly as much input
// as output data, we don't need to copy it into the internal buffer
// and can directly forward it to the callback.
void * in_buf = input_buffer;
unsigned long pop_input_count = 0u;
if (input_buffer && !output_buffer) {
output_frames = *input_frames_count;
} else if(input_buffer) {
if (internal_input_buffer.length() != 0) {
// In this case we have pending input data left and have
// to first append the input so we can pass it as one pointer
// to the callback
internal_input_buffer.push(static_cast<T*>(input_buffer),
frames_to_samples(*input_frames_count));
in_buf = internal_input_buffer.data();
pop_input_count = frames_to_samples(output_frames);
} else if(*input_frames_count > output_frames) {
// In this case we have more input that we need output and
// fill the overflowing input into internal_input_buffer
// Since we have no other pending data, we can nonetheless
// pass the current input data directly to the callback
assert(pop_input_count == 0);
unsigned long samples_off = frames_to_samples(output_frames);
internal_input_buffer.push(static_cast<T*>(input_buffer) + samples_off,
frames_to_samples(*input_frames_count - output_frames));
}
}
long rv = data_callback(stream, user_ptr, in_buf, output_buffer, output_frames);
if (input_buffer) {
if (pop_input_count) {
internal_input_buffer.pop(nullptr, pop_input_count);
}
*input_frames_count = output_frames;
drop_audio_if_needed();
}
return rv;
}
template<typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::cubeb_resampler_speex(InputProcessor * input_processor,
OutputProcessor * output_processor,
cubeb_stream * s,
cubeb_data_callback cb,
void * ptr)
: input_processor(input_processor)
, output_processor(output_processor)
, stream(s)
, data_callback(cb)
, user_ptr(ptr)
{
if (input_processor && output_processor) {
// Add some delay on the processor that has the lowest delay so that the
// streams are synchronized.
uint32_t in_latency = input_processor->latency();
uint32_t out_latency = output_processor->latency();
if (in_latency > out_latency) {
uint32_t latency_diff = in_latency - out_latency;
output_processor->add_latency(latency_diff);
} else if (in_latency < out_latency) {
uint32_t latency_diff = out_latency - in_latency;
input_processor->add_latency(latency_diff);
}
fill_internal = &cubeb_resampler_speex::fill_internal_duplex;
} else if (input_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_input;
} else if (output_processor) {
fill_internal = &cubeb_resampler_speex::fill_internal_output;
}
}
template<typename T, typename InputProcessor, typename OutputProcessor>
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::~cubeb_resampler_speex()
{ }
template<typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed)
{
/* Input and output buffers, typed */
T * in_buffer = reinterpret_cast<T*>(input_buffer);
T * out_buffer = reinterpret_cast<T*>(output_buffer);
return (this->*fill_internal)(in_buffer, input_frames_count,
out_buffer, output_frames_needed);
}
template<typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill_internal_output(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed)
{
assert(!input_buffer && (!input_frames_count || *input_frames_count == 0) &&
output_buffer && output_frames_needed);
if (!draining) {
long got = 0;
T * out_unprocessed = nullptr;
long output_frames_before_processing = 0;
/* fill directly the input buffer of the output processor to save a copy */
output_frames_before_processing =
output_processor->input_needed_for_output(output_frames_needed);
out_unprocessed =
output_processor->input_buffer(output_frames_before_processing);
got = data_callback(stream, user_ptr,
nullptr, out_unprocessed,
output_frames_before_processing);
if (got < output_frames_before_processing) {
draining = true;
if (got < 0) {
return got;
}
}
output_processor->written(got);
}
/* Process the output. If not enough frames have been returned from the
* callback, drain the processors. */
return output_processor->output(output_buffer, output_frames_needed);
}
template<typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill_internal_input(T * input_buffer, long * input_frames_count,
T * output_buffer, long /*output_frames_needed*/)
{
assert(input_buffer && input_frames_count && *input_frames_count &&
!output_buffer);
/* The input data, after eventual resampling. This is passed to the callback. */
T * resampled_input = nullptr;
uint32_t resampled_frame_count = input_processor->output_for_input(*input_frames_count);
/* process the input, and present exactly `output_frames_needed` in the
* callback. */
input_processor->input(input_buffer, *input_frames_count);
size_t frames_resampled = 0;
resampled_input = input_processor->output(resampled_frame_count, &frames_resampled);
*input_frames_count = frames_resampled;
long got = data_callback(stream, user_ptr,
resampled_input, nullptr, resampled_frame_count);
/* Return the number of initial input frames or part of it.
* Since output_frames_needed == 0 in input scenario, the only
* available number outside resampler is the initial number of frames. */
return (*input_frames_count) * (got / resampled_frame_count);
}
template<typename T, typename InputProcessor, typename OutputProcessor>
long
cubeb_resampler_speex<T, InputProcessor, OutputProcessor>
::fill_internal_duplex(T * in_buffer, long * input_frames_count,
T * out_buffer, long output_frames_needed)
{
if (draining) {
// discard input and drain any signal remaining in the resampler.
return output_processor->output(out_buffer, output_frames_needed);
}
/* The input data, after eventual resampling. This is passed to the callback. */
T * resampled_input = nullptr;
/* The output buffer passed down in the callback, that might be resampled. */
T * out_unprocessed = nullptr;
long output_frames_before_processing = 0;
/* The number of frames returned from the callback. */
long got = 0;
/* We need to determine how much frames to present to the consumer.
* - If we have a two way stream, but we're only resampling input, we resample
* the input to the number of output frames.
* - If we have a two way stream, but we're only resampling the output, we
* resize the input buffer of the output resampler to the number of input
* frames, and we resample it afterwards.
* - If we resample both ways, we resample the input to the number of frames
* we would need to pass down to the consumer (before resampling the output),
* get the output data, and resample it to the number of frames needed by the
* caller. */
output_frames_before_processing =
output_processor->input_needed_for_output(output_frames_needed);
/* fill directly the input buffer of the output processor to save a copy */
out_unprocessed =
output_processor->input_buffer(output_frames_before_processing);
if (in_buffer) {
/* process the input, and present exactly `output_frames_needed` in the
* callback. */
input_processor->input(in_buffer, *input_frames_count);
size_t frames_resampled = 0;
resampled_input =
input_processor->output(output_frames_before_processing, &frames_resampled);
*input_frames_count = frames_resampled;
} else {
resampled_input = nullptr;
}
got = data_callback(stream, user_ptr,
resampled_input, out_unprocessed,
output_frames_before_processing);
if (got < output_frames_before_processing) {
draining = true;
if (got < 0) {
return got;
}
}
output_processor->written(got);
input_processor->drop_audio_if_needed();
/* Process the output. If not enough frames have been returned from the
* callback, drain the processors. */
got = output_processor->output(out_buffer, output_frames_needed);
output_processor->drop_audio_if_needed();
return got;
}
/* Resampler C API */
cubeb_resampler *
cubeb_resampler_create(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback,
void * user_ptr,
cubeb_resampler_quality quality)
{
cubeb_sample_format format;
assert(input_params || output_params);
if (input_params) {
format = input_params->format;
} else {
format = output_params->format;
}
switch(format) {
case CUBEB_SAMPLE_S16NE:
return cubeb_resampler_create_internal<short>(stream,
input_params,
output_params,
target_rate,
callback,
user_ptr,
quality);
case CUBEB_SAMPLE_FLOAT32NE:
return cubeb_resampler_create_internal<float>(stream,
input_params,
output_params,
target_rate,
callback,
user_ptr,
quality);
default:
assert(false);
return nullptr;
}
}
long
cubeb_resampler_fill(cubeb_resampler * resampler,
void * input_buffer,
long * input_frames_count,
void * output_buffer,
long output_frames_needed)
{
return resampler->fill(input_buffer, input_frames_count,
output_buffer, output_frames_needed);
}
void
cubeb_resampler_destroy(cubeb_resampler * resampler)
{
delete resampler;
}
long
cubeb_resampler_latency(cubeb_resampler * resampler)
{
return resampler->latency();
}

View file

@ -0,0 +1,85 @@
/*
* Copyright © 2014 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_RESAMPLER_H
#define CUBEB_RESAMPLER_H
#include "cubeb/cubeb.h"
#if defined(__cplusplus)
extern "C" {
#endif
typedef struct cubeb_resampler cubeb_resampler;
typedef enum {
CUBEB_RESAMPLER_QUALITY_VOIP,
CUBEB_RESAMPLER_QUALITY_DEFAULT,
CUBEB_RESAMPLER_QUALITY_DESKTOP
} cubeb_resampler_quality;
/**
* Create a resampler to adapt the requested sample rate into something that
* is accepted by the audio backend.
* @param stream A cubeb_stream instance supplied to the data callback.
* @param input_params Used to calculate bytes per frame and buffer size for
* resampling of the input side of the stream. NULL if input should not be
* resampled.
* @param output_params Used to calculate bytes per frame and buffer size for
* resampling of the output side of the stream. NULL if output should not be
* resampled.
* @param target_rate The sampling rate after resampling for the input side of
* the stream, and/or the sampling rate prior to resampling of the output side
* of the stream.
* @param callback A callback to request data for resampling.
* @param user_ptr User data supplied to the data callback.
* @param quality Quality of the resampler.
* @retval A non-null pointer if success.
*/
cubeb_resampler * cubeb_resampler_create(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback,
void * user_ptr,
cubeb_resampler_quality quality);
/**
* Fill the buffer with frames acquired using the data callback. Resampling will
* happen if necessary.
* @param resampler A cubeb_resampler instance.
* @param input_buffer A buffer of input samples
* @param input_frame_count The size of the buffer. Returns the number of frames
* consumed.
* @param output_buffer The buffer to be filled.
* @param output_frames_needed Number of frames that should be produced.
* @retval Number of frames that are actually produced.
* @retval CUBEB_ERROR on error.
*/
long cubeb_resampler_fill(cubeb_resampler * resampler,
void * input_buffer,
long * input_frame_count,
void * output_buffer,
long output_frames_needed);
/**
* Destroy a cubeb_resampler.
* @param resampler A cubeb_resampler instance.
*/
void cubeb_resampler_destroy(cubeb_resampler * resampler);
/**
* Returns the latency, in frames, of the resampler.
* @param resampler A cubeb resampler instance.
* @retval The latency, in frames, induced by the resampler.
*/
long cubeb_resampler_latency(cubeb_resampler * resampler);
#if defined(__cplusplus)
}
#endif
#endif /* CUBEB_RESAMPLER_H */

View file

@ -0,0 +1,602 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_RESAMPLER_INTERNAL)
#define CUBEB_RESAMPLER_INTERNAL
#include <cmath>
#include <cassert>
#include <algorithm>
#include <memory>
#ifdef CUBEB_GECKO_BUILD
#include "mozilla/UniquePtr.h"
// In libc++, symbols such as std::unique_ptr may be defined in std::__1.
// The _LIBCPP_BEGIN_NAMESPACE_STD and _LIBCPP_END_NAMESPACE_STD macros
// will expand to the correct namespace.
#ifdef _LIBCPP_BEGIN_NAMESPACE_STD
#define MOZ_BEGIN_STD_NAMESPACE _LIBCPP_BEGIN_NAMESPACE_STD
#define MOZ_END_STD_NAMESPACE _LIBCPP_END_NAMESPACE_STD
#else
#define MOZ_BEGIN_STD_NAMESPACE namespace std {
#define MOZ_END_STD_NAMESPACE }
#endif
MOZ_BEGIN_STD_NAMESPACE
using mozilla::DefaultDelete;
using mozilla::UniquePtr;
#define default_delete DefaultDelete
#define unique_ptr UniquePtr
MOZ_END_STD_NAMESPACE
#endif
#include "cubeb/cubeb.h"
#include "cubeb_utils.h"
#include "cubeb-speex-resampler.h"
#include "cubeb_resampler.h"
#include <stdio.h>
/* This header file contains the internal C++ API of the resamplers, for testing. */
// When dropping audio input frames to prevent building
// an input delay, this function returns the number of frames
// to keep in the buffer.
// @parameter sample_rate The sample rate of the stream.
// @return A number of frames to keep.
uint32_t min_buffered_audio_frame(uint32_t sample_rate);
int to_speex_quality(cubeb_resampler_quality q);
struct cubeb_resampler {
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long frames_needed) = 0;
virtual long latency() = 0;
virtual ~cubeb_resampler() {}
};
/** Base class for processors. This is just used to share methods for now. */
class processor {
public:
explicit processor(uint32_t channels)
: channels(channels)
{}
protected:
size_t frames_to_samples(size_t frames) const
{
return frames * channels;
}
size_t samples_to_frames(size_t samples) const
{
assert(!(samples % channels));
return samples / channels;
}
/** The number of channel of the audio buffers to be resampled. */
const uint32_t channels;
};
template<typename T>
class passthrough_resampler : public cubeb_resampler
, public processor {
public:
passthrough_resampler(cubeb_stream * s,
cubeb_data_callback cb,
void * ptr,
uint32_t input_channels,
uint32_t sample_rate);
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames);
virtual long latency()
{
return 0;
}
void drop_audio_if_needed()
{
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
uint32_t available = samples_to_frames(internal_input_buffer.length());
if (available > to_keep) {
internal_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
/* This allows to buffer some input to account for the fact that we buffer
* some inputs. */
auto_array<T> internal_input_buffer;
uint32_t sample_rate;
};
/** Bidirectional resampler, can resample an input and an output stream, or just
* an input stream or output stream. In this case a delay is inserted in the
* opposite direction to keep the streams synchronized. */
template<typename T, typename InputProcessing, typename OutputProcessing>
class cubeb_resampler_speex : public cubeb_resampler {
public:
cubeb_resampler_speex(InputProcessing * input_processor,
OutputProcessing * output_processor,
cubeb_stream * s,
cubeb_data_callback cb,
void * ptr);
virtual ~cubeb_resampler_speex();
virtual long fill(void * input_buffer, long * input_frames_count,
void * output_buffer, long output_frames_needed);
virtual long latency()
{
if (input_processor && output_processor) {
assert(input_processor->latency() == output_processor->latency());
return input_processor->latency();
} else if (input_processor) {
return input_processor->latency();
} else {
return output_processor->latency();
}
}
private:
typedef long(cubeb_resampler_speex::*processing_callback)(T * input_buffer, long * input_frames_count, T * output_buffer, long output_frames_needed);
long fill_internal_duplex(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
long fill_internal_input(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
long fill_internal_output(T * input_buffer, long * input_frames_count,
T * output_buffer, long output_frames_needed);
std::unique_ptr<InputProcessing> input_processor;
std::unique_ptr<OutputProcessing> output_processor;
processing_callback fill_internal;
cubeb_stream * const stream;
const cubeb_data_callback data_callback;
void * const user_ptr;
bool draining = false;
};
/** Handles one way of a (possibly) duplex resampler, working on interleaved
* audio buffers of type T. This class is designed so that the number of frames
* coming out of the resampler can be precisely controled. It manages its own
* input buffer, and can use the caller's output buffer, or allocate its own. */
template<typename T>
class cubeb_resampler_speex_one_way : public processor {
public:
/** The sample type of this resampler, either 16-bit integers or 32-bit
* floats. */
typedef T sample_type;
/** Construct a resampler resampling from #source_rate to #target_rate, that
* can be arbitrary, strictly positive number.
* @parameter channels The number of channels this resampler will resample.
* @parameter source_rate The sample-rate of the audio input.
* @parameter target_rate The sample-rate of the audio output.
* @parameter quality A number between 0 (fast, low quality) and 10 (slow,
* high quality). */
cubeb_resampler_speex_one_way(uint32_t channels,
uint32_t source_rate,
uint32_t target_rate,
int quality)
: processor(channels)
, resampling_ratio(static_cast<float>(source_rate) / target_rate)
, source_rate(source_rate)
, additional_latency(0)
, leftover_samples(0)
{
int r;
speex_resampler = speex_resampler_init(channels, source_rate,
target_rate, quality, &r);
assert(r == RESAMPLER_ERR_SUCCESS && "resampler allocation failure");
}
/** Destructor, deallocate the resampler */
virtual ~cubeb_resampler_speex_one_way()
{
speex_resampler_destroy(speex_resampler);
}
/** Sometimes, it is necessary to add latency on one way of a two-way
* resampler so that the stream are synchronized. This must be called only on
* a fresh resampler, otherwise, silent samples will be inserted in the
* stream.
* @param frames the number of frames of latency to add. */
void add_latency(size_t frames)
{
additional_latency += frames;
resampling_in_buffer.push_silence(frames_to_samples(frames));
}
/* Fill the resampler with `input_frame_count` frames. */
void input(T * input_buffer, size_t input_frame_count)
{
resampling_in_buffer.push(input_buffer,
frames_to_samples(input_frame_count));
}
/** Outputs exactly `output_frame_count` into `output_buffer`.
* `output_buffer` has to be at least `output_frame_count` long. */
size_t output(T * output_buffer, size_t output_frame_count)
{
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
uint32_t out_len = output_frame_count;
speex_resample(resampling_in_buffer.data(), &in_len,
output_buffer, &out_len);
/* This shifts back any unresampled samples to the beginning of the input
buffer. */
resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
return out_len;
}
size_t output_for_input(uint32_t input_frames)
{
return (size_t)floorf((input_frames + samples_to_frames(resampling_in_buffer.length()))
/ resampling_ratio);
}
/** Returns a buffer containing exactly `output_frame_count` resampled frames.
* The consumer should not hold onto the pointer. */
T * output(size_t output_frame_count, size_t * input_frames_used)
{
if (resampling_out_buffer.capacity() < frames_to_samples(output_frame_count)) {
resampling_out_buffer.reserve(frames_to_samples(output_frame_count));
}
uint32_t in_len = samples_to_frames(resampling_in_buffer.length());
uint32_t out_len = output_frame_count;
speex_resample(resampling_in_buffer.data(), &in_len,
resampling_out_buffer.data(), &out_len);
assert(out_len == output_frame_count);
/* This shifts back any unresampled samples to the beginning of the input
buffer. */
resampling_in_buffer.pop(nullptr, frames_to_samples(in_len));
*input_frames_used = in_len;
return resampling_out_buffer.data();
}
/** Get the latency of the resampler, in output frames. */
uint32_t latency() const
{
/* The documentation of the resampler talks about "samples" here, but it
* only consider a single channel here so it's the same number of frames. */
int latency = 0;
latency =
speex_resampler_get_output_latency(speex_resampler) + additional_latency;
assert(latency >= 0);
return latency;
}
/** Returns the number of frames to pass in the input of the resampler to have
* exactly `output_frame_count` resampled frames. This can return a number
* slightly bigger than what is strictly necessary, but it guaranteed that the
* number of output frames will be exactly equal. */
uint32_t input_needed_for_output(uint32_t output_frame_count) const
{
int32_t unresampled_frames_left = samples_to_frames(resampling_in_buffer.length());
int32_t resampled_frames_left = samples_to_frames(resampling_out_buffer.length());
float input_frames_needed =
(output_frame_count - unresampled_frames_left) * resampling_ratio
- resampled_frames_left;
if (input_frames_needed < 0) {
return 0;
}
return (uint32_t)ceilf(input_frames_needed);
}
/** Returns a pointer to the input buffer, that contains empty space for at
* least `frame_count` elements. This is useful so that consumer can directly
* write into the input buffer of the resampler. The pointer returned is
* adjusted so that leftover data are not overwritten.
*/
T * input_buffer(size_t frame_count)
{
leftover_samples = resampling_in_buffer.length();
resampling_in_buffer.reserve(leftover_samples +
frames_to_samples(frame_count));
return resampling_in_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
void written(size_t written_frames)
{
resampling_in_buffer.set_length(leftover_samples +
frames_to_samples(written_frames));
}
void drop_audio_if_needed()
{
// Keep at most 100ms buffered.
uint32_t available = samples_to_frames(resampling_in_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(source_rate);
if (available > to_keep) {
resampling_in_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
/** Wrapper for the speex resampling functions to have a typed
* interface. */
void speex_resample(float * input_buffer, uint32_t * input_frame_count,
float * output_buffer, uint32_t * output_frame_count)
{
#ifndef NDEBUG
int rv;
rv =
#endif
speex_resampler_process_interleaved_float(speex_resampler,
input_buffer,
input_frame_count,
output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
void speex_resample(short * input_buffer, uint32_t * input_frame_count,
short * output_buffer, uint32_t * output_frame_count)
{
#ifndef NDEBUG
int rv;
rv =
#endif
speex_resampler_process_interleaved_int(speex_resampler,
input_buffer,
input_frame_count,
output_buffer,
output_frame_count);
assert(rv == RESAMPLER_ERR_SUCCESS);
}
/** The state for the speex resampler used internaly. */
SpeexResamplerState * speex_resampler;
/** Source rate / target rate. */
const float resampling_ratio;
const uint32_t source_rate;
/** Storage for the input frames, to be resampled. Also contains
* any unresampled frames after resampling. */
auto_array<T> resampling_in_buffer;
/* Storage for the resampled frames, to be passed back to the caller. */
auto_array<T> resampling_out_buffer;
/** Additional latency inserted into the pipeline for synchronisation. */
uint32_t additional_latency;
/** When `input_buffer` is called, this allows tracking the number of samples
that were in the buffer. */
uint32_t leftover_samples;
};
/** This class allows delaying an audio stream by `frames` frames. */
template<typename T>
class delay_line : public processor {
public:
/** Constructor
* @parameter frames the number of frames of delay.
* @parameter channels the number of channels of this delay line.
* @parameter sample_rate sample-rate of the audio going through this delay line */
delay_line(uint32_t frames, uint32_t channels, uint32_t sample_rate)
: processor(channels)
, length(frames)
, leftover_samples(0)
, sample_rate(sample_rate)
{
/* Fill the delay line with some silent frames to add latency. */
delay_input_buffer.push_silence(frames * channels);
}
/* Add some latency to the delay line.
* @param frames the number of frames of latency to add. */
void add_latency(size_t frames)
{
length += frames;
delay_input_buffer.push_silence(frames_to_samples(frames));
}
/** Push some frames into the delay line.
* @parameter buffer the frames to push.
* @parameter frame_count the number of frames in #buffer. */
void input(T * buffer, uint32_t frame_count)
{
delay_input_buffer.push(buffer, frames_to_samples(frame_count));
}
/** Pop some frames from the internal buffer, into a internal output buffer.
* @parameter frames_needed the number of frames to be returned.
* @return a buffer containing the delayed frames. The consumer should not
* hold onto the pointer. */
T * output(uint32_t frames_needed, size_t * input_frames_used)
{
if (delay_output_buffer.capacity() < frames_to_samples(frames_needed)) {
delay_output_buffer.reserve(frames_to_samples(frames_needed));
}
delay_output_buffer.clear();
delay_output_buffer.push(delay_input_buffer.data(),
frames_to_samples(frames_needed));
delay_input_buffer.pop(nullptr, frames_to_samples(frames_needed));
*input_frames_used = frames_needed;
return delay_output_buffer.data();
}
/** Get a pointer to the first writable location in the input buffer>
* @parameter frames_needed the number of frames the user needs to write into
* the buffer.
* @returns a pointer to a location in the input buffer where #frames_needed
* can be writen. */
T * input_buffer(uint32_t frames_needed)
{
leftover_samples = delay_input_buffer.length();
delay_input_buffer.reserve(leftover_samples + frames_to_samples(frames_needed));
return delay_input_buffer.data() + leftover_samples;
}
/** This method works with `input_buffer`, and allows to inform the processor
how much frames have been written in the provided buffer. */
void written(size_t frames_written)
{
delay_input_buffer.set_length(leftover_samples +
frames_to_samples(frames_written));
}
/** Drains the delay line, emptying the buffer.
* @parameter output_buffer the buffer in which the frames are written.
* @parameter frames_needed the maximum number of frames to write.
* @return the actual number of frames written. */
size_t output(T * output_buffer, uint32_t frames_needed)
{
uint32_t in_len = samples_to_frames(delay_input_buffer.length());
uint32_t out_len = frames_needed;
uint32_t to_pop = std::min(in_len, out_len);
delay_input_buffer.pop(output_buffer, frames_to_samples(to_pop));
return to_pop;
}
/** Returns the number of frames one needs to input into the delay line to get
* #frames_needed frames back.
* @parameter frames_needed the number of frames one want to write into the
* delay_line
* @returns the number of frames one will get. */
size_t input_needed_for_output(uint32_t frames_needed) const
{
return frames_needed;
}
/** Returns the number of frames produces for `input_frames` frames in input */
size_t output_for_input(uint32_t input_frames)
{
return input_frames;
}
/** The number of frames this delay line delays the stream by.
* @returns The number of frames of delay. */
size_t latency()
{
return length;
}
void drop_audio_if_needed()
{
size_t available = samples_to_frames(delay_input_buffer.length());
uint32_t to_keep = min_buffered_audio_frame(sample_rate);
if (available > to_keep) {
delay_input_buffer.pop(nullptr, frames_to_samples(available - to_keep));
}
}
private:
/** The length, in frames, of this delay line */
uint32_t length;
/** When `input_buffer` is called, this allows tracking the number of samples
that where in the buffer. */
uint32_t leftover_samples;
/** The input buffer, where the delay is applied. */
auto_array<T> delay_input_buffer;
/** The output buffer. This is only ever used if using the ::output with a
* single argument. */
auto_array<T> delay_output_buffer;
uint32_t sample_rate;
};
/** This sits behind the C API and is more typed. */
template<typename T>
cubeb_resampler *
cubeb_resampler_create_internal(cubeb_stream * stream,
cubeb_stream_params * input_params,
cubeb_stream_params * output_params,
unsigned int target_rate,
cubeb_data_callback callback,
void * user_ptr,
cubeb_resampler_quality quality)
{
std::unique_ptr<cubeb_resampler_speex_one_way<T>> input_resampler = nullptr;
std::unique_ptr<cubeb_resampler_speex_one_way<T>> output_resampler = nullptr;
std::unique_ptr<delay_line<T>> input_delay = nullptr;
std::unique_ptr<delay_line<T>> output_delay = nullptr;
assert((input_params || output_params) &&
"need at least one valid parameter pointer.");
/* All the streams we have have a sample rate that matches the target
sample rate, use a no-op resampler, that simply forwards the buffers to the
callback. */
if (((input_params && input_params->rate == target_rate) &&
(output_params && output_params->rate == target_rate)) ||
(input_params && !output_params && (input_params->rate == target_rate)) ||
(output_params && !input_params && (output_params->rate == target_rate))) {
return new passthrough_resampler<T>(stream, callback,
user_ptr,
input_params ? input_params->channels : 0,
target_rate);
}
/* Determine if we need to resampler one or both directions, and create the
resamplers. */
if (output_params && (output_params->rate != target_rate)) {
output_resampler.reset(
new cubeb_resampler_speex_one_way<T>(output_params->channels,
target_rate,
output_params->rate,
to_speex_quality(quality)));
if (!output_resampler) {
return NULL;
}
}
if (input_params && (input_params->rate != target_rate)) {
input_resampler.reset(
new cubeb_resampler_speex_one_way<T>(input_params->channels,
input_params->rate,
target_rate,
to_speex_quality(quality)));
if (!input_resampler) {
return NULL;
}
}
/* If we resample only one direction but we have a duplex stream, insert a
* delay line with a length equal to the resampler latency of the
* other direction so that the streams are synchronized. */
if (input_resampler && !output_resampler && input_params && output_params) {
output_delay.reset(new delay_line<T>(input_resampler->latency(),
output_params->channels,
output_params->rate));
if (!output_delay) {
return NULL;
}
} else if (output_resampler && !input_resampler && input_params && output_params) {
input_delay.reset(new delay_line<T>(output_resampler->latency(),
input_params->channels,
output_params->rate));
if (!input_delay) {
return NULL;
}
}
if (input_resampler && output_resampler) {
return new cubeb_resampler_speex<T,
cubeb_resampler_speex_one_way<T>,
cubeb_resampler_speex_one_way<T>>
(input_resampler.release(),
output_resampler.release(),
stream, callback, user_ptr);
} else if (input_resampler) {
return new cubeb_resampler_speex<T,
cubeb_resampler_speex_one_way<T>,
delay_line<T>>
(input_resampler.release(),
output_delay.release(),
stream, callback, user_ptr);
} else {
return new cubeb_resampler_speex<T,
delay_line<T>,
cubeb_resampler_speex_one_way<T>>
(input_delay.release(),
output_resampler.release(),
stream, callback, user_ptr);
}
}
#endif /* CUBEB_RESAMPLER_INTERNAL */

View file

@ -0,0 +1,159 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_RING_ARRAY_H
#define CUBEB_RING_ARRAY_H
#include "cubeb_utils.h"
/** Ring array of pointers is used to hold buffers. In case that
asynchronous producer/consumer callbacks do not arrive in a
repeated order the ring array stores the buffers and fetch
them in the correct order. */
typedef struct {
AudioBuffer * buffer_array; /**< Array that hold pointers of the allocated space for the buffers. */
unsigned int tail; /**< Index of the last element (first to deliver). */
unsigned int count; /**< Number of elements in the array. */
unsigned int capacity; /**< Total length of the array. */
} ring_array;
static int
single_audiobuffer_init(AudioBuffer * buffer,
uint32_t bytesPerFrame,
uint32_t channelsPerFrame,
uint32_t frames)
{
assert(buffer);
assert(bytesPerFrame > 0 && channelsPerFrame && frames > 0);
size_t size = bytesPerFrame * frames;
buffer->mData = operator new(size);
if (buffer->mData == NULL) {
return CUBEB_ERROR;
}
PodZero(static_cast<char*>(buffer->mData), size);
buffer->mNumberChannels = channelsPerFrame;
buffer->mDataByteSize = size;
return CUBEB_OK;
}
/** Initialize the ring array.
@param ra The ring_array pointer of allocated structure.
@retval 0 on success. */
int
ring_array_init(ring_array * ra,
uint32_t capacity,
uint32_t bytesPerFrame,
uint32_t channelsPerFrame,
uint32_t framesPerBuffer)
{
assert(ra);
if (capacity == 0 || bytesPerFrame == 0 ||
channelsPerFrame == 0 || framesPerBuffer == 0) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
ra->capacity = capacity;
ra->tail = 0;
ra->count = 0;
ra->buffer_array = new AudioBuffer[ra->capacity];
PodZero(ra->buffer_array, ra->capacity);
if (ra->buffer_array == NULL) {
return CUBEB_ERROR;
}
for (unsigned int i = 0; i < ra->capacity; ++i) {
if (single_audiobuffer_init(&ra->buffer_array[i],
bytesPerFrame,
channelsPerFrame,
framesPerBuffer) != CUBEB_OK) {
return CUBEB_ERROR;
}
}
return CUBEB_OK;
}
/** Destroy the ring array.
@param ra The ring_array pointer.*/
void
ring_array_destroy(ring_array * ra)
{
assert(ra);
if (ra->buffer_array == NULL){
return;
}
for (unsigned int i = 0; i < ra->capacity; ++i) {
if (ra->buffer_array[i].mData) {
operator delete(ra->buffer_array[i].mData);
}
}
delete [] ra->buffer_array;
}
/** Get the allocated buffer to be stored with fresh data.
@param ra The ring_array pointer.
@retval Pointer of the allocated space to be stored with fresh data or NULL if full. */
AudioBuffer *
ring_array_get_free_buffer(ring_array * ra)
{
assert(ra && ra->buffer_array);
assert(ra->buffer_array[0].mData != NULL);
if (ra->count == ra->capacity) {
return NULL;
}
assert(ra->count == 0 || (ra->tail + ra->count) % ra->capacity != ra->tail);
AudioBuffer * ret = &ra->buffer_array[(ra->tail + ra->count) % ra->capacity];
++ra->count;
assert(ra->count <= ra->capacity);
return ret;
}
/** Get the next available buffer with data.
@param ra The ring_array pointer.
@retval Pointer of the next in order data buffer or NULL if empty. */
AudioBuffer *
ring_array_get_data_buffer(ring_array * ra)
{
assert(ra && ra->buffer_array);
assert(ra->buffer_array[0].mData != NULL);
if (ra->count == 0) {
return NULL;
}
AudioBuffer * ret = &ra->buffer_array[ra->tail];
ra->tail = (ra->tail + 1) % ra->capacity;
assert(ra->tail < ra->capacity);
assert(ra->count > 0);
--ra->count;
return ret;
}
/** When array is empty get the first allocated buffer in the array.
@param ra The ring_array pointer.
@retval If arrays is empty, pointer of the allocated space else NULL. */
AudioBuffer *
ring_array_get_dummy_buffer(ring_array * ra)
{
assert(ra && ra->buffer_array);
assert(ra->capacity > 0);
if (ra->count > 0) {
return NULL;
}
return &ra->buffer_array[0];
}
#endif //CUBEB_RING_ARRAY_H

View file

@ -0,0 +1,495 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_RING_BUFFER_H
#define CUBEB_RING_BUFFER_H
#include "cubeb_utils.h"
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <memory>
#include <thread>
/**
* Single producer single consumer lock-free and wait-free ring buffer.
*
* This data structure allows producing data from one thread, and consuming it on
* another thread, safely and without explicit synchronization. If used on two
* threads, this data structure uses atomics for thread safety. It is possible
* to disable the use of atomics at compile time and only use this data
* structure on one thread.
*
* The role for the producer and the consumer must be constant, i.e., the
* producer should always be on one thread and the consumer should always be on
* another thread.
*
* Some words about the inner workings of this class:
* - Capacity is fixed. Only one allocation is performed, in the constructor.
* When reading and writing, the return value of the method allows checking if
* the ring buffer is empty or full.
* - We always keep the read index at least one element ahead of the write
* index, so we can distinguish between an empty and a full ring buffer: an
* empty ring buffer is when the write index is at the same position as the
* read index. A full buffer is when the write index is exactly one position
* before the read index.
* - We synchronize updates to the read index after having read the data, and
* the write index after having written the data. This means that the each
* thread can only touch a portion of the buffer that is not touched by the
* other thread.
* - Callers are expected to provide buffers. When writing to the queue,
* elements are copied into the internal storage from the buffer passed in.
* When reading from the queue, the user is expected to provide a buffer.
* Because this is a ring buffer, data might not be contiguous in memory,
* providing an external buffer to copy into is an easy way to have linear
* data for further processing.
*/
template <typename T>
class ring_buffer_base
{
public:
/**
* Constructor for a ring buffer.
*
* This performs an allocation, but is the only allocation that will happen
* for the life time of a `ring_buffer_base`.
*
* @param capacity The maximum number of element this ring buffer will hold.
*/
ring_buffer_base(int capacity)
/* One more element to distinguish from empty and full buffer. */
: capacity_(capacity + 1)
{
assert(storage_capacity() <
std::numeric_limits<int>::max() / 2 &&
"buffer too large for the type of index used.");
assert(capacity_ > 0);
data_.reset(new T[storage_capacity()]);
/* If this queue is using atomics, initializing those members as the last
* action in the constructor acts as a full barrier, and allow capacity() to
* be thread-safe. */
write_index_ = 0;
read_index_ = 0;
}
/**
* Push `count` zero or default constructed elements in the array.
*
* Only safely called on the producer thread.
*
* @param count The number of elements to enqueue.
* @return The number of element enqueued.
*/
int enqueue_default(int count)
{
return enqueue(nullptr, count);
}
/**
* @brief Put an element in the queue
*
* Only safely called on the producer thread.
*
* @param element The element to put in the queue.
*
* @return 1 if the element was inserted, 0 otherwise.
*/
int enqueue(T& element)
{
return enqueue(&element, 1);
}
/**
* Push `count` elements in the ring buffer.
*
* Only safely called on the producer thread.
*
* @param elements a pointer to a buffer containing at least `count` elements.
* If `elements` is nullptr, zero or default constructed elements are enqueued.
* @param count The number of elements to read from `elements`
* @return The number of elements successfully coped from `elements` and inserted
* into the ring buffer.
*/
int enqueue(T * elements, int count)
{
#ifndef NDEBUG
assert_correct_thread(producer_id);
#endif
int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
int wr_idx = write_index_.load(std::memory_order::memory_order_relaxed);
if (full_internal(rd_idx, wr_idx)) {
return 0;
}
int to_write =
std::min(available_write_internal(rd_idx, wr_idx), count);
/* First part, from the write index to the end of the array. */
int first_part = std::min(storage_capacity() - wr_idx,
to_write);
/* Second part, from the beginning of the array */
int second_part = to_write - first_part;
if (elements) {
Copy(data_.get() + wr_idx, elements, first_part);
Copy(data_.get(), elements + first_part, second_part);
} else {
ConstructDefault(data_.get() + wr_idx, first_part);
ConstructDefault(data_.get(), second_part);
}
write_index_.store(increment_index(wr_idx, to_write), std::memory_order::memory_order_release);
return to_write;
}
/**
* Retrieve at most `count` elements from the ring buffer, and copy them to
* `elements`, if non-null.
*
* Only safely called on the consumer side.
*
* @param elements A pointer to a buffer with space for at least `count`
* elements. If `elements` is `nullptr`, `count` element will be discarded.
* @param count The maximum number of elements to dequeue.
* @return The number of elements written to `elements`.
*/
int dequeue(T * elements, int count)
{
#ifndef NDEBUG
assert_correct_thread(consumer_id);
#endif
int wr_idx = write_index_.load(std::memory_order::memory_order_acquire);
int rd_idx = read_index_.load(std::memory_order::memory_order_relaxed);
if (empty_internal(rd_idx, wr_idx)) {
return 0;
}
int to_read =
std::min(available_read_internal(rd_idx, wr_idx), count);
int first_part = std::min(storage_capacity() - rd_idx, to_read);
int second_part = to_read - first_part;
if (elements) {
Copy(elements, data_.get() + rd_idx, first_part);
Copy(elements + first_part, data_.get(), second_part);
}
read_index_.store(increment_index(rd_idx, to_read), std::memory_order::memory_order_relaxed);
return to_read;
}
/**
* Get the number of available element for consuming.
*
* Only safely called on the consumer thread.
*
* @return The number of available elements for reading.
*/
int available_read() const
{
#ifndef NDEBUG
assert_correct_thread(consumer_id);
#endif
return available_read_internal(read_index_.load(std::memory_order::memory_order_relaxed),
write_index_.load(std::memory_order::memory_order_relaxed));
}
/**
* Get the number of available elements for consuming.
*
* Only safely called on the producer thread.
*
* @return The number of empty slots in the buffer, available for writing.
*/
int available_write() const
{
#ifndef NDEBUG
assert_correct_thread(producer_id);
#endif
return available_write_internal(read_index_.load(std::memory_order::memory_order_relaxed),
write_index_.load(std::memory_order::memory_order_relaxed));
}
/**
* Get the total capacity, for this ring buffer.
*
* Can be called safely on any thread.
*
* @return The maximum capacity of this ring buffer.
*/
int capacity() const
{
return storage_capacity() - 1;
}
/**
* Reset the consumer and producer thread identifier, in case the thread are
* being changed. This has to be externally synchronized. This is no-op when
* asserts are disabled.
*/
void reset_thread_ids()
{
#ifndef NDEBUG
consumer_id = producer_id = std::thread::id();
#endif
}
private:
/** Return true if the ring buffer is empty.
*
* @param read_index the read index to consider
* @param write_index the write index to consider
* @return true if the ring buffer is empty, false otherwise.
**/
bool empty_internal(int read_index,
int write_index) const
{
return write_index == read_index;
}
/** Return true if the ring buffer is full.
*
* This happens if the write index is exactly one element behind the read
* index.
*
* @param read_index the read index to consider
* @param write_index the write index to consider
* @return true if the ring buffer is full, false otherwise.
**/
bool full_internal(int read_index,
int write_index) const
{
return (write_index + 1) % storage_capacity() == read_index;
}
/**
* Return the size of the storage. It is one more than the number of elements
* that can be stored in the buffer.
*
* @return the number of elements that can be stored in the buffer.
*/
int storage_capacity() const
{
return capacity_;
}
/**
* Returns the number of elements available for reading.
*
* @return the number of available elements for reading.
*/
int
available_read_internal(int read_index,
int write_index) const
{
if (write_index >= read_index) {
return write_index - read_index;
} else {
return write_index + storage_capacity() - read_index;
}
}
/**
* Returns the number of empty elements, available for writing.
*
* @return the number of elements that can be written into the array.
*/
int
available_write_internal(int read_index,
int write_index) const
{
/* We substract one element here to always keep at least one sample
* free in the buffer, to distinguish between full and empty array. */
int rv = read_index - write_index - 1;
if (write_index >= read_index) {
rv += storage_capacity();
}
return rv;
}
/**
* Increments an index, wrapping it around the storage.
*
* @param index a reference to the index to increment.
* @param increment the number by which `index` is incremented.
* @return the new index.
*/
int
increment_index(int index, int increment) const
{
assert(increment >= 0);
return (index + increment) % storage_capacity();
}
/**
* @brief This allows checking that enqueue (resp. dequeue) are always called
* by the right thread.
*
* @param id the id of the thread that has called the calling method first.
*/
#ifndef NDEBUG
static void assert_correct_thread(std::thread::id& id)
{
if (id == std::thread::id()) {
id = std::this_thread::get_id();
return;
}
assert(id == std::this_thread::get_id());
}
#endif
/** Index at which the oldest element is at, in samples. */
std::atomic<int> read_index_;
/** Index at which to write new elements. `write_index` is always at
* least one element ahead of `read_index_`. */
std::atomic<int> write_index_;
/** Maximum number of elements that can be stored in the ring buffer. */
const int capacity_;
/** Data storage */
std::unique_ptr<T[]> data_;
#ifndef NDEBUG
/** The id of the only thread that is allowed to read from the queue. */
mutable std::thread::id consumer_id;
/** The id of the only thread that is allowed to write from the queue. */
mutable std::thread::id producer_id;
#endif
};
/**
* Adapter for `ring_buffer_base` that exposes an interface in frames.
*/
template <typename T>
class audio_ring_buffer_base
{
public:
/**
* @brief Constructor.
*
* @param channel_count Number of channels.
* @param capacity_in_frames The capacity in frames.
*/
audio_ring_buffer_base(int channel_count, int capacity_in_frames)
: channel_count(channel_count)
, ring_buffer(frames_to_samples(capacity_in_frames))
{
assert(channel_count > 0);
}
/**
* @brief Enqueue silence.
*
* Only safely called on the producer thread.
*
* @param frame_count The number of frames of silence to enqueue.
* @return The number of frames of silence actually written to the queue.
*/
int enqueue_default(int frame_count)
{
return samples_to_frames(ring_buffer.enqueue(nullptr, frames_to_samples(frame_count)));
}
/**
* @brief Enqueue `frames_count` frames of audio.
*
* Only safely called from the producer thread.
*
* @param [in] frames If non-null, the frames to enqueue.
* Otherwise, silent frames are enqueued.
* @param frame_count The number of frames to enqueue.
*
* @return The number of frames enqueued
*/
int enqueue(T * frames, int frame_count)
{
return samples_to_frames(ring_buffer.enqueue(frames, frames_to_samples(frame_count)));
}
/**
* @brief Removes `frame_count` frames from the buffer, and
* write them to `frames` if it is non-null.
*
* Only safely called on the consumer thread.
*
* @param frames If non-null, the frames are copied to `frames`.
* Otherwise, they are dropped.
* @param frame_count The number of frames to remove.
*
* @return The number of frames actually dequeud.
*/
int dequeue(T * frames, int frame_count)
{
return samples_to_frames(ring_buffer.dequeue(frames, frames_to_samples(frame_count)));
}
/**
* Get the number of available frames of audio for consuming.
*
* Only safely called on the consumer thread.
*
* @return The number of available frames of audio for reading.
*/
int available_read() const
{
return samples_to_frames(ring_buffer.available_read());
}
/**
* Get the number of available frames of audio for consuming.
*
* Only safely called on the producer thread.
*
* @return The number of empty slots in the buffer, available for writing.
*/
int available_write() const
{
return samples_to_frames(ring_buffer.available_write());
}
/**
* Get the total capacity, for this ring buffer.
*
* Can be called safely on any thread.
*
* @return The maximum capacity of this ring buffer.
*/
int capacity() const
{
return samples_to_frames(ring_buffer.capacity());
}
private:
/**
* @brief Frames to samples conversion.
*
* @param frames The number of frames.
*
* @return A number of samples.
*/
int frames_to_samples(int frames) const
{
return frames * channel_count;
}
/**
* @brief Samples to frames conversion.
*
* @param samples The number of samples.
*
* @return A number of frames.
*/
int samples_to_frames(int samples) const
{
return samples / channel_count;
}
/** Number of channels of audio that will stream through this ring buffer. */
int channel_count;
/** The underlying ring buffer that is used to store the data. */
ring_buffer_base<T> ring_buffer;
};
/**
* Lock-free instantiation of the `ring_buffer_base` type. This is safe to use
* from two threads, one producer, one consumer (that never change role),
* without explicit synchronization.
*/
template<typename T>
using lock_free_queue = ring_buffer_base<T>;
/**
* Lock-free instantiation of the `audio_ring_buffer` type. This is safe to use
* from two threads, one producer, one consumer (that never change role),
* without explicit synchronization.
*/
template<typename T>
using lock_free_audio_ring_buffer = audio_ring_buffer_base<T>;
#endif // CUBEB_RING_BUFFER_H

669
dep/cubeb/src/cubeb_sndio.c Normal file
View file

@ -0,0 +1,669 @@
/*
* Copyright (c) 2011 Alexandre Ratchov <alex@caoua.org>
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include <inttypes.h>
#include <math.h>
#include <poll.h>
#include <pthread.h>
#include <sndio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <assert.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#if defined(CUBEB_SNDIO_DEBUG)
#define DPR(...) fprintf(stderr, __VA_ARGS__);
#else
#define DPR(...) do {} while(0)
#endif
#ifdef DISABLE_LIBSNDIO_DLOPEN
#define WRAP(x) x
#else
#define WRAP(x) cubeb_##x
#define LIBSNDIO_API_VISIT(X) \
X(sio_close) \
X(sio_eof) \
X(sio_getpar) \
X(sio_initpar) \
X(sio_nfds) \
X(sio_onmove) \
X(sio_open) \
X(sio_pollfd) \
X(sio_read) \
X(sio_revents) \
X(sio_setpar) \
X(sio_start) \
X(sio_stop) \
X(sio_write) \
#define MAKE_TYPEDEF(x) static typeof(x) * cubeb_##x;
LIBSNDIO_API_VISIT(MAKE_TYPEDEF);
#undef MAKE_TYPEDEF
#endif
static struct cubeb_ops const sndio_ops;
struct cubeb {
struct cubeb_ops const * ops;
void * libsndio;
};
struct cubeb_stream {
/* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context;
void * arg; /* user arg to {data,state}_cb */
/**/
pthread_t th; /* to run real-time audio i/o */
pthread_mutex_t mtx; /* protects hdl and pos */
struct sio_hdl *hdl; /* link us to sndio */
int mode; /* bitmap of SIO_{PLAY,REC} */
int active; /* cubec_start() called */
int conv; /* need float->s16 conversion */
unsigned char *rbuf; /* rec data consumed from here */
unsigned char *pbuf; /* play data is prepared here */
unsigned int nfr; /* number of frames in ibuf and obuf */
unsigned int rbpf; /* rec bytes per frame */
unsigned int pbpf; /* play bytes per frame */
unsigned int rchan; /* number of rec channels */
unsigned int pchan; /* number of play channels */
unsigned int nblks; /* number of blocks in the buffer */
uint64_t hwpos; /* frame number Joe hears right now */
uint64_t swpos; /* number of frames produced/consumed */
cubeb_data_callback data_cb; /* cb to preapare data */
cubeb_state_callback state_cb; /* cb to notify about state changes */
float volume; /* current volume */
};
static void
s16_setvol(void *ptr, long nsamp, float volume)
{
int16_t *dst = ptr;
int32_t mult = volume * 32768;
int32_t s;
while (nsamp-- > 0) {
s = *dst;
s = (s * mult) >> 15;
*(dst++) = s;
}
}
static void
float_to_s16(void *ptr, long nsamp, float volume)
{
int16_t *dst = ptr;
float *src = ptr;
float mult = volume * 32768;
int s;
while (nsamp-- > 0) {
s = lrintf(*(src++) * mult);
if (s < -32768)
s = -32768;
else if (s > 32767)
s = 32767;
*(dst++) = s;
}
}
static void
s16_to_float(void *ptr, long nsamp)
{
int16_t *src = ptr;
float *dst = ptr;
src += nsamp;
dst += nsamp;
while (nsamp-- > 0)
*(--dst) = (1. / 32768) * *(--src);
}
static const char *
sndio_get_device()
{
#ifndef __OpenBSD__
/*
* On other platforms default to sndio devices,
* so cubebs other backends can be used instead.
*/
const char *dev = getenv("AUDIODEVICE");
if (dev == NULL || *dev == '\0')
return "snd/0";
return dev;
#else
return SIO_DEVANY;
#endif
}
static void
sndio_onmove(void *arg, int delta)
{
cubeb_stream *s = (cubeb_stream *)arg;
s->hwpos += delta;
}
static void *
sndio_mainloop(void *arg)
{
struct pollfd *pfds;
cubeb_stream *s = arg;
int n, eof = 0, prime, nfds, events, revents, state = CUBEB_STATE_STARTED;
size_t pstart = 0, pend = 0, rstart = 0, rend = 0;
long nfr;
nfds = WRAP(sio_nfds)(s->hdl);
pfds = calloc(nfds, sizeof (struct pollfd));
if (pfds == NULL)
return NULL;
DPR("sndio_mainloop()\n");
s->state_cb(s, s->arg, CUBEB_STATE_STARTED);
pthread_mutex_lock(&s->mtx);
if (!WRAP(sio_start)(s->hdl)) {
pthread_mutex_unlock(&s->mtx);
free(pfds);
return NULL;
}
DPR("sndio_mainloop(), started\n");
if (s->mode & SIO_PLAY) {
pstart = pend = s->nfr * s->pbpf;
prime = s->nblks;
if (s->mode & SIO_REC) {
memset(s->rbuf, 0, s->nfr * s->rbpf);
rstart = rend = s->nfr * s->rbpf;
}
} else {
prime = 0;
rstart = 0;
rend = s->nfr * s->rbpf;
}
for (;;) {
if (!s->active) {
DPR("sndio_mainloop() stopped\n");
state = CUBEB_STATE_STOPPED;
break;
}
/* do we have a complete block? */
if ((!(s->mode & SIO_PLAY) || pstart == pend) &&
(!(s->mode & SIO_REC) || rstart == rend)) {
if (eof) {
DPR("sndio_mainloop() drained\n");
state = CUBEB_STATE_DRAINED;
break;
}
if ((s->mode & SIO_REC) && s->conv)
s16_to_float(s->rbuf, s->nfr * s->rchan);
/* invoke call-back, it returns less that s->nfr if done */
pthread_mutex_unlock(&s->mtx);
nfr = s->data_cb(s, s->arg, s->rbuf, s->pbuf, s->nfr);
pthread_mutex_lock(&s->mtx);
if (nfr < 0) {
DPR("sndio_mainloop() cb err\n");
state = CUBEB_STATE_ERROR;
break;
}
s->swpos += nfr;
/* was this last call-back invocation (aka end-of-stream) ? */
if (nfr < s->nfr) {
if (!(s->mode & SIO_PLAY) || nfr == 0) {
state = CUBEB_STATE_DRAINED;
break;
}
/* need to write (aka drain) the partial play block we got */
pend = nfr * s->pbpf;
eof = 1;
}
if (prime > 0)
prime--;
if (s->mode & SIO_PLAY) {
if (s->conv)
float_to_s16(s->pbuf, nfr * s->pchan, s->volume);
else
s16_setvol(s->pbuf, nfr * s->pchan, s->volume);
}
if (s->mode & SIO_REC)
rstart = 0;
if (s->mode & SIO_PLAY)
pstart = 0;
}
events = 0;
if ((s->mode & SIO_REC) && rstart < rend && prime == 0)
events |= POLLIN;
if ((s->mode & SIO_PLAY) && pstart < pend)
events |= POLLOUT;
nfds = WRAP(sio_pollfd)(s->hdl, pfds, events);
if (nfds > 0) {
pthread_mutex_unlock(&s->mtx);
n = poll(pfds, nfds, -1);
pthread_mutex_lock(&s->mtx);
if (n < 0)
continue;
}
revents = WRAP(sio_revents)(s->hdl, pfds);
if (revents & POLLHUP) {
state = CUBEB_STATE_ERROR;
break;
}
if (revents & POLLOUT) {
n = WRAP(sio_write)(s->hdl, s->pbuf + pstart, pend - pstart);
if (n == 0 && WRAP(sio_eof)(s->hdl)) {
DPR("sndio_mainloop() werr\n");
state = CUBEB_STATE_ERROR;
break;
}
pstart += n;
}
if (revents & POLLIN) {
n = WRAP(sio_read)(s->hdl, s->rbuf + rstart, rend - rstart);
if (n == 0 && WRAP(sio_eof)(s->hdl)) {
DPR("sndio_mainloop() rerr\n");
state = CUBEB_STATE_ERROR;
break;
}
rstart += n;
}
/* skip rec block, if not recording (yet) */
if (prime > 0 && (s->mode & SIO_REC))
rstart = rend;
}
WRAP(sio_stop)(s->hdl);
s->hwpos = s->swpos;
pthread_mutex_unlock(&s->mtx);
s->state_cb(s, s->arg, state);
free(pfds);
return NULL;
}
/*static*/ int
sndio_init(cubeb **context, char const *context_name)
{
void * libsndio = NULL;
struct sio_hdl *hdl;
assert(context);
#ifndef DISABLE_LIBSNDIO_DLOPEN
libsndio = dlopen("libsndio.so.7.0", RTLD_LAZY);
if (!libsndio) {
libsndio = dlopen("libsndio.so", RTLD_LAZY);
if (!libsndio) {
DPR("sndio_init(%s) failed dlopen(libsndio.so)\n", context_name);
return CUBEB_ERROR;
}
}
#define LOAD(x) { \
cubeb_##x = dlsym(libsndio, #x); \
if (!cubeb_##x) { \
DPR("sndio_init(%s) failed dlsym(%s)\n", context_name, #x); \
dlclose(libsndio); \
return CUBEB_ERROR; \
} \
}
LIBSNDIO_API_VISIT(LOAD);
#undef LOAD
#endif
/* test if sndio works */
hdl = WRAP(sio_open)(sndio_get_device(), SIO_PLAY, 1);
if (hdl == NULL) {
return CUBEB_ERROR;
}
WRAP(sio_close)(hdl);
DPR("sndio_init(%s)\n", context_name);
*context = malloc(sizeof(**context));
if (*context == NULL)
return CUBEB_ERROR;
(*context)->libsndio = libsndio;
(*context)->ops = &sndio_ops;
(void)context_name;
return CUBEB_OK;
}
static char const *
sndio_get_backend_id(cubeb *context)
{
return "sndio";
}
static void
sndio_destroy(cubeb *context)
{
DPR("sndio_destroy()\n");
if (context->libsndio)
dlclose(context->libsndio);
free(context);
}
static int
sndio_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void *user_ptr)
{
cubeb_stream *s;
struct sio_par wpar, rpar;
cubeb_sample_format format;
int rate;
size_t bps;
DPR("sndio_stream_init(%s)\n", stream_name);
s = malloc(sizeof(cubeb_stream));
if (s == NULL)
return CUBEB_ERROR;
memset(s, 0, sizeof(cubeb_stream));
s->mode = 0;
if (input_stream_params) {
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
DPR("sndio_stream_init(), loopback not supported\n");
goto err;
}
s->mode |= SIO_REC;
format = input_stream_params->format;
rate = input_stream_params->rate;
}
if (output_stream_params) {
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
DPR("sndio_stream_init(), loopback not supported\n");
goto err;
}
s->mode |= SIO_PLAY;
format = output_stream_params->format;
rate = output_stream_params->rate;
}
if (s->mode == 0) {
DPR("sndio_stream_init(), neither playing nor recording\n");
goto err;
}
s->context = context;
s->hdl = WRAP(sio_open)(sndio_get_device(), s->mode, 1);
if (s->hdl == NULL) {
DPR("sndio_stream_init(), sio_open() failed\n");
goto err;
}
WRAP(sio_initpar)(&wpar);
wpar.sig = 1;
wpar.bits = 16;
switch (format) {
case CUBEB_SAMPLE_S16LE:
wpar.le = 1;
break;
case CUBEB_SAMPLE_S16BE:
wpar.le = 0;
break;
case CUBEB_SAMPLE_FLOAT32NE:
wpar.le = SIO_LE_NATIVE;
break;
default:
DPR("sndio_stream_init() unsupported format\n");
goto err;
}
wpar.rate = rate;
if (s->mode & SIO_REC)
wpar.rchan = input_stream_params->channels;
if (s->mode & SIO_PLAY)
wpar.pchan = output_stream_params->channels;
wpar.appbufsz = latency_frames;
if (!WRAP(sio_setpar)(s->hdl, &wpar) || !WRAP(sio_getpar)(s->hdl, &rpar)) {
DPR("sndio_stream_init(), sio_setpar() failed\n");
goto err;
}
if (rpar.bits != wpar.bits || rpar.le != wpar.le ||
rpar.sig != wpar.sig || rpar.rate != wpar.rate ||
((s->mode & SIO_REC) && rpar.rchan != wpar.rchan) ||
((s->mode & SIO_PLAY) && rpar.pchan != wpar.pchan)) {
DPR("sndio_stream_init() unsupported params\n");
goto err;
}
WRAP(sio_onmove)(s->hdl, sndio_onmove, s);
s->active = 0;
s->nfr = rpar.round;
s->rbpf = rpar.bps * rpar.rchan;
s->pbpf = rpar.bps * rpar.pchan;
s->rchan = rpar.rchan;
s->pchan = rpar.pchan;
s->nblks = rpar.bufsz / rpar.round;
s->data_cb = data_callback;
s->state_cb = state_callback;
s->arg = user_ptr;
s->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
s->hwpos = s->swpos = 0;
if (format == CUBEB_SAMPLE_FLOAT32LE) {
s->conv = 1;
bps = sizeof(float);
} else {
s->conv = 0;
bps = rpar.bps;
}
if (s->mode & SIO_PLAY) {
s->pbuf = malloc(bps * rpar.pchan * rpar.round);
if (s->pbuf == NULL)
goto err;
}
if (s->mode & SIO_REC) {
s->rbuf = malloc(bps * rpar.rchan * rpar.round);
if (s->rbuf == NULL)
goto err;
}
s->volume = 1.;
*stream = s;
DPR("sndio_stream_init() end, ok\n");
(void)context;
(void)stream_name;
return CUBEB_OK;
err:
if (s->hdl)
WRAP(sio_close)(s->hdl);
if (s->pbuf)
free(s->pbuf);
if (s->rbuf)
free(s->pbuf);
free(s);
return CUBEB_ERROR;
}
static int
sndio_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
assert(ctx && max_channels);
*max_channels = 8;
return CUBEB_OK;
}
static int
sndio_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
{
/*
* We've no device-independent prefered rate; any rate will work if
* sndiod is running. If it isn't, 48kHz is what is most likely to
* work as most (but not all) devices support it.
*/
*rate = 48000;
return CUBEB_OK;
}
static int
sndio_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * latency_frames)
{
/*
* We've no device-independent minimum latency.
*/
*latency_frames = 2048;
return CUBEB_OK;
}
static void
sndio_stream_destroy(cubeb_stream *s)
{
DPR("sndio_stream_destroy()\n");
WRAP(sio_close)(s->hdl);
if (s->mode & SIO_PLAY)
free(s->pbuf);
if (s->mode & SIO_REC)
free(s->rbuf);
free(s);
}
static int
sndio_stream_start(cubeb_stream *s)
{
int err;
DPR("sndio_stream_start()\n");
s->active = 1;
err = pthread_create(&s->th, NULL, sndio_mainloop, s);
if (err) {
s->active = 0;
return CUBEB_ERROR;
}
return CUBEB_OK;
}
static int
sndio_stream_stop(cubeb_stream *s)
{
void *dummy;
DPR("sndio_stream_stop()\n");
if (s->active) {
s->active = 0;
pthread_join(s->th, &dummy);
}
return CUBEB_OK;
}
static int
sndio_stream_get_position(cubeb_stream *s, uint64_t *p)
{
pthread_mutex_lock(&s->mtx);
DPR("sndio_stream_get_position() %" PRId64 "\n", s->hwpos);
*p = s->hwpos;
pthread_mutex_unlock(&s->mtx);
return CUBEB_OK;
}
static int
sndio_stream_set_volume(cubeb_stream *s, float volume)
{
DPR("sndio_stream_set_volume(%f)\n", volume);
pthread_mutex_lock(&s->mtx);
if (volume < 0.)
volume = 0.;
else if (volume > 1.0)
volume = 1.;
s->volume = volume;
pthread_mutex_unlock(&s->mtx);
return CUBEB_OK;
}
int
sndio_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
// http://www.openbsd.org/cgi-bin/man.cgi?query=sio_open
// in the "Measuring the latency and buffers usage" paragraph.
*latency = stm->swpos - stm->hwpos;
return CUBEB_OK;
}
static int
sndio_enumerate_devices(cubeb *context, cubeb_device_type type,
cubeb_device_collection *collection)
{
static char dev[] = SIO_DEVANY;
cubeb_device_info *device;
device = malloc(sizeof(cubeb_device_info));
if (device == NULL)
return CUBEB_ERROR;
device->devid = dev; /* passed to stream_init() */
device->device_id = dev; /* printable in UI */
device->friendly_name = dev; /* same, but friendly */
device->group_id = dev; /* actual device if full-duplex */
device->vendor_name = NULL; /* may be NULL */
device->type = type; /* Input/Output */
device->state = CUBEB_DEVICE_STATE_ENABLED;
device->preferred = CUBEB_DEVICE_PREF_ALL;
device->format = CUBEB_DEVICE_FMT_S16NE;
device->default_format = CUBEB_DEVICE_FMT_S16NE;
device->max_channels = 16;
device->default_rate = 48000;
device->min_rate = 4000;
device->max_rate = 192000;
device->latency_lo = 480;
device->latency_hi = 9600;
collection->device = device;
collection->count = 1;
return CUBEB_OK;
}
static int
sndio_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
free(collection->device);
return CUBEB_OK;
}
static struct cubeb_ops const sndio_ops = {
.init = sndio_init,
.get_backend_id = sndio_get_backend_id,
.get_max_channel_count = sndio_get_max_channel_count,
.get_min_latency = sndio_get_min_latency,
.get_preferred_sample_rate = sndio_get_preferred_sample_rate,
.enumerate_devices = sndio_enumerate_devices,
.device_collection_destroy = sndio_device_collection_destroy,
.destroy = sndio_destroy,
.stream_init = sndio_stream_init,
.stream_destroy = sndio_stream_destroy,
.stream_start = sndio_stream_start,
.stream_stop = sndio_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = sndio_stream_get_position,
.stream_get_latency = sndio_stream_get_latency,
.stream_set_volume = sndio_stream_set_volume,
.stream_get_current_device = NULL,
.stream_device_destroy = NULL,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};

View file

@ -0,0 +1,155 @@
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include "cubeb_strings.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#define CUBEB_STRINGS_INLINE_COUNT 4
struct cubeb_strings {
uint32_t size;
uint32_t count;
char ** data;
char * small_store[CUBEB_STRINGS_INLINE_COUNT];
};
int
cubeb_strings_init(cubeb_strings ** strings)
{
cubeb_strings* strs = NULL;
if (!strings) {
return CUBEB_ERROR;
}
strs = calloc(1, sizeof(cubeb_strings));
assert(strs);
if (!strs) {
return CUBEB_ERROR;
}
strs->size = sizeof(strs->small_store) / sizeof(strs->small_store[0]);
strs->count = 0;
strs->data = strs->small_store;
*strings = strs;
return CUBEB_OK;
}
void
cubeb_strings_destroy(cubeb_strings * strings)
{
char ** sp = NULL;
char ** se = NULL;
if (!strings) {
return;
}
sp = strings->data;
se = sp + strings->count;
for ( ; sp != se; sp++) {
if (*sp) {
free(*sp);
}
}
if (strings->data != strings->small_store) {
free(strings->data);
}
free(strings);
}
/** Look for string in string storage.
@param strings Opaque pointer to interned string storage.
@param s String to look up.
@retval Read-only string or NULL if not found. */
static char const *
cubeb_strings_lookup(cubeb_strings * strings, char const * s)
{
char ** sp = NULL;
char ** se = NULL;
if (!strings || !s) {
return NULL;
}
sp = strings->data;
se = sp + strings->count;
for ( ; sp != se; sp++) {
if (*sp && strcmp(*sp, s) == 0) {
return *sp;
}
}
return NULL;
}
static char const *
cubeb_strings_push(cubeb_strings * strings, char const * s)
{
char * is = NULL;
if (strings->count == strings->size) {
char ** new_data;
uint32_t value_size = sizeof(char const *);
uint32_t new_size = strings->size * 2;
if (!new_size || value_size > (uint32_t)-1 / new_size) {
// overflow
return NULL;
}
if (strings->small_store == strings->data) {
// First time heap allocation.
new_data = malloc(new_size * value_size);
if (new_data) {
memcpy(new_data, strings->small_store, sizeof(strings->small_store));
}
} else {
new_data = realloc(strings->data, new_size * value_size);
}
if (!new_data) {
// out of memory
return NULL;
}
strings->size = new_size;
strings->data = new_data;
}
is = strdup(s);
strings->data[strings->count++] = is;
return is;
}
char const *
cubeb_strings_intern(cubeb_strings * strings, char const * s)
{
char const * is = NULL;
if (!strings || !s) {
return NULL;
}
is = cubeb_strings_lookup(strings, s);
if (is) {
return is;
}
return cubeb_strings_push(strings, s);
}

View file

@ -0,0 +1,44 @@
/*
* Copyright © 2011 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#ifndef CUBEB_STRINGS_H
#define CUBEB_STRINGS_H
#include "cubeb/cubeb.h"
#if defined(__cplusplus)
extern "C" {
#endif
/** Opaque handle referencing interned string storage. */
typedef struct cubeb_strings cubeb_strings;
/** Initialize an interned string structure.
@param strings An out param where an opaque pointer to the
interned string storage will be returned.
@retval CUBEB_OK in case of success.
@retval CUBEB_ERROR in case of error. */
CUBEB_EXPORT int cubeb_strings_init(cubeb_strings ** strings);
/** Destroy an interned string structure freeing all associated memory.
@param strings An opaque pointer to the interned string storage to
destroy. */
CUBEB_EXPORT void cubeb_strings_destroy(cubeb_strings * strings);
/** Add string to internal storage.
@param strings Opaque pointer to interned string storage.
@param s String to add to storage.
@retval CUBEB_OK
@retval CUBEB_ERROR
*/
CUBEB_EXPORT char const * cubeb_strings_intern(cubeb_strings * strings, char const * s);
#if defined(__cplusplus)
}
#endif
#endif // !CUBEB_STRINGS_H

752
dep/cubeb/src/cubeb_sun.c Normal file
View file

@ -0,0 +1,752 @@
/*
* Copyright © 2019 Nia Alarie
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include <sys/audioio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#define BYTES_TO_FRAMES(bytes, channels) \
(bytes / (channels * sizeof(int16_t)))
#define FRAMES_TO_BYTES(frames, channels) \
(frames * (channels * sizeof(int16_t)))
/* Default to 4 + 1 for the default device. */
#ifndef SUN_DEVICE_COUNT
#define SUN_DEVICE_COUNT (5)
#endif
/* Supported well by most hardware. */
#ifndef SUN_PREFER_RATE
#define SUN_PREFER_RATE (48000)
#endif
/* Standard acceptable minimum. */
#ifndef SUN_LATENCY_MS
#define SUN_LATENCY_MS (40)
#endif
#ifndef SUN_DEFAULT_DEVICE
#define SUN_DEFAULT_DEVICE "/dev/audio"
#endif
#ifndef SUN_POLL_TIMEOUT
#define SUN_POLL_TIMEOUT (1000)
#endif
#ifndef SUN_BUFFER_FRAMES
#define SUN_BUFFER_FRAMES (32)
#endif
/*
* Supported on NetBSD regardless of hardware.
*/
#ifndef SUN_MAX_CHANNELS
# ifdef __NetBSD__
# define SUN_MAX_CHANNELS (12)
# else
# define SUN_MAX_CHANNELS (2)
# endif
#endif
#ifndef SUN_MIN_RATE
#define SUN_MIN_RATE (1000)
#endif
#ifndef SUN_MAX_RATE
#define SUN_MAX_RATE (192000)
#endif
static struct cubeb_ops const sun_ops;
struct cubeb {
struct cubeb_ops const * ops;
};
struct cubeb_stream {
struct cubeb * context;
void * user_ptr;
pthread_t thread;
pthread_mutex_t mutex; /* protects running, volume, frames_written */
int floating;
int running;
int play_fd;
int record_fd;
float volume;
struct audio_info p_info; /* info for the play fd */
struct audio_info r_info; /* info for the record fd */
cubeb_data_callback data_cb;
cubeb_state_callback state_cb;
int16_t * play_buf;
int16_t * record_buf;
float * f_play_buf;
float * f_record_buf;
char input_name[32];
char output_name[32];
uint64_t frames_written;
uint64_t blocks_written;
};
int
sun_init(cubeb ** context, char const * context_name)
{
cubeb * c;
(void)context_name;
if ((c = calloc(1, sizeof(cubeb))) == NULL) {
return CUBEB_ERROR;
}
c->ops = &sun_ops;
*context = c;
return CUBEB_OK;
}
static void
sun_destroy(cubeb * context)
{
free(context);
}
static char const *
sun_get_backend_id(cubeb * context)
{
return "sun";
}
static int
sun_get_preferred_sample_rate(cubeb * context, uint32_t * rate)
{
(void)context;
*rate = SUN_PREFER_RATE;
return CUBEB_OK;
}
static int
sun_get_max_channel_count(cubeb * context, uint32_t * max_channels)
{
(void)context;
*max_channels = SUN_MAX_CHANNELS;
return CUBEB_OK;
}
static int
sun_get_min_latency(cubeb * context, cubeb_stream_params params,
uint32_t * latency_frames)
{
(void)context;
*latency_frames = SUN_LATENCY_MS * params.rate / 1000;
return CUBEB_OK;
}
static int
sun_get_hwinfo(const char * device, struct audio_info * format,
int * props, struct audio_device * dev)
{
int fd = -1;
if ((fd = open(device, O_RDONLY)) == -1) {
goto error;
}
#ifdef AUDIO_GETFORMAT
if (ioctl(fd, AUDIO_GETFORMAT, format) != 0) {
goto error;
}
#endif
#ifdef AUDIO_GETPROPS
if (ioctl(fd, AUDIO_GETPROPS, props) != 0) {
goto error;
}
#endif
if (ioctl(fd, AUDIO_GETDEV, dev) != 0) {
goto error;
}
close(fd);
return CUBEB_OK;
error:
if (fd != -1) {
close(fd);
}
return CUBEB_ERROR;
}
/*
* XXX: PR kern/54264
*/
static int
sun_prinfo_verify_sanity(struct audio_prinfo * prinfo)
{
return prinfo->precision >= 8 && prinfo->precision <= 32 &&
prinfo->channels >= 1 && prinfo->channels < SUN_MAX_CHANNELS &&
prinfo->sample_rate < SUN_MAX_RATE && prinfo->sample_rate > SUN_MIN_RATE;
}
static int
sun_enumerate_devices(cubeb * context, cubeb_device_type type,
cubeb_device_collection * collection)
{
unsigned i;
cubeb_device_info device = {0};
char dev[16] = SUN_DEFAULT_DEVICE;
char dev_friendly[64];
struct audio_info hwfmt;
struct audio_device hwname;
struct audio_prinfo *prinfo = NULL;
int hwprops;
collection->device = calloc(SUN_DEVICE_COUNT, sizeof(cubeb_device_info));
if (collection->device == NULL) {
return CUBEB_ERROR;
}
collection->count = 0;
for (i = 0; i < SUN_DEVICE_COUNT; ++i) {
if (i > 0) {
(void)snprintf(dev, sizeof(dev), "/dev/audio%u", i - 1);
}
if (sun_get_hwinfo(dev, &hwfmt, &hwprops, &hwname) != CUBEB_OK) {
continue;
}
#ifdef AUDIO_GETPROPS
device.type = 0;
if ((hwprops & AUDIO_PROP_CAPTURE) != 0 &&
sun_prinfo_verify_sanity(&hwfmt.record)) {
/* the device supports recording, probably */
device.type |= CUBEB_DEVICE_TYPE_INPUT;
}
if ((hwprops & AUDIO_PROP_PLAYBACK) != 0 &&
sun_prinfo_verify_sanity(&hwfmt.play)) {
/* the device supports playback, probably */
device.type |= CUBEB_DEVICE_TYPE_OUTPUT;
}
switch (device.type) {
case 0:
/* device doesn't do input or output, aliens probably involved */
continue;
case CUBEB_DEVICE_TYPE_INPUT:
if ((type & CUBEB_DEVICE_TYPE_INPUT) == 0) {
/* this device is input only, not scanning for those, skip it */
continue;
}
break;
case CUBEB_DEVICE_TYPE_OUTPUT:
if ((type & CUBEB_DEVICE_TYPE_OUTPUT) == 0) {
/* this device is output only, not scanning for those, skip it */
continue;
}
break;
}
if ((type & CUBEB_DEVICE_TYPE_INPUT) != 0) {
prinfo = &hwfmt.record;
}
if ((type & CUBEB_DEVICE_TYPE_OUTPUT) != 0) {
prinfo = &hwfmt.play;
}
#endif
if (i > 0) {
(void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (%d)",
hwname.name, hwname.version, hwname.config, i - 1);
} else {
(void)snprintf(dev_friendly, sizeof(dev_friendly), "%s %s %s (default)",
hwname.name, hwname.version, hwname.config);
}
device.devid = (void *)(uintptr_t)i;
device.device_id = strdup(dev);
device.friendly_name = strdup(dev_friendly);
device.group_id = strdup(dev);
device.vendor_name = strdup(hwname.name);
device.type = type;
device.state = CUBEB_DEVICE_STATE_ENABLED;
device.preferred = (i == 0) ? CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
#ifdef AUDIO_GETFORMAT
device.max_channels = prinfo->channels;
device.default_rate = prinfo->sample_rate;
#else
device.max_channels = 2;
device.default_rate = SUN_PREFER_RATE;
#endif
device.default_format = CUBEB_DEVICE_FMT_S16NE;
device.format = CUBEB_DEVICE_FMT_S16NE;
device.min_rate = SUN_MIN_RATE;
device.max_rate = SUN_MAX_RATE;
device.latency_lo = SUN_LATENCY_MS * SUN_MIN_RATE / 1000;
device.latency_hi = SUN_LATENCY_MS * SUN_MAX_RATE / 1000;
collection->device[collection->count++] = device;
}
return CUBEB_OK;
}
static int
sun_device_collection_destroy(cubeb * context,
cubeb_device_collection * collection)
{
unsigned i;
for (i = 0; i < collection->count; ++i) {
free((char *)collection->device[i].device_id);
free((char *)collection->device[i].friendly_name);
free((char *)collection->device[i].group_id);
free((char *)collection->device[i].vendor_name);
}
free(collection->device);
return CUBEB_OK;
}
static int
sun_copy_params(int fd, cubeb_stream * stream, cubeb_stream_params * params,
struct audio_info * info, struct audio_prinfo * prinfo)
{
prinfo->channels = params->channels;
prinfo->sample_rate = params->rate;
prinfo->precision = 16;
#ifdef AUDIO_ENCODING_SLINEAR_LE
switch (params->format) {
case CUBEB_SAMPLE_S16LE:
prinfo->encoding = AUDIO_ENCODING_SLINEAR_LE;
break;
case CUBEB_SAMPLE_S16BE:
prinfo->encoding = AUDIO_ENCODING_SLINEAR_BE;
break;
case CUBEB_SAMPLE_FLOAT32NE:
stream->floating = 1;
prinfo->encoding = AUDIO_ENCODING_SLINEAR;
break;
default:
LOG("Unsupported format");
return CUBEB_ERROR_INVALID_FORMAT;
}
#else
switch (params->format) {
case CUBEB_SAMPLE_S16NE:
prinfo->encoding = AUDIO_ENCODING_LINEAR;
break;
case CUBEB_SAMPLE_FLOAT32NE:
stream->floating = 1;
prinfo->encoding = AUDIO_ENCODING_LINEAR;
break;
default:
LOG("Unsupported format");
return CUBEB_ERROR_INVALID_FORMAT;
}
#endif
if (ioctl(fd, AUDIO_SETINFO, info) == -1) {
return CUBEB_ERROR;
}
if (ioctl(fd, AUDIO_GETINFO, info) == -1) {
return CUBEB_ERROR;
}
return CUBEB_OK;
}
static int
sun_stream_stop(cubeb_stream * s)
{
pthread_mutex_lock(&s->mutex);
if (s->running) {
s->running = 0;
pthread_mutex_unlock(&s->mutex);
pthread_join(s->thread, NULL);
} else {
pthread_mutex_unlock(&s->mutex);
}
return CUBEB_OK;
}
static void
sun_stream_destroy(cubeb_stream * s)
{
pthread_mutex_destroy(&s->mutex);
sun_stream_stop(s);
if (s->play_fd != -1) {
close(s->play_fd);
}
if (s->record_fd != -1) {
close(s->record_fd);
}
free(s->f_play_buf);
free(s->f_record_buf);
free(s->play_buf);
free(s->record_buf);
free(s);
}
static void
sun_float_to_linear(float * in, int16_t * out,
unsigned channels, long frames, float vol)
{
unsigned i, sample_count = frames * channels;
float multiplier = vol * 0x8000;
for (i = 0; i < sample_count; ++i) {
int32_t sample = lrintf(in[i] * multiplier);
if (sample < -0x8000) {
out[i] = -0x8000;
} else if (sample > 0x7fff) {
out[i] = 0x7fff;
} else {
out[i] = sample;
}
}
}
static void
sun_linear_to_float(int16_t * in, float * out,
unsigned channels, long frames)
{
unsigned i, sample_count = frames * channels;
for (i = 0; i < sample_count; ++i) {
out[i] = (1.0 / 0x8000) * in[i];
}
}
static void
sun_linear_set_vol(int16_t * buf, unsigned channels, long frames, float vol)
{
unsigned i, sample_count = frames * channels;
int32_t multiplier = vol * 0x8000;
for (i = 0; i < sample_count; ++i) {
buf[i] = (buf[i] * multiplier) >> 15;
}
}
static void *
sun_io_routine(void * arg)
{
cubeb_stream *s = arg;
cubeb_state state = CUBEB_STATE_STARTED;
size_t to_read = 0;
long to_write = 0;
size_t write_ofs = 0;
size_t read_ofs = 0;
int drain = 0;
s->state_cb(s, s->user_ptr, CUBEB_STATE_STARTED);
while (state != CUBEB_STATE_ERROR) {
pthread_mutex_lock(&s->mutex);
if (!s->running) {
pthread_mutex_unlock(&s->mutex);
state = CUBEB_STATE_STOPPED;
break;
}
pthread_mutex_unlock(&s->mutex);
if (s->floating) {
if (s->record_fd != -1) {
sun_linear_to_float(s->record_buf, s->f_record_buf,
s->r_info.record.channels, SUN_BUFFER_FRAMES);
}
to_write = s->data_cb(s, s->user_ptr,
s->f_record_buf, s->f_play_buf, SUN_BUFFER_FRAMES);
if (to_write == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
break;
}
if (s->play_fd != -1) {
pthread_mutex_lock(&s->mutex);
sun_float_to_linear(s->f_play_buf, s->play_buf,
s->p_info.play.channels, to_write, s->volume);
pthread_mutex_unlock(&s->mutex);
}
} else {
to_write = s->data_cb(s, s->user_ptr,
s->record_buf, s->play_buf, SUN_BUFFER_FRAMES);
if (to_write == CUBEB_ERROR) {
state = CUBEB_STATE_ERROR;
break;
}
if (s->play_fd != -1) {
pthread_mutex_lock(&s->mutex);
sun_linear_set_vol(s->play_buf, s->p_info.play.channels, to_write, s->volume);
pthread_mutex_unlock(&s->mutex);
}
}
if (to_write < SUN_BUFFER_FRAMES) {
drain = 1;
}
to_write = s->play_fd != -1 ? to_write : 0;
to_read = s->record_fd != -1 ? SUN_BUFFER_FRAMES : 0;
write_ofs = 0;
read_ofs = 0;
while (to_write > 0 || to_read > 0) {
size_t bytes;
ssize_t n, frames;
if (to_write > 0) {
bytes = FRAMES_TO_BYTES(to_write, s->p_info.play.channels);
if ((n = write(s->play_fd, s->play_buf + write_ofs, bytes)) < 0) {
state = CUBEB_STATE_ERROR;
break;
}
frames = BYTES_TO_FRAMES(n, s->p_info.play.channels);
pthread_mutex_lock(&s->mutex);
s->frames_written += frames;
pthread_mutex_unlock(&s->mutex);
to_write -= frames;
write_ofs += frames;
}
if (to_read > 0) {
bytes = FRAMES_TO_BYTES(to_read, s->r_info.record.channels);
if ((n = read(s->record_fd, s->record_buf + read_ofs, bytes)) < 0) {
state = CUBEB_STATE_ERROR;
break;
}
frames = BYTES_TO_FRAMES(n, s->r_info.record.channels);
to_read -= frames;
read_ofs += frames;
}
}
if (drain && state != CUBEB_STATE_ERROR) {
state = CUBEB_STATE_DRAINED;
break;
}
}
s->state_cb(s, s->user_ptr, state);
return NULL;
}
static int
sun_stream_init(cubeb * context,
cubeb_stream ** stream,
char const * stream_name,
cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned latency_frames,
cubeb_data_callback data_callback,
cubeb_state_callback state_callback,
void * user_ptr)
{
int ret = CUBEB_OK;
cubeb_stream *s = NULL;
(void)stream_name;
(void)latency_frames;
if ((s = calloc(1, sizeof(cubeb_stream))) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
s->record_fd = -1;
s->play_fd = -1;
if (input_device != 0) {
snprintf(s->input_name, sizeof(s->input_name),
"/dev/audio%zu", (uintptr_t)input_device - 1);
} else {
snprintf(s->input_name, sizeof(s->input_name), "%s", SUN_DEFAULT_DEVICE);
}
if (output_device != 0) {
snprintf(s->output_name, sizeof(s->output_name),
"/dev/audio%zu", (uintptr_t)output_device - 1);
} else {
snprintf(s->output_name, sizeof(s->output_name), "%s", SUN_DEFAULT_DEVICE);
}
if (input_stream_params != NULL) {
if (input_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
LOG("Loopback not supported");
ret = CUBEB_ERROR_NOT_SUPPORTED;
goto error;
}
if (s->record_fd == -1) {
if ((s->record_fd = open(s->input_name, O_RDONLY)) == -1) {
LOG("Audio device cannot be opened as read-only");
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
}
AUDIO_INITINFO(&s->r_info);
#ifdef AUMODE_RECORD
s->r_info.mode = AUMODE_RECORD;
#endif
if ((ret = sun_copy_params(s->record_fd, s, input_stream_params,
&s->r_info, &s->r_info.record)) != CUBEB_OK) {
LOG("Setting record params failed");
goto error;
}
}
if (output_stream_params != NULL) {
if (output_stream_params->prefs & CUBEB_STREAM_PREF_LOOPBACK) {
LOG("Loopback not supported");
ret = CUBEB_ERROR_NOT_SUPPORTED;
goto error;
}
if (s->play_fd == -1) {
if ((s->play_fd = open(s->output_name, O_WRONLY)) == -1) {
LOG("Audio device cannot be opened as write-only");
ret = CUBEB_ERROR_DEVICE_UNAVAILABLE;
goto error;
}
}
AUDIO_INITINFO(&s->p_info);
#ifdef AUMODE_PLAY
s->p_info.mode = AUMODE_PLAY;
#endif
if ((ret = sun_copy_params(s->play_fd, s, output_stream_params,
&s->p_info, &s->p_info.play)) != CUBEB_OK) {
LOG("Setting play params failed");
goto error;
}
}
s->context = context;
s->volume = 1.0;
s->state_cb = state_callback;
s->data_cb = data_callback;
s->user_ptr = user_ptr;
if (pthread_mutex_init(&s->mutex, NULL) != 0) {
LOG("Failed to create mutex");
goto error;
}
if (s->play_fd != -1 && (s->play_buf = calloc(SUN_BUFFER_FRAMES,
s->p_info.play.channels * sizeof(int16_t))) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
if (s->record_fd != -1 && (s->record_buf = calloc(SUN_BUFFER_FRAMES,
s->r_info.record.channels * sizeof(int16_t))) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
if (s->floating) {
if (s->play_fd != -1 && (s->f_play_buf = calloc(SUN_BUFFER_FRAMES,
s->p_info.play.channels * sizeof(float))) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
if (s->record_fd != -1 && (s->f_record_buf = calloc(SUN_BUFFER_FRAMES,
s->r_info.record.channels * sizeof(float))) == NULL) {
ret = CUBEB_ERROR;
goto error;
}
}
*stream = s;
return CUBEB_OK;
error:
if (s != NULL) {
sun_stream_destroy(s);
}
return ret;
}
static int
sun_stream_start(cubeb_stream * s)
{
s->running = 1;
if (pthread_create(&s->thread, NULL, sun_io_routine, s) != 0) {
LOG("Couldn't create thread");
return CUBEB_ERROR;
}
return CUBEB_OK;
}
static int
sun_stream_get_position(cubeb_stream * s, uint64_t * position)
{
#ifdef AUDIO_GETOOFFS
struct audio_offset offset;
if (ioctl(s->play_fd, AUDIO_GETOOFFS, &offset) == -1) {
return CUBEB_ERROR;
}
s->blocks_written += offset.deltablks;
*position = BYTES_TO_FRAMES(s->blocks_written * s->p_info.blocksize,
s->p_info.play.channels);
return CUBEB_OK;
#else
pthread_mutex_lock(&s->mutex);
*position = s->frames_written;
pthread_mutex_unlock(&s->mutex);
return CUBEB_OK;
#endif
}
static int
sun_stream_get_latency(cubeb_stream * stream, uint32_t * latency)
{
#ifdef AUDIO_GETBUFINFO
struct audio_info info;
if (ioctl(stream->play_fd, AUDIO_GETBUFINFO, &info) == -1) {
return CUBEB_ERROR;
}
*latency = BYTES_TO_FRAMES(info.play.seek + info.blocksize,
info.play.channels);
return CUBEB_OK;
#else
cubeb_stream_params params;
params.rate = stream->p_info.play.sample_rate;
return sun_get_min_latency(NULL, params, latency);
#endif
}
static int
sun_stream_set_volume(cubeb_stream * stream, float volume)
{
pthread_mutex_lock(&stream->mutex);
stream->volume = volume;
pthread_mutex_unlock(&stream->mutex);
return CUBEB_OK;
}
static int
sun_get_current_device(cubeb_stream * stream, cubeb_device ** const device)
{
*device = calloc(1, sizeof(cubeb_device));
if (*device == NULL) {
return CUBEB_ERROR;
}
(*device)->input_name = stream->record_fd != -1 ?
strdup(stream->input_name) : NULL;
(*device)->output_name = stream->play_fd != -1 ?
strdup(stream->output_name) : NULL;
return CUBEB_OK;
}
static int
sun_stream_device_destroy(cubeb_stream * stream, cubeb_device * device)
{
(void)stream;
free(device->input_name);
free(device->output_name);
free(device);
return CUBEB_OK;
}
static struct cubeb_ops const sun_ops = {
.init = sun_init,
.get_backend_id = sun_get_backend_id,
.get_max_channel_count = sun_get_max_channel_count,
.get_min_latency = sun_get_min_latency,
.get_preferred_sample_rate = sun_get_preferred_sample_rate,
.enumerate_devices = sun_enumerate_devices,
.device_collection_destroy = sun_device_collection_destroy,
.destroy = sun_destroy,
.stream_init = sun_stream_init,
.stream_destroy = sun_stream_destroy,
.stream_start = sun_stream_start,
.stream_stop = sun_stream_stop,
.stream_reset_default_device = NULL,
.stream_get_position = sun_stream_get_position,
.stream_get_latency = sun_stream_get_latency,
.stream_set_volume = sun_stream_set_volume,
.stream_get_current_device = sun_get_current_device,
.stream_device_destroy = sun_stream_device_destroy,
.stream_register_device_changed_callback = NULL,
.register_device_collection_changed = NULL
};

View file

@ -0,0 +1,23 @@
/*
* Copyright © 2018 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#include "cubeb_utils.h"
size_t cubeb_sample_size(cubeb_sample_format format)
{
switch (format) {
case CUBEB_SAMPLE_S16LE:
case CUBEB_SAMPLE_S16BE:
return sizeof(int16_t);
case CUBEB_SAMPLE_FLOAT32LE:
case CUBEB_SAMPLE_FLOAT32BE:
return sizeof(float);
default:
// should never happen as all cases are handled above.
assert(false);
}
}

343
dep/cubeb/src/cubeb_utils.h Normal file
View file

@ -0,0 +1,343 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_UTILS)
#define CUBEB_UTILS
#include "cubeb/cubeb.h"
#ifdef __cplusplus
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include <mutex>
#include <type_traits>
#if defined(_WIN32)
#include "cubeb_utils_win.h"
#else
#include "cubeb_utils_unix.h"
#endif
/** Similar to memcpy, but accounts for the size of an element. */
template<typename T>
void PodCopy(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination && source);
memcpy(destination, source, count * sizeof(T));
}
/** Similar to memmove, but accounts for the size of an element. */
template<typename T>
void PodMove(T * destination, const T * source, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination && source);
memmove(destination, source, count * sizeof(T));
}
/** Similar to a memset to zero, but accounts for the size of an element. */
template<typename T>
void PodZero(T * destination, size_t count)
{
static_assert(std::is_trivial<T>::value, "Requires trivial type");
assert(destination);
memset(destination, 0, count * sizeof(T));
}
namespace {
template<typename T, typename Trait>
void Copy(T * destination, const T * source, size_t count, Trait)
{
for (size_t i = 0; i < count; i++) {
destination[i] = source[i];
}
}
template<typename T>
void Copy(T * destination, const T * source, size_t count, std::true_type)
{
PodCopy(destination, source, count);
}
}
/**
* This allows copying a number of elements from a `source` pointer to a
* `destination` pointer, using `memcpy` if it is safe to do so, or a loop that
* calls the constructors and destructors otherwise.
*/
template<typename T>
void Copy(T * destination, const T * source, size_t count)
{
assert(destination && source);
Copy(destination, source, count, typename std::is_trivial<T>::type());
}
namespace {
template<typename T, typename Trait>
void ConstructDefault(T * destination, size_t count, Trait)
{
for (size_t i = 0; i < count; i++) {
destination[i] = T();
}
}
template<typename T>
void ConstructDefault(T * destination,
size_t count, std::true_type)
{
PodZero(destination, count);
}
}
/**
* This allows zeroing (using memset) or default-constructing a number of
* elements calling the constructors and destructors if necessary.
*/
template<typename T>
void ConstructDefault(T * destination, size_t count)
{
assert(destination);
ConstructDefault(destination, count,
typename std::is_arithmetic<T>::type());
}
template<typename T>
class auto_array
{
public:
explicit auto_array(uint32_t capacity = 0)
: data_(capacity ? new T[capacity] : nullptr)
, capacity_(capacity)
, length_(0)
{}
~auto_array()
{
delete [] data_;
}
/** Get a constant pointer to the underlying data. */
T * data() const
{
return data_;
}
T * end() const
{
return data_ + length_;
}
const T& at(size_t index) const
{
assert(index < length_ && "out of range");
return data_[index];
}
T& at(size_t index)
{
assert(index < length_ && "out of range");
return data_[index];
}
/** Get how much underlying storage this auto_array has. */
size_t capacity() const
{
return capacity_;
}
/** Get how much elements this auto_array contains. */
size_t length() const
{
return length_;
}
/** Keeps the storage, but removes all the elements from the array. */
void clear()
{
length_ = 0;
}
/** Change the storage of this auto array, copying the elements to the new
* storage.
* @returns true in case of success
* @returns false if the new capacity is not big enough to accomodate for the
* elements in the array.
*/
bool reserve(size_t new_capacity)
{
if (new_capacity < length_) {
return false;
}
T * new_data = new T[new_capacity];
if (data_ && length_) {
PodCopy(new_data, data_, length_);
}
capacity_ = new_capacity;
delete [] data_;
data_ = new_data;
return true;
}
/** Append `length` elements to the end of the array, resizing the array if
* needed.
* @parameter elements the elements to append to the array.
* @parameter length the number of elements to append to the array.
*/
void push(const T * elements, size_t length)
{
if (length_ + length > capacity_) {
reserve(length_ + length);
}
PodCopy(data_ + length_, elements, length);
length_ += length;
}
/** Append `length` zero-ed elements to the end of the array, resizing the
* array if needed.
* @parameter length the number of elements to append to the array.
*/
void push_silence(size_t length)
{
if (length_ + length > capacity_) {
reserve(length + length_);
}
PodZero(data_ + length_, length);
length_ += length;
}
/** Prepend `length` zero-ed elements to the end of the array, resizing the
* array if needed.
* @parameter length the number of elements to prepend to the array.
*/
void push_front_silence(size_t length)
{
if (length_ + length > capacity_) {
reserve(length + length_);
}
PodMove(data_ + length, data_, length_);
PodZero(data_, length);
length_ += length;
}
/** Return the number of free elements in the array. */
size_t available() const
{
return capacity_ - length_;
}
/** Copies `length` elements to `elements` if it is not null, and shift
* the remaining elements of the `auto_array` to the beginning.
* @parameter elements a buffer to copy the elements to, or nullptr.
* @parameter length the number of elements to copy.
* @returns true in case of success.
* @returns false if the auto_array contains less than `length` elements. */
bool pop(T * elements, size_t length)
{
if (length > length_) {
return false;
}
if (elements) {
PodCopy(elements, data_, length);
}
PodMove(data_, data_ + length, length_ - length);
length_ -= length;
return true;
}
void set_length(size_t length)
{
assert(length <= capacity_);
length_ = length;
}
private:
/** The underlying storage */
T * data_;
/** The size, in number of elements, of the storage. */
size_t capacity_;
/** The number of elements the array contains. */
size_t length_;
};
struct auto_array_wrapper {
virtual void push(void * elements, size_t length) = 0;
virtual size_t length() = 0;
virtual void push_silence(size_t length) = 0;
virtual bool pop(size_t length) = 0;
virtual void * data() = 0;
virtual void * end() = 0;
virtual void clear() = 0;
virtual bool reserve(size_t capacity) = 0;
virtual void set_length(size_t length) = 0;
virtual ~auto_array_wrapper() {}
};
template <typename T>
struct auto_array_wrapper_impl : public auto_array_wrapper {
auto_array_wrapper_impl() {}
explicit auto_array_wrapper_impl(uint32_t size)
: ar(size)
{}
void push(void * elements, size_t length) override {
ar.push(static_cast<T *>(elements), length);
}
size_t length() override {
return ar.length();
}
void push_silence(size_t length) override {
ar.push_silence(length);
}
bool pop(size_t length) override {
return ar.pop(nullptr, length);
}
void * data() override {
return ar.data();
}
void * end() override {
return ar.end();
}
void clear() override {
ar.clear();
}
bool reserve(size_t capacity) override {
return ar.reserve(capacity);
}
void set_length(size_t length) override {
ar.set_length(length);
}
~auto_array_wrapper_impl() {
ar.clear();
}
private:
auto_array<T> ar;
};
extern "C" {
size_t cubeb_sample_size(cubeb_sample_format format);
}
using auto_lock = std::lock_guard<owned_critical_section>;
#endif // __cplusplus
#endif /* CUBEB_UTILS */

View file

@ -0,0 +1,89 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_UTILS_UNIX)
#define CUBEB_UTILS_UNIX
#include <pthread.h>
#include <errno.h>
#include <stdio.h>
/* This wraps a critical section to track the owner in debug mode. */
class owned_critical_section
{
public:
owned_critical_section()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
#ifndef NDEBUG
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
#else
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
#endif
#ifndef NDEBUG
int r =
#endif
pthread_mutex_init(&mutex, &attr);
#ifndef NDEBUG
assert(r == 0);
#endif
pthread_mutexattr_destroy(&attr);
}
~owned_critical_section()
{
#ifndef NDEBUG
int r =
#endif
pthread_mutex_destroy(&mutex);
#ifndef NDEBUG
assert(r == 0);
#endif
}
void lock()
{
#ifndef NDEBUG
int r =
#endif
pthread_mutex_lock(&mutex);
#ifndef NDEBUG
assert(r == 0 && "Deadlock");
#endif
}
void unlock()
{
#ifndef NDEBUG
int r =
#endif
pthread_mutex_unlock(&mutex);
#ifndef NDEBUG
assert(r == 0 && "Unlocking unlocked mutex");
#endif
}
void assert_current_thread_owns()
{
#ifndef NDEBUG
int r = pthread_mutex_lock(&mutex);
assert(r == EDEADLK);
#endif
}
private:
pthread_mutex_t mutex;
// Disallow copy and assignment because pthread_mutex_t cannot be copied.
owned_critical_section(const owned_critical_section&);
owned_critical_section& operator=(const owned_critical_section&);
};
#endif /* CUBEB_UTILS_UNIX */

View file

@ -0,0 +1,71 @@
/*
* Copyright © 2016 Mozilla Foundation
*
* This program is made available under an ISC-style license. See the
* accompanying file LICENSE for details.
*/
#if !defined(CUBEB_UTILS_WIN)
#define CUBEB_UTILS_WIN
#include <windows.h>
#include "cubeb-internal.h"
/* This wraps a critical section to track the owner in debug mode, adapted from
NSPR and http://blogs.msdn.com/b/oldnewthing/archive/2013/07/12/10433554.aspx */
class owned_critical_section
{
public:
owned_critical_section()
#ifndef NDEBUG
: owner(0)
#endif
{
InitializeCriticalSection(&critical_section);
}
~owned_critical_section()
{
DeleteCriticalSection(&critical_section);
}
void lock()
{
EnterCriticalSection(&critical_section);
#ifndef NDEBUG
XASSERT(owner != GetCurrentThreadId() && "recursive locking");
owner = GetCurrentThreadId();
#endif
}
void unlock()
{
#ifndef NDEBUG
/* GetCurrentThreadId cannot return 0: it is not a the valid thread id */
owner = 0;
#endif
LeaveCriticalSection(&critical_section);
}
/* This is guaranteed to have the good behaviour if it succeeds. The behaviour
is undefined otherwise. */
void assert_current_thread_owns()
{
#ifndef NDEBUG
/* This implies owner != 0, because GetCurrentThreadId cannot return 0. */
XASSERT(owner == GetCurrentThreadId());
#endif
}
private:
CRITICAL_SECTION critical_section;
#ifndef NDEBUG
DWORD owner;
#endif
// Disallow copy and assignment because CRICICAL_SECTION cannot be copied.
owned_critical_section(const owned_critical_section&);
owned_critical_section& operator=(const owned_critical_section&);
};
#endif /* CUBEB_UTILS_WIN */

File diff suppressed because it is too large Load diff

1067
dep/cubeb/src/cubeb_winmm.c Normal file

File diff suppressed because it is too large Load diff

235
dep/cubeb/src/speex/arch.h Normal file
View file

@ -0,0 +1,235 @@
/* Copyright (C) 2003 Jean-Marc Valin */
/**
@file arch.h
@brief Various architecture definitions Speex
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCH_H
#define ARCH_H
/* A couple test to catch stupid option combinations */
#ifdef FIXED_POINT
#ifdef FLOATING_POINT
#error You cannot compile as floating point and fixed point at the same time
#endif
#ifdef _USE_SSE
#error SSE is only for floating-point
#endif
#if ((defined (ARM4_ASM)||defined (ARM4_ASM)) && defined(BFIN_ASM)) || (defined (ARM4_ASM)&&defined(ARM5E_ASM))
#error Make up your mind. What CPU do you have?
#endif
#ifdef VORBIS_PSYCHO
#error Vorbis-psy model currently not implemented in fixed-point
#endif
#else
#ifndef FLOATING_POINT
#error You now need to define either FIXED_POINT or FLOATING_POINT
#endif
#if defined (ARM4_ASM) || defined(ARM5E_ASM) || defined(BFIN_ASM)
#error I suppose you can have a [ARM4/ARM5E/Blackfin] that has float instructions?
#endif
#ifdef FIXED_POINT_DEBUG
#error "Don't you think enabling fixed-point is a good thing to do if you want to debug that?"
#endif
#endif
#ifndef OUTSIDE_SPEEX
#include "speex/speexdsp_types.h"
#endif
#define ABS(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute integer value. */
#define ABS16(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 16-bit value. */
#define MIN16(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define MAX16(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 16-bit value. */
#define ABS32(x) ((x) < 0 ? (-(x)) : (x)) /**< Absolute 32-bit value. */
#define MIN32(a,b) ((a) < (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#define MAX32(a,b) ((a) > (b) ? (a) : (b)) /**< Maximum 32-bit value. */
#ifdef FIXED_POINT
typedef spx_int16_t spx_word16_t;
typedef spx_int32_t spx_word32_t;
typedef spx_word32_t spx_mem_t;
typedef spx_word16_t spx_coef_t;
typedef spx_word16_t spx_lsp_t;
typedef spx_word32_t spx_sig_t;
#define Q15ONE 32767
#define LPC_SCALING 8192
#define SIG_SCALING 16384
#define LSP_SCALING 8192.
#define GAMMA_SCALING 32768.
#define GAIN_SCALING 64
#define GAIN_SCALING_1 0.015625
#define LPC_SHIFT 13
#define LSP_SHIFT 13
#define SIG_SHIFT 14
#define GAIN_SHIFT 6
#define WORD2INT(x) ((x) < -32767 ? -32768 : ((x) > 32766 ? 32767 : (x)))
#define VERY_SMALL 0
#define VERY_LARGE32 ((spx_word32_t)2147483647)
#define VERY_LARGE16 ((spx_word16_t)32767)
#define Q15_ONE ((spx_word16_t)32767)
#ifdef FIXED_DEBUG
#include "fixed_debug.h"
#else
#include "fixed_generic.h"
#ifdef ARM5E_ASM
#include "fixed_arm5e.h"
#elif defined (ARM4_ASM)
#include "fixed_arm4.h"
#elif defined (BFIN_ASM)
#include "fixed_bfin.h"
#endif
#endif
#else
typedef float spx_mem_t;
typedef float spx_coef_t;
typedef float spx_lsp_t;
typedef float spx_sig_t;
typedef float spx_word16_t;
typedef float spx_word32_t;
#define Q15ONE 1.0f
#define LPC_SCALING 1.f
#define SIG_SCALING 1.f
#define LSP_SCALING 1.f
#define GAMMA_SCALING 1.f
#define GAIN_SCALING 1.f
#define GAIN_SCALING_1 1.f
#define VERY_SMALL 1e-15f
#define VERY_LARGE32 1e15f
#define VERY_LARGE16 1e15f
#define Q15_ONE ((spx_word16_t)1.f)
#define QCONST16(x,bits) (x)
#define QCONST32(x,bits) (x)
#define NEG16(x) (-(x))
#define NEG32(x) (-(x))
#define EXTRACT16(x) (x)
#define EXTEND32(x) (x)
#define SHR16(a,shift) (a)
#define SHL16(a,shift) (a)
#define SHR32(a,shift) (a)
#define SHL32(a,shift) (a)
#define PSHR16(a,shift) (a)
#define PSHR32(a,shift) (a)
#define VSHR32(a,shift) (a)
#define SATURATE16(x,a) (x)
#define SATURATE32(x,a) (x)
#define SATURATE32PSHR(x,shift,a) (x)
#define PSHR(a,shift) (a)
#define SHR(a,shift) (a)
#define SHL(a,shift) (a)
#define SATURATE(x,a) (x)
#define ADD16(a,b) ((a)+(b))
#define SUB16(a,b) ((a)-(b))
#define ADD32(a,b) ((a)+(b))
#define SUB32(a,b) ((a)-(b))
#define MULT16_16_16(a,b) ((a)*(b))
#define MULT16_16(a,b) ((spx_word32_t)(a)*(spx_word32_t)(b))
#define MAC16_16(c,a,b) ((c)+(spx_word32_t)(a)*(spx_word32_t)(b))
#define MULT16_32_Q11(a,b) ((a)*(b))
#define MULT16_32_Q13(a,b) ((a)*(b))
#define MULT16_32_Q14(a,b) ((a)*(b))
#define MULT16_32_Q15(a,b) ((a)*(b))
#define MULT16_32_P15(a,b) ((a)*(b))
#define MAC16_32_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_32_Q15(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q11(c,a,b) ((c)+(a)*(b))
#define MAC16_16_Q13(c,a,b) ((c)+(a)*(b))
#define MAC16_16_P13(c,a,b) ((c)+(a)*(b))
#define MULT16_16_Q11_32(a,b) ((a)*(b))
#define MULT16_16_Q13(a,b) ((a)*(b))
#define MULT16_16_Q14(a,b) ((a)*(b))
#define MULT16_16_Q15(a,b) ((a)*(b))
#define MULT16_16_P15(a,b) ((a)*(b))
#define MULT16_16_P13(a,b) ((a)*(b))
#define MULT16_16_P14(a,b) ((a)*(b))
#define DIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
#define PDIV32_16(a,b) (((spx_word32_t)(a))/(spx_word16_t)(b))
#define DIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
#define PDIV32(a,b) (((spx_word32_t)(a))/(spx_word32_t)(b))
#define WORD2INT(x) ((x) < -32767.5f ? -32768 : \
((x) > 32766.5f ? 32767 : (spx_int16_t)floor(.5 + (x))))
#endif
#if defined (CONFIG_TI_C54X) || defined (CONFIG_TI_C55X)
/* 2 on TI C5x DSP */
#define BYTES_PER_CHAR 2
#define BITS_PER_CHAR 16
#define LOG2_BITS_PER_CHAR 4
#else
#define BYTES_PER_CHAR 1
#define BITS_PER_CHAR 8
#define LOG2_BITS_PER_CHAR 3
#endif
#ifdef FIXED_DEBUG
extern long long spx_mips;
#endif
#endif

View file

@ -0,0 +1,110 @@
/* Copyright (C) 2003 Jean-Marc Valin */
/**
@file fixed_generic.h
@brief Generic fixed-point operations
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef FIXED_GENERIC_H
#define FIXED_GENERIC_H
#define QCONST16(x,bits) ((spx_word16_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
#define QCONST32(x,bits) ((spx_word32_t)(.5+(x)*(((spx_word32_t)1)<<(bits))))
#define NEG16(x) (-(x))
#define NEG32(x) (-(x))
#define EXTRACT16(x) ((spx_word16_t)(x))
#define EXTEND32(x) ((spx_word32_t)(x))
#define SHR16(a,shift) ((a) >> (shift))
#define SHL16(a,shift) ((a) << (shift))
#define SHR32(a,shift) ((a) >> (shift))
#define SHL32(a,shift) ((a) << (shift))
#define PSHR16(a,shift) (SHR16((a)+((1<<((shift))>>1)),shift))
#define PSHR32(a,shift) (SHR32((a)+((EXTEND32(1)<<((shift))>>1)),shift))
#define VSHR32(a, shift) (((shift)>0) ? SHR32(a, shift) : SHL32(a, -(shift)))
#define SATURATE16(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
#define SATURATE32(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
#define SATURATE32PSHR(x,shift,a) (((x)>=(SHL32(a,shift))) ? (a) : \
(x)<=-(SHL32(a,shift)) ? -(a) : \
(PSHR32(x, shift)))
#define SHR(a,shift) ((a) >> (shift))
#define SHL(a,shift) ((spx_word32_t)(a) << (shift))
#define PSHR(a,shift) (SHR((a)+((EXTEND32(1)<<((shift))>>1)),shift))
#define SATURATE(x,a) (((x)>(a) ? (a) : (x)<-(a) ? -(a) : (x)))
#define ADD16(a,b) ((spx_word16_t)((spx_word16_t)(a)+(spx_word16_t)(b)))
#define SUB16(a,b) ((spx_word16_t)(a)-(spx_word16_t)(b))
#define ADD32(a,b) ((spx_word32_t)(a)+(spx_word32_t)(b))
#define SUB32(a,b) ((spx_word32_t)(a)-(spx_word32_t)(b))
/* result fits in 16 bits */
#define MULT16_16_16(a,b) ((((spx_word16_t)(a))*((spx_word16_t)(b))))
/* (spx_word32_t)(spx_word16_t) gives TI compiler a hint that it's 16x16->32 multiply */
#define MULT16_16(a,b) (((spx_word32_t)(spx_word16_t)(a))*((spx_word32_t)(spx_word16_t)(b)))
#define MAC16_16(c,a,b) (ADD32((c),MULT16_16((a),(b))))
#define MULT16_32_Q12(a,b) ADD32(MULT16_16((a),SHR((b),12)), SHR(MULT16_16((a),((b)&0x00000fff)),12))
#define MULT16_32_Q13(a,b) ADD32(MULT16_16((a),SHR((b),13)), SHR(MULT16_16((a),((b)&0x00001fff)),13))
#define MULT16_32_Q14(a,b) ADD32(MULT16_16((a),SHR((b),14)), SHR(MULT16_16((a),((b)&0x00003fff)),14))
#define MULT16_32_Q11(a,b) ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11))
#define MAC16_32_Q11(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),11)), SHR(MULT16_16((a),((b)&0x000007ff)),11)))
#define MULT16_32_P15(a,b) ADD32(MULT16_16((a),SHR((b),15)), PSHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MULT16_32_Q15(a,b) ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15))
#define MAC16_32_Q15(c,a,b) ADD32(c,ADD32(MULT16_16((a),SHR((b),15)), SHR(MULT16_16((a),((b)&0x00007fff)),15)))
#define MAC16_16_Q11(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),11)))
#define MAC16_16_Q13(c,a,b) (ADD32((c),SHR(MULT16_16((a),(b)),13)))
#define MAC16_16_P13(c,a,b) (ADD32((c),SHR(ADD32(4096,MULT16_16((a),(b))),13)))
#define MULT16_16_Q11_32(a,b) (SHR(MULT16_16((a),(b)),11))
#define MULT16_16_Q13(a,b) (SHR(MULT16_16((a),(b)),13))
#define MULT16_16_Q14(a,b) (SHR(MULT16_16((a),(b)),14))
#define MULT16_16_Q15(a,b) (SHR(MULT16_16((a),(b)),15))
#define MULT16_16_P13(a,b) (SHR(ADD32(4096,MULT16_16((a),(b))),13))
#define MULT16_16_P14(a,b) (SHR(ADD32(8192,MULT16_16((a),(b))),14))
#define MULT16_16_P15(a,b) (SHR(ADD32(16384,MULT16_16((a),(b))),15))
#define MUL_16_32_R15(a,bh,bl) ADD32(MULT16_16((a),(bh)), SHR(MULT16_16((a),(bl)),15))
#define DIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a))/((spx_word16_t)(b))))
#define PDIV32_16(a,b) ((spx_word16_t)(((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word16_t)(b))))
#define DIV32(a,b) (((spx_word32_t)(a))/((spx_word32_t)(b)))
#define PDIV32(a,b) (((spx_word32_t)(a)+((spx_word16_t)(b)>>1))/((spx_word32_t)(b)))
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,201 @@
/* Copyright (C) 2007-2008 Jean-Marc Valin
* Copyright (C) 2008 Thorvald Natvig
* Copyright (C) 2011 Texas Instruments
* author Jyri Sarha
*/
/**
@file resample_neon.h
@brief Resampler functions (NEON version)
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <arm_neon.h>
#ifdef FIXED_POINT
#ifdef __thumb2__
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("ssat %[ret], #16, %[a]"
: [ret] "=&r" (ret)
: [a] "r" (a)
: );
return ret;
}
#else
static inline int32_t saturate_32bit_to_16bit(int32_t a) {
int32_t ret;
asm ("vmov.s32 d0[0], %[a]\n"
"vqmovn.s32 d0, q0\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [a] "r" (a)
: "q0");
return ret;
}
#endif
#undef WORD2INT
#define WORD2INT(x) (saturate_32bit_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
static inline int32_t inner_product_single(const int16_t *a, const int16_t *b, unsigned int len)
{
int32_t ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %[len], #0\n"
" bne 1f\n"
" vld1.16 {d16}, [%[b]]!\n"
" vld1.16 {d20}, [%[a]]!\n"
" subs %[remainder], %[remainder], #4\n"
" vmull.s16 q0, d16, d20\n"
" beq 5f\n"
" b 4f\n"
"1:"
" vld1.16 {d16, d17, d18, d19}, [%[b]]!\n"
" vld1.16 {d20, d21, d22, d23}, [%[a]]!\n"
" subs %[len], %[len], #16\n"
" vmull.s16 q0, d16, d20\n"
" vmlal.s16 q0, d17, d21\n"
" vmlal.s16 q0, d18, d22\n"
" vmlal.s16 q0, d19, d23\n"
" beq 3f\n"
"2:"
" vld1.16 {d16, d17, d18, d19}, [%[b]]!\n"
" vld1.16 {d20, d21, d22, d23}, [%[a]]!\n"
" subs %[len], %[len], #16\n"
" vmlal.s16 q0, d16, d20\n"
" vmlal.s16 q0, d17, d21\n"
" vmlal.s16 q0, d18, d22\n"
" vmlal.s16 q0, d19, d23\n"
" bne 2b\n"
"3:"
" cmp %[remainder], #0\n"
" beq 5f\n"
"4:"
" vld1.16 {d16}, [%[b]]!\n"
" vld1.16 {d20}, [%[a]]!\n"
" subs %[remainder], %[remainder], #4\n"
" vmlal.s16 q0, d16, d20\n"
" bne 4b\n"
"5:"
" vaddl.s32 q0, d0, d1\n"
" vadd.s64 d0, d0, d1\n"
" vqmovn.s64 d0, q0\n"
" vqrshrn.s32 d0, q0, #15\n"
" vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+r" (len), [remainder] "+r" (remainder)
:
: "cc", "q0",
"d16", "d17", "d18", "d19",
"d20", "d21", "d22", "d23");
return ret;
}
#elif defined(FLOATING_POINT)
static inline int32_t saturate_float_to_16bit(float a) {
int32_t ret;
asm ("vmov.f32 d0[0], %[a]\n"
"vcvt.s32.f32 d0, d0, #15\n"
"vqrshrn.s32 d0, q0, #15\n"
"vmov.s16 %[ret], d0[0]\n"
: [ret] "=&r" (ret)
: [a] "r" (a)
: "q0");
return ret;
}
#undef WORD2INT
#define WORD2INT(x) (saturate_float_to_16bit(x))
#define OVERRIDE_INNER_PRODUCT_SINGLE
/* Only works when len % 4 == 0 */
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
float ret;
uint32_t remainder = len % 16;
len = len - remainder;
asm volatile (" cmp %[len], #0\n"
" bne 1f\n"
" vld1.32 {q4}, [%[b]]!\n"
" vld1.32 {q8}, [%[a]]!\n"
" subs %[remainder], %[remainder], #4\n"
" vmul.f32 q0, q4, q8\n"
" bne 4f\n"
" b 5f\n"
"1:"
" vld1.32 {q4, q5}, [%[b]]!\n"
" vld1.32 {q8, q9}, [%[a]]!\n"
" vld1.32 {q6, q7}, [%[b]]!\n"
" vld1.32 {q10, q11}, [%[a]]!\n"
" subs %[len], %[len], #16\n"
" vmul.f32 q0, q4, q8\n"
" vmul.f32 q1, q5, q9\n"
" vmul.f32 q2, q6, q10\n"
" vmul.f32 q3, q7, q11\n"
" beq 3f\n"
"2:"
" vld1.32 {q4, q5}, [%[b]]!\n"
" vld1.32 {q8, q9}, [%[a]]!\n"
" vld1.32 {q6, q7}, [%[b]]!\n"
" vld1.32 {q10, q11}, [%[a]]!\n"
" subs %[len], %[len], #16\n"
" vmla.f32 q0, q4, q8\n"
" vmla.f32 q1, q5, q9\n"
" vmla.f32 q2, q6, q10\n"
" vmla.f32 q3, q7, q11\n"
" bne 2b\n"
"3:"
" vadd.f32 q4, q0, q1\n"
" vadd.f32 q5, q2, q3\n"
" cmp %[remainder], #0\n"
" vadd.f32 q0, q4, q5\n"
" beq 5f\n"
"4:"
" vld1.32 {q6}, [%[b]]!\n"
" vld1.32 {q10}, [%[a]]!\n"
" subs %[remainder], %[remainder], #4\n"
" vmla.f32 q0, q6, q10\n"
" bne 4b\n"
"5:"
" vadd.f32 d0, d0, d1\n"
" vpadd.f32 d0, d0, d0\n"
" vmov.f32 %[ret], d0[0]\n"
: [ret] "=&r" (ret), [a] "+r" (a), [b] "+r" (b),
[len] "+l" (len), [remainder] "+l" (remainder)
:
: "cc", "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", "q8",
"q9", "q10", "q11");
return ret;
}
#endif

View file

@ -0,0 +1,128 @@
/* Copyright (C) 2007-2008 Jean-Marc Valin
* Copyright (C) 2008 Thorvald Natvig
*/
/**
@file resample_sse.h
@brief Resampler functions (SSE version)
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <xmmintrin.h>
#define OVERRIDE_INNER_PRODUCT_SINGLE
static inline float inner_product_single(const float *a, const float *b, unsigned int len)
{
int i;
float ret;
__m128 sum = _mm_setzero_ps();
for (i=0;i<len;i+=8)
{
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i)));
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4)));
}
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
_mm_store_ss(&ret, sum);
return ret;
}
#define OVERRIDE_INTERPOLATE_PRODUCT_SINGLE
static inline float interpolate_product_single(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
int i;
float ret;
__m128 sum = _mm_setzero_ps();
__m128 f = _mm_loadu_ps(frac);
for(i=0;i<len;i+=2)
{
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample)));
sum = _mm_add_ps(sum, _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample)));
}
sum = _mm_mul_ps(f, sum);
sum = _mm_add_ps(sum, _mm_movehl_ps(sum, sum));
sum = _mm_add_ss(sum, _mm_shuffle_ps(sum, sum, 0x55));
_mm_store_ss(&ret, sum);
return ret;
}
#ifdef _USE_SSE2
#include <emmintrin.h>
#define OVERRIDE_INNER_PRODUCT_DOUBLE
static inline double inner_product_double(const float *a, const float *b, unsigned int len)
{
int i;
double ret;
__m128d sum = _mm_setzero_pd();
__m128 t;
for (i=0;i<len;i+=8)
{
t = _mm_mul_ps(_mm_loadu_ps(a+i), _mm_loadu_ps(b+i));
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
t = _mm_mul_ps(_mm_loadu_ps(a+i+4), _mm_loadu_ps(b+i+4));
sum = _mm_add_pd(sum, _mm_cvtps_pd(t));
sum = _mm_add_pd(sum, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
}
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
_mm_store_sd(&ret, sum);
return ret;
}
#define OVERRIDE_INTERPOLATE_PRODUCT_DOUBLE
static inline double interpolate_product_double(const float *a, const float *b, unsigned int len, const spx_uint32_t oversample, float *frac) {
int i;
double ret;
__m128d sum;
__m128d sum1 = _mm_setzero_pd();
__m128d sum2 = _mm_setzero_pd();
__m128 f = _mm_loadu_ps(frac);
__m128d f1 = _mm_cvtps_pd(f);
__m128d f2 = _mm_cvtps_pd(_mm_movehl_ps(f,f));
__m128 t;
for(i=0;i<len;i+=2)
{
t = _mm_mul_ps(_mm_load1_ps(a+i), _mm_loadu_ps(b+i*oversample));
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
t = _mm_mul_ps(_mm_load1_ps(a+i+1), _mm_loadu_ps(b+(i+1)*oversample));
sum1 = _mm_add_pd(sum1, _mm_cvtps_pd(t));
sum2 = _mm_add_pd(sum2, _mm_cvtps_pd(_mm_movehl_ps(t, t)));
}
sum1 = _mm_mul_pd(f1, sum1);
sum2 = _mm_mul_pd(f2, sum2);
sum = _mm_add_pd(sum1, sum2);
sum = _mm_add_sd(sum, _mm_unpackhi_pd(sum, sum));
_mm_store_sd(&ret, sum);
return ret;
}
#endif

View file

@ -0,0 +1,10 @@
#ifndef __SPEEX_TYPES_H__
#define __SPEEX_TYPES_H__
/* these are filled in by configure */
typedef int16_t spx_int16_t;
typedef uint16_t spx_uint16_t;
typedef int32_t spx_int32_t;
typedef uint32_t spx_uint32_t;
#endif

View file

@ -0,0 +1,343 @@
/* Copyright (C) 2007 Jean-Marc Valin
File: speex_resampler.h
Resampling code
The design goals of this code are:
- Very fast algorithm
- Low memory requirement
- Good *perceptual* quality (and not best SNR)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef SPEEX_RESAMPLER_H
#define SPEEX_RESAMPLER_H
#ifdef OUTSIDE_SPEEX
/********* WARNING: MENTAL SANITY ENDS HERE *************/
/* If the resampler is defined outside of Speex, we change the symbol names so that
there won't be any clash if linking with Speex later on. */
/* #define RANDOM_PREFIX your software name here */
#ifndef RANDOM_PREFIX
#error "Please define RANDOM_PREFIX (above) to something specific to your project to prevent symbol name clashes"
#endif
#define CAT_PREFIX2(a,b) a ## b
#define CAT_PREFIX(a,b) CAT_PREFIX2(a, b)
#define speex_resampler_init CAT_PREFIX(RANDOM_PREFIX,_resampler_init)
#define speex_resampler_init_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_init_frac)
#define speex_resampler_destroy CAT_PREFIX(RANDOM_PREFIX,_resampler_destroy)
#define speex_resampler_process_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_float)
#define speex_resampler_process_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_int)
#define speex_resampler_process_interleaved_float CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_float)
#define speex_resampler_process_interleaved_int CAT_PREFIX(RANDOM_PREFIX,_resampler_process_interleaved_int)
#define speex_resampler_set_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate)
#define speex_resampler_get_rate CAT_PREFIX(RANDOM_PREFIX,_resampler_get_rate)
#define speex_resampler_set_rate_frac CAT_PREFIX(RANDOM_PREFIX,_resampler_set_rate_frac)
#define speex_resampler_get_ratio CAT_PREFIX(RANDOM_PREFIX,_resampler_get_ratio)
#define speex_resampler_set_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_set_quality)
#define speex_resampler_get_quality CAT_PREFIX(RANDOM_PREFIX,_resampler_get_quality)
#define speex_resampler_set_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_input_stride)
#define speex_resampler_get_input_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_stride)
#define speex_resampler_set_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_set_output_stride)
#define speex_resampler_get_output_stride CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_stride)
#define speex_resampler_get_input_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_input_latency)
#define speex_resampler_get_output_latency CAT_PREFIX(RANDOM_PREFIX,_resampler_get_output_latency)
#define speex_resampler_skip_zeros CAT_PREFIX(RANDOM_PREFIX,_resampler_skip_zeros)
#define speex_resampler_reset_mem CAT_PREFIX(RANDOM_PREFIX,_resampler_reset_mem)
#define speex_resampler_strerror CAT_PREFIX(RANDOM_PREFIX,_resampler_strerror)
#define spx_int16_t short
#define spx_int32_t int
#define spx_uint16_t unsigned short
#define spx_uint32_t unsigned int
#define speex_assert(cond)
#else /* OUTSIDE_SPEEX */
#include "speexdsp_types.h"
#endif /* OUTSIDE_SPEEX */
#ifdef __cplusplus
extern "C" {
#endif
#define SPEEX_RESAMPLER_QUALITY_MAX 10
#define SPEEX_RESAMPLER_QUALITY_MIN 0
#define SPEEX_RESAMPLER_QUALITY_DEFAULT 4
#define SPEEX_RESAMPLER_QUALITY_VOIP 3
#define SPEEX_RESAMPLER_QUALITY_DESKTOP 5
enum {
RESAMPLER_ERR_SUCCESS = 0,
RESAMPLER_ERR_ALLOC_FAILED = 1,
RESAMPLER_ERR_BAD_STATE = 2,
RESAMPLER_ERR_INVALID_ARG = 3,
RESAMPLER_ERR_PTR_OVERLAP = 4,
RESAMPLER_ERR_OVERFLOW = 5,
RESAMPLER_ERR_MAX_ERROR
};
struct SpeexResamplerState_;
typedef struct SpeexResamplerState_ SpeexResamplerState;
/** Create a new resampler with integer input and output rates.
* @param nb_channels Number of channels to be processed
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality.
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
SpeexResamplerState *speex_resampler_init(spx_uint32_t nb_channels,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
int *err);
/** Create a new resampler with fractional input/output rates. The sampling
* rate ratio is an arbitrary rational number with both the numerator and
* denominator being 32-bit integers.
* @param nb_channels Number of channels to be processed
* @param ratio_num Numerator of the sampling rate ratio
* @param ratio_den Denominator of the sampling rate ratio
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
* @param quality Resampling quality between 0 and 10, where 0 has poor quality
* and 10 has very high quality.
* @return Newly created resampler state
* @retval NULL Error: not enough memory
*/
SpeexResamplerState *speex_resampler_init_frac(spx_uint32_t nb_channels,
spx_uint32_t ratio_num,
spx_uint32_t ratio_den,
spx_uint32_t in_rate,
spx_uint32_t out_rate,
int quality,
int *err);
/** Destroy a resampler state.
* @param st Resampler state
*/
void speex_resampler_destroy(SpeexResamplerState *st);
/** Resample a float array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the
* number of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
int speex_resampler_process_float(SpeexResamplerState *st,
spx_uint32_t channel_index,
const float *in,
spx_uint32_t *in_len,
float *out,
spx_uint32_t *out_len);
/** Resample an int array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param channel_index Index of the channel to process for the multi-channel
* base (0 otherwise)
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written
*/
int speex_resampler_process_int(SpeexResamplerState *st,
spx_uint32_t channel_index,
const spx_int16_t *in,
spx_uint32_t *in_len,
spx_int16_t *out,
spx_uint32_t *out_len);
/** Resample an interleaved float array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed. This is all per-channel.
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
int speex_resampler_process_interleaved_float(SpeexResamplerState *st,
const float *in,
spx_uint32_t *in_len,
float *out,
spx_uint32_t *out_len);
/** Resample an interleaved int array. The input and output buffers must *not* overlap.
* @param st Resampler state
* @param in Input buffer
* @param in_len Number of input samples in the input buffer. Returns the number
* of samples processed. This is all per-channel.
* @param out Output buffer
* @param out_len Size of the output buffer. Returns the number of samples written.
* This is all per-channel.
*/
int speex_resampler_process_interleaved_int(SpeexResamplerState *st,
const spx_int16_t *in,
spx_uint32_t *in_len,
spx_int16_t *out,
spx_uint32_t *out_len);
/** Set (change) the input/output sampling rates (integer value).
* @param st Resampler state
* @param in_rate Input sampling rate (integer number of Hz).
* @param out_rate Output sampling rate (integer number of Hz).
*/
int speex_resampler_set_rate(SpeexResamplerState *st,
spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current input/output sampling rates (integer value).
* @param st Resampler state
* @param in_rate Input sampling rate (integer number of Hz) copied.
* @param out_rate Output sampling rate (integer number of Hz) copied.
*/
void speex_resampler_get_rate(SpeexResamplerState *st,
spx_uint32_t *in_rate,
spx_uint32_t *out_rate);
/** Set (change) the input/output sampling rates and resampling ratio
* (fractional values in Hz supported).
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio
* @param ratio_den Denominator of the sampling rate ratio
* @param in_rate Input sampling rate rounded to the nearest integer (in Hz).
* @param out_rate Output sampling rate rounded to the nearest integer (in Hz).
*/
int speex_resampler_set_rate_frac(SpeexResamplerState *st,
spx_uint32_t ratio_num,
spx_uint32_t ratio_den,
spx_uint32_t in_rate,
spx_uint32_t out_rate);
/** Get the current resampling ratio. This will be reduced to the least
* common denominator.
* @param st Resampler state
* @param ratio_num Numerator of the sampling rate ratio copied
* @param ratio_den Denominator of the sampling rate ratio copied
*/
void speex_resampler_get_ratio(SpeexResamplerState *st,
spx_uint32_t *ratio_num,
spx_uint32_t *ratio_den);
/** Set (change) the conversion quality.
* @param st Resampler state
* @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
int speex_resampler_set_quality(SpeexResamplerState *st,
int quality);
/** Get the conversion quality.
* @param st Resampler state
* @param quality Resampling quality between 0 and 10, where 0 has poor
* quality and 10 has very high quality.
*/
void speex_resampler_get_quality(SpeexResamplerState *st,
int *quality);
/** Set (change) the input stride.
* @param st Resampler state
* @param stride Input stride
*/
void speex_resampler_set_input_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the input stride.
* @param st Resampler state
* @param stride Input stride copied
*/
void speex_resampler_get_input_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Set (change) the output stride.
* @param st Resampler state
* @param stride Output stride
*/
void speex_resampler_set_output_stride(SpeexResamplerState *st,
spx_uint32_t stride);
/** Get the output stride.
* @param st Resampler state copied
* @param stride Output stride
*/
void speex_resampler_get_output_stride(SpeexResamplerState *st,
spx_uint32_t *stride);
/** Get the latency introduced by the resampler measured in input samples.
* @param st Resampler state
*/
int speex_resampler_get_input_latency(SpeexResamplerState *st);
/** Get the latency introduced by the resampler measured in output samples.
* @param st Resampler state
*/
int speex_resampler_get_output_latency(SpeexResamplerState *st);
/** Make sure that the first samples to go out of the resamplers don't have
* leading zeros. This is only useful before starting to use a newly created
* resampler. It is recommended to use that when resampling an audio file, as
* it will generate a file with the same length. For real-time processing,
* it is probably easier not to use this call (so that the output duration
* is the same for the first frame).
* @param st Resampler state
*/
int speex_resampler_skip_zeros(SpeexResamplerState *st);
/** Reset a resampler so a new (unrelated) stream can be processed.
* @param st Resampler state
*/
int speex_resampler_reset_mem(SpeexResamplerState *st);
/** Returns the English meaning for an error code
* @param err Error code
* @return English string
*/
const char *speex_resampler_strerror(int err);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,115 @@
/* Copyright (C) 2002 Jean-Marc Valin */
/**
@file stack_alloc.h
@brief Temporary memory allocation on stack
*/
/*
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
- Neither the name of the Xiph.org Foundation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef STACK_ALLOC_H
#define STACK_ALLOC_H
#ifdef USE_ALLOCA
# ifdef WIN32
# include <malloc.h>
# else
# ifdef HAVE_ALLOCA_H
# include <alloca.h>
# else
# include <stdlib.h>
# endif
# endif
#endif
/**
* @def ALIGN(stack, size)
*
* Aligns the stack to a 'size' boundary
*
* @param stack Stack
* @param size New size boundary
*/
/**
* @def PUSH(stack, size, type)
*
* Allocates 'size' elements of type 'type' on the stack
*
* @param stack Stack
* @param size Number of elements
* @param type Type of element
*/
/**
* @def VARDECL(var)
*
* Declare variable on stack
*
* @param var Variable to declare
*/
/**
* @def ALLOC(var, size, type)
*
* Allocate 'size' elements of 'type' on stack
*
* @param var Name of variable to allocate
* @param size Number of elements
* @param type Type of element
*/
#ifdef ENABLE_VALGRIND
#include <valgrind/memcheck.h>
#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
#define PUSH(stack, size, type) (VALGRIND_MAKE_NOACCESS(stack, 1000),ALIGN((stack),sizeof(type)),VALGRIND_MAKE_WRITABLE(stack, ((size)*sizeof(type))),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
#else
#define ALIGN(stack, size) ((stack) += ((size) - (long)(stack)) & ((size) - 1))
#define PUSH(stack, size, type) (ALIGN((stack),sizeof(type)),(stack)+=((size)*sizeof(type)),(type*)((stack)-((size)*sizeof(type))))
#endif
#if defined(VAR_ARRAYS)
#define VARDECL(var)
#define ALLOC(var, size, type) type var[size]
#elif defined(USE_ALLOCA)
#define VARDECL(var) var
#define ALLOC(var, size, type) var = alloca(sizeof(type)*(size))
#else
#define VARDECL(var) var
#define ALLOC(var, size, type) var = PUSH(stack, size, type)
#endif
#endif

View file

@ -27,6 +27,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tinyxml2", "dep\tinyxml2\ti
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "duckstation-qt", "src\duckstation-qt\duckstation-qt.vcxproj", "{28F14272-0EC4-41BB-849F-182ADB81AF70}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cubeb", "dep\cubeb\cubeb.vcxproj", "{72F9423C-91EE-4487-AAC6-555ED6F61AA1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -211,6 +213,22 @@ Global
{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
{28F14272-0EC4-41BB-849F-182ADB81AF70}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x64.ActiveCfg = Debug|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x64.Build.0 = Debug|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x86.ActiveCfg = Debug|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Debug|x86.Build.0 = Debug|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x64.ActiveCfg = DebugFast|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x64.Build.0 = DebugFast|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x86.ActiveCfg = DebugFast|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.DebugFast|x86.Build.0 = DebugFast|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x64.ActiveCfg = Release|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x64.Build.0 = Release|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x86.ActiveCfg = Release|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.Release|x86.Build.0 = Release|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x64.ActiveCfg = ReleaseLTCG|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x64.Build.0 = ReleaseLTCG|x64
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x86.ActiveCfg = ReleaseLTCG|Win32
{72F9423C-91EE-4487-AAC6-555ED6F61AA1}.ReleaseLTCG|x86.Build.0 = ReleaseLTCG|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -223,6 +241,7 @@ Global
{ACE32F47-2960-4FB3-9F77-2C375625BF61} = {BA490C0E-497D-4634-A21E-E65012006385}
{3773F4CC-614E-4028-8595-22E08CA649E3} = {BA490C0E-497D-4634-A21E-E65012006385}
{933118A9-68C5-47B4-B151-B03C93961623} = {BA490C0E-497D-4634-A21E-E65012006385}
{72F9423C-91EE-4487-AAC6-555ED6F61AA1} = {BA490C0E-497D-4634-A21E-E65012006385}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {26E40B32-7C1D-48D0-95F4-1A500E054028}

View file

@ -41,6 +41,10 @@ public:
void WriteSamples(const SampleType* samples, u32 num_samples);
void EndWrite(u32 num_samples);
static std::unique_ptr<AudioStream> CreateNullAudioStream();
static std::unique_ptr<AudioStream> CreateCubebAudioStream();
protected:
virtual bool OpenDevice() = 0;
virtual void PauseDevice(bool paused) = 0;

View file

@ -42,6 +42,7 @@
<ClInclude Include="byte_stream.h" />
<ClInclude Include="cd_image.h" />
<ClInclude Include="cpu_detect.h" />
<ClInclude Include="cubeb_audio_stream.h" />
<ClInclude Include="d3d11\shader_compiler.h" />
<ClInclude Include="d3d11\staging_texture.h" />
<ClInclude Include="d3d11\stream_buffer.h" />
@ -74,6 +75,7 @@
<ClCompile Include="cd_image.cpp" />
<ClCompile Include="cd_image_bin.cpp" />
<ClCompile Include="cd_image_cue.cpp" />
<ClCompile Include="cubeb_audio_stream.cpp" />
<ClCompile Include="d3d11\shader_compiler.cpp" />
<ClCompile Include="d3d11\staging_texture.cpp" />
<ClCompile Include="d3d11\stream_buffer.cpp" />
@ -99,6 +101,9 @@
<Natvis Include="bitfield.natvis" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\dep\cubeb\cubeb.vcxproj">
<Project>{72f9423c-91ee-4487-aac6-555ed6f61aa1}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\glad\glad.vcxproj">
<Project>{43540154-9e1e-409c-834f-b84be5621388}</Project>
</ProjectReference>
@ -243,7 +248,7 @@
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
@ -269,7 +274,7 @@
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<SupportJustMyCode>false</SupportJustMyCode>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@ -298,7 +303,7 @@
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<ConformanceMode>true</ConformanceMode>
@ -324,7 +329,7 @@
<PreprocessorDefinitions>_ITERATOR_DEBUG_LEVEL=1;WIN32;_DEBUGFAST;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<SupportJustMyCode>false</SupportJustMyCode>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
@ -354,7 +359,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<WholeProgramOptimization>false</WholeProgramOptimization>
@ -384,7 +389,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<OmitFramePointers>true</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
@ -414,7 +419,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>
<WholeProgramOptimization>false</WholeProgramOptimization>
@ -444,7 +449,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(SolutionDir)dep\glad\include;$(SolutionDir)dep\cubeb\include;$(SolutionDir)dep\libcue\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<OmitFramePointers>true</OmitFramePointers>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<LanguageStandard>stdcpp17</LanguageStandard>

View file

@ -46,6 +46,7 @@
<ClInclude Include="string_util.h" />
<ClInclude Include="md5_digest.h" />
<ClInclude Include="cpu_detect.h" />
<ClInclude Include="cubeb_audio_stream.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="jit_code_buffer.cpp" />
@ -88,6 +89,7 @@
<ClCompile Include="file_system.cpp" />
<ClCompile Include="string_util.cpp" />
<ClCompile Include="md5_digest.cpp" />
<ClCompile Include="cubeb_audio_stream.cpp" />
</ItemGroup>
<ItemGroup>
<Natvis Include="bitfield.natvis" />

View file

@ -0,0 +1,120 @@
#include "cubeb_audio_stream.h"
#include "common/assert.h"
#include "common/log.h"
Log_SetChannel(CubebAudioStream);
CubebAudioStream::CubebAudioStream() = default;
CubebAudioStream::~CubebAudioStream()
{
if (IsOpen())
CubebAudioStream::CloseDevice();
}
bool CubebAudioStream::OpenDevice()
{
Assert(!IsOpen());
int rv = cubeb_init(&m_cubeb_context, "DuckStation", nullptr);
if (rv != CUBEB_OK)
{
Log_ErrorPrintf("Could not initialize cubeb context: %d", rv);
return false;
}
cubeb_stream_params params = {};
params.format = CUBEB_SAMPLE_S16LE;
params.rate = m_output_sample_rate;
params.channels = m_channels;
params.layout = CUBEB_LAYOUT_UNDEFINED;
params.prefs = CUBEB_STREAM_PREF_NONE;
u32 latency_frames = 0;
rv = cubeb_get_min_latency(m_cubeb_context, &params, &latency_frames);
if (rv != CUBEB_OK)
{
Log_ErrorPrintf("Could not get minimum latency: %d", rv);
cubeb_destroy(m_cubeb_context);
m_cubeb_context = nullptr;
return false;
}
Log_InfoPrintf("Minimum latency in frames: %u", latency_frames);
if (latency_frames > m_buffer_size)
Log_WarningPrintf("Minimum latency is above buffer size: %u vs %u", latency_frames, m_buffer_size);
else
latency_frames = m_buffer_size;
char stream_name[32];
std::snprintf(stream_name, sizeof(stream_name), "AudioStream_%p", this);
rv = cubeb_stream_init(m_cubeb_context, &m_cubeb_stream, stream_name, nullptr, nullptr, nullptr, &params,
latency_frames, DataCallback, StateCallback, this);
if (rv != CUBEB_OK)
{
Log_ErrorPrintf("Could not create stream: %d", rv);
cubeb_destroy(m_cubeb_context);
m_cubeb_context = nullptr;
return false;
}
return true;
}
void CubebAudioStream::PauseDevice(bool paused)
{
if (paused == m_paused)
return;
int rv = paused ? cubeb_stream_stop(m_cubeb_stream) : cubeb_stream_start(m_cubeb_stream);
if (rv != CUBEB_OK)
{
Log_ErrorPrintf("cubeb_stream_%s failed: %d", paused ? "stop" : "start", rv);
return;
}
}
void CubebAudioStream::CloseDevice()
{
Assert(IsOpen());
if (!m_paused)
cubeb_stream_stop(m_cubeb_stream);
cubeb_stream_destroy(m_cubeb_stream);
m_cubeb_stream = nullptr;
cubeb_destroy(m_cubeb_context);
m_cubeb_context = nullptr;
}
long CubebAudioStream::DataCallback(cubeb_stream* stm, void* user_ptr, const void* input_buffer, void* output_buffer,
long nframes)
{
CubebAudioStream* const this_ptr = static_cast<CubebAudioStream*>(user_ptr);
const u32 read_frames =
this_ptr->ReadSamples(reinterpret_cast<SampleType*>(output_buffer), static_cast<u32>(nframes));
const u32 silence_frames = static_cast<u32>(nframes) - read_frames;
if (silence_frames > 0)
{
std::memset(reinterpret_cast<SampleType*>(output_buffer) + (read_frames * this_ptr->m_channels), 0,
silence_frames * this_ptr->m_channels * sizeof(SampleType));
}
return nframes;
}
void CubebAudioStream::StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state)
{
CubebAudioStream* const this_ptr = static_cast<CubebAudioStream*>(user_ptr);
this_ptr->m_paused = (state != CUBEB_STATE_STARTED);
}
void CubebAudioStream::BufferAvailable() {}
std::unique_ptr<AudioStream> AudioStream::CreateCubebAudioStream()
{
return std::make_unique<CubebAudioStream>();
}

View file

@ -0,0 +1,27 @@
#pragma once
#include "common/audio_stream.h"
#include "cubeb/cubeb.h"
#include <cstdint>
class CubebAudioStream final : public AudioStream
{
public:
CubebAudioStream();
~CubebAudioStream();
protected:
bool IsOpen() const { return m_cubeb_stream != nullptr; }
bool OpenDevice() override;
void PauseDevice(bool paused) override;
void CloseDevice() override;
void BufferAvailable() override;
static long DataCallback(cubeb_stream* stm, void* user_ptr, const void* input_buffer, void* output_buffer,
long nframes);
static void StateCallback(cubeb_stream* stream, void* user_ptr, cubeb_state state);
cubeb* m_cubeb_context = nullptr;
cubeb_stream* m_cubeb_stream = nullptr;
bool m_paused = true;
};

View file

@ -19,7 +19,7 @@ void NullAudioStream::BufferAvailable()
DropBuffer();
}
std::unique_ptr<AudioStream> NullAudioStream::Create()
std::unique_ptr<AudioStream> AudioStream::CreateNullAudioStream()
{
return std::unique_ptr<AudioStream>(new NullAudioStream());
return std::make_unique<NullAudioStream>();
}

View file

@ -4,16 +4,12 @@
class NullAudioStream final : public AudioStream
{
public:
NullAudioStream();
~NullAudioStream();
static std::unique_ptr<AudioStream> Create();
protected:
bool OpenDevice() override;
void PauseDevice(bool paused) override;
void CloseDevice() override;
void BufferAvailable() override;
private:
NullAudioStream();
};

View file

@ -53,6 +53,7 @@ enum class AudioBackend : u8
{
Null,
Default,
Cubeb,
Count
};

View file

@ -2,7 +2,6 @@
#include "common/assert.h"
#include "common/byte_stream.h"
#include "common/log.h"
#include "common/null_audio_stream.h"
#include "common/string_util.h"
#include "core/controller.h"
#include "core/game_list.h"
@ -28,7 +27,6 @@ QtHostInterface::QtHostInterface(QObject* parent)
checkSettings();
createGameList();
doUpdateInputMap();
createAudioStream();
createThread();
}
@ -446,15 +444,26 @@ void QtHostInterface::doBootSystem(QString initial_filename, QString initial_sav
void QtHostInterface::createAudioStream()
{
// Qt at least on Windows seems to want a buffer size of at least 8KB.
// m_audio_stream = QtAudioStream::Create();
// if (!m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4))
switch (m_settings.audio_backend)
{
case AudioBackend::Default:
case AudioBackend::Cubeb:
m_audio_stream = AudioStream::CreateCubebAudioStream();
break;
case AudioBackend::Null:
default:
m_audio_stream = AudioStream::CreateNullAudioStream();
break;
}
if (!m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4))
{
qWarning() << "Failed to configure audio stream, falling back to null output";
// fall back to null output
m_audio_stream.reset();
m_audio_stream = NullAudioStream::Create();
m_audio_stream = AudioStream::CreateNullAudioStream();
m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS, AUDIO_BUFFER_SIZE, 4);
}
}
@ -483,6 +492,10 @@ void QtHostInterface::doStopThread()
void QtHostInterface::threadEntryPoint()
{
m_worker_thread_event_loop = new QEventLoop();
createAudioStream();
// TODO: Event which flags the thread as ready
while (!m_shutdown_flag.load())
{
if (!m_system || m_paused)
@ -525,6 +538,7 @@ void QtHostInterface::threadEntryPoint()
}
m_system.reset();
m_audio_stream.reset();
delete m_worker_thread_event_loop;
m_worker_thread_event_loop = nullptr;

View file

@ -2,7 +2,6 @@
#include "common/assert.h"
#include "common/byte_stream.h"
#include "common/log.h"
#include "common/null_audio_stream.h"
#include "common/string_util.h"
#include "core/controller.h"
#include "core/gpu.h"
@ -126,7 +125,11 @@ void SDLHostInterface::CreateAudioStream()
switch (m_settings.audio_backend)
{
case AudioBackend::Null:
m_audio_stream = NullAudioStream::Create();
m_audio_stream = AudioStream::CreateNullAudioStream();
break;
case AudioBackend::Cubeb:
m_audio_stream = AudioStream::CreateCubebAudioStream();
break;
case AudioBackend::Default:
@ -139,7 +142,7 @@ void SDLHostInterface::CreateAudioStream()
{
ReportError("Failed to recreate audio stream, falling back to null");
m_audio_stream.reset();
m_audio_stream = NullAudioStream::Create();
m_audio_stream = AudioStream::CreateNullAudioStream();
if (!m_audio_stream->Reconfigure(AUDIO_SAMPLE_RATE, AUDIO_CHANNELS))
Panic("Failed to reconfigure null audio stream");
}