mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-24 09:14:04 +02:00
Merge branch 'dev' into dev-0.12
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
\#docs/
|
||||
_private/
|
||||
build/
|
||||
debugging/
|
||||
evaluations/
|
||||
examples/build/bin/
|
||||
@@ -32,6 +33,7 @@ website/docs/
|
||||
*.vcxproj.user
|
||||
.vs/
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Below are individual files that I may start version controlling later or delete outright.
|
||||
examples/build/COSMO.txt
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
v0.11.22 - TBD
|
||||
=====================
|
||||
* Starting from version 0.12, miniaudio will be switching to a split .c/h pair, away from a single header. In preparation for this, a file named "miniaudio.c" has been added to repository. Currently this is just a simple wrapper around miniaudio.h and `MINIAUDIO_IMPLEMENTATION`. Nothing has changed in miniaudio.h, however when version 0.12 is released you will need to use miniaudio.c for the implementation. It's recommended you start the transition away from `MINIAUDIO_IMPLEMENTATION` and towards miniaudio.c. If you want to keep building your project as a single translation unit, you can do `#include "miniaudio.c"` which will continue to be supported with version 0.12 and beyond.
|
||||
* Add `MA_SOUND_FLAG_LOOPING` and `MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING` flags. These can be used to initialize sounds and resource managed data sources to loop by default. This is the recommended way to enable looping for streams. The `isLooping` config option in `ma_sound_config` and `ma_resource_manager_data_source_config` has been deprecated. If you are using those, you should switch to the new flag or else you'll get compiler errors when upgrading to a future version.
|
||||
* `ma_rb_commit_read()`, `ma_rb_commit_write()`, `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` no longer return `MA_AT_END`. The reason for this change is that there's no real notion of an "end" in a ring buffer which makes this result code confusing. In addition, it's possible for these functions to return something other than `MA_SUCCESS` when the operation completed successfully which adds to the confusion. The correct way to check if there is any more room in the ring buffer is to look at the frame count returned by `*rb_acquire_read/write()`.
|
||||
* `ma_rb_commit_read()`, `ma_rb_commit_write()`, `ma_pcm_rb_commit_read()` and `ma_pcm_rb_commit_write()` no longer return `MA_AT_END`. The reason for this change is that there's no real notion of an "end" in a ring buffer which makes this result code confusing. In addition, it's possible for these functions to return something other than `MA_SUCCESS` even when the operation completed successfully which adds to the confusion. The correct way to check if there is any more room in the ring buffer is to look at the frame count returned by `*rb_acquire_read/write()`.
|
||||
* The `ma_pcm_rb` data source implementation has been modified to pad output data with silence if there is not enough data in the ring buffer to fill the request. What this means is for an `ma_pcm_rb`, `ma_data_source_read_pcm_frames()` should no longer return a frame count of less than what you requested, and will therefore never return `MA_AT_END` which does not make sense for a ring buffer since it does not have the notion of an end. This change should make it much easier to use a ring buffer as the data source for a `ma_sound`.
|
||||
* Fix a bug relating to node detachment.
|
||||
* Fix a bug where amplification with `ma_device_set_master_volume()` does not work.
|
||||
@@ -14,7 +15,7 @@ v0.11.22 - TBD
|
||||
* AAudio: Fix an error where the device is silenced after rerouting. With this change, miniaudio will no longer give AAudio a hint to use your supplied period size which will therefore result in AAudio using its default latency configuration. If you want AAudio to try to use the period size you supply in the device config, which is the old behaviour, set `aaudio.allowSetBufferCapacity` to true in the device config. Note, however, if you do this you may end up with errors when rerouting between devices.
|
||||
* AAudio: The default minimum SDK version has been increased from 26 to 27 when enabling AAudio. If you need to support version 26, you can use `#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 26`.
|
||||
* AAudio: Fix ma_device_get_info() implementation.
|
||||
* PulseAudio: Allow setting the channel map requested from PulseAudio in device configs
|
||||
* PulseAudio: Allow setting the channel map requested from PulseAudio in device configs.
|
||||
|
||||
|
||||
v0.11.21 - 2023-11-15
|
||||
|
||||
+500
@@ -0,0 +1,500 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(miniaudio
|
||||
VERSION 0.11
|
||||
)
|
||||
|
||||
|
||||
# Options
|
||||
option(MINIAUDIO_BUILD_EXAMPLES "Build miniaudio examples" OFF)
|
||||
option(MINIAUDIO_BUILD_TESTS "Build miniaudio tests" OFF)
|
||||
option(MINIAUDIO_FORCE_CXX "Use C++ compiler for C files" OFF)
|
||||
option(MINIAUDIO_FORCE_C89 "Use C89 standard" OFF)
|
||||
option(MINIAUDIO_NO_LIBVORBIS "Disable miniaudio_libvorbis" OFF)
|
||||
option(MINIAUDIO_NO_LIBOPUS "Disable miniaudio_libopus" OFF)
|
||||
option(MINIAUDIO_NO_WASAPI "Disable the WASAPI backend" OFF)
|
||||
option(MINIAUDIO_NO_DSOUND "Disable the DirectSound backend" OFF)
|
||||
option(MINIAUDIO_NO_WINMM "Disable the WinMM backend" OFF)
|
||||
option(MINIAUDIO_NO_ALSA "Disable the ALSA backend" OFF)
|
||||
option(MINIAUDIO_NO_PULSEAUDIO "Disable the PulseAudio backend" OFF)
|
||||
option(MINIAUDIO_NO_JACK "Disable the JACK backend" OFF)
|
||||
option(MINIAUDIO_NO_COREAUDIO "Disable the CoreAudio backend" OFF)
|
||||
option(MINIAUDIO_NO_SNDIO "Disable the sndio backend" OFF)
|
||||
option(MINIAUDIO_NO_AUDIO4 "Disable the audio(4) backend" OFF)
|
||||
option(MINIAUDIO_NO_OSS "Disable the OSS backend" OFF)
|
||||
option(MINIAUDIO_NO_AAUDIO "Disable the AAudio backend" OFF)
|
||||
option(MINIAUDIO_NO_OPENSL "Disable the OpenSL|ES backend" OFF)
|
||||
option(MINIAUDIO_NO_WEBAUDIO "Disable the Web Audio backend" OFF)
|
||||
option(MINIAUDIO_NO_CUSTOM "Disable support for custom backends" OFF)
|
||||
option(MINIAUDIO_NO_NULL "Disable the null backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS "Only enable specific backends. Backends can be enabled with MINIAUDIO_ENABLE_[BACKEND]" OFF)
|
||||
option(MINIAUDIO_ENABLE_WASAPI "Enable the WASAPI backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_DSOUND "Enable the DirectSound backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_WINMM "Enable the WinMM backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_ALSA "Enable the ALSA backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_PULSEAUDIO "Enable the PulseAudio backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_JACK "Enable the JACK backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_COREAUDIO "Enable the CoreAudio backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_SNDIO "Enable the sndio backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_AUDIO4 "Enable the audio(4) backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_OSS "Enable the OSS backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_AAUDIO "Enable the AAudio backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_OPENSL "Enable the OpenSL|ES backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_WEBAUDIO "Enable the Web Audio backend" OFF)
|
||||
option(MINIAUDIO_ENABLE_CUSTOM "Enable support for custom backends" OFF)
|
||||
option(MINIAUDIO_ENABLE_NULL "Enable the null backend" OFF)
|
||||
option(MINIAUDIO_NO_DECODING "Disable decoding APIs" OFF)
|
||||
option(MINIAUDIO_NO_ENCODING "Disable encoding APIs" OFF)
|
||||
option(MINIAUDIO_NO_WAV "Disable the built-in WAV decoder" OFF)
|
||||
option(MINIAUDIO_NO_FLAC "Disable the built-in FLAC decoder" OFF)
|
||||
option(MINIAUDIO_NO_MP3 "Disable the built-in MP3 decoder" OFF)
|
||||
option(MINIAUDIO_NO_DEVICEIO "Disable audio playback and capture" OFF)
|
||||
option(MINIAUDIO_NO_RESOURCE_MANAGER "Disable the resource manager API" OFF)
|
||||
option(MINIAUDIO_NO_NODE_GRAPH "Disable the node graph API" OFF)
|
||||
option(MINIAUDIO_NO_ENGINE "Disable the high-level engine API" OFF)
|
||||
option(MINIAUDIO_NO_THREADING "Disable threading. Must be used with MINIAUDIO_NO_DEVICEIO" OFF)
|
||||
option(MINIAUDIO_NO_GENERATION "Disable generation APIs such as ma_waveform" OFF)
|
||||
option(MINIAUDIO_NO_SSE2 "Disable SSE2 optimizations" OFF)
|
||||
option(MINIAUDIO_NO_AVX2 "Disable AVX2 optimizations" OFF)
|
||||
option(MINIAUDIO_NO_NEON "Disable NEON optimizations" OFF)
|
||||
option(MINIAUDIO_NO_RUNTIME_LINKING "Disable runtime linking" OFF)
|
||||
option(MINIAUDIO_USE_STDINT "Use <stdint.h> for sized types" OFF)
|
||||
option(MINIAUDIO_DEBUG_OUTPUT "Enable stdout debug output" OFF)
|
||||
|
||||
|
||||
# Construct compiler options.
|
||||
set(COMPILE_OPTIONS)
|
||||
|
||||
if(MINIAUDIO_FORCE_CXX)
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
list(APPEND COMPILE_OPTIONS -x c++)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
list(APPEND COMPILE_OPTIONS /TP)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MINIAUDIO_FORCE_C89)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
list(APPEND COMPILE_OPTIONS -std=c89)
|
||||
elseif(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
# MSVC does not have an option for forcing C89.
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Warnings
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
list(APPEND COMPILE_OPTIONS -Wall -Wextra -Wpedantic)
|
||||
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
list(APPEND COMPILE_OPTIONS /W4)
|
||||
endif()
|
||||
|
||||
|
||||
# Construct compiler defines
|
||||
set(COMPILE_DEFINES)
|
||||
|
||||
if(MINIAUDIO_NO_WASAPI)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_WASAPI)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_DSOUND)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_DSOUND)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_WINMM)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_WINMM)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_ALSA)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_ALSA)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_PULSEAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_PULSEAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_JACK)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_JACK)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_COREAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_COREAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_SNDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_SNDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_AUDIO4)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_AUDIO4)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_OSS)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_OSS)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_AAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_AAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_OPENSL)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_OPENSL)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_WEBAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_WEBAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_CUSTOM)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_CUSTOM)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_NULL)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_NULL)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_ONLY_SPECIFIC_BACKENDS)
|
||||
if(MINIAUDIO_ENABLE_WASAPI)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_WASAPI)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_DSOUND)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_DSOUND)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_WINMM)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_WINMM)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_ALSA)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_ALSA)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_PULSEAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_PULSEAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_JACK)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_JACK)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_COREAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_COREAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_SNDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_SNDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_AUDIO4)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_AUDIO4)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_OSS)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_OSS)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_AAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_AAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_OPENSL)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_OPENSL)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_WEBAUDIO)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_WEBAUDIO)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_CUSTOM)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_CUSTOM)
|
||||
endif()
|
||||
if(MINIAUDIO_ENABLE_NULL)
|
||||
list(APPEND COMPILE_DEFINES MA_ENABLE_NULL)
|
||||
endif()
|
||||
endif()
|
||||
if(MINIAUDIO_NO_DECODING)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_DECODING)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_ENCODING)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_ENCODING)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_WAV)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_WAV)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_FLAC)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_FLAC)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_MP3)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_MP3)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_DEVICEIO)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_DEVICE_IO)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_RESOURCE_MANAGER)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_RESOURCE_MANAGER)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_NODE_GRAPH)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_NODE_GRAPH)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_ENGINE)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_ENGINE)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_THREADING)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_THREADING)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_GENERATION)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_GENERATION)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_SSE2)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_SSE2)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_AVX2)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_AVX2)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_NEON)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_NEON)
|
||||
endif()
|
||||
if(MINIAUDIO_NO_RUNTIME_LINKING)
|
||||
list(APPEND COMPILE_DEFINES MA_NO_RUNTIME_LINKING)
|
||||
endif()
|
||||
if(MINIAUDIO_USE_STDINT)
|
||||
list(APPEND COMPILE_DEFINES MA_USE_STDINT)
|
||||
endif()
|
||||
if(MINIAUDIO_DEBUG_OUTPUT)
|
||||
list(APPEND COMPILE_DEFINES MA_DEBUG_OUTPUT)
|
||||
endif()
|
||||
|
||||
|
||||
# External Libraries
|
||||
if(NOT MINIAUDIO_NO_LIBVORBIS)
|
||||
find_library(LIBVORBISFILE NAMES vorbisfile)
|
||||
if(LIBVORBISFILE)
|
||||
message(STATUS "Found libvorbisfile: ${LIBVORBISFILE}")
|
||||
set(HAS_LIBVORBIS TRUE)
|
||||
else()
|
||||
message(STATUS "libvorbisfile not found. miniaudio_libvorbis will be excluded.")
|
||||
endif()
|
||||
|
||||
|
||||
endif()
|
||||
|
||||
if(NOT MINIAUDIO_NO_LIBOPUS)
|
||||
find_library(LIBOPUSFILE NAMES opusfile)
|
||||
if(LIBOPUSFILE)
|
||||
message(STATUS "Found libopusfile: ${LIBOPUSFILE}")
|
||||
|
||||
# opusfile is very annoying because they do "#include <opus_multistream.h>" in opusfile.h which results
|
||||
# in an error unless we explicitly add the include path to the opus include directory.
|
||||
find_path(OPUSFILE_INCLUDE_DIR
|
||||
NAMES opus/opusfile.h
|
||||
DOC "Directory containing opusfile.h"
|
||||
)
|
||||
|
||||
if(OPUSFILE_INCLUDE_DIR)
|
||||
message(STATUS "Found opusfile.h in ${OPUSFILE_INCLUDE_DIR}")
|
||||
set(HAS_LIBOPUS TRUE)
|
||||
else()
|
||||
message(STATUS "Could not find opusfile.h. miniaudio_libopus will be excluded.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "libopusfile not found. miniaudio_libopus will be excluded.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_library(SDL2_LIBRARY NAMES SDL2)
|
||||
if(SDL2_LIBRARY)
|
||||
message(STATUS "Found SDL2: ${SDL2_LIBRARY}")
|
||||
set(HAS_SDL2 TRUE)
|
||||
else()
|
||||
message(STATUS "SDL2 not found. SDL2 examples will be excluded.")
|
||||
endif()
|
||||
|
||||
# SteamAudio has an annoying SDK setup. In the lib folder there is a folder for each platform. We need to specify the
|
||||
# platform we're compiling for.
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
# Assume 64-bit. Now we need to check if it's for Windows or Linux.
|
||||
if(WIN32)
|
||||
set(STEAMAUDIO_ARCH windows-x64)
|
||||
else()
|
||||
set(STEAMAUDIO_ARCH linux-x64)
|
||||
endif()
|
||||
else()
|
||||
# Assume 32-bit. Now we need to check if it's for Windows or Linux.
|
||||
if(WIN32)
|
||||
set(STEAMAUDIO_ARCH windows-x86)
|
||||
else()
|
||||
set(STEAMAUDIO_ARCH linux-x86)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# When searching for SteamAudio, we'll support installing it in the extras/steamaudio directory.
|
||||
set(STEAMAUDIO_FIND_LIBRARY_HINTS)
|
||||
list(APPEND STEAMAUDIO_FIND_LIBRARY_HINTS ${CMAKE_CURRENT_SOURCE_DIR}/extras/steamaudio/lib/${STEAMAUDIO_ARCH})
|
||||
|
||||
if(WIN32)
|
||||
else()
|
||||
list(APPEND STEAMAUDIO_FIND_LIBRARY_HINTS /opt/steamaudio/lib/${STEAMAUDIO_ARCH})
|
||||
list(APPEND STEAMAUDIO_FIND_LIBRARY_HINTS /usr/local/steamaudio/lib/${STEAMAUDIO_ARCH})
|
||||
endif()
|
||||
|
||||
set(STEAMAUDIO_FIND_HEADER_HINTS)
|
||||
list(APPEND STEAMAUDIO_FIND_HEADER_HINTS ${CMAKE_CURRENT_SOURCE_DIR}/extras/steamaudio/include)
|
||||
|
||||
if(WIN32)
|
||||
else()
|
||||
list(APPEND STEAMAUDIO_FIND_HEADER_HINTS /opt/steamaudio/include)
|
||||
list(APPEND STEAMAUDIO_FIND_HEADER_HINTS /usr/local/steamaudio/include)
|
||||
endif()
|
||||
|
||||
|
||||
find_library(STEAMAUDIO_LIBRARY NAMES phonon HINTS ${STEAMAUDIO_FIND_LIBRARY_HINTS})
|
||||
if(STEAMAUDIO_LIBRARY)
|
||||
message(STATUS "Found SteamAudio: ${STEAMAUDIO_LIBRARY}")
|
||||
|
||||
find_path(STEAMAUDIO_INCLUDE_DIR
|
||||
NAMES phonon.h
|
||||
HINTS ${STEAMAUDIO_FIND_HEADER_HINTS}
|
||||
)
|
||||
if(STEAMAUDIO_INCLUDE_DIR)
|
||||
message(STATUS "Found phonon.h in ${STEAMAUDIO_INCLUDE_DIR}")
|
||||
set(HAS_STEAMAUDIO TRUE)
|
||||
else()
|
||||
message(STATUS "Could not find phonon.h. miniaudio_engine_steamaudio will be excluded.")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "SteamAudio not found. miniaudio_engine_steamaudio will be excluded.")
|
||||
endif()
|
||||
|
||||
|
||||
# Link libraries
|
||||
set(COMMON_LINK_LIBRARIES)
|
||||
|
||||
if (UNIX)
|
||||
list(APPEND COMMON_LINK_LIBRARIES dl) # For dlopen(), etc. Most compilers will link to this by default, but some may not.
|
||||
list(APPEND COMMON_LINK_LIBRARIES pthread) # Some compilers will not link to pthread by default so list it here just in case.
|
||||
list(APPEND COMMON_LINK_LIBRARIES m)
|
||||
|
||||
# If we're compiling for 32-bit ARM we need to link to -latomic.
|
||||
if(CMAKE_SYSTEM_PROCESSOR MATCHES "^arm" AND NOT CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64")
|
||||
list(APPEND COMMON_LINK_LIBRARIES atomic)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
# Static Libraries
|
||||
add_library(miniaudio STATIC
|
||||
miniaudio.c
|
||||
miniaudio.h
|
||||
)
|
||||
|
||||
target_compile_options (miniaudio PRIVATE ${COMPILE_OPTIONS})
|
||||
target_compile_definitions(miniaudio PRIVATE ${COMPILE_DEFINES})
|
||||
|
||||
|
||||
if(HAS_LIBVORBIS)
|
||||
add_library(miniaudio_libvorbis STATIC
|
||||
extras/decoders/libvorbis/miniaudio_libvorbis.c
|
||||
extras/decoders/libvorbis/miniaudio_libvorbis.h
|
||||
)
|
||||
|
||||
target_compile_options (miniaudio_libvorbis PRIVATE ${COMPILE_OPTIONS})
|
||||
target_compile_definitions(miniaudio_libvorbis PRIVATE ${COMPILE_DEFINES})
|
||||
target_link_libraries (miniaudio_libvorbis PRIVATE ${LIBVORBISFILE})
|
||||
endif()
|
||||
|
||||
if(HAS_LIBOPUS)
|
||||
add_library(miniaudio_libopus STATIC
|
||||
extras/decoders/libopus/miniaudio_libopus.c
|
||||
extras/decoders/libopus/miniaudio_libopus.h
|
||||
)
|
||||
|
||||
target_compile_options (miniaudio_libopus PRIVATE ${COMPILE_OPTIONS})
|
||||
target_compile_definitions(miniaudio_libopus PRIVATE ${COMPILE_DEFINES})
|
||||
target_include_directories(miniaudio_libopus PRIVATE ${OPUSFILE_INCLUDE_DIR}/opus)
|
||||
target_link_libraries (miniaudio_libopus PRIVATE ${LIBOPUSFILE})
|
||||
endif()
|
||||
|
||||
|
||||
# Interface with common options to simplify the setup of tests and examples. Note that we don't pass
|
||||
# in COMPILE_DEFINES here because want to allow the tests and examples to define their own defines. If
|
||||
# we were to use COMPILE_DEFINES here many of the tests and examples would not compile.
|
||||
add_library(miniaudio_common_options INTERFACE)
|
||||
target_compile_options(miniaudio_common_options INTERFACE ${COMPILE_OPTIONS})
|
||||
target_link_libraries (miniaudio_common_options INTERFACE ${COMMON_LINK_LIBRARIES})
|
||||
|
||||
# Tests
|
||||
#
|
||||
# All tests are compiled as a single translation unit. There is no need to add miniaudio as a link library.
|
||||
if(MINIAUDIO_BUILD_TESTS)
|
||||
enable_testing()
|
||||
|
||||
set(TESTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
|
||||
function (add_miniaudio_test name source)
|
||||
add_executable(${name} ${TESTS_DIR}/${source})
|
||||
target_link_libraries(${name} PRIVATE miniaudio_common_options)
|
||||
endfunction()
|
||||
|
||||
add_miniaudio_test(miniaudio_test_deviceio test_deviceio/ma_test_deviceio.c)
|
||||
add_test(NAME miniaudio_test_deviceio COMMAND miniaudio_test_deviceio --auto)
|
||||
|
||||
# This is just to test the C++ build. Don't do add_test() for this.
|
||||
add_miniaudio_test(miniaudio_test_cpp test_cpp/ma_test_cpp.cpp)
|
||||
|
||||
add_miniaudio_test(miniaudio_test_automated test_automated/ma_test_automated.c)
|
||||
add_test(NAME miniaudio_test_automated COMMAND miniaudio_test_automated)
|
||||
|
||||
add_miniaudio_test(miniaudio_test_filtering test_filtering/ma_test_filtering.c)
|
||||
add_test(NAME miniaudio_test_filtering COMMAND miniaudio_test_filtering ${CMAKE_CURRENT_SOURCE_DIR}/data/16-44100-stereo.flac)
|
||||
|
||||
add_miniaudio_test(miniaudio_test_generation test_generation/ma_test_generation.c)
|
||||
add_test(NAME miniaudio_test_generation COMMAND miniaudio_test_generation)
|
||||
endif()
|
||||
|
||||
# Examples
|
||||
#
|
||||
# Like tests, all examples are compiled as a single translation unit. There is no need to add miniaudio as a link library.
|
||||
if (MINIAUDIO_BUILD_EXAMPLES)
|
||||
set(EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/examples)
|
||||
|
||||
function (add_miniaudio_example name source)
|
||||
add_executable(${name} ${EXAMPLES_DIR}/${source})
|
||||
target_link_libraries(${name} PRIVATE miniaudio_common_options)
|
||||
endfunction()
|
||||
|
||||
add_miniaudio_example(miniaudio_custom_backend custom_backend.c)
|
||||
|
||||
add_miniaudio_example(miniaudio_custom_decoder_engine custom_decoder_engine.c)
|
||||
if(HAS_LIBVORBIS)
|
||||
target_link_libraries(miniaudio_custom_decoder_engine PRIVATE miniaudio_libvorbis)
|
||||
else()
|
||||
target_compile_definitions(miniaudio_custom_decoder_engine PRIVATE MA_NO_LIBVORBIS)
|
||||
message(STATUS "miniaudio_libvorbis is disabled. Vorbis support is disabled in miniaudio_custom_decoder_engine.")
|
||||
endif()
|
||||
if(HAS_LIBOPUS)
|
||||
target_link_libraries(miniaudio_custom_decoder_engine PRIVATE miniaudio_libopus)
|
||||
else()
|
||||
target_compile_definitions(miniaudio_custom_decoder_engine PRIVATE MA_NO_LIBOPUS)
|
||||
message(STATUS "miniaudio_libopus is disabled. Opus support is disabled in miniaudio_custom_decoder_engine.")
|
||||
endif()
|
||||
|
||||
add_miniaudio_example(miniaudio_custom_decoder custom_decoder.c)
|
||||
if(HAS_LIBVORBIS)
|
||||
target_link_libraries(miniaudio_custom_decoder PRIVATE miniaudio_libvorbis)
|
||||
else()
|
||||
target_compile_definitions(miniaudio_custom_decoder PRIVATE MA_NO_LIBVORBIS)
|
||||
message(STATUS "miniaudio_libvorbis is disabled. Vorbis support is disabled in miniaudio_custom_decoder.")
|
||||
endif()
|
||||
if(HAS_LIBOPUS)
|
||||
target_link_libraries(miniaudio_custom_decoder PRIVATE miniaudio_libopus)
|
||||
else()
|
||||
target_compile_definitions(miniaudio_custom_decoder PRIVATE MA_NO_LIBOPUS)
|
||||
message(STATUS "miniaudio_libopus is disabled. Opus support is disabled in miniaudio_custom_decoder.")
|
||||
endif()
|
||||
|
||||
add_miniaudio_example(miniaudio_data_source_chaining data_source_chaining.c)
|
||||
add_miniaudio_example(miniaudio_duplex_effect duplex_effect.c)
|
||||
add_miniaudio_example(miniaudio_engine_advanced engine_advanced.c)
|
||||
add_miniaudio_example(miniaudio_engine_effects engine_effects.c)
|
||||
add_miniaudio_example(miniaudio_engine_hello_world engine_hello_world.c)
|
||||
|
||||
if(HAS_SDL2)
|
||||
add_miniaudio_example(miniaudio_engine_sdl engine_sdl.c)
|
||||
target_link_libraries(miniaudio_engine_sdl PRIVATE ${SDL2_LIBRARY})
|
||||
else()
|
||||
message(STATUS "SDL2 could not be found. miniaudio_engine_sdl has been excluded.")
|
||||
endif()
|
||||
|
||||
if(HAS_STEAMAUDIO)
|
||||
add_miniaudio_example(miniaudio_engine_steamaudio engine_steamaudio.c)
|
||||
target_include_directories(miniaudio_engine_steamaudio PRIVATE ${STEAMAUDIO_INCLUDE_DIR})
|
||||
target_link_libraries (miniaudio_engine_steamaudio PRIVATE ${STEAMAUDIO_LIBRARY})
|
||||
else()
|
||||
message(STATUS "SteamAudio could not be found. miniaudio_engine_steamaudio has been excluded.")
|
||||
endif()
|
||||
|
||||
add_miniaudio_example(miniaudio_hilo_interop hilo_interop.c)
|
||||
add_miniaudio_example(miniaudio_node_graph node_graph.c)
|
||||
add_miniaudio_example(miniaudio_resource_manager_advanced resource_manager_advanced.c)
|
||||
add_miniaudio_example(miniaudio_resource_manager resource_manager.c)
|
||||
add_miniaudio_example(miniaudio_simple_capture simple_capture.c)
|
||||
add_miniaudio_example(miniaudio_simple_duplex simple_duplex.c)
|
||||
add_miniaudio_example(miniaudio_simple_enumeration simple_enumeration.c)
|
||||
add_miniaudio_example(miniaudio_simple_loopback simple_loopback.c)
|
||||
add_miniaudio_example(miniaudio_simple_looping simple_looping.c)
|
||||
add_miniaudio_example(miniaudio_simple_mixing simple_mixing.c)
|
||||
add_miniaudio_example(miniaudio_simple_playback_sine simple_playback_sine.c)
|
||||
add_miniaudio_example(miniaudio_simple_playback simple_playback.c)
|
||||
add_miniaudio_example(miniaudio_simple_spatialization simple_spatialization.c)
|
||||
endif()
|
||||
@@ -51,7 +51,6 @@ Examples
|
||||
This example shows one way to play a sound using the high level API.
|
||||
|
||||
```c
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -80,7 +79,6 @@ int main()
|
||||
This example shows how to decode and play a sound using the low level API.
|
||||
|
||||
```c
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -149,28 +147,14 @@ More examples can be found in the [examples](examples) folder or online here: ht
|
||||
|
||||
Building
|
||||
========
|
||||
Do the following in one source file:
|
||||
```c
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
```
|
||||
Then just compile. There's no need to install any dependencies. On Windows and macOS there's no need to link
|
||||
to anything. On Linux just link to `-lpthread`, `-lm` and `-ldl`. On BSD just link to `-lpthread` and `-lm`.
|
||||
On iOS you need to compile as Objective-C.
|
||||
Just compile miniaudio.c like any other source file and include miniaudio.h like a normal header. There's no need
|
||||
to install any dependencies. On Windows and macOS there's no need to link to anything. On Linux and BSD just link
|
||||
to `-lpthread` and `-lm`. On iOS you need to compile as Objective-C. Link to `-ldl` if you get errors about
|
||||
`dlopen()`, etc.
|
||||
|
||||
If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, etc. you
|
||||
need to link with `-latomic`.
|
||||
|
||||
If you prefer separate .h and .c files, you can find a split version of miniaudio in the extras/miniaudio_split
|
||||
folder. From here you can use miniaudio as a traditional .c and .h library - just add miniaudio.c to your source
|
||||
tree like any other source file and include miniaudio.h like a normal header. If you prefer compiling as a
|
||||
single translation unit (AKA unity builds), you can just #include the .c file in your main source file:
|
||||
```c
|
||||
#include "miniaudio.c"
|
||||
```
|
||||
Note that the split version is auto-generated using a tool and is based on the main file in the root directory.
|
||||
If you want to contribute, please make the change in the main file.
|
||||
|
||||
ABI compatibility is not guaranteed between versions so take care if compiling as a DLL/SO. The suggested way
|
||||
to integrate miniaudio is by adding it directly to your source tree.
|
||||
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,6 @@
|
||||
Sounds in this folder are used for testing purposes. They are all in the public domain. Below is a
|
||||
list of all the places I pulled these sounds from.
|
||||
|
||||
---
|
||||
|
||||
https://freesound.org/people/josefpres/sounds/788664/
|
||||
+141
-4
@@ -24,13 +24,150 @@ the decoder via the decoder config (`ma_decoder_config`). You need to implement
|
||||
of your custom decoders. See `ma_decoding_backend_vtable` for the functions you need to implement.
|
||||
The `onInitFile`, `onInitFileW` and `onInitMemory` functions are optional.
|
||||
*/
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "../miniaudio.h"
|
||||
#include "../extras/miniaudio_libvorbis.h"
|
||||
#include "../extras/miniaudio_libopus.h"
|
||||
|
||||
/*
|
||||
For now these need to be declared before miniaudio.c due to some compatibility issues with the old
|
||||
MINIAUDIO_IMPLEMENTATION system. This will change from version 0.12.
|
||||
*/
|
||||
#include "../extras/decoders/libvorbis/miniaudio_libvorbis.h"
|
||||
#include "../extras/decoders/libopus/miniaudio_libopus.h"
|
||||
#include "../miniaudio.c"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libvorbis* pVorbis;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
||||
if (pVorbis == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pVorbis;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libvorbis* pVorbis;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
||||
if (pVorbis == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pVorbis;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
}
|
||||
|
||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
|
||||
{
|
||||
ma_decoding_backend_init__libvorbis,
|
||||
ma_decoding_backend_init_file__libvorbis,
|
||||
NULL, /* onInitFileW() */
|
||||
NULL, /* onInitMemory() */
|
||||
ma_decoding_backend_uninit__libvorbis
|
||||
};
|
||||
|
||||
|
||||
|
||||
static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libopus* pOpus;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
|
||||
if (pOpus == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pOpus, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pOpus;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libopus* pOpus;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
|
||||
if (pOpus == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pOpus, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pOpus;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
ma_libopus* pOpus = (ma_libopus*)pBackend;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
ma_libopus_uninit(pOpus, pAllocationCallbacks);
|
||||
ma_free(pOpus, pAllocationCallbacks);
|
||||
}
|
||||
|
||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
|
||||
{
|
||||
ma_decoding_backend_init__libopus,
|
||||
ma_decoding_backend_init_file__libopus,
|
||||
NULL, /* onInitFileW() */
|
||||
NULL, /* onInitMemory() */
|
||||
ma_decoding_backend_uninit__libopus
|
||||
};
|
||||
|
||||
|
||||
|
||||
void data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
Demonstrates how to implement a custom decoder and use it with the high level API.
|
||||
|
||||
This is the same as the custom_decoder example, only it's used with the high level engine API
|
||||
rather than the low level decoding API. You can use this to add support for Opus to your games, for
|
||||
example (via libopus).
|
||||
*/
|
||||
|
||||
/*
|
||||
For now these need to be declared before miniaudio.c due to some compatibility issues with the old
|
||||
MINIAUDIO_IMPLEMENTATION system. This will change from version 0.12.
|
||||
*/
|
||||
#include "../extras/decoders/libvorbis/miniaudio_libvorbis.h"
|
||||
#include "../extras/decoders/libopus/miniaudio_libopus.h"
|
||||
#include "../miniaudio.c"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static ma_result ma_decoding_backend_init__libvorbis(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libvorbis* pVorbis;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
||||
if (pVorbis == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libvorbis_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pVorbis;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_decoding_backend_init_file__libvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libvorbis* pVorbis;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pVorbis = (ma_libvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
||||
if (pVorbis == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libvorbis_init_file(pFilePath, pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pVorbis;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static void ma_decoding_backend_uninit__libvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pBackend;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
ma_libvorbis_uninit(pVorbis, pAllocationCallbacks);
|
||||
ma_free(pVorbis, pAllocationCallbacks);
|
||||
}
|
||||
|
||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libvorbis =
|
||||
{
|
||||
ma_decoding_backend_init__libvorbis,
|
||||
ma_decoding_backend_init_file__libvorbis,
|
||||
NULL, /* onInitFileW() */
|
||||
NULL, /* onInitMemory() */
|
||||
ma_decoding_backend_uninit__libvorbis
|
||||
};
|
||||
|
||||
|
||||
|
||||
static ma_result ma_decoding_backend_init__libopus(void* pUserData, ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libopus* pOpus;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
|
||||
if (pOpus == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libopus_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pOpus);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pOpus, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pOpus;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static ma_result ma_decoding_backend_init_file__libopus(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
||||
{
|
||||
ma_result result;
|
||||
ma_libopus* pOpus;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
pOpus = (ma_libopus*)ma_malloc(sizeof(*pOpus), pAllocationCallbacks);
|
||||
if (pOpus == NULL) {
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
result = ma_libopus_init_file(pFilePath, pConfig, pAllocationCallbacks, pOpus);
|
||||
if (result != MA_SUCCESS) {
|
||||
ma_free(pOpus, pAllocationCallbacks);
|
||||
return result;
|
||||
}
|
||||
|
||||
*ppBackend = pOpus;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
static void ma_decoding_backend_uninit__libopus(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
ma_libopus* pOpus = (ma_libopus*)pBackend;
|
||||
|
||||
(void)pUserData;
|
||||
|
||||
ma_libopus_uninit(pOpus, pAllocationCallbacks);
|
||||
ma_free(pOpus, pAllocationCallbacks);
|
||||
}
|
||||
|
||||
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_libopus =
|
||||
{
|
||||
ma_decoding_backend_init__libopus,
|
||||
ma_decoding_backend_init_file__libopus,
|
||||
NULL, /* onInitFileW() */
|
||||
NULL, /* onInitMemory() */
|
||||
ma_decoding_backend_uninit__libopus
|
||||
};
|
||||
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ma_result result;
|
||||
ma_resource_manager_config resourceManagerConfig;
|
||||
ma_resource_manager resourceManager;
|
||||
ma_engine_config engineConfig;
|
||||
ma_engine engine;
|
||||
|
||||
/*
|
||||
Add your custom backend vtables here. The order in the array defines the order of priority. The
|
||||
vtables will be passed in to the resource manager config.
|
||||
*/
|
||||
ma_decoding_backend_vtable* pCustomBackendVTables[] =
|
||||
{
|
||||
&g_ma_decoding_backend_vtable_libvorbis,
|
||||
&g_ma_decoding_backend_vtable_libopus
|
||||
};
|
||||
|
||||
|
||||
if (argc < 2) {
|
||||
printf("No input file.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Using custom decoding backends requires a resource manager. */
|
||||
resourceManagerConfig = ma_resource_manager_config_init();
|
||||
resourceManagerConfig.ppCustomDecodingBackendVTables = pCustomBackendVTables;
|
||||
resourceManagerConfig.customDecodingBackendCount = sizeof(pCustomBackendVTables) / sizeof(pCustomBackendVTables[0]);
|
||||
resourceManagerConfig.pCustomDecodingBackendUserData = NULL; /* <-- This will be passed in to the pUserData parameter of each function in the decoding backend vtables. */
|
||||
|
||||
result = ma_resource_manager_init(&resourceManagerConfig, &resourceManager);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to initialize resource manager.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Once we have a resource manager we can create the engine. */
|
||||
engineConfig = ma_engine_config_init();
|
||||
engineConfig.pResourceManager = &resourceManager;
|
||||
|
||||
result = ma_engine_init(&engineConfig, &engine);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to initialize engine.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/* Now we can play our sound. */
|
||||
result = ma_engine_play_sound(&engine, argv[1], NULL);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to play sound.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
printf("Press Enter to quit...");
|
||||
getchar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -49,6 +49,8 @@ ma_decoder* g_pDecoders;
|
||||
|
||||
static ma_data_source* next_callback_tail(ma_data_source* pDataSource)
|
||||
{
|
||||
(void)pDataSource; /* Unused. */
|
||||
|
||||
if (g_decoderCount > 0) { /* <-- We check for this in main() so should never happen. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ head.
|
||||
#include "../miniaudio.h"
|
||||
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <SDL.h> /* Change this to your include location. Might be <SDL2/SDL.h>. */
|
||||
#include <SDL2/SDL.h> /* Change this to your include location. Might be <SDL.h>. */
|
||||
|
||||
#define CHANNELS 2 /* Must be stereo for this example. */
|
||||
#define SAMPLE_RATE 48000
|
||||
@@ -23,6 +23,8 @@ static ma_sound g_sound; /* This example will play only a single soun
|
||||
|
||||
void data_callback(void* pUserData, ma_uint8* pBuffer, int bufferSizeInBytes)
|
||||
{
|
||||
(void)pUserData;
|
||||
|
||||
/* Reading is just a matter of reading straight from the engine. */
|
||||
ma_uint32 bufferSizeInFrames = (ma_uint32)bufferSizeInBytes / ma_get_bytes_per_frame(ma_format_f32, ma_engine_get_channels(&g_engine));
|
||||
ma_engine_read_pcm_frames(&g_engine, pBuffer, bufferSizeInFrames, NULL);
|
||||
|
||||
@@ -118,7 +118,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
|
||||
pBinauralNode->ppBuffersIn[0] = (float*)ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, 1);
|
||||
} else {
|
||||
/* Slow path. Need to deinterleave the input data. */
|
||||
ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), pBinauralNode->ppBuffersIn);
|
||||
ma_deinterleave_pcm_frames(ma_format_f32, inputBufferDesc.numChannels, framesToProcessThisIteration, ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessed, inputBufferDesc.numChannels), (void**)&pBinauralNode->ppBuffersIn[0]);
|
||||
}
|
||||
|
||||
inputBufferDesc.data = pBinauralNode->ppBuffersIn;
|
||||
@@ -128,7 +128,7 @@ static void ma_steamaudio_binaural_node_process_pcm_frames(ma_node* pNode, const
|
||||
iplBinauralEffectApply(pBinauralNode->iplEffect, &binauralParams, &inputBufferDesc, &outputBufferDesc);
|
||||
|
||||
/* Interleave straight into the output buffer. */
|
||||
ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, pBinauralNode->ppBuffersOut, ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
|
||||
ma_interleave_pcm_frames(ma_format_f32, 2, framesToProcessThisIteration, (const void**)&pBinauralNode->ppBuffersOut[0], ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessed, 2));
|
||||
|
||||
/* Advance. */
|
||||
totalFramesProcessed += framesToProcessThisIteration;
|
||||
|
||||
@@ -28,6 +28,8 @@ void capture_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFr
|
||||
ma_result result;
|
||||
ma_uint32 framesWritten;
|
||||
|
||||
(void)pFramesOut;
|
||||
|
||||
/* We need to write to the ring buffer. Need to do this in a loop. */
|
||||
framesWritten = 0;
|
||||
while (framesWritten < frameCount) {
|
||||
|
||||
@@ -266,7 +266,7 @@ int main(int argc, char** argv)
|
||||
ma_thread_create(&jobThread, ma_thread_priority_default, 0, custom_job_thread, &resourceManager, NULL);
|
||||
|
||||
/* Create each data source from the resource manager. Note that the caller is the owner. */
|
||||
for (iFile = 0; iFile < ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) {
|
||||
for (iFile = 0; iFile < (int)ma_countof(g_dataSources) && iFile < argc-1; iFile += 1) {
|
||||
result = ma_resource_manager_data_source_init(
|
||||
&resourceManager,
|
||||
argv[iFile+1],
|
||||
|
||||
@@ -29,7 +29,7 @@ ma_bool32* g_pDecodersAtEnd;
|
||||
|
||||
ma_event g_stopEvent; /* <-- Signaled by the audio thread, waited on by the main thread. */
|
||||
|
||||
ma_bool32 are_all_decoders_at_end()
|
||||
ma_bool32 are_all_decoders_at_end(void)
|
||||
{
|
||||
ma_uint32 iDecoder;
|
||||
for (iDecoder = 0; iDecoder < g_decoderCount; ++iDecoder) {
|
||||
|
||||
Vendored
+8207
File diff suppressed because it is too large
Load Diff
Vendored
+1177
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,458 @@
|
||||
#ifndef miniaudio_libopus_c
|
||||
#define miniaudio_libopus_c
|
||||
|
||||
#include "miniaudio_libopus.h"
|
||||
|
||||
#include <opusfile.h>
|
||||
#include <string.h> /* For memset(). */
|
||||
|
||||
static ma_result ma_libopus_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
{
|
||||
return ma_libopus_read_pcm_frames((ma_libopus*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
||||
}
|
||||
|
||||
static ma_result ma_libopus_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
||||
{
|
||||
return ma_libopus_seek_to_pcm_frame((ma_libopus*)pDataSource, frameIndex);
|
||||
}
|
||||
|
||||
static ma_result ma_libopus_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
||||
{
|
||||
return ma_libopus_get_data_format((ma_libopus*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
||||
}
|
||||
|
||||
static ma_result ma_libopus_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
||||
{
|
||||
return ma_libopus_get_cursor_in_pcm_frames((ma_libopus*)pDataSource, pCursor);
|
||||
}
|
||||
|
||||
static ma_result ma_libopus_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
||||
{
|
||||
return ma_libopus_get_length_in_pcm_frames((ma_libopus*)pDataSource, pLength);
|
||||
}
|
||||
|
||||
static ma_data_source_vtable g_ma_libopus_ds_vtable =
|
||||
{
|
||||
ma_libopus_ds_read,
|
||||
ma_libopus_ds_seek,
|
||||
ma_libopus_ds_get_data_format,
|
||||
ma_libopus_ds_get_cursor,
|
||||
ma_libopus_ds_get_length,
|
||||
NULL, /* onSetLooping */
|
||||
0 /* flags */
|
||||
};
|
||||
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
static int ma_libopus_of_callback__read(void* pUserData, unsigned char* pBufferOut, int bytesToRead)
|
||||
{
|
||||
ma_libopus* pOpus = (ma_libopus*)pUserData;
|
||||
ma_result result;
|
||||
size_t bytesRead;
|
||||
|
||||
result = pOpus->onRead(pOpus->pReadSeekTellUserData, (void*)pBufferOut, bytesToRead, &bytesRead);
|
||||
|
||||
if (result != MA_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)bytesRead;
|
||||
}
|
||||
|
||||
static int ma_libopus_of_callback__seek(void* pUserData, ogg_int64_t offset, int whence)
|
||||
{
|
||||
ma_libopus* pOpus = (ma_libopus*)pUserData;
|
||||
ma_result result;
|
||||
ma_seek_origin origin;
|
||||
|
||||
if (whence == SEEK_SET) {
|
||||
origin = ma_seek_origin_start;
|
||||
} else if (whence == SEEK_END) {
|
||||
origin = ma_seek_origin_end;
|
||||
} else {
|
||||
origin = ma_seek_origin_current;
|
||||
}
|
||||
|
||||
result = pOpus->onSeek(pOpus->pReadSeekTellUserData, offset, origin);
|
||||
if (result != MA_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static opus_int64 ma_libopus_of_callback__tell(void* pUserData)
|
||||
{
|
||||
ma_libopus* pOpus = (ma_libopus*)pUserData;
|
||||
ma_result result;
|
||||
ma_int64 cursor;
|
||||
|
||||
if (pOpus->onTell == NULL) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = pOpus->onTell(pOpus->pReadSeekTellUserData, &cursor);
|
||||
if (result != MA_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return cursor;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ma_result ma_libopus_init_internal(const ma_decoding_backend_config* pConfig, ma_libopus* pOpus)
|
||||
{
|
||||
ma_result result;
|
||||
ma_data_source_config dataSourceConfig;
|
||||
|
||||
if (pOpus == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
memset(pOpus, 0, sizeof(*pOpus));
|
||||
pOpus->format = ma_format_f32; /* f32 by default. */
|
||||
|
||||
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
|
||||
pOpus->format = pConfig->preferredFormat;
|
||||
} else {
|
||||
/* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
|
||||
}
|
||||
|
||||
dataSourceConfig = ma_data_source_config_init();
|
||||
dataSourceConfig.vtable = &g_ma_libopus_ds_vtable;
|
||||
|
||||
result = ma_data_source_init(&dataSourceConfig, &pOpus->ds);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result; /* Failed to initialize the base data source. */
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
|
||||
|
||||
result = ma_libopus_init_internal(pConfig, pOpus);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (onRead == NULL || onSeek == NULL) {
|
||||
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
|
||||
}
|
||||
|
||||
pOpus->onRead = onRead;
|
||||
pOpus->onSeek = onSeek;
|
||||
pOpus->onTell = onTell;
|
||||
pOpus->pReadSeekTellUserData = pReadSeekTellUserData;
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
int libopusResult;
|
||||
OpusFileCallbacks libopusCallbacks;
|
||||
|
||||
/* We can now initialize the Opus decoder. This must be done after we've set up the callbacks. */
|
||||
libopusCallbacks.read = ma_libopus_of_callback__read;
|
||||
libopusCallbacks.seek = ma_libopus_of_callback__seek;
|
||||
libopusCallbacks.close = NULL;
|
||||
libopusCallbacks.tell = ma_libopus_of_callback__tell;
|
||||
|
||||
pOpus->of = op_open_callbacks(pOpus, &libopusCallbacks, NULL, 0, &libopusResult);
|
||||
if (pOpus->of == NULL) {
|
||||
return MA_INVALID_FILE;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. */
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libopus. */
|
||||
|
||||
result = ma_libopus_init_internal(pConfig, pOpus);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
int libopusResult;
|
||||
|
||||
pOpus->of = op_open_file(pFilePath, &libopusResult);
|
||||
if (pOpus->of == NULL) {
|
||||
return MA_INVALID_FILE;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. */
|
||||
(void)pFilePath;
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
if (pOpus == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)pAllocationCallbacks;
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
op_free((OggOpusFile*)pOpus->of);
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
ma_data_source_uninit(&pOpus->ds);
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
{
|
||||
if (pFramesRead != NULL) {
|
||||
*pFramesRead = 0;
|
||||
}
|
||||
|
||||
if (frameCount == 0) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (pOpus == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
/* We always use floating point format. */
|
||||
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
|
||||
ma_uint64 totalFramesRead;
|
||||
ma_format format;
|
||||
ma_uint32 channels;
|
||||
|
||||
ma_libopus_get_data_format(pOpus, &format, &channels, NULL, NULL, 0);
|
||||
|
||||
totalFramesRead = 0;
|
||||
while (totalFramesRead < frameCount) {
|
||||
long libopusResult;
|
||||
ma_uint64 framesToRead;
|
||||
ma_uint64 framesRemaining;
|
||||
|
||||
framesRemaining = (frameCount - totalFramesRead);
|
||||
framesToRead = 1024;
|
||||
if (framesToRead > framesRemaining) {
|
||||
framesToRead = framesRemaining;
|
||||
}
|
||||
|
||||
if (format == ma_format_f32) {
|
||||
libopusResult = op_read_float((OggOpusFile*)pOpus->of, (float* )ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)(framesToRead * channels), NULL);
|
||||
} else {
|
||||
libopusResult = op_read ((OggOpusFile*)pOpus->of, (opus_int16*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)(framesToRead * channels), NULL);
|
||||
}
|
||||
|
||||
if (libopusResult < 0) {
|
||||
result = MA_ERROR; /* Error while decoding. */
|
||||
break;
|
||||
} else {
|
||||
totalFramesRead += libopusResult;
|
||||
|
||||
if (libopusResult == 0) {
|
||||
result = MA_AT_END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pFramesRead != NULL) {
|
||||
*pFramesRead = totalFramesRead;
|
||||
}
|
||||
|
||||
if (result == MA_SUCCESS && totalFramesRead == 0) {
|
||||
result = MA_AT_END;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
|
||||
(void)pFramesOut;
|
||||
(void)frameCount;
|
||||
(void)pFramesRead;
|
||||
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex)
|
||||
{
|
||||
if (pOpus == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
int libopusResult = op_pcm_seek((OggOpusFile*)pOpus->of, (ogg_int64_t)frameIndex);
|
||||
if (libopusResult != 0) {
|
||||
if (libopusResult == OP_ENOSEEK) {
|
||||
return MA_INVALID_OPERATION; /* Not seekable. */
|
||||
} else if (libopusResult == OP_EINVAL) {
|
||||
return MA_INVALID_ARGS;
|
||||
} else {
|
||||
return MA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
|
||||
(void)frameIndex;
|
||||
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
||||
{
|
||||
/* Defaults for safety. */
|
||||
if (pFormat != NULL) {
|
||||
*pFormat = ma_format_unknown;
|
||||
}
|
||||
if (pChannels != NULL) {
|
||||
*pChannels = 0;
|
||||
}
|
||||
if (pSampleRate != NULL) {
|
||||
*pSampleRate = 0;
|
||||
}
|
||||
if (pChannelMap != NULL) {
|
||||
memset(pChannelMap, 0, sizeof(*pChannelMap) * channelMapCap);
|
||||
}
|
||||
|
||||
if (pOpus == NULL) {
|
||||
return MA_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (pFormat != NULL) {
|
||||
*pFormat = pOpus->format;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
ma_uint32 channels = op_channel_count((OggOpusFile*)pOpus->of, -1);
|
||||
|
||||
if (pChannels != NULL) {
|
||||
*pChannels = channels;
|
||||
}
|
||||
|
||||
if (pSampleRate != NULL) {
|
||||
*pSampleRate = 48000;
|
||||
}
|
||||
|
||||
if (pChannelMap != NULL) {
|
||||
ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, channels);
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor)
|
||||
{
|
||||
if (pCursor == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
*pCursor = 0; /* Safety. */
|
||||
|
||||
if (pOpus == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
ogg_int64_t offset = op_pcm_tell((OggOpusFile*)pOpus->of);
|
||||
if (offset < 0) {
|
||||
return MA_INVALID_FILE;
|
||||
}
|
||||
|
||||
*pCursor = (ma_uint64)offset;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength)
|
||||
{
|
||||
if (pLength == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
*pLength = 0; /* Safety. */
|
||||
|
||||
if (pOpus == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBOPUS)
|
||||
{
|
||||
ogg_int64_t length = op_pcm_total((OggOpusFile*)pOpus->of, -1);
|
||||
if (length < 0) {
|
||||
return MA_ERROR;
|
||||
}
|
||||
|
||||
*pLength = (ma_uint64)length;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libopus is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* miniaudio_libopus_c */
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This implements a data source that decodes Opus streams via libopus + libopusfile
|
||||
|
||||
This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom
|
||||
decoding backend. See the custom_decoder example.
|
||||
*/
|
||||
#ifndef miniaudio_libopus_h
|
||||
#define miniaudio_libopus_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../../../miniaudio.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_data_source_base ds; /* The libopus decoder can be used independently as a data source. */
|
||||
ma_read_proc onRead;
|
||||
ma_seek_proc onSeek;
|
||||
ma_tell_proc onTell;
|
||||
void* pReadSeekTellUserData;
|
||||
ma_format format; /* Will be either f32 or s16. */
|
||||
/*OggOpusFile**/ void* of; /* Typed as void* so we can avoid a dependency on opusfile in the header section. */
|
||||
} ma_libopus;
|
||||
|
||||
MA_API ma_result ma_libopus_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus);
|
||||
MA_API ma_result ma_libopus_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libopus* pOpus);
|
||||
MA_API void ma_libopus_uninit(ma_libopus* pOpus, const ma_allocation_callbacks* pAllocationCallbacks);
|
||||
MA_API ma_result ma_libopus_read_pcm_frames(ma_libopus* pOpus, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
||||
MA_API ma_result ma_libopus_seek_to_pcm_frame(ma_libopus* pOpus, ma_uint64 frameIndex);
|
||||
MA_API ma_result ma_libopus_get_data_format(ma_libopus* pOpus, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
||||
MA_API ma_result ma_libopus_get_cursor_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pCursor);
|
||||
MA_API ma_result ma_libopus_get_length_in_pcm_frames(ma_libopus* pOpus, ma_uint64* pLength);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* miniaudio_libopus_h */
|
||||
|
||||
@@ -0,0 +1,488 @@
|
||||
#ifndef miniaudio_libvorbis_c
|
||||
#define miniaudio_libvorbis_c
|
||||
|
||||
#include "miniaudio_libvorbis.h"
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
#ifndef OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#endif
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#endif
|
||||
|
||||
#include <string.h> /* For memset(). */
|
||||
|
||||
static ma_result ma_libvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
{
|
||||
return ma_libvorbis_read_pcm_frames((ma_libvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
||||
}
|
||||
|
||||
static ma_result ma_libvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
||||
{
|
||||
return ma_libvorbis_seek_to_pcm_frame((ma_libvorbis*)pDataSource, frameIndex);
|
||||
}
|
||||
|
||||
static ma_result ma_libvorbis_ds_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
||||
{
|
||||
return ma_libvorbis_get_data_format((ma_libvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
||||
}
|
||||
|
||||
static ma_result ma_libvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
||||
{
|
||||
return ma_libvorbis_get_cursor_in_pcm_frames((ma_libvorbis*)pDataSource, pCursor);
|
||||
}
|
||||
|
||||
static ma_result ma_libvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
||||
{
|
||||
return ma_libvorbis_get_length_in_pcm_frames((ma_libvorbis*)pDataSource, pLength);
|
||||
}
|
||||
|
||||
static ma_data_source_vtable g_ma_libvorbis_ds_vtable =
|
||||
{
|
||||
ma_libvorbis_ds_read,
|
||||
ma_libvorbis_ds_seek,
|
||||
ma_libvorbis_ds_get_data_format,
|
||||
ma_libvorbis_ds_get_cursor,
|
||||
ma_libvorbis_ds_get_length,
|
||||
NULL, /* onSetLooping */
|
||||
0 /* flags */
|
||||
};
|
||||
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
static size_t ma_libvorbis_vf_callback__read(void* pBufferOut, size_t size, size_t count, void* pUserData)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
|
||||
ma_result result;
|
||||
size_t bytesToRead;
|
||||
size_t bytesRead;
|
||||
|
||||
/* For consistency with fread(). If `size` of `count` is 0, return 0 immediately without changing anything. */
|
||||
if (size == 0 || count == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bytesToRead = size * count;
|
||||
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
|
||||
if (result != MA_SUCCESS) {
|
||||
/* Not entirely sure what to return here. What if an error occurs, but some data was read and bytesRead is > 0? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bytesRead / size;
|
||||
}
|
||||
|
||||
static int ma_libvorbis_vf_callback__seek(void* pUserData, ogg_int64_t offset, int whence)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
|
||||
ma_result result;
|
||||
ma_seek_origin origin;
|
||||
|
||||
if (whence == SEEK_SET) {
|
||||
origin = ma_seek_origin_start;
|
||||
} else if (whence == SEEK_END) {
|
||||
origin = ma_seek_origin_end;
|
||||
} else {
|
||||
origin = ma_seek_origin_current;
|
||||
}
|
||||
|
||||
result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, offset, origin);
|
||||
if (result != MA_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long ma_libvorbis_vf_callback__tell(void* pUserData)
|
||||
{
|
||||
ma_libvorbis* pVorbis = (ma_libvorbis*)pUserData;
|
||||
ma_result result;
|
||||
ma_int64 cursor;
|
||||
|
||||
result = pVorbis->onTell(pVorbis->pReadSeekTellUserData, &cursor);
|
||||
if (result != MA_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (long)cursor;
|
||||
}
|
||||
#endif
|
||||
|
||||
static ma_result ma_libvorbis_init_internal(const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
|
||||
{
|
||||
ma_result result;
|
||||
ma_data_source_config dataSourceConfig;
|
||||
|
||||
if (pVorbis == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
memset(pVorbis, 0, sizeof(*pVorbis));
|
||||
pVorbis->format = ma_format_f32; /* f32 by default. */
|
||||
|
||||
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
|
||||
pVorbis->format = pConfig->preferredFormat;
|
||||
} else {
|
||||
/* Getting here means something other than f32 and s16 was specified. Just leave this unset to use the default format. */
|
||||
}
|
||||
|
||||
dataSourceConfig = ma_data_source_config_init();
|
||||
dataSourceConfig.vtable = &g_ma_libvorbis_ds_vtable;
|
||||
|
||||
result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result; /* Failed to initialize the base data source. */
|
||||
}
|
||||
|
||||
pVorbis->vf = (OggVorbis_File*)ma_malloc(sizeof(OggVorbis_File), pAllocationCallbacks);
|
||||
if (pVorbis->vf == NULL) {
|
||||
ma_data_source_uninit(&pVorbis->ds);
|
||||
return MA_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */
|
||||
|
||||
result = ma_libvorbis_init_internal(pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (onRead == NULL || onSeek == NULL) {
|
||||
return MA_INVALID_ARGS; /* onRead and onSeek are mandatory. */
|
||||
}
|
||||
|
||||
pVorbis->onRead = onRead;
|
||||
pVorbis->onSeek = onSeek;
|
||||
pVorbis->onTell = onTell;
|
||||
pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
int libvorbisResult;
|
||||
ov_callbacks libvorbisCallbacks;
|
||||
|
||||
/* We can now initialize the vorbis decoder. This must be done after we've set up the callbacks. */
|
||||
libvorbisCallbacks.read_func = ma_libvorbis_vf_callback__read;
|
||||
libvorbisCallbacks.seek_func = ma_libvorbis_vf_callback__seek;
|
||||
libvorbisCallbacks.close_func = NULL;
|
||||
libvorbisCallbacks.tell_func = ma_libvorbis_vf_callback__tell;
|
||||
|
||||
libvorbisResult = ov_open_callbacks(pVorbis, (OggVorbis_File*)pVorbis->vf, NULL, 0, libvorbisCallbacks);
|
||||
if (libvorbisResult < 0) {
|
||||
return MA_INVALID_FILE;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. */
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis)
|
||||
{
|
||||
ma_result result;
|
||||
|
||||
(void)pAllocationCallbacks; /* Can't seem to find a way to configure memory allocations in libvorbis. */
|
||||
|
||||
result = ma_libvorbis_init_internal(pConfig, pAllocationCallbacks, pVorbis);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
int libvorbisResult;
|
||||
|
||||
libvorbisResult = ov_fopen(pFilePath, (OggVorbis_File*)pVorbis->vf);
|
||||
if (libvorbisResult < 0) {
|
||||
return MA_INVALID_FILE;
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. */
|
||||
(void)pFilePath;
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
|
||||
{
|
||||
if (pVorbis == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
(void)pAllocationCallbacks;
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
ov_clear((OggVorbis_File*)pVorbis->vf);
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
}
|
||||
#endif
|
||||
|
||||
ma_data_source_uninit(&pVorbis->ds);
|
||||
ma_free(pVorbis->vf, pAllocationCallbacks);
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
||||
{
|
||||
if (pFramesRead != NULL) {
|
||||
*pFramesRead = 0;
|
||||
}
|
||||
|
||||
if (frameCount == 0) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
if (pVorbis == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
/* We always use floating point format. */
|
||||
ma_result result = MA_SUCCESS; /* Must be initialized to MA_SUCCESS. */
|
||||
ma_uint64 totalFramesRead;
|
||||
ma_format format;
|
||||
ma_uint32 channels;
|
||||
|
||||
ma_libvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
|
||||
|
||||
totalFramesRead = 0;
|
||||
while (totalFramesRead < frameCount) {
|
||||
long libvorbisResult;
|
||||
ma_uint64 framesToRead;
|
||||
ma_uint64 framesRemaining;
|
||||
|
||||
framesRemaining = (frameCount - totalFramesRead);
|
||||
framesToRead = 1024;
|
||||
if (framesToRead > framesRemaining) {
|
||||
framesToRead = framesRemaining;
|
||||
}
|
||||
|
||||
if (format == ma_format_f32) {
|
||||
float** ppFramesF32;
|
||||
|
||||
libvorbisResult = ov_read_float((OggVorbis_File*)pVorbis->vf, &ppFramesF32, (int)framesToRead, NULL);
|
||||
if (libvorbisResult < 0) {
|
||||
result = MA_ERROR; /* Error while decoding. */
|
||||
break;
|
||||
} else {
|
||||
/* Frames need to be interleaved. */
|
||||
ma_interleave_pcm_frames(format, channels, libvorbisResult, (const void**)ppFramesF32, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels));
|
||||
totalFramesRead += libvorbisResult;
|
||||
|
||||
if (libvorbisResult == 0) {
|
||||
result = MA_AT_END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
libvorbisResult = ov_read((OggVorbis_File*)pVorbis->vf, ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), framesToRead * ma_get_bytes_per_frame(format, channels), 0, 2, 1, NULL);
|
||||
if (libvorbisResult < 0) {
|
||||
result = MA_ERROR; /* Error while decoding. */
|
||||
break;
|
||||
} else {
|
||||
/* Conveniently, there's no need to interleaving when using ov_read(). I'm not sure why ov_read_float() is different in that regard... */
|
||||
totalFramesRead += libvorbisResult / ma_get_bytes_per_frame(format, channels);
|
||||
|
||||
if (libvorbisResult == 0) {
|
||||
result = MA_AT_END;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pFramesRead != NULL) {
|
||||
*pFramesRead = totalFramesRead;
|
||||
}
|
||||
|
||||
if (result == MA_SUCCESS && totalFramesRead == 0) {
|
||||
result = MA_AT_END;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
|
||||
(void)pFramesOut;
|
||||
(void)frameCount;
|
||||
(void)pFramesRead;
|
||||
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex)
|
||||
{
|
||||
if (pVorbis == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
int libvorbisResult = ov_pcm_seek((OggVorbis_File*)pVorbis->vf, (ogg_int64_t)frameIndex);
|
||||
if (libvorbisResult != 0) {
|
||||
if (libvorbisResult == OV_ENOSEEK) {
|
||||
return MA_INVALID_OPERATION; /* Not seekable. */
|
||||
} else if (libvorbisResult == OV_EINVAL) {
|
||||
return MA_INVALID_ARGS;
|
||||
} else {
|
||||
return MA_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
|
||||
(void)frameIndex;
|
||||
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
||||
{
|
||||
/* Defaults for safety. */
|
||||
if (pFormat != NULL) {
|
||||
*pFormat = ma_format_unknown;
|
||||
}
|
||||
if (pChannels != NULL) {
|
||||
*pChannels = 0;
|
||||
}
|
||||
if (pSampleRate != NULL) {
|
||||
*pSampleRate = 0;
|
||||
}
|
||||
if (pChannelMap != NULL) {
|
||||
memset(pChannelMap, 0, sizeof(*pChannelMap) * channelMapCap);
|
||||
}
|
||||
|
||||
if (pVorbis == NULL) {
|
||||
return MA_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (pFormat != NULL) {
|
||||
*pFormat = pVorbis->format;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
vorbis_info* pInfo = ov_info((OggVorbis_File*)pVorbis->vf, 0);
|
||||
if (pInfo == NULL) {
|
||||
return MA_INVALID_OPERATION;
|
||||
}
|
||||
|
||||
if (pChannels != NULL) {
|
||||
*pChannels = pInfo->channels;
|
||||
}
|
||||
|
||||
if (pSampleRate != NULL) {
|
||||
*pSampleRate = pInfo->rate;
|
||||
}
|
||||
|
||||
if (pChannelMap != NULL) {
|
||||
ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pInfo->channels);
|
||||
}
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor)
|
||||
{
|
||||
if (pCursor == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
*pCursor = 0; /* Safety. */
|
||||
|
||||
if (pVorbis == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
ogg_int64_t offset = ov_pcm_tell((OggVorbis_File*)pVorbis->vf);
|
||||
if (offset < 0) {
|
||||
return MA_INVALID_FILE;
|
||||
}
|
||||
|
||||
*pCursor = (ma_uint64)offset;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength)
|
||||
{
|
||||
if (pLength == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
*pLength = 0; /* Safety. */
|
||||
|
||||
if (pVorbis == NULL) {
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
#if !defined(MA_NO_LIBVORBIS)
|
||||
{
|
||||
/* I don't know how to reliably retrieve the length in frames using libvorbis, so returning 0 for now. */
|
||||
*pLength = 0;
|
||||
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
#else
|
||||
{
|
||||
/* libvorbis is disabled. Should never hit this since initialization would have failed. */
|
||||
MA_ASSERT(MA_FALSE);
|
||||
return MA_NOT_IMPLEMENTED;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* miniaudio_libvorbis_c */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile
|
||||
|
||||
This object can be plugged into any `ma_data_source_*()` API and can also be used as a custom
|
||||
decoding backend. See the custom_decoder example.
|
||||
*/
|
||||
#ifndef miniaudio_libvorbis_h
|
||||
#define miniaudio_libvorbis_h
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../../../miniaudio.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ma_data_source_base ds; /* The libvorbis decoder can be used independently as a data source. */
|
||||
ma_read_proc onRead;
|
||||
ma_seek_proc onSeek;
|
||||
ma_tell_proc onTell;
|
||||
void* pReadSeekTellUserData;
|
||||
ma_format format; /* Will be either f32 or s16. */
|
||||
/*OggVorbis_File**/ void* vf; /* Typed as void* so we can avoid a dependency on opusfile in the header section. */
|
||||
} ma_libvorbis;
|
||||
|
||||
MA_API ma_result ma_libvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis);
|
||||
MA_API ma_result ma_libvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_libvorbis* pVorbis);
|
||||
MA_API void ma_libvorbis_uninit(ma_libvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
|
||||
MA_API ma_result ma_libvorbis_read_pcm_frames(ma_libvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
||||
MA_API ma_result ma_libvorbis_seek_to_pcm_frame(ma_libvorbis* pVorbis, ma_uint64 frameIndex);
|
||||
MA_API ma_result ma_libvorbis_get_data_format(ma_libvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
||||
MA_API ma_result ma_libvorbis_get_cursor_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pCursor);
|
||||
MA_API ma_result ma_libvorbis_get_length_in_pcm_frames(ma_libvorbis* pVorbis, ma_uint64* pLength);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* miniaudio_libvorbis_h */
|
||||
@@ -1,3 +1,5 @@
|
||||
/* THIS HAS BEEN DEPRECATED! Use the libopus decoder in extras/decoders/libopus instead. */
|
||||
|
||||
/*
|
||||
This implements a data source that decodes Opus streams via libopus + libopusfile
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
/* THIS HAS BEEN DEPRECATED! Use the libvorbis decoder in extras/decoders/libvorbis instead. */
|
||||
|
||||
/*
|
||||
This implements a data source that decodes Vorbis streams via libvorbis + libvorbisfile
|
||||
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
+130
-54
@@ -12,15 +12,18 @@ GitHub: https://github.com/mackron/miniaudio
|
||||
/*
|
||||
1. Introduction
|
||||
===============
|
||||
miniaudio is a single file library for audio playback and capture. To use it, do the following in
|
||||
one .c file:
|
||||
To use miniaudio, include "miniaudio.h":
|
||||
|
||||
```c
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
```
|
||||
|
||||
You can do `#include "miniaudio.h"` in other parts of the program just like any other header.
|
||||
The implementation is contained in "miniaudio.c". Just compile this like any other source file. You
|
||||
can include miniaudio.c if you want to compile your project as a single translation unit:
|
||||
|
||||
```c
|
||||
#include "miniaudio.c"
|
||||
```
|
||||
|
||||
miniaudio includes both low level and high level APIs. The low level API is good for those who want
|
||||
to do all of their mixing themselves and only require a light weight interface to the underlying
|
||||
@@ -483,21 +486,12 @@ link the relevant frameworks but should compile cleanly out of the box with Xcod
|
||||
through the command line requires linking to `-lpthread` and `-lm`.
|
||||
|
||||
Due to the way miniaudio links to frameworks at runtime, your application may not pass Apple's
|
||||
notarization process. To fix this there are two options. The first is to use the
|
||||
`MA_NO_RUNTIME_LINKING` option, like so:
|
||||
|
||||
```c
|
||||
#ifdef __APPLE__
|
||||
#define MA_NO_RUNTIME_LINKING
|
||||
#endif
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "miniaudio.h"
|
||||
```
|
||||
|
||||
This will require linking with `-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`.
|
||||
If you get errors about AudioToolbox, try with `-framework AudioUnit` instead. You may get this when
|
||||
using older versions of iOS. Alternatively, if you would rather keep using runtime linking you can
|
||||
add the following to your entitlements.xcent file:
|
||||
notarization process. To fix this there are two options. The first is to compile with
|
||||
`-DMA_NO_RUNTIME_LINKING` which in turn will require linking with
|
||||
`-framework CoreFoundation -framework CoreAudio -framework AudioToolbox`. If you get errors about
|
||||
AudioToolbox, try with `-framework AudioUnit` instead. You may get this when using older versions
|
||||
of iOS. Alternatively, if you would rather keep using runtime linking you can add the following to
|
||||
your entitlements.xcent file:
|
||||
|
||||
```
|
||||
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
|
||||
@@ -555,7 +549,7 @@ To run locally, you'll need to use emrun:
|
||||
|
||||
2.7. Build Options
|
||||
------------------
|
||||
`#define` these options before including miniaudio.h.
|
||||
`#define` these options before including miniaudio.c, or pass them as compiler flags:
|
||||
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| Option | Description |
|
||||
@@ -586,6 +580,8 @@ To run locally, you'll need to use emrun:
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_NO_WEBAUDIO | Disables the Web Audio backend. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_NO_CUSTOM | Disables support for custom backends. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_NO_NULL | Disables the null backend. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_ENABLE_ONLY_SPECIFIC_BACKENDS | Disables all backends by default and requires `MA_ENABLE_*` to |
|
||||
@@ -630,6 +626,9 @@ To run locally, you'll need to use emrun:
|
||||
| MA_ENABLE_WEBAUDIO | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
|
||||
| | enable the Web Audio backend. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_ENABLE_CUSTOM | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
|
||||
| | enable custom backends. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_ENABLE_NULL | Used in conjunction with MA_ENABLE_ONLY_SPECIFIC_BACKENDS to |
|
||||
| | enable the null backend. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
@@ -693,11 +692,30 @@ To run locally, you'll need to use emrun:
|
||||
| | You may need to enable this if your target platform does not allow |
|
||||
| | runtime linking via `dlopen()`. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_USE_STDINT | (Pass this in as a compiler flag. Do not `#define` this before |
|
||||
| | miniaudio.c) Forces the use of stdint.h for sized types. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_DEBUG_OUTPUT | Enable `printf()` output of debug logs (`MA_LOG_LEVEL_DEBUG`). |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_COINIT_VALUE | Windows only. The value to pass to internal calls to |
|
||||
| | `CoInitializeEx()`. Defaults to `COINIT_MULTITHREADED`. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_FORCE_UWP | Windows only. Affects only the WASAPI backend. Will force the |
|
||||
| | WASAPI backend to use the UWP code path instead of the regular |
|
||||
| | desktop path. This is normally auto-detected and should rarely be |
|
||||
| | needed to be used explicitly, but can be useful for debugging. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_ON_THREAD_ENTRY | Defines some code that will be executed as soon as an internal |
|
||||
| | miniaudio-managed thread is created. This will be the first thing |
|
||||
| | to be executed by the thread entry point. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_ON_THREAD_EXIT | Defines some code that will be executed from the entry point of an |
|
||||
| | internal miniaudio-managed thread upon exit. This will be the last |
|
||||
| | thing to be executed before the thread's entry point exits. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_THREAD_DEFAULT_STACK_SIZE | If set, specifies the default stack size used by miniaudio-managed |
|
||||
| | threads. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
| MA_API | Controls how public APIs should be decorated. Default is `extern`. |
|
||||
+----------------------------------+--------------------------------------------------------------------+
|
||||
|
||||
@@ -9840,7 +9858,7 @@ Utilities
|
||||
************************************************************************************************************************************************************/
|
||||
|
||||
/*
|
||||
Calculates a buffer size in milliseconds from the specified number of frames and sample rate.
|
||||
Calculates a buffer size in milliseconds (rounded up) from the specified number of frames and sample rate.
|
||||
*/
|
||||
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
|
||||
|
||||
@@ -18020,7 +18038,16 @@ MA_API void ma_dlclose(ma_log* pLog, ma_handle handle)
|
||||
#ifdef MA_WIN32
|
||||
FreeLibrary((HMODULE)handle);
|
||||
#else
|
||||
/* Hack for Android bug (see https://github.com/android/ndk/issues/360). Calling dlclose() pre-API 28 may segfault. */
|
||||
#if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
|
||||
{
|
||||
dlclose((void*)handle);
|
||||
}
|
||||
#else
|
||||
{
|
||||
(void)handle;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
(void)pLog;
|
||||
@@ -23396,6 +23423,14 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
|
||||
}
|
||||
|
||||
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
||||
/* If we have a mapped buffer we need to release it. */
|
||||
if (pDevice->wasapi.pMappedBufferCapture != NULL) {
|
||||
ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
|
||||
pDevice->wasapi.pMappedBufferCapture = NULL;
|
||||
pDevice->wasapi.mappedBufferCaptureCap = 0;
|
||||
pDevice->wasapi.mappedBufferCaptureLen = 0;
|
||||
}
|
||||
|
||||
hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
||||
if (FAILED(hr)) {
|
||||
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal capture device.");
|
||||
@@ -23409,31 +23444,34 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
|
||||
return ma_result_from_HRESULT(hr);
|
||||
}
|
||||
|
||||
/* If we have a mapped buffer we need to release it. */
|
||||
if (pDevice->wasapi.pMappedBufferCapture != NULL) {
|
||||
ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
|
||||
pDevice->wasapi.pMappedBufferCapture = NULL;
|
||||
pDevice->wasapi.mappedBufferCaptureCap = 0;
|
||||
pDevice->wasapi.mappedBufferCaptureLen = 0;
|
||||
}
|
||||
|
||||
ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
|
||||
}
|
||||
|
||||
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
||||
if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
|
||||
ma_silence_pcm_frames(
|
||||
ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
|
||||
pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen,
|
||||
pDevice->playback.internalFormat, pDevice->playback.internalChannels
|
||||
);
|
||||
ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
|
||||
pDevice->wasapi.pMappedBufferPlayback = NULL;
|
||||
pDevice->wasapi.mappedBufferPlaybackCap = 0;
|
||||
pDevice->wasapi.mappedBufferPlaybackLen = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
The buffer needs to be drained before stopping the device. Not doing this will result in the last few frames not getting output to
|
||||
the speakers. This is a problem for very short sounds because it'll result in a significant portion of it not getting played.
|
||||
*/
|
||||
if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {
|
||||
/* We need to make sure we put a timeout here or else we'll risk getting stuck in a deadlock in some cases. */
|
||||
DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate;
|
||||
DWORD waitTime = (pDevice->wasapi.actualBufferSizeInFramesPlayback * 1000) / pDevice->playback.internalSampleRate;
|
||||
|
||||
if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
|
||||
WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
|
||||
}
|
||||
else {
|
||||
ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1;
|
||||
} else {
|
||||
ma_uint32 prevFramesAvailablePlayback = (ma_uint32)-1;
|
||||
ma_uint32 framesAvailablePlayback;
|
||||
for (;;) {
|
||||
result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
|
||||
@@ -23449,13 +23487,13 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
|
||||
Just a safety check to avoid an infinite loop. If this iteration results in a situation where the number of available frames
|
||||
has not changed, get out of the loop. I don't think this should ever happen, but I think it's nice to have just in case.
|
||||
*/
|
||||
if (framesAvailablePlayback == prevFramesAvaialablePlayback) {
|
||||
if (framesAvailablePlayback == prevFramesAvailablePlayback) {
|
||||
break;
|
||||
}
|
||||
prevFramesAvaialablePlayback = framesAvailablePlayback;
|
||||
prevFramesAvailablePlayback = framesAvailablePlayback;
|
||||
|
||||
WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000);
|
||||
ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */
|
||||
WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23467,19 +23505,20 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
|
||||
}
|
||||
|
||||
/* The audio client needs to be reset otherwise restarting will fail. */
|
||||
hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
||||
{
|
||||
ma_int32 retries = 5;
|
||||
|
||||
while ((hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback)) == MA_AUDCLNT_E_BUFFER_OPERATION_PENDING && retries > 0) {
|
||||
ma_sleep(10);
|
||||
retries -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal playback device.");
|
||||
return ma_result_from_HRESULT(hr);
|
||||
}
|
||||
|
||||
if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
|
||||
ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
|
||||
pDevice->wasapi.pMappedBufferPlayback = NULL;
|
||||
pDevice->wasapi.mappedBufferPlaybackCap = 0;
|
||||
pDevice->wasapi.mappedBufferPlaybackLen = 0;
|
||||
}
|
||||
|
||||
ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
|
||||
}
|
||||
|
||||
@@ -30438,16 +30477,18 @@ static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFra
|
||||
|
||||
static ma_pa_stream* ma_device__pa_stream_new__pulse(ma_device* pDevice, const char* pStreamName, const ma_pa_sample_spec* ss, const ma_pa_channel_map* cmap)
|
||||
{
|
||||
static int g_StreamCounter = 0;
|
||||
static ma_atomic_uint32 g_StreamCounter = { 0 };
|
||||
char actualStreamName[256];
|
||||
|
||||
if (pStreamName != NULL) {
|
||||
ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
|
||||
} else {
|
||||
ma_strcpy_s(actualStreamName, sizeof(actualStreamName), "miniaudio:");
|
||||
ma_itoa_s(g_StreamCounter, actualStreamName + 8, sizeof(actualStreamName)-8, 10); /* 8 = strlen("miniaudio:") */
|
||||
const char* pBaseName = "miniaudio:";
|
||||
size_t baseNameLen = strlen(pBaseName);
|
||||
ma_strcpy_s(actualStreamName, sizeof(actualStreamName), pBaseName);
|
||||
ma_itoa_s((int)ma_atomic_uint32_get(&g_StreamCounter), actualStreamName + baseNameLen, sizeof(actualStreamName)-baseNameLen, 10);
|
||||
}
|
||||
g_StreamCounter += 1;
|
||||
ma_atomic_uint32_fetch_add(&g_StreamCounter, 1);
|
||||
|
||||
return ((ma_pa_stream_new_proc)pDevice->pContext->pulse.pa_stream_new)((ma_pa_context*)pDevice->pulse.pPulseContext, actualStreamName, ss, cmap);
|
||||
}
|
||||
@@ -37797,7 +37838,9 @@ AAudio Backend
|
||||
******************************************************************************/
|
||||
#ifdef MA_HAS_AAUDIO
|
||||
|
||||
/*#include <AAudio/AAudio.h>*/
|
||||
#ifdef MA_NO_RUNTIME_LINKING
|
||||
#include <AAudio/AAudio.h>
|
||||
#endif
|
||||
|
||||
typedef int32_t ma_aaudio_result_t;
|
||||
typedef int32_t ma_aaudio_direction_t;
|
||||
@@ -38741,6 +38784,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext)
|
||||
|
||||
static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
||||
{
|
||||
#if !defined(MA_NO_RUNTIME_LINKING)
|
||||
size_t i;
|
||||
const char* libNames[] = {
|
||||
"libaaudio.so"
|
||||
@@ -38786,7 +38830,39 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_
|
||||
pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst");
|
||||
pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart");
|
||||
pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop");
|
||||
|
||||
#else
|
||||
pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)AAudio_createStreamBuilder;
|
||||
pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)AAudioStreamBuilder_delete;
|
||||
pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)AAudioStreamBuilder_setDeviceId;
|
||||
pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)AAudioStreamBuilder_setDirection;
|
||||
pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)AAudioStreamBuilder_setSharingMode;
|
||||
pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)AAudioStreamBuilder_setFormat;
|
||||
pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)AAudioStreamBuilder_setChannelCount;
|
||||
pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)AAudioStreamBuilder_setSampleRate;
|
||||
pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)AAudioStreamBuilder_setBufferCapacityInFrames;
|
||||
pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)AAudioStreamBuilder_setFramesPerDataCallback;
|
||||
pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)AAudioStreamBuilder_setDataCallback;
|
||||
pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)AAudioStreamBuilder_setErrorCallback;
|
||||
pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)AAudioStreamBuilder_setPerformanceMode;
|
||||
pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)AAudioStreamBuilder_setUsage;
|
||||
pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)AAudioStreamBuilder_setContentType;
|
||||
pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)AAudioStreamBuilder_setInputPreset;
|
||||
#if defined(__ANDROID_API__) && __ANDROID_API__ >= 29
|
||||
pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)AAudioStreamBuilder_setAllowedCapturePolicy;
|
||||
#endif
|
||||
pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)AAudioStreamBuilder_openStream;
|
||||
pContext->aaudio.AAudioStream_close = (ma_proc)AAudioStream_close;
|
||||
pContext->aaudio.AAudioStream_getState = (ma_proc)AAudioStream_getState;
|
||||
pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)AAudioStream_waitForStateChange;
|
||||
pContext->aaudio.AAudioStream_getFormat = (ma_proc)AAudioStream_getFormat;
|
||||
pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)AAudioStream_getChannelCount;
|
||||
pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)AAudioStream_getSampleRate;
|
||||
pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)AAudioStream_getBufferCapacityInFrames;
|
||||
pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)AAudioStream_getFramesPerDataCallback;
|
||||
pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)AAudioStream_getFramesPerBurst;
|
||||
pContext->aaudio.AAudioStream_requestStart = (ma_proc)AAudioStream_requestStart;
|
||||
pContext->aaudio.AAudioStream_requestStop = (ma_proc)AAudioStream_requestStop;
|
||||
#endif
|
||||
|
||||
pCallbacks->onContextInit = ma_context_init__aaudio;
|
||||
pCallbacks->onContextUninit = ma_context_uninit__aaudio;
|
||||
@@ -43123,7 +43199,7 @@ MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32
|
||||
return 0;
|
||||
}
|
||||
|
||||
return bufferSizeInFrames*1000 / sampleRate;
|
||||
return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate;
|
||||
}
|
||||
|
||||
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
|
||||
@@ -47951,7 +48027,7 @@ static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_hea
|
||||
return MA_INVALID_ARGS;
|
||||
}
|
||||
|
||||
bpf2Count = pConfig->channels / 2;
|
||||
bpf2Count = pConfig->order / 2;
|
||||
|
||||
pHeapLayout->sizeInBytes = 0;
|
||||
|
||||
@@ -56487,7 +56563,7 @@ MA_API const char* ma_channel_position_to_string(ma_channel channel)
|
||||
case MA_CHANNEL_LFE : return "CHANNEL_LFE";
|
||||
case MA_CHANNEL_BACK_LEFT : return "CHANNEL_BACK_LEFT";
|
||||
case MA_CHANNEL_BACK_RIGHT : return "CHANNEL_BACK_RIGHT";
|
||||
case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER ";
|
||||
case MA_CHANNEL_FRONT_LEFT_CENTER : return "CHANNEL_FRONT_LEFT_CENTER";
|
||||
case MA_CHANNEL_FRONT_RIGHT_CENTER: return "CHANNEL_FRONT_RIGHT_CENTER";
|
||||
case MA_CHANNEL_BACK_CENTER : return "CHANNEL_BACK_CENTER";
|
||||
case MA_CHANNEL_SIDE_LEFT : return "CHANNEL_SIDE_LEFT";
|
||||
@@ -74730,7 +74806,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float
|
||||
ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */
|
||||
}
|
||||
|
||||
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound)));
|
||||
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0));
|
||||
|
||||
frameCountIn = (ma_uint32)framesJustRead;
|
||||
frameCountOut = framesRemaining;
|
||||
|
||||
@@ -4,31 +4,7 @@
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
size_t iTest;
|
||||
ma_register_test("Data Conversion", test_entry__data_converter);
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
result = ma_register_test("Data Conversion", test_entry__data_converter);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (iTest = 0; iTest < g_Tests.count; iTest += 1) {
|
||||
printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName);
|
||||
result = g_Tests.pTests[iTest].onEntry(argc, argv);
|
||||
printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED");
|
||||
|
||||
if (result != 0) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return -1; /* Something failed. */
|
||||
} else {
|
||||
return 0; /* Everything passed. */
|
||||
}
|
||||
return ma_run_tests(argc, argv);
|
||||
}
|
||||
@@ -41,11 +41,11 @@ ma_result test_data_converter__resampling_expected_output_fixed_interval(ma_data
|
||||
MA_ASSERT(frameCountPerIteration < ma_countof(input));
|
||||
|
||||
/* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */
|
||||
for (i = 0; i < ma_countof(input); i += 1) {
|
||||
for (i = 0; i < (ma_int16)ma_countof(input); i += 1) {
|
||||
input[i] = i;
|
||||
}
|
||||
|
||||
for (i = 0; i < ma_countof(input); i += (ma_int16)frameCountPerIteration) {
|
||||
for (i = 0; i < (ma_int16)ma_countof(input); i += (ma_int16)frameCountPerIteration) {
|
||||
ma_int16 output[4096];
|
||||
ma_uint64 outputFrameCount;
|
||||
ma_uint64 inputFrameCount;
|
||||
@@ -159,7 +159,7 @@ ma_result test_data_converter__resampling_expected_output_by_algorithm(ma_resamp
|
||||
}
|
||||
}
|
||||
|
||||
ma_result test_data_converter__resampling_expected_output()
|
||||
ma_result test_data_converter__resampling_expected_output(void)
|
||||
{
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
@@ -188,11 +188,11 @@ ma_result test_data_converter__resampling_required_input_fixed_interval(ma_data_
|
||||
MA_ASSERT(frameCountPerIteration < ma_countof(input));
|
||||
|
||||
/* Fill the input buffer with sequential numbers so we can get an idea on the state of things. Useful for inspecting the linear backend in particular. */
|
||||
for (i = 0; i < ma_countof(input); i += 1) {
|
||||
for (i = 0; i < (ma_int16)ma_countof(input); i += 1) {
|
||||
input[i] = i;
|
||||
}
|
||||
|
||||
for (i = 0; i < ma_countof(input); i += (ma_int16)frameCountPerIteration) {
|
||||
for (i = 0; i < (ma_int16)ma_countof(input); i += (ma_int16)frameCountPerIteration) {
|
||||
ma_int16 output[4096];
|
||||
ma_uint64 outputFrameCount;
|
||||
ma_uint64 inputFrameCount;
|
||||
@@ -306,7 +306,7 @@ ma_result test_data_converter__resampling_required_input_by_algorithm(ma_resampl
|
||||
}
|
||||
}
|
||||
|
||||
ma_result test_data_converter__resampling_required_input()
|
||||
ma_result test_data_converter__resampling_required_input(void)
|
||||
{
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
@@ -327,7 +327,7 @@ ma_result test_data_converter__resampling_required_input()
|
||||
|
||||
|
||||
|
||||
ma_result test_data_converter__resampling()
|
||||
ma_result test_data_converter__resampling(void)
|
||||
{
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#define MINIAUDIO_IMPLEMENTATION
|
||||
#include "../../miniaudio.h"
|
||||
#include "../../miniaudio.c"
|
||||
#include "../../external/fs/fs.c"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define MAX_TESTS 64
|
||||
#define TEST_OUTPUT_DIR "res/output"
|
||||
#define TEST_OUTPUT_DIR "output"
|
||||
|
||||
typedef int (* ma_test_entry_proc)(int argc, char** argv);
|
||||
|
||||
@@ -37,3 +37,37 @@ ma_result ma_register_test(const char* pName, ma_test_entry_proc onEntry)
|
||||
return MA_SUCCESS;
|
||||
}
|
||||
|
||||
int ma_run_tests(int argc, char** argv)
|
||||
{
|
||||
int result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
size_t iTest;
|
||||
fs* pFS;
|
||||
|
||||
if (fs_init(NULL, &pFS) != FS_SUCCESS) {
|
||||
printf("Failed to initialize the file system.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs_mkdir(pFS, TEST_OUTPUT_DIR);
|
||||
|
||||
for (iTest = 0; iTest < g_Tests.count; iTest += 1) {
|
||||
printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName);
|
||||
result = g_Tests.pTests[iTest].onEntry(argc, argv);
|
||||
printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED");
|
||||
|
||||
if (result != 0) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
fs_uninit(pFS);
|
||||
pFS = NULL;
|
||||
|
||||
if (hasError) {
|
||||
return 1; /* Something failed. */
|
||||
} else {
|
||||
return 0; /* Everything passed. */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
USAGE: ma_test_deviceio [input/output file] [mode] [backend] [waveform] [noise]
|
||||
USAGE: ma_test_deviceio [input/output file] [mode] [backend] [waveform] [noise] [--auto]
|
||||
|
||||
In playback mode the input file is optional, in which case a waveform or noise source will be used instead. For capture and loopback modes
|
||||
it must specify an output parameter, and must be specified. In duplex mode it is optional, but if specified will be an output file that
|
||||
@@ -43,6 +43,10 @@ are specified the last one on the command line will have priority.
|
||||
*/
|
||||
#include "../test_common/ma_test_common.c"
|
||||
|
||||
#ifndef AUTO_CLOSE_TIME_IN_MILLISECONDS
|
||||
#define AUTO_CLOSE_TIME_IN_MILLISECONDS 5000
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
source_type_waveform,
|
||||
@@ -60,6 +64,8 @@ static struct
|
||||
ma_decoder decoder;
|
||||
ma_encoder encoder;
|
||||
ma_bool32 hasEncoder; /* Used for duplex mode to determine whether or not audio data should be written to a file. */
|
||||
ma_bool32 wantsToClose;
|
||||
ma_uint64 runTimeInFrames; /* Only used in auto mode. */
|
||||
} g_State;
|
||||
|
||||
const char* get_mode_description(ma_device_type deviceType)
|
||||
@@ -340,6 +346,11 @@ void on_notification(const ma_device_notification* pNotification)
|
||||
|
||||
void on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
||||
{
|
||||
g_State.runTimeInFrames += frameCount;
|
||||
if (g_State.runTimeInFrames >= (g_State.device.sampleRate * AUTO_CLOSE_TIME_IN_MILLISECONDS) / 1000) {
|
||||
g_State.wantsToClose = MA_TRUE;
|
||||
}
|
||||
|
||||
switch (pDevice->type)
|
||||
{
|
||||
case ma_device_type_playback:
|
||||
@@ -403,6 +414,7 @@ int main(int argc, char** argv)
|
||||
ma_noise_type noiseType = ma_noise_type_white;
|
||||
const char* pFilePath = NULL; /* Input or output file path, depending on the mode. */
|
||||
ma_bool32 enumerate = MA_TRUE;
|
||||
ma_bool32 interactive = MA_TRUE;
|
||||
|
||||
/* Default to a sine wave if nothing is passed into the command line. */
|
||||
waveformType = ma_waveform_type_sine;
|
||||
@@ -410,6 +422,11 @@ int main(int argc, char** argv)
|
||||
|
||||
/* We need to iterate over the command line arguments and gather our settings. */
|
||||
for (iarg = 1; iarg < argc; iarg += 1) {
|
||||
if (strcmp(argv[iarg], "--auto") == 0) {
|
||||
interactive = MA_FALSE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* mode */
|
||||
if (try_parse_mode(argv[iarg], &deviceType)) {
|
||||
continue;
|
||||
@@ -577,7 +594,8 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
/* Now we just keep looping and wait for user input. */
|
||||
for (;;) {
|
||||
while (!g_State.wantsToClose) {
|
||||
if (interactive) {
|
||||
int c;
|
||||
|
||||
if (ma_device_is_started(&g_State.device)) {
|
||||
@@ -594,6 +612,7 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
if (c == 'q' || c == 'Q') {
|
||||
g_State.wantsToClose = MA_TRUE;
|
||||
break;
|
||||
}
|
||||
if (c == 'p' || c == 'P') {
|
||||
@@ -609,6 +628,10 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Running in auto-close mode. Just sleep for a bit. The data callback will control when this loop aborts. */
|
||||
ma_sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
|
||||
@@ -9,12 +9,14 @@ ma_result filtering_init_decoder_and_encoder(const char* pInputFilePath, const c
|
||||
decoderConfig = ma_decoder_config_init(format, channels, sampleRate);
|
||||
result = ma_decoder_init_file(pInputFilePath, &decoderConfig, pDecoder);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to open \"%s\" for decoding. %s\n", pInputFilePath, ma_result_description(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, pDecoder->outputFormat, pDecoder->outputChannels, pDecoder->outputSampleRate);
|
||||
result = ma_encoder_init_file(pOutputFilePath, &encoderConfig, pEncoder);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to open \"%s\" for encoding. %s\n", pOutputFilePath, ma_result_description(result));
|
||||
ma_decoder_uninit(pDecoder);
|
||||
return result;
|
||||
}
|
||||
@@ -33,67 +35,14 @@ ma_result filtering_init_decoder_and_encoder(const char* pInputFilePath, const c
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
size_t iTest;
|
||||
ma_register_test("Dithering", test_entry__dithering);
|
||||
ma_register_test("Low-Pass Filtering", test_entry__lpf);
|
||||
ma_register_test("High-Pass Filtering", test_entry__hpf);
|
||||
ma_register_test("Band-Pass Filtering", test_entry__bpf);
|
||||
ma_register_test("Notching Filtering", test_entry__notch);
|
||||
ma_register_test("Peaking EQ Filtering", test_entry__peak);
|
||||
ma_register_test("Low Shelf Filtering", test_entry__loshelf);
|
||||
ma_register_test("High Shelf Filtering", test_entry__hishelf);
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
result = ma_register_test("Dithering", test_entry__dithering);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("Low-Pass Filtering", test_entry__lpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("High-Pass Filtering", test_entry__hpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("Band-Pass Filtering", test_entry__bpf);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("Notching Filtering", test_entry__notch);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("Peaking EQ Filtering", test_entry__peak);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("Low Shelf Filtering", test_entry__loshelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = ma_register_test("High Shelf Filtering", test_entry__hishelf);
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
|
||||
for (iTest = 0; iTest < g_Tests.count; iTest += 1) {
|
||||
printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName);
|
||||
result = g_Tests.pTests[iTest].onEntry(argc, argv);
|
||||
printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED");
|
||||
|
||||
if (result != 0) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return -1; /* Something failed. */
|
||||
} else {
|
||||
return 0; /* Everything passed. */
|
||||
}
|
||||
return ma_run_tests(argc, argv);
|
||||
}
|
||||
|
||||
@@ -6,36 +6,8 @@
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
size_t iTest;
|
||||
ma_register_test("Noise", test_entry__noise);
|
||||
ma_register_test("Waveform", test_entry__waveform);
|
||||
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
|
||||
result = ma_register_test("Noise", test_entry__noise);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = ma_register_test("Waveform", test_entry__waveform);
|
||||
if (result != MA_SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (iTest = 0; iTest < g_Tests.count; iTest += 1) {
|
||||
printf("=== BEGIN %s ===\n", g_Tests.pTests[iTest].pName);
|
||||
result = g_Tests.pTests[iTest].onEntry(argc, argv);
|
||||
printf("=== END %s : %s ===\n", g_Tests.pTests[iTest].pName, (result == 0) ? "PASSED" : "FAILED");
|
||||
|
||||
if (result != 0) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return -1; /* Something failed. */
|
||||
} else {
|
||||
return 0; /* Everything passed. */
|
||||
}
|
||||
return ma_run_tests(argc, argv);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ ma_result test_noise__by_format_and_type(ma_format format, ma_noise_type type, c
|
||||
encoderConfig = ma_encoder_config_init(ma_encoding_format_wav, format, noiseConfig.channels, 48000);
|
||||
result = ma_encoder_init_file(pFileName, &encoderConfig, &encoder);
|
||||
if (result != MA_SUCCESS) {
|
||||
printf("Failed to open \"%s\" for writing. %s\n", pFileName, ma_result_description(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -65,17 +66,17 @@ ma_result test_noise__s16()
|
||||
ma_result result;
|
||||
ma_bool32 hasError = MA_FALSE;
|
||||
|
||||
result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_white, TEST_OUTPUT_DIR"/output/noise_s16_white.wav");
|
||||
result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_white, TEST_OUTPUT_DIR"/noise_s16_white.wav");
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_pink, TEST_OUTPUT_DIR"/output/noise_s16_pink.wav");
|
||||
result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_pink, TEST_OUTPUT_DIR"/noise_s16_pink.wav");
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_brownian, TEST_OUTPUT_DIR"/output/noise_s16_brownian.wav");
|
||||
result = test_noise__by_format_and_type(ma_format_s16, ma_noise_type_brownian, TEST_OUTPUT_DIR"/noise_s16_brownian.wav");
|
||||
if (result != MA_SUCCESS) {
|
||||
hasError = MA_TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user