mirror of
https://github.com/4jcraft/4jcraft.git
synced 2026-08-09 03:32:25 +00:00
83267 lines
3.3 MiB
83267 lines
3.3 MiB
/*
|
|
miniaudio.h by David Reid
|
|
This is free and unencumbered software released into the public domain.
|
|
|
|
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
|
software, either in source code form or as a compiled binary, for any purpose,
|
|
commercial or non-commercial, and by any means.
|
|
|
|
In jurisdictions that recognize copyright laws, the author or authors of this
|
|
software dedicate any and all copyright interest in the software to the public
|
|
domain. We make this dedication for the benefit of the public at large and to
|
|
the detriment of our heirs and successors. We intend this dedication to be an
|
|
overt act of relinquishment in perpetuity of all present and future rights to
|
|
this software under copyright law.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
For more information, please refer to <http://unlicense.org/>
|
|
*/
|
|
#ifndef miniaudio_h
|
|
#define miniaudio_h
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#define MA_STRINGIFY(x) #x
|
|
#define MA_XSTRINGIFY(x) MA_STRINGIFY(x)
|
|
|
|
#define MA_VERSION_MAJOR 0
|
|
#define MA_VERSION_MINOR 11
|
|
#define MA_VERSION_REVISION 24
|
|
#define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION)
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4201)
|
|
#pragma warning(disable:4214)
|
|
#pragma warning(disable:4324)
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic ignored "-Wc11-extensions"
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) || defined(__ppc64__)
|
|
#define MA_SIZEOF_PTR 8
|
|
#else
|
|
#define MA_SIZEOF_PTR 4
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
|
|
#if defined(MA_USE_STDINT)
|
|
#include <stdint.h>
|
|
typedef int8_t ma_int8;
|
|
typedef uint8_t ma_uint8;
|
|
typedef int16_t ma_int16;
|
|
typedef uint16_t ma_uint16;
|
|
typedef int32_t ma_int32;
|
|
typedef uint32_t ma_uint32;
|
|
typedef int64_t ma_int64;
|
|
typedef uint64_t ma_uint64;
|
|
#else
|
|
typedef signed char ma_int8;
|
|
typedef unsigned char ma_uint8;
|
|
typedef signed short ma_int16;
|
|
typedef unsigned short ma_uint16;
|
|
typedef signed int ma_int32;
|
|
typedef unsigned int ma_uint32;
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
typedef signed long long ma_int64;
|
|
typedef unsigned long long ma_uint64;
|
|
#else
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wlong-long"
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic ignored "-Wc++11-long-long"
|
|
#endif
|
|
#endif
|
|
typedef signed long long ma_int64;
|
|
typedef unsigned long long ma_uint64;
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if MA_SIZEOF_PTR == 8
|
|
typedef ma_uint64 ma_uintptr;
|
|
#else
|
|
typedef ma_uint32 ma_uintptr;
|
|
#endif
|
|
|
|
typedef ma_uint8 ma_bool8;
|
|
typedef ma_uint32 ma_bool32;
|
|
#define MA_TRUE 1
|
|
#define MA_FALSE 0
|
|
|
|
typedef float ma_float;
|
|
typedef double ma_double;
|
|
|
|
typedef void* ma_handle;
|
|
typedef void* ma_ptr;
|
|
|
|
#if defined(__GNUC__)
|
|
typedef void (*ma_proc)(void);
|
|
#else
|
|
typedef void* ma_proc;
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED)
|
|
typedef ma_uint16 wchar_t;
|
|
#endif
|
|
|
|
#ifndef NULL
|
|
#define NULL 0
|
|
#endif
|
|
|
|
#if defined(SIZE_MAX)
|
|
#define MA_SIZE_MAX SIZE_MAX
|
|
#else
|
|
#define MA_SIZE_MAX 0xFFFFFFFF
|
|
#endif
|
|
|
|
#define MA_UINT64_MAX (((ma_uint64)0xFFFFFFFF << 32) | (ma_uint64)0xFFFFFFFF)
|
|
|
|
#if defined(_WIN32)
|
|
#define MA_WIN32
|
|
#if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)))
|
|
#define MA_WIN32_UWP
|
|
#elif defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)
|
|
#define MA_WIN32_GDK
|
|
#elif defined(NXDK)
|
|
#define MA_WIN32_NXDK
|
|
#else
|
|
#define MA_WIN32_DESKTOP
|
|
#endif
|
|
|
|
#if defined(NXDK)
|
|
#define MA_XBOX
|
|
|
|
#if defined(NXDK)
|
|
#define MA_XBOX_NXDK
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#if defined(__MSDOS__) || defined(MSDOS) || defined(_MSDOS) || defined(__DOS__)
|
|
#define MA_DOS
|
|
|
|
#ifndef MA_NO_THREADING
|
|
#define MA_NO_THREADING
|
|
#endif
|
|
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
#define MA_NO_RUNTIME_LINKING
|
|
#endif
|
|
#endif
|
|
#if !defined(MA_WIN32) && !defined(MA_DOS)
|
|
#define MA_POSIX
|
|
|
|
#if !defined(MA_NO_THREADING)
|
|
#ifndef MA_NO_PTHREAD_IN_HEADER
|
|
#include <pthread.h>
|
|
typedef pthread_t ma_pthread_t;
|
|
typedef pthread_mutex_t ma_pthread_mutex_t;
|
|
typedef pthread_cond_t ma_pthread_cond_t;
|
|
#else
|
|
typedef ma_uintptr ma_pthread_t;
|
|
typedef union ma_pthread_mutex_t { char __data[40]; ma_uint64 __alignment; } ma_pthread_mutex_t;
|
|
typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t;
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__unix__)
|
|
#define MA_UNIX
|
|
#endif
|
|
#if defined(__linux__)
|
|
#define MA_LINUX
|
|
#endif
|
|
#if defined(__APPLE__)
|
|
#define MA_APPLE
|
|
#endif
|
|
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
|
|
#define MA_BSD
|
|
#endif
|
|
#if defined(__ANDROID__)
|
|
#define MA_ANDROID
|
|
#endif
|
|
#if defined(__EMSCRIPTEN__)
|
|
#define MA_EMSCRIPTEN
|
|
#endif
|
|
#if defined(__ORBIS__)
|
|
#define MA_ORBIS
|
|
#endif
|
|
#if defined(__PROSPERO__)
|
|
#define MA_PROSPERO
|
|
#endif
|
|
#if defined(__3DS__)
|
|
#define MA_3DS
|
|
#endif
|
|
#if defined(__SWITCH__) || defined(__NX__)
|
|
#define MA_SWITCH
|
|
#endif
|
|
#if defined(__BEOS__) || defined(__HAIKU__)
|
|
#define MA_BEOS
|
|
#endif
|
|
#if defined(__HAIKU__)
|
|
#define MA_HAIKU
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_FALLTHROUGH) && defined(__cplusplus) && __cplusplus >= 201703L
|
|
#define MA_FALLTHROUGH [[fallthrough]]
|
|
#endif
|
|
#if !defined(MA_FALLTHROUGH) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L
|
|
#define MA_FALLTHROUGH [[fallthrough]]
|
|
#endif
|
|
#if !defined(MA_FALLTHROUGH) && defined(__has_attribute)
|
|
#if __has_attribute(fallthrough)
|
|
#define MA_FALLTHROUGH __attribute__((fallthrough))
|
|
#endif
|
|
#endif
|
|
#if !defined(MA_FALLTHROUGH)
|
|
#define MA_FALLTHROUGH ((void)0)
|
|
#endif
|
|
|
|
#ifdef _MSC_VER
|
|
#define MA_INLINE __forceinline
|
|
|
|
#if _MSC_VER >= 1400
|
|
#define MA_NO_INLINE __declspec(noinline)
|
|
#else
|
|
#define MA_NO_INLINE
|
|
#endif
|
|
#elif defined(__GNUC__)
|
|
#if defined(__STRICT_ANSI__)
|
|
#define MA_GNUC_INLINE_HINT __inline__
|
|
#else
|
|
#define MA_GNUC_INLINE_HINT inline
|
|
#endif
|
|
|
|
#if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__)
|
|
#define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline))
|
|
#define MA_NO_INLINE __attribute__((noinline))
|
|
#else
|
|
#define MA_INLINE MA_GNUC_INLINE_HINT
|
|
#define MA_NO_INLINE
|
|
#endif
|
|
#elif defined(__WATCOMC__)
|
|
#define MA_INLINE __inline
|
|
#define MA_NO_INLINE
|
|
#else
|
|
#define MA_INLINE
|
|
#define MA_NO_INLINE
|
|
#endif
|
|
|
|
#if defined(MA_DLL)
|
|
#if defined(_WIN32)
|
|
#define MA_DLL_IMPORT __declspec(dllimport)
|
|
#define MA_DLL_EXPORT __declspec(dllexport)
|
|
#define MA_DLL_PRIVATE static
|
|
#else
|
|
#if defined(__GNUC__) && __GNUC__ >= 4
|
|
#define MA_DLL_IMPORT __attribute__((visibility("default")))
|
|
#define MA_DLL_EXPORT __attribute__((visibility("default")))
|
|
#define MA_DLL_PRIVATE __attribute__((visibility("hidden")))
|
|
#else
|
|
#define MA_DLL_IMPORT
|
|
#define MA_DLL_EXPORT
|
|
#define MA_DLL_PRIVATE static
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_API)
|
|
#if defined(MA_DLL)
|
|
#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
|
|
#define MA_API MA_DLL_EXPORT
|
|
#else
|
|
#define MA_API MA_DLL_IMPORT
|
|
#endif
|
|
#else
|
|
#define MA_API extern
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_STATIC)
|
|
#if defined(MA_DLL)
|
|
#define MA_PRIVATE MA_DLL_PRIVATE
|
|
#else
|
|
#define MA_PRIVATE static
|
|
#endif
|
|
#endif
|
|
|
|
#define MA_SIMD_ALIGNMENT 32
|
|
|
|
#if !defined(MA_POSIX) && defined(MA_WIN32)
|
|
typedef wchar_t ma_wchar_win32;
|
|
#else
|
|
typedef ma_uint16 ma_wchar_win32;
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
MA_LOG_LEVEL_DEBUG = 4,
|
|
MA_LOG_LEVEL_INFO = 3,
|
|
MA_LOG_LEVEL_WARNING = 2,
|
|
MA_LOG_LEVEL_ERROR = 1
|
|
} ma_log_level;
|
|
|
|
#if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)
|
|
#include <stdalign.h>
|
|
#define MA_ATOMIC(alignment, type) _Alignas(alignment) type
|
|
#else
|
|
#if defined(__GNUC__)
|
|
#define MA_ATOMIC(alignment, type) type __attribute__((aligned(alignment)))
|
|
#elif defined(_MSC_VER) && _MSC_VER > 1200
|
|
#define MA_ATOMIC(alignment, type) __declspec(align(alignment)) type
|
|
#else
|
|
#define MA_ATOMIC(alignment, type) type
|
|
#endif
|
|
#endif
|
|
|
|
typedef struct ma_context ma_context;
|
|
typedef struct ma_device ma_device;
|
|
|
|
typedef ma_uint8 ma_channel;
|
|
typedef enum
|
|
{
|
|
MA_CHANNEL_NONE = 0,
|
|
MA_CHANNEL_MONO = 1,
|
|
MA_CHANNEL_FRONT_LEFT = 2,
|
|
MA_CHANNEL_FRONT_RIGHT = 3,
|
|
MA_CHANNEL_FRONT_CENTER = 4,
|
|
MA_CHANNEL_LFE = 5,
|
|
MA_CHANNEL_BACK_LEFT = 6,
|
|
MA_CHANNEL_BACK_RIGHT = 7,
|
|
MA_CHANNEL_FRONT_LEFT_CENTER = 8,
|
|
MA_CHANNEL_FRONT_RIGHT_CENTER = 9,
|
|
MA_CHANNEL_BACK_CENTER = 10,
|
|
MA_CHANNEL_SIDE_LEFT = 11,
|
|
MA_CHANNEL_SIDE_RIGHT = 12,
|
|
MA_CHANNEL_TOP_CENTER = 13,
|
|
MA_CHANNEL_TOP_FRONT_LEFT = 14,
|
|
MA_CHANNEL_TOP_FRONT_CENTER = 15,
|
|
MA_CHANNEL_TOP_FRONT_RIGHT = 16,
|
|
MA_CHANNEL_TOP_BACK_LEFT = 17,
|
|
MA_CHANNEL_TOP_BACK_CENTER = 18,
|
|
MA_CHANNEL_TOP_BACK_RIGHT = 19,
|
|
MA_CHANNEL_AUX_0 = 20,
|
|
MA_CHANNEL_AUX_1 = 21,
|
|
MA_CHANNEL_AUX_2 = 22,
|
|
MA_CHANNEL_AUX_3 = 23,
|
|
MA_CHANNEL_AUX_4 = 24,
|
|
MA_CHANNEL_AUX_5 = 25,
|
|
MA_CHANNEL_AUX_6 = 26,
|
|
MA_CHANNEL_AUX_7 = 27,
|
|
MA_CHANNEL_AUX_8 = 28,
|
|
MA_CHANNEL_AUX_9 = 29,
|
|
MA_CHANNEL_AUX_10 = 30,
|
|
MA_CHANNEL_AUX_11 = 31,
|
|
MA_CHANNEL_AUX_12 = 32,
|
|
MA_CHANNEL_AUX_13 = 33,
|
|
MA_CHANNEL_AUX_14 = 34,
|
|
MA_CHANNEL_AUX_15 = 35,
|
|
MA_CHANNEL_AUX_16 = 36,
|
|
MA_CHANNEL_AUX_17 = 37,
|
|
MA_CHANNEL_AUX_18 = 38,
|
|
MA_CHANNEL_AUX_19 = 39,
|
|
MA_CHANNEL_AUX_20 = 40,
|
|
MA_CHANNEL_AUX_21 = 41,
|
|
MA_CHANNEL_AUX_22 = 42,
|
|
MA_CHANNEL_AUX_23 = 43,
|
|
MA_CHANNEL_AUX_24 = 44,
|
|
MA_CHANNEL_AUX_25 = 45,
|
|
MA_CHANNEL_AUX_26 = 46,
|
|
MA_CHANNEL_AUX_27 = 47,
|
|
MA_CHANNEL_AUX_28 = 48,
|
|
MA_CHANNEL_AUX_29 = 49,
|
|
MA_CHANNEL_AUX_30 = 50,
|
|
MA_CHANNEL_AUX_31 = 51,
|
|
|
|
MA_CHANNEL_POSITION_COUNT,
|
|
|
|
MA_CHANNEL_LEFT = MA_CHANNEL_FRONT_LEFT,
|
|
MA_CHANNEL_RIGHT = MA_CHANNEL_FRONT_RIGHT,
|
|
} _ma_channel_position;
|
|
|
|
typedef enum
|
|
{
|
|
MA_SUCCESS = 0,
|
|
MA_ERROR = -1,
|
|
MA_INVALID_ARGS = -2,
|
|
MA_INVALID_OPERATION = -3,
|
|
MA_OUT_OF_MEMORY = -4,
|
|
MA_OUT_OF_RANGE = -5,
|
|
MA_ACCESS_DENIED = -6,
|
|
MA_DOES_NOT_EXIST = -7,
|
|
MA_ALREADY_EXISTS = -8,
|
|
MA_TOO_MANY_OPEN_FILES = -9,
|
|
MA_INVALID_FILE = -10,
|
|
MA_TOO_BIG = -11,
|
|
MA_PATH_TOO_LONG = -12,
|
|
MA_NAME_TOO_LONG = -13,
|
|
MA_NOT_DIRECTORY = -14,
|
|
MA_IS_DIRECTORY = -15,
|
|
MA_DIRECTORY_NOT_EMPTY = -16,
|
|
MA_AT_END = -17,
|
|
MA_NO_SPACE = -18,
|
|
MA_BUSY = -19,
|
|
MA_IO_ERROR = -20,
|
|
MA_INTERRUPT = -21,
|
|
MA_UNAVAILABLE = -22,
|
|
MA_ALREADY_IN_USE = -23,
|
|
MA_BAD_ADDRESS = -24,
|
|
MA_BAD_SEEK = -25,
|
|
MA_BAD_PIPE = -26,
|
|
MA_DEADLOCK = -27,
|
|
MA_TOO_MANY_LINKS = -28,
|
|
MA_NOT_IMPLEMENTED = -29,
|
|
MA_NO_MESSAGE = -30,
|
|
MA_BAD_MESSAGE = -31,
|
|
MA_NO_DATA_AVAILABLE = -32,
|
|
MA_INVALID_DATA = -33,
|
|
MA_TIMEOUT = -34,
|
|
MA_NO_NETWORK = -35,
|
|
MA_NOT_UNIQUE = -36,
|
|
MA_NOT_SOCKET = -37,
|
|
MA_NO_ADDRESS = -38,
|
|
MA_BAD_PROTOCOL = -39,
|
|
MA_PROTOCOL_UNAVAILABLE = -40,
|
|
MA_PROTOCOL_NOT_SUPPORTED = -41,
|
|
MA_PROTOCOL_FAMILY_NOT_SUPPORTED = -42,
|
|
MA_ADDRESS_FAMILY_NOT_SUPPORTED = -43,
|
|
MA_SOCKET_NOT_SUPPORTED = -44,
|
|
MA_CONNECTION_RESET = -45,
|
|
MA_ALREADY_CONNECTED = -46,
|
|
MA_NOT_CONNECTED = -47,
|
|
MA_CONNECTION_REFUSED = -48,
|
|
MA_NO_HOST = -49,
|
|
MA_IN_PROGRESS = -50,
|
|
MA_CANCELLED = -51,
|
|
MA_MEMORY_ALREADY_MAPPED = -52,
|
|
|
|
MA_CRC_MISMATCH = -100,
|
|
|
|
MA_FORMAT_NOT_SUPPORTED = -200,
|
|
MA_DEVICE_TYPE_NOT_SUPPORTED = -201,
|
|
MA_SHARE_MODE_NOT_SUPPORTED = -202,
|
|
MA_NO_BACKEND = -203,
|
|
MA_NO_DEVICE = -204,
|
|
MA_API_NOT_FOUND = -205,
|
|
MA_INVALID_DEVICE_CONFIG = -206,
|
|
MA_LOOP = -207,
|
|
MA_BACKEND_NOT_ENABLED = -208,
|
|
|
|
MA_DEVICE_NOT_INITIALIZED = -300,
|
|
MA_DEVICE_ALREADY_INITIALIZED = -301,
|
|
MA_DEVICE_NOT_STARTED = -302,
|
|
MA_DEVICE_NOT_STOPPED = -303,
|
|
|
|
MA_FAILED_TO_INIT_BACKEND = -400,
|
|
MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401,
|
|
MA_FAILED_TO_START_BACKEND_DEVICE = -402,
|
|
MA_FAILED_TO_STOP_BACKEND_DEVICE = -403
|
|
} ma_result;
|
|
|
|
#define MA_MIN_CHANNELS 1
|
|
#ifndef MA_MAX_CHANNELS
|
|
#define MA_MAX_CHANNELS 254
|
|
#endif
|
|
|
|
#ifndef MA_MAX_FILTER_ORDER
|
|
#define MA_MAX_FILTER_ORDER 8
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
ma_stream_format_pcm = 0
|
|
} ma_stream_format;
|
|
|
|
typedef enum
|
|
{
|
|
ma_stream_layout_interleaved = 0,
|
|
ma_stream_layout_deinterleaved
|
|
} ma_stream_layout;
|
|
|
|
typedef enum
|
|
{
|
|
ma_dither_mode_none = 0,
|
|
ma_dither_mode_rectangle,
|
|
ma_dither_mode_triangle
|
|
} ma_dither_mode;
|
|
|
|
typedef enum
|
|
{
|
|
ma_format_unknown = 0,
|
|
ma_format_u8 = 1,
|
|
ma_format_s16 = 2,
|
|
ma_format_s24 = 3,
|
|
ma_format_s32 = 4,
|
|
ma_format_f32 = 5,
|
|
ma_format_count
|
|
} ma_format;
|
|
|
|
typedef enum
|
|
{
|
|
ma_standard_sample_rate_48000 = 48000,
|
|
ma_standard_sample_rate_44100 = 44100,
|
|
|
|
ma_standard_sample_rate_32000 = 32000,
|
|
ma_standard_sample_rate_24000 = 24000,
|
|
ma_standard_sample_rate_22050 = 22050,
|
|
|
|
ma_standard_sample_rate_88200 = 88200,
|
|
ma_standard_sample_rate_96000 = 96000,
|
|
ma_standard_sample_rate_176400 = 176400,
|
|
ma_standard_sample_rate_192000 = 192000,
|
|
|
|
ma_standard_sample_rate_16000 = 16000,
|
|
ma_standard_sample_rate_11025 = 11025,
|
|
ma_standard_sample_rate_8000 = 8000,
|
|
|
|
ma_standard_sample_rate_352800 = 352800,
|
|
ma_standard_sample_rate_384000 = 384000,
|
|
|
|
ma_standard_sample_rate_min = ma_standard_sample_rate_8000,
|
|
ma_standard_sample_rate_max = ma_standard_sample_rate_384000,
|
|
ma_standard_sample_rate_count = 14
|
|
} ma_standard_sample_rate;
|
|
|
|
typedef enum
|
|
{
|
|
ma_channel_mix_mode_rectangular = 0,
|
|
ma_channel_mix_mode_simple,
|
|
ma_channel_mix_mode_custom_weights,
|
|
ma_channel_mix_mode_default = ma_channel_mix_mode_rectangular
|
|
} ma_channel_mix_mode;
|
|
|
|
typedef enum
|
|
{
|
|
ma_standard_channel_map_microsoft,
|
|
ma_standard_channel_map_alsa,
|
|
ma_standard_channel_map_rfc3551,
|
|
ma_standard_channel_map_flac,
|
|
ma_standard_channel_map_vorbis,
|
|
ma_standard_channel_map_sound4,
|
|
ma_standard_channel_map_sndio,
|
|
ma_standard_channel_map_webaudio = ma_standard_channel_map_flac,
|
|
ma_standard_channel_map_default = ma_standard_channel_map_microsoft
|
|
} ma_standard_channel_map;
|
|
|
|
typedef enum
|
|
{
|
|
ma_performance_profile_low_latency = 0,
|
|
ma_performance_profile_conservative
|
|
} ma_performance_profile;
|
|
|
|
typedef struct
|
|
{
|
|
void* pUserData;
|
|
void* (* onMalloc)(size_t sz, void* pUserData);
|
|
void* (* onRealloc)(void* p, size_t sz, void* pUserData);
|
|
void (* onFree)(void* p, void* pUserData);
|
|
} ma_allocation_callbacks;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 state;
|
|
} ma_lcg;
|
|
|
|
#define MA_ATOMIC_SAFE_TYPE_DECL(c89TypeExtension, typeSize, type) \
|
|
typedef struct \
|
|
{ \
|
|
MA_ATOMIC(typeSize, ma_##type) value; \
|
|
} ma_atomic_##type; \
|
|
|
|
#define MA_ATOMIC_SAFE_TYPE_DECL_PTR(type) \
|
|
typedef struct \
|
|
{ \
|
|
MA_ATOMIC(MA_SIZEOF_PTR, ma_##type*) value; \
|
|
} ma_atomic_ptr_##type; \
|
|
|
|
MA_ATOMIC_SAFE_TYPE_DECL(32, 4, uint32)
|
|
MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, int32)
|
|
MA_ATOMIC_SAFE_TYPE_DECL(64, 8, uint64)
|
|
MA_ATOMIC_SAFE_TYPE_DECL(f32, 4, float)
|
|
MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32)
|
|
|
|
typedef ma_uint32 ma_spinlock;
|
|
|
|
#ifndef MA_NO_THREADING
|
|
typedef enum
|
|
{
|
|
ma_thread_priority_idle = -5,
|
|
ma_thread_priority_lowest = -4,
|
|
ma_thread_priority_low = -3,
|
|
ma_thread_priority_normal = -2,
|
|
ma_thread_priority_high = -1,
|
|
ma_thread_priority_highest = 0,
|
|
ma_thread_priority_realtime = 1,
|
|
ma_thread_priority_default = 0
|
|
} ma_thread_priority;
|
|
|
|
#if defined(MA_POSIX)
|
|
typedef ma_pthread_t ma_thread;
|
|
#elif defined(MA_WIN32)
|
|
typedef ma_handle ma_thread;
|
|
#endif
|
|
|
|
#if defined(MA_POSIX)
|
|
typedef ma_pthread_mutex_t ma_mutex;
|
|
#elif defined(MA_WIN32)
|
|
typedef ma_handle ma_mutex;
|
|
#endif
|
|
|
|
#if defined(MA_POSIX)
|
|
typedef struct
|
|
{
|
|
ma_uint32 value;
|
|
ma_pthread_mutex_t lock;
|
|
ma_pthread_cond_t cond;
|
|
} ma_event;
|
|
#elif defined(MA_WIN32)
|
|
typedef ma_handle ma_event;
|
|
#endif
|
|
|
|
#if defined(MA_POSIX)
|
|
typedef struct
|
|
{
|
|
int value;
|
|
ma_pthread_mutex_t lock;
|
|
ma_pthread_cond_t cond;
|
|
} ma_semaphore;
|
|
#elif defined(MA_WIN32)
|
|
typedef ma_handle ma_semaphore;
|
|
#endif
|
|
#else
|
|
#ifndef MA_NO_DEVICE_IO
|
|
#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
|
|
#endif
|
|
#endif
|
|
|
|
MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
|
|
|
|
MA_API const char* ma_version_string(void);
|
|
|
|
#include <stdarg.h>
|
|
|
|
#if defined(__has_attribute)
|
|
#if __has_attribute(format)
|
|
#define MA_ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
|
|
#endif
|
|
#endif
|
|
#ifndef MA_ATTRIBUTE_FORMAT
|
|
#define MA_ATTRIBUTE_FORMAT(fmt, va)
|
|
#endif
|
|
|
|
#ifndef MA_MAX_LOG_CALLBACKS
|
|
#define MA_MAX_LOG_CALLBACKS 4
|
|
#endif
|
|
|
|
typedef void (* ma_log_callback_proc)(void* pUserData, ma_uint32 level, const char* pMessage);
|
|
|
|
typedef struct
|
|
{
|
|
ma_log_callback_proc onLog;
|
|
void* pUserData;
|
|
} ma_log_callback;
|
|
|
|
MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData);
|
|
|
|
typedef struct
|
|
{
|
|
ma_log_callback callbacks[MA_MAX_LOG_CALLBACKS];
|
|
ma_uint32 callbackCount;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
#ifndef MA_NO_THREADING
|
|
ma_mutex lock;
|
|
#endif
|
|
} ma_log;
|
|
|
|
MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog);
|
|
MA_API void ma_log_uninit(ma_log* pLog);
|
|
MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback);
|
|
MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback);
|
|
MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage);
|
|
MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args);
|
|
MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...) MA_ATTRIBUTE_FORMAT(3, 4);
|
|
|
|
typedef union
|
|
{
|
|
float f32;
|
|
ma_int32 s32;
|
|
} ma_biquad_coefficient;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
double b0;
|
|
double b1;
|
|
double b2;
|
|
double a0;
|
|
double a1;
|
|
double a2;
|
|
} ma_biquad_config;
|
|
|
|
MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_biquad_coefficient b0;
|
|
ma_biquad_coefficient b1;
|
|
ma_biquad_coefficient b2;
|
|
ma_biquad_coefficient a1;
|
|
ma_biquad_coefficient a2;
|
|
ma_biquad_coefficient* pR1;
|
|
ma_biquad_coefficient* pR2;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_biquad;
|
|
|
|
MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ);
|
|
MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ);
|
|
MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ);
|
|
MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ);
|
|
MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double cutoffFrequency;
|
|
double q;
|
|
} ma_lpf1_config, ma_lpf2_config;
|
|
|
|
MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
|
|
MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_biquad_coefficient a;
|
|
ma_biquad_coefficient* pR1;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_lpf1;
|
|
|
|
MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF);
|
|
MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF);
|
|
MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF);
|
|
MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF);
|
|
MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_lpf2;
|
|
|
|
MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pHPF);
|
|
MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF);
|
|
MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF);
|
|
MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF);
|
|
MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double cutoffFrequency;
|
|
ma_uint32 order;
|
|
} ma_lpf_config;
|
|
|
|
MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 lpf1Count;
|
|
ma_uint32 lpf2Count;
|
|
ma_lpf1* pLPF1;
|
|
ma_lpf2* pLPF2;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_lpf;
|
|
|
|
MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF);
|
|
MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF);
|
|
MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF);
|
|
MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF);
|
|
MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double cutoffFrequency;
|
|
double q;
|
|
} ma_hpf1_config, ma_hpf2_config;
|
|
|
|
MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency);
|
|
MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_biquad_coefficient a;
|
|
ma_biquad_coefficient* pR1;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_hpf1;
|
|
|
|
MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF);
|
|
MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pHPF);
|
|
MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF);
|
|
MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_hpf2;
|
|
|
|
MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF);
|
|
MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF);
|
|
MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF);
|
|
MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double cutoffFrequency;
|
|
ma_uint32 order;
|
|
} ma_hpf_config;
|
|
|
|
MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 hpf1Count;
|
|
ma_uint32 hpf2Count;
|
|
ma_hpf1* pHPF1;
|
|
ma_hpf2* pHPF2;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_hpf;
|
|
|
|
MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF);
|
|
MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF);
|
|
MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF);
|
|
MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double cutoffFrequency;
|
|
double q;
|
|
} ma_bpf2_config;
|
|
|
|
MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_bpf2;
|
|
|
|
MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF);
|
|
MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF);
|
|
MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF);
|
|
MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double cutoffFrequency;
|
|
ma_uint32 order;
|
|
} ma_bpf_config;
|
|
|
|
MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 bpf2Count;
|
|
ma_bpf2* pBPF2;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_bpf;
|
|
|
|
MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF);
|
|
MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF);
|
|
MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF);
|
|
MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double q;
|
|
double frequency;
|
|
} ma_notch2_config, ma_notch_config;
|
|
|
|
MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_notch2;
|
|
|
|
MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter);
|
|
MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter);
|
|
MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter);
|
|
MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double gainDB;
|
|
double q;
|
|
double frequency;
|
|
} ma_peak2_config, ma_peak_config;
|
|
|
|
MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_peak2;
|
|
|
|
MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter);
|
|
MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter);
|
|
MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter);
|
|
MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double gainDB;
|
|
double shelfSlope;
|
|
double frequency;
|
|
} ma_loshelf2_config, ma_loshelf_config;
|
|
|
|
MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_loshelf2;
|
|
|
|
MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter);
|
|
MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter);
|
|
MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter);
|
|
MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double gainDB;
|
|
double shelfSlope;
|
|
double frequency;
|
|
} ma_hishelf2_config, ma_hishelf_config;
|
|
|
|
MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_biquad bq;
|
|
} ma_hishelf2;
|
|
|
|
MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter);
|
|
MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter);
|
|
MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter);
|
|
MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 delayInFrames;
|
|
ma_bool32 delayStart;
|
|
float wet;
|
|
float dry;
|
|
float decay;
|
|
} ma_delay_config;
|
|
|
|
MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
|
|
|
|
typedef struct
|
|
{
|
|
ma_delay_config config;
|
|
ma_uint32 cursor;
|
|
ma_uint32 bufferSizeInFrames;
|
|
float* pBuffer;
|
|
} ma_delay;
|
|
|
|
MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay);
|
|
MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount);
|
|
MA_API void ma_delay_set_wet(ma_delay* pDelay, float value);
|
|
MA_API float ma_delay_get_wet(const ma_delay* pDelay);
|
|
MA_API void ma_delay_set_dry(ma_delay* pDelay, float value);
|
|
MA_API float ma_delay_get_dry(const ma_delay* pDelay);
|
|
MA_API void ma_delay_set_decay(ma_delay* pDelay, float value);
|
|
MA_API float ma_delay_get_decay(const ma_delay* pDelay);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 channels;
|
|
ma_uint32 smoothTimeInFrames;
|
|
} ma_gainer_config;
|
|
|
|
MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames);
|
|
|
|
typedef struct
|
|
{
|
|
ma_gainer_config config;
|
|
ma_uint32 t;
|
|
float masterVolume;
|
|
float* pOldGains;
|
|
float* pNewGains;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_gainer;
|
|
|
|
MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer);
|
|
MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer);
|
|
MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain);
|
|
MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains);
|
|
MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume);
|
|
MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume);
|
|
|
|
typedef enum
|
|
{
|
|
ma_pan_mode_balance = 0,
|
|
ma_pan_mode_pan
|
|
} ma_pan_mode;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_pan_mode mode;
|
|
float pan;
|
|
} ma_panner_config;
|
|
|
|
MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_pan_mode mode;
|
|
float pan;
|
|
} ma_panner;
|
|
|
|
MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner);
|
|
MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode);
|
|
MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner);
|
|
MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan);
|
|
MA_API float ma_panner_get_pan(const ma_panner* pPanner);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
} ma_fader_config;
|
|
|
|
MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
|
|
|
|
typedef struct
|
|
{
|
|
ma_fader_config config;
|
|
float volumeBeg;
|
|
float volumeEnd;
|
|
ma_uint64 lengthInFrames;
|
|
ma_int64 cursorInFrames;
|
|
} ma_fader;
|
|
|
|
MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader);
|
|
MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
|
|
MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames);
|
|
MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames);
|
|
MA_API float ma_fader_get_current_volume(const ma_fader* pFader);
|
|
|
|
typedef struct
|
|
{
|
|
float x;
|
|
float y;
|
|
float z;
|
|
} ma_vec3f;
|
|
|
|
typedef struct
|
|
{
|
|
ma_vec3f v;
|
|
ma_spinlock lock;
|
|
} ma_atomic_vec3f;
|
|
|
|
typedef enum
|
|
{
|
|
ma_attenuation_model_none,
|
|
ma_attenuation_model_inverse,
|
|
ma_attenuation_model_linear,
|
|
ma_attenuation_model_exponential
|
|
} ma_attenuation_model;
|
|
|
|
typedef enum
|
|
{
|
|
ma_positioning_absolute,
|
|
ma_positioning_relative
|
|
} ma_positioning;
|
|
|
|
typedef enum
|
|
{
|
|
ma_handedness_right,
|
|
ma_handedness_left
|
|
} ma_handedness;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 channelsOut;
|
|
ma_channel* pChannelMapOut;
|
|
ma_handedness handedness;
|
|
float coneInnerAngleInRadians;
|
|
float coneOuterAngleInRadians;
|
|
float coneOuterGain;
|
|
float speedOfSound;
|
|
ma_vec3f worldUp;
|
|
} ma_spatializer_listener_config;
|
|
|
|
MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut);
|
|
|
|
typedef struct
|
|
{
|
|
ma_spatializer_listener_config config;
|
|
ma_atomic_vec3f position;
|
|
ma_atomic_vec3f direction;
|
|
ma_atomic_vec3f velocity;
|
|
ma_bool32 isEnabled;
|
|
|
|
ma_bool32 _ownsHeap;
|
|
void* _pHeap;
|
|
} ma_spatializer_listener;
|
|
|
|
MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener);
|
|
MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
|
|
MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
|
|
MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound);
|
|
MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener);
|
|
MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled);
|
|
MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_channel* pChannelMapIn;
|
|
ma_attenuation_model attenuationModel;
|
|
ma_positioning positioning;
|
|
ma_handedness handedness;
|
|
float minGain;
|
|
float maxGain;
|
|
float minDistance;
|
|
float maxDistance;
|
|
float rolloff;
|
|
float coneInnerAngleInRadians;
|
|
float coneOuterAngleInRadians;
|
|
float coneOuterGain;
|
|
float dopplerFactor;
|
|
float directionalAttenuationFactor;
|
|
float minSpatializationChannelGain;
|
|
ma_uint32 gainSmoothTimeInFrames;
|
|
} ma_spatializer_config;
|
|
|
|
MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_channel* pChannelMapIn;
|
|
ma_attenuation_model attenuationModel;
|
|
ma_positioning positioning;
|
|
ma_handedness handedness;
|
|
float minGain;
|
|
float maxGain;
|
|
float minDistance;
|
|
float maxDistance;
|
|
float rolloff;
|
|
float coneInnerAngleInRadians;
|
|
float coneOuterAngleInRadians;
|
|
float coneOuterGain;
|
|
float dopplerFactor;
|
|
float directionalAttenuationFactor;
|
|
ma_uint32 gainSmoothTimeInFrames;
|
|
ma_atomic_vec3f position;
|
|
ma_atomic_vec3f direction;
|
|
ma_atomic_vec3f velocity;
|
|
float dopplerPitch;
|
|
float minSpatializationChannelGain;
|
|
ma_gainer gainer;
|
|
float* pNewChannelGainsOut;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_spatializer;
|
|
|
|
MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer);
|
|
MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume);
|
|
MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume);
|
|
MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer);
|
|
MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel);
|
|
MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning);
|
|
MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff);
|
|
MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain);
|
|
MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain);
|
|
MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance);
|
|
MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance);
|
|
MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
|
|
MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
|
|
MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor);
|
|
MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor);
|
|
MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z);
|
|
MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer);
|
|
MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRateIn;
|
|
ma_uint32 sampleRateOut;
|
|
ma_uint32 lpfOrder;
|
|
double lpfNyquistFactor;
|
|
} ma_linear_resampler_config;
|
|
|
|
MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
|
|
|
typedef struct
|
|
{
|
|
ma_linear_resampler_config config;
|
|
ma_uint32 inAdvanceInt;
|
|
ma_uint32 inAdvanceFrac;
|
|
ma_uint32 inTimeInt;
|
|
ma_uint32 inTimeFrac;
|
|
union
|
|
{
|
|
float* f32;
|
|
ma_int16* s16;
|
|
} x0;
|
|
union
|
|
{
|
|
float* f32;
|
|
ma_int16* s16;
|
|
} x1;
|
|
ma_lpf lpf;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_linear_resampler;
|
|
|
|
MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler);
|
|
MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler);
|
|
MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
|
|
MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
|
MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut);
|
|
MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler);
|
|
MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler);
|
|
MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
|
|
MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
|
|
MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler);
|
|
|
|
typedef struct ma_resampler_config ma_resampler_config;
|
|
|
|
typedef void ma_resampling_backend;
|
|
typedef struct
|
|
{
|
|
ma_result (* onGetHeapSize )(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
|
|
ma_result (* onInit )(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend);
|
|
void (* onUninit )(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
ma_result (* onProcess )(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
|
|
ma_result (* onSetRate )(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
|
ma_uint64 (* onGetInputLatency )(void* pUserData, const ma_resampling_backend* pBackend);
|
|
ma_uint64 (* onGetOutputLatency )(void* pUserData, const ma_resampling_backend* pBackend);
|
|
ma_result (* onGetRequiredInputFrameCount )(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
|
|
ma_result (* onGetExpectedOutputFrameCount)(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
|
|
ma_result (* onReset )(void* pUserData, ma_resampling_backend* pBackend);
|
|
} ma_resampling_backend_vtable;
|
|
|
|
typedef enum
|
|
{
|
|
ma_resample_algorithm_linear = 0,
|
|
ma_resample_algorithm_custom,
|
|
} ma_resample_algorithm;
|
|
|
|
struct ma_resampler_config
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRateIn;
|
|
ma_uint32 sampleRateOut;
|
|
ma_resample_algorithm algorithm;
|
|
ma_resampling_backend_vtable* pBackendVTable;
|
|
void* pBackendUserData;
|
|
struct
|
|
{
|
|
ma_uint32 lpfOrder;
|
|
} linear;
|
|
};
|
|
|
|
MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm);
|
|
|
|
typedef struct
|
|
{
|
|
ma_resampling_backend* pBackend;
|
|
ma_resampling_backend_vtable* pBackendVTable;
|
|
void* pBackendUserData;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRateIn;
|
|
ma_uint32 sampleRateOut;
|
|
union
|
|
{
|
|
ma_linear_resampler linear;
|
|
} state;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_resampler;
|
|
|
|
MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler);
|
|
|
|
MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler);
|
|
|
|
MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
|
|
|
|
MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
|
|
|
MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio);
|
|
|
|
MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler);
|
|
|
|
MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler);
|
|
|
|
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
|
|
|
|
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
|
|
|
|
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);
|
|
|
|
typedef enum
|
|
{
|
|
ma_channel_conversion_path_unknown,
|
|
ma_channel_conversion_path_passthrough,
|
|
ma_channel_conversion_path_mono_out,
|
|
ma_channel_conversion_path_mono_in,
|
|
ma_channel_conversion_path_shuffle,
|
|
ma_channel_conversion_path_weights
|
|
} ma_channel_conversion_path;
|
|
|
|
typedef enum
|
|
{
|
|
ma_mono_expansion_mode_duplicate = 0,
|
|
ma_mono_expansion_mode_average,
|
|
ma_mono_expansion_mode_stereo_only,
|
|
ma_mono_expansion_mode_default = ma_mono_expansion_mode_duplicate
|
|
} ma_mono_expansion_mode;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
const ma_channel* pChannelMapIn;
|
|
const ma_channel* pChannelMapOut;
|
|
ma_channel_mix_mode mixingMode;
|
|
ma_bool32 calculateLFEFromSpatialChannels;
|
|
float** ppWeights;
|
|
} ma_channel_converter_config;
|
|
|
|
MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_channel_mix_mode mixingMode;
|
|
ma_channel_conversion_path conversionPath;
|
|
ma_channel* pChannelMapIn;
|
|
ma_channel* pChannelMapOut;
|
|
ma_uint8* pShuffleTable;
|
|
union
|
|
{
|
|
float** f32;
|
|
ma_int32** s16;
|
|
} weights;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_channel_converter;
|
|
|
|
MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter);
|
|
MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter);
|
|
MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
|
|
MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format formatIn;
|
|
ma_format formatOut;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_uint32 sampleRateIn;
|
|
ma_uint32 sampleRateOut;
|
|
ma_channel* pChannelMapIn;
|
|
ma_channel* pChannelMapOut;
|
|
ma_dither_mode ditherMode;
|
|
ma_channel_mix_mode channelMixMode;
|
|
ma_bool32 calculateLFEFromSpatialChannels;
|
|
float** ppChannelWeights;
|
|
ma_bool32 allowDynamicSampleRate;
|
|
ma_resampler_config resampling;
|
|
} ma_data_converter_config;
|
|
|
|
MA_API ma_data_converter_config ma_data_converter_config_init_default(void);
|
|
MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
|
|
|
typedef enum
|
|
{
|
|
ma_data_converter_execution_path_passthrough,
|
|
ma_data_converter_execution_path_format_only,
|
|
ma_data_converter_execution_path_channels_only,
|
|
ma_data_converter_execution_path_resample_only,
|
|
ma_data_converter_execution_path_resample_first,
|
|
ma_data_converter_execution_path_channels_first
|
|
} ma_data_converter_execution_path;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format formatIn;
|
|
ma_format formatOut;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_uint32 sampleRateIn;
|
|
ma_uint32 sampleRateOut;
|
|
ma_dither_mode ditherMode;
|
|
ma_data_converter_execution_path executionPath;
|
|
ma_channel_converter channelConverter;
|
|
ma_resampler resampler;
|
|
ma_bool8 hasPreFormatConversion;
|
|
ma_bool8 hasPostFormatConversion;
|
|
ma_bool8 hasChannelConverter;
|
|
ma_bool8 hasResampler;
|
|
ma_bool8 isPassthrough;
|
|
|
|
ma_bool8 _ownsHeap;
|
|
void* _pHeap;
|
|
} ma_data_converter;
|
|
|
|
MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter);
|
|
MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter);
|
|
MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
|
|
MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut);
|
|
MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut);
|
|
MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter);
|
|
MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter);
|
|
MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);
|
|
MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);
|
|
MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter);
|
|
|
|
MA_API void ma_pcm_u8_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_u8_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_u8_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_u8_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s16_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s16_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s16_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s16_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s24_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s24_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s24_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s24_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_s32_to_f32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_f32_to_u8(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_f32_to_s16(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_f32_to_s24(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_f32_to_s32(void* pOut, const void* pIn, ma_uint64 count, ma_dither_mode ditherMode);
|
|
MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode);
|
|
MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode);
|
|
|
|
MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames);
|
|
|
|
MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames);
|
|
|
|
#define MA_CHANNEL_INDEX_NULL 255
|
|
|
|
MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
|
|
|
|
MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels);
|
|
|
|
MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels);
|
|
|
|
MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels);
|
|
|
|
MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels);
|
|
|
|
MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels);
|
|
|
|
MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels);
|
|
|
|
MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels);
|
|
|
|
MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition);
|
|
|
|
MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex);
|
|
|
|
MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap);
|
|
|
|
MA_API const char* ma_channel_position_to_string(ma_channel channel);
|
|
|
|
MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn);
|
|
MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig);
|
|
|
|
typedef void ma_data_source;
|
|
|
|
#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001
|
|
|
|
typedef struct
|
|
{
|
|
ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex);
|
|
ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor);
|
|
ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength);
|
|
ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping);
|
|
ma_uint32 flags;
|
|
} ma_data_source_vtable;
|
|
|
|
typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource);
|
|
|
|
typedef struct
|
|
{
|
|
const ma_data_source_vtable* vtable;
|
|
} ma_data_source_config;
|
|
|
|
MA_API ma_data_source_config ma_data_source_config_init(void);
|
|
|
|
typedef struct
|
|
{
|
|
const ma_data_source_vtable* vtable;
|
|
ma_uint64 rangeBegInFrames;
|
|
ma_uint64 rangeEndInFrames;
|
|
ma_uint64 loopBegInFrames;
|
|
ma_uint64 loopEndInFrames;
|
|
ma_data_source* pCurrent;
|
|
ma_data_source* pNext;
|
|
ma_data_source_get_next_proc onGetNext;
|
|
MA_ATOMIC(4, ma_bool32) isLooping;
|
|
} ma_data_source_base;
|
|
|
|
MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource);
|
|
MA_API void ma_data_source_uninit(ma_data_source* pDataSource);
|
|
MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked);
|
|
MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked);
|
|
MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds);
|
|
MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor);
|
|
MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength);
|
|
MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor);
|
|
MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength);
|
|
MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping);
|
|
MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource);
|
|
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames);
|
|
MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames);
|
|
MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames);
|
|
MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames);
|
|
MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource);
|
|
MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource);
|
|
MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource);
|
|
MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource);
|
|
MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext);
|
|
MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource);
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint64 cursor;
|
|
ma_uint64 sizeInFrames;
|
|
const void* pData;
|
|
} ma_audio_buffer_ref;
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef);
|
|
MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef);
|
|
MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames);
|
|
MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
|
|
MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount);
|
|
MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount);
|
|
MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef);
|
|
MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor);
|
|
MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength);
|
|
MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint64 sizeInFrames;
|
|
const void* pData;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
} ma_audio_buffer_config;
|
|
|
|
MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_audio_buffer_ref ref;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_bool32 ownsData;
|
|
ma_uint8 _pExtraData[1];
|
|
} ma_audio_buffer;
|
|
|
|
MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
|
|
MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer);
|
|
MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer);
|
|
MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer);
|
|
MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer);
|
|
MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop);
|
|
MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount);
|
|
MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount);
|
|
MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer);
|
|
MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor);
|
|
MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength);
|
|
MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames);
|
|
|
|
typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page;
|
|
struct ma_paged_audio_buffer_page
|
|
{
|
|
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext;
|
|
ma_uint64 sizeInFrames;
|
|
ma_uint8 pAudioData[1];
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_paged_audio_buffer_page head;
|
|
MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail;
|
|
} ma_paged_audio_buffer_data;
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData);
|
|
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData);
|
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData);
|
|
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength);
|
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage);
|
|
MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage);
|
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_paged_audio_buffer_data* pData;
|
|
} ma_paged_audio_buffer_config;
|
|
|
|
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData);
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_paged_audio_buffer_data* pData;
|
|
ma_paged_audio_buffer_page* pCurrent;
|
|
ma_uint64 relativeCursor;
|
|
ma_uint64 absoluteCursor;
|
|
} ma_paged_audio_buffer;
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer);
|
|
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer);
|
|
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor);
|
|
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength);
|
|
|
|
typedef struct
|
|
{
|
|
void* pBuffer;
|
|
ma_uint32 subbufferSizeInBytes;
|
|
ma_uint32 subbufferCount;
|
|
ma_uint32 subbufferStrideInBytes;
|
|
MA_ATOMIC(4, ma_uint32) encodedReadOffset;
|
|
MA_ATOMIC(4, ma_uint32) encodedWriteOffset;
|
|
ma_bool8 ownsBuffer;
|
|
ma_bool8 clearOnWriteAcquire;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
} ma_rb;
|
|
|
|
MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
|
|
MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB);
|
|
MA_API void ma_rb_uninit(ma_rb* pRB);
|
|
MA_API void ma_rb_reset(ma_rb* pRB);
|
|
MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
|
|
MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes);
|
|
MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut);
|
|
MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes);
|
|
MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes);
|
|
MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes);
|
|
MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB);
|
|
MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB);
|
|
MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB);
|
|
MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB);
|
|
MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB);
|
|
MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex);
|
|
MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer);
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_rb rb;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
} ma_pcm_rb;
|
|
|
|
MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);
|
|
MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB);
|
|
MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB);
|
|
MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB);
|
|
MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
|
|
MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames);
|
|
MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut);
|
|
MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames);
|
|
MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
|
|
MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames);
|
|
MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex);
|
|
MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer);
|
|
MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB);
|
|
MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB);
|
|
MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate);
|
|
|
|
typedef struct
|
|
{
|
|
ma_pcm_rb rb;
|
|
} ma_duplex_rb;
|
|
|
|
MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB);
|
|
MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB);
|
|
|
|
MA_API const char* ma_result_description(ma_result result);
|
|
|
|
MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
MA_API const char* ma_get_format_name(ma_format format);
|
|
|
|
MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels);
|
|
|
|
MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format);
|
|
static MA_INLINE ma_uint32 ma_get_bytes_per_frame(ma_format format, ma_uint32 channels) { return ma_get_bytes_per_sample(format) * channels; }
|
|
|
|
MA_API const char* ma_log_level_to_string(ma_uint32 logLevel);
|
|
|
|
MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock);
|
|
|
|
MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock);
|
|
|
|
MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock);
|
|
|
|
#ifndef MA_NO_THREADING
|
|
|
|
MA_API ma_result ma_mutex_init(ma_mutex* pMutex);
|
|
|
|
MA_API void ma_mutex_uninit(ma_mutex* pMutex);
|
|
|
|
MA_API void ma_mutex_lock(ma_mutex* pMutex);
|
|
|
|
MA_API void ma_mutex_unlock(ma_mutex* pMutex);
|
|
|
|
MA_API ma_result ma_event_init(ma_event* pEvent);
|
|
|
|
MA_API void ma_event_uninit(ma_event* pEvent);
|
|
|
|
MA_API ma_result ma_event_wait(ma_event* pEvent);
|
|
|
|
MA_API ma_result ma_event_signal(ma_event* pEvent);
|
|
|
|
MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore);
|
|
MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore);
|
|
MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore);
|
|
MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore);
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
#ifndef MA_NO_THREADING
|
|
ma_event e;
|
|
#endif
|
|
ma_uint32 counter;
|
|
} ma_fence;
|
|
|
|
MA_API ma_result ma_fence_init(ma_fence* pFence);
|
|
MA_API void ma_fence_uninit(ma_fence* pFence);
|
|
MA_API ma_result ma_fence_acquire(ma_fence* pFence);
|
|
MA_API ma_result ma_fence_release(ma_fence* pFence);
|
|
MA_API ma_result ma_fence_wait(ma_fence* pFence);
|
|
|
|
typedef void ma_async_notification;
|
|
|
|
typedef struct
|
|
{
|
|
void (* onSignal)(ma_async_notification* pNotification);
|
|
} ma_async_notification_callbacks;
|
|
|
|
MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification);
|
|
|
|
typedef struct
|
|
{
|
|
ma_async_notification_callbacks cb;
|
|
ma_bool32 signalled;
|
|
} ma_async_notification_poll;
|
|
|
|
MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll);
|
|
MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll);
|
|
|
|
typedef struct
|
|
{
|
|
ma_async_notification_callbacks cb;
|
|
#ifndef MA_NO_THREADING
|
|
ma_event e;
|
|
#endif
|
|
} ma_async_notification_event;
|
|
|
|
MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent);
|
|
MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent);
|
|
MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent);
|
|
MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 capacity;
|
|
} ma_slot_allocator_config;
|
|
|
|
MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity);
|
|
|
|
typedef struct
|
|
{
|
|
MA_ATOMIC(4, ma_uint32) bitfield;
|
|
} ma_slot_allocator_group;
|
|
|
|
typedef struct
|
|
{
|
|
ma_slot_allocator_group* pGroups;
|
|
ma_uint32* pSlots;
|
|
ma_uint32 count;
|
|
ma_uint32 capacity;
|
|
|
|
ma_bool32 _ownsHeap;
|
|
void* _pHeap;
|
|
} ma_slot_allocator;
|
|
|
|
MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator);
|
|
MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator);
|
|
MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot);
|
|
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot);
|
|
|
|
typedef struct ma_job ma_job;
|
|
|
|
typedef ma_result (* ma_job_proc)(ma_job* pJob);
|
|
|
|
typedef enum
|
|
{
|
|
MA_JOB_TYPE_QUIT = 0,
|
|
MA_JOB_TYPE_CUSTOM,
|
|
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM,
|
|
MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM,
|
|
|
|
MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE,
|
|
|
|
MA_JOB_TYPE_COUNT
|
|
} ma_job_type;
|
|
|
|
struct ma_job
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
ma_uint16 code;
|
|
ma_uint16 slot;
|
|
ma_uint32 refcount;
|
|
} breakup;
|
|
ma_uint64 allocation;
|
|
} toc;
|
|
MA_ATOMIC(8, ma_uint64) next;
|
|
ma_uint32 order;
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
ma_job_proc proc;
|
|
ma_uintptr data0;
|
|
ma_uintptr data1;
|
|
} custom;
|
|
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
void* pResourceManager;
|
|
void* pDataBufferNode;
|
|
char* pFilePath;
|
|
wchar_t* pFilePathW;
|
|
ma_uint32 flags;
|
|
ma_async_notification* pInitNotification;
|
|
ma_async_notification* pDoneNotification;
|
|
ma_fence* pInitFence;
|
|
ma_fence* pDoneFence;
|
|
} loadDataBufferNode;
|
|
struct
|
|
{
|
|
void* pResourceManager;
|
|
void* pDataBufferNode;
|
|
ma_async_notification* pDoneNotification;
|
|
ma_fence* pDoneFence;
|
|
} freeDataBufferNode;
|
|
struct
|
|
{
|
|
void* pResourceManager;
|
|
void* pDataBufferNode;
|
|
void* pDecoder;
|
|
ma_async_notification* pDoneNotification;
|
|
ma_fence* pDoneFence;
|
|
} pageDataBufferNode;
|
|
|
|
struct
|
|
{
|
|
void* pDataBuffer;
|
|
ma_async_notification* pInitNotification;
|
|
ma_async_notification* pDoneNotification;
|
|
ma_fence* pInitFence;
|
|
ma_fence* pDoneFence;
|
|
ma_uint64 rangeBegInPCMFrames;
|
|
ma_uint64 rangeEndInPCMFrames;
|
|
ma_uint64 loopPointBegInPCMFrames;
|
|
ma_uint64 loopPointEndInPCMFrames;
|
|
ma_uint32 isLooping;
|
|
} loadDataBuffer;
|
|
struct
|
|
{
|
|
void* pDataBuffer;
|
|
ma_async_notification* pDoneNotification;
|
|
ma_fence* pDoneFence;
|
|
} freeDataBuffer;
|
|
|
|
struct
|
|
{
|
|
void* pDataStream;
|
|
char* pFilePath;
|
|
wchar_t* pFilePathW;
|
|
ma_uint64 initialSeekPoint;
|
|
ma_async_notification* pInitNotification;
|
|
ma_fence* pInitFence;
|
|
} loadDataStream;
|
|
struct
|
|
{
|
|
void* pDataStream;
|
|
ma_async_notification* pDoneNotification;
|
|
ma_fence* pDoneFence;
|
|
} freeDataStream;
|
|
struct
|
|
{
|
|
void* pDataStream;
|
|
ma_uint32 pageIndex;
|
|
} pageDataStream;
|
|
struct
|
|
{
|
|
void* pDataStream;
|
|
ma_uint64 frameIndex;
|
|
} seekDataStream;
|
|
} resourceManager;
|
|
|
|
union
|
|
{
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
void* pDevice;
|
|
ma_uint32 deviceType;
|
|
} reroute;
|
|
} aaudio;
|
|
} device;
|
|
} data;
|
|
};
|
|
|
|
MA_API ma_job ma_job_init(ma_uint16 code);
|
|
MA_API ma_result ma_job_process(ma_job* pJob);
|
|
|
|
typedef enum
|
|
{
|
|
MA_JOB_QUEUE_FLAG_NON_BLOCKING = 0x00000001
|
|
} ma_job_queue_flags;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 flags;
|
|
ma_uint32 capacity;
|
|
} ma_job_queue_config;
|
|
|
|
MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 flags;
|
|
ma_uint32 capacity;
|
|
MA_ATOMIC(8, ma_uint64) head;
|
|
MA_ATOMIC(8, ma_uint64) tail;
|
|
#ifndef MA_NO_THREADING
|
|
ma_semaphore sem;
|
|
#endif
|
|
ma_slot_allocator allocator;
|
|
ma_job* pJobs;
|
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
|
ma_spinlock lock;
|
|
#endif
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_job_queue;
|
|
|
|
MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue);
|
|
MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue);
|
|
MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob);
|
|
MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob);
|
|
|
|
#ifndef MA_NO_DEVICE_IO
|
|
#if defined(MA_WIN32) && !defined(MA_XBOX)
|
|
#define MA_SUPPORT_WASAPI
|
|
|
|
#if defined(MA_WIN32_DESKTOP)
|
|
#define MA_SUPPORT_DSOUND
|
|
#define MA_SUPPORT_WINMM
|
|
#define MA_SUPPORT_JACK
|
|
#endif
|
|
#endif
|
|
#if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO)
|
|
#if defined(MA_LINUX)
|
|
#if !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
|
|
#define MA_SUPPORT_ALSA
|
|
#endif
|
|
#endif
|
|
#if !defined(MA_BSD) && !defined(MA_ANDROID) && !defined(MA_EMSCRIPTEN)
|
|
#define MA_SUPPORT_PULSEAUDIO
|
|
#define MA_SUPPORT_JACK
|
|
#endif
|
|
#if defined(__OpenBSD__)
|
|
#define MA_SUPPORT_SNDIO
|
|
#endif
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
#define MA_SUPPORT_AUDIO4
|
|
#endif
|
|
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
|
#define MA_SUPPORT_OSS
|
|
#endif
|
|
#endif
|
|
#if defined(MA_ANDROID)
|
|
#define MA_SUPPORT_AAUDIO
|
|
#define MA_SUPPORT_OPENSL
|
|
#endif
|
|
#if defined(MA_APPLE)
|
|
#define MA_SUPPORT_COREAUDIO
|
|
#endif
|
|
#if defined(MA_EMSCRIPTEN)
|
|
#define MA_SUPPORT_WEBAUDIO
|
|
#endif
|
|
|
|
#define MA_SUPPORT_CUSTOM
|
|
|
|
#if !defined(MA_EMSCRIPTEN)
|
|
#define MA_SUPPORT_NULL
|
|
#endif
|
|
|
|
#if defined(MA_SUPPORT_WASAPI) && !defined(MA_NO_WASAPI) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WASAPI))
|
|
#define MA_HAS_WASAPI
|
|
#endif
|
|
#if defined(MA_SUPPORT_DSOUND) && !defined(MA_NO_DSOUND) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_DSOUND))
|
|
#define MA_HAS_DSOUND
|
|
#endif
|
|
#if defined(MA_SUPPORT_WINMM) && !defined(MA_NO_WINMM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WINMM))
|
|
#define MA_HAS_WINMM
|
|
#endif
|
|
#if defined(MA_SUPPORT_ALSA) && !defined(MA_NO_ALSA) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_ALSA))
|
|
#define MA_HAS_ALSA
|
|
#endif
|
|
#if defined(MA_SUPPORT_PULSEAUDIO) && !defined(MA_NO_PULSEAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_PULSEAUDIO))
|
|
#define MA_HAS_PULSEAUDIO
|
|
#endif
|
|
#if defined(MA_SUPPORT_JACK) && !defined(MA_NO_JACK) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_JACK))
|
|
#define MA_HAS_JACK
|
|
#endif
|
|
#if defined(MA_SUPPORT_COREAUDIO) && !defined(MA_NO_COREAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_COREAUDIO))
|
|
#define MA_HAS_COREAUDIO
|
|
#endif
|
|
#if defined(MA_SUPPORT_SNDIO) && !defined(MA_NO_SNDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_SNDIO))
|
|
#define MA_HAS_SNDIO
|
|
#endif
|
|
#if defined(MA_SUPPORT_AUDIO4) && !defined(MA_NO_AUDIO4) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AUDIO4))
|
|
#define MA_HAS_AUDIO4
|
|
#endif
|
|
#if defined(MA_SUPPORT_OSS) && !defined(MA_NO_OSS) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OSS))
|
|
#define MA_HAS_OSS
|
|
#endif
|
|
#if defined(MA_SUPPORT_AAUDIO) && !defined(MA_NO_AAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_AAUDIO))
|
|
#define MA_HAS_AAUDIO
|
|
#endif
|
|
#if defined(MA_SUPPORT_OPENSL) && !defined(MA_NO_OPENSL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_OPENSL))
|
|
#define MA_HAS_OPENSL
|
|
#endif
|
|
#if defined(MA_SUPPORT_WEBAUDIO) && !defined(MA_NO_WEBAUDIO) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_WEBAUDIO))
|
|
#define MA_HAS_WEBAUDIO
|
|
#endif
|
|
#if defined(MA_SUPPORT_CUSTOM) && !defined(MA_NO_CUSTOM) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_CUSTOM))
|
|
#define MA_HAS_CUSTOM
|
|
#endif
|
|
#if defined(MA_SUPPORT_NULL) && !defined(MA_NO_NULL) && (!defined(MA_ENABLE_ONLY_SPECIFIC_BACKENDS) || defined(MA_ENABLE_NULL))
|
|
#define MA_HAS_NULL
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
ma_device_state_uninitialized = 0,
|
|
ma_device_state_stopped = 1,
|
|
ma_device_state_started = 2,
|
|
ma_device_state_starting = 3,
|
|
ma_device_state_stopping = 4
|
|
} ma_device_state;
|
|
|
|
MA_ATOMIC_SAFE_TYPE_DECL(i32, 4, device_state)
|
|
|
|
#ifdef MA_SUPPORT_WASAPI
|
|
typedef struct
|
|
{
|
|
void* lpVtbl;
|
|
ma_uint32 counter;
|
|
ma_device* pDevice;
|
|
} ma_IMMNotificationClient;
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
ma_backend_wasapi,
|
|
ma_backend_dsound,
|
|
ma_backend_winmm,
|
|
ma_backend_coreaudio,
|
|
ma_backend_sndio,
|
|
ma_backend_audio4,
|
|
ma_backend_oss,
|
|
ma_backend_pulseaudio,
|
|
ma_backend_alsa,
|
|
ma_backend_jack,
|
|
ma_backend_aaudio,
|
|
ma_backend_opensl,
|
|
ma_backend_webaudio,
|
|
ma_backend_custom,
|
|
ma_backend_null
|
|
} ma_backend;
|
|
|
|
#define MA_BACKEND_COUNT (ma_backend_null+1)
|
|
|
|
typedef struct
|
|
{
|
|
ma_bool32 noThread;
|
|
ma_uint32 jobQueueCapacity;
|
|
ma_uint32 jobQueueFlags;
|
|
} ma_device_job_thread_config;
|
|
|
|
MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void);
|
|
|
|
typedef struct
|
|
{
|
|
ma_thread thread;
|
|
ma_job_queue jobQueue;
|
|
ma_bool32 _hasThread;
|
|
} ma_device_job_thread;
|
|
|
|
MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread);
|
|
MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob);
|
|
MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob);
|
|
|
|
typedef enum
|
|
{
|
|
ma_device_notification_type_started,
|
|
ma_device_notification_type_stopped,
|
|
ma_device_notification_type_rerouted,
|
|
ma_device_notification_type_interruption_began,
|
|
ma_device_notification_type_interruption_ended,
|
|
ma_device_notification_type_unlocked
|
|
} ma_device_notification_type;
|
|
|
|
typedef struct
|
|
{
|
|
ma_device* pDevice;
|
|
ma_device_notification_type type;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
int _unused;
|
|
} started;
|
|
struct
|
|
{
|
|
int _unused;
|
|
} stopped;
|
|
struct
|
|
{
|
|
int _unused;
|
|
} rerouted;
|
|
struct
|
|
{
|
|
int _unused;
|
|
} interruption;
|
|
} data;
|
|
} ma_device_notification;
|
|
|
|
typedef void (* ma_device_notification_proc)(const ma_device_notification* pNotification);
|
|
|
|
typedef void (* ma_device_data_proc)(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
|
|
|
|
typedef void (* ma_stop_proc)(ma_device* pDevice);
|
|
|
|
typedef enum
|
|
{
|
|
ma_device_type_playback = 1,
|
|
ma_device_type_capture = 2,
|
|
ma_device_type_duplex = ma_device_type_playback | ma_device_type_capture,
|
|
ma_device_type_loopback = 4
|
|
} ma_device_type;
|
|
|
|
typedef enum
|
|
{
|
|
ma_share_mode_shared = 0,
|
|
ma_share_mode_exclusive
|
|
} ma_share_mode;
|
|
|
|
typedef enum
|
|
{
|
|
ma_ios_session_category_default = 0,
|
|
ma_ios_session_category_none,
|
|
ma_ios_session_category_ambient,
|
|
ma_ios_session_category_solo_ambient,
|
|
ma_ios_session_category_playback,
|
|
ma_ios_session_category_record,
|
|
ma_ios_session_category_play_and_record,
|
|
ma_ios_session_category_multi_route
|
|
} ma_ios_session_category;
|
|
|
|
typedef enum
|
|
{
|
|
ma_ios_session_category_option_mix_with_others = 0x01,
|
|
ma_ios_session_category_option_duck_others = 0x02,
|
|
ma_ios_session_category_option_allow_bluetooth = 0x04,
|
|
ma_ios_session_category_option_default_to_speaker = 0x08,
|
|
ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11,
|
|
ma_ios_session_category_option_allow_bluetooth_a2dp = 0x20,
|
|
ma_ios_session_category_option_allow_air_play = 0x40,
|
|
} ma_ios_session_category_option;
|
|
|
|
typedef enum
|
|
{
|
|
ma_opensl_stream_type_default = 0,
|
|
ma_opensl_stream_type_voice,
|
|
ma_opensl_stream_type_system,
|
|
ma_opensl_stream_type_ring,
|
|
ma_opensl_stream_type_media,
|
|
ma_opensl_stream_type_alarm,
|
|
ma_opensl_stream_type_notification
|
|
} ma_opensl_stream_type;
|
|
|
|
typedef enum
|
|
{
|
|
ma_opensl_recording_preset_default = 0,
|
|
ma_opensl_recording_preset_generic,
|
|
ma_opensl_recording_preset_camcorder,
|
|
ma_opensl_recording_preset_voice_recognition,
|
|
ma_opensl_recording_preset_voice_communication,
|
|
ma_opensl_recording_preset_voice_unprocessed
|
|
} ma_opensl_recording_preset;
|
|
|
|
typedef enum
|
|
{
|
|
ma_wasapi_usage_default = 0,
|
|
ma_wasapi_usage_games,
|
|
ma_wasapi_usage_pro_audio,
|
|
} ma_wasapi_usage;
|
|
|
|
typedef enum
|
|
{
|
|
ma_aaudio_usage_default = 0,
|
|
ma_aaudio_usage_media,
|
|
ma_aaudio_usage_voice_communication,
|
|
ma_aaudio_usage_voice_communication_signalling,
|
|
ma_aaudio_usage_alarm,
|
|
ma_aaudio_usage_notification,
|
|
ma_aaudio_usage_notification_ringtone,
|
|
ma_aaudio_usage_notification_event,
|
|
ma_aaudio_usage_assistance_accessibility,
|
|
ma_aaudio_usage_assistance_navigation_guidance,
|
|
ma_aaudio_usage_assistance_sonification,
|
|
ma_aaudio_usage_game,
|
|
ma_aaudio_usage_assitant,
|
|
ma_aaudio_usage_emergency,
|
|
ma_aaudio_usage_safety,
|
|
ma_aaudio_usage_vehicle_status,
|
|
ma_aaudio_usage_announcement
|
|
} ma_aaudio_usage;
|
|
|
|
typedef enum
|
|
{
|
|
ma_aaudio_content_type_default = 0,
|
|
ma_aaudio_content_type_speech,
|
|
ma_aaudio_content_type_music,
|
|
ma_aaudio_content_type_movie,
|
|
ma_aaudio_content_type_sonification
|
|
} ma_aaudio_content_type;
|
|
|
|
typedef enum
|
|
{
|
|
ma_aaudio_input_preset_default = 0,
|
|
ma_aaudio_input_preset_generic,
|
|
ma_aaudio_input_preset_camcorder,
|
|
ma_aaudio_input_preset_voice_recognition,
|
|
ma_aaudio_input_preset_voice_communication,
|
|
ma_aaudio_input_preset_unprocessed,
|
|
ma_aaudio_input_preset_voice_performance
|
|
} ma_aaudio_input_preset;
|
|
|
|
typedef enum
|
|
{
|
|
ma_aaudio_allow_capture_default = 0,
|
|
ma_aaudio_allow_capture_by_all,
|
|
ma_aaudio_allow_capture_by_system,
|
|
ma_aaudio_allow_capture_by_none
|
|
} ma_aaudio_allowed_capture_policy;
|
|
|
|
typedef union
|
|
{
|
|
ma_int64 counter;
|
|
double counterD;
|
|
} ma_timer;
|
|
|
|
typedef union
|
|
{
|
|
ma_wchar_win32 wasapi[64];
|
|
ma_uint8 dsound[16];
|
|
ma_uint32 winmm;
|
|
char alsa[256];
|
|
char pulse[256];
|
|
int jack;
|
|
char coreaudio[256];
|
|
char sndio[256];
|
|
char audio4[256];
|
|
char oss[64];
|
|
ma_int32 aaudio;
|
|
ma_uint32 opensl;
|
|
char webaudio[32];
|
|
union
|
|
{
|
|
int i;
|
|
char s[256];
|
|
void* p;
|
|
} custom;
|
|
int nullbackend;
|
|
} ma_device_id;
|
|
|
|
MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB);
|
|
|
|
typedef struct ma_context_config ma_context_config;
|
|
typedef struct ma_device_config ma_device_config;
|
|
typedef struct ma_backend_callbacks ma_backend_callbacks;
|
|
|
|
#define MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE (1U << 1)
|
|
|
|
#ifndef MA_MAX_DEVICE_NAME_LENGTH
|
|
#define MA_MAX_DEVICE_NAME_LENGTH 255
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
ma_device_id id;
|
|
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
|
|
ma_bool32 isDefault;
|
|
|
|
ma_uint32 nativeDataFormatCount;
|
|
struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 flags;
|
|
} nativeDataFormats[ 64];
|
|
} ma_device_info;
|
|
|
|
struct ma_device_config
|
|
{
|
|
ma_device_type deviceType;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 periodSizeInFrames;
|
|
ma_uint32 periodSizeInMilliseconds;
|
|
ma_uint32 periods;
|
|
ma_performance_profile performanceProfile;
|
|
ma_bool8 noPreSilencedOutputBuffer;
|
|
ma_bool8 noClip;
|
|
ma_bool8 noDisableDenormals;
|
|
ma_bool8 noFixedSizedCallback;
|
|
ma_device_data_proc dataCallback;
|
|
ma_device_notification_proc notificationCallback;
|
|
ma_stop_proc stopCallback;
|
|
void* pUserData;
|
|
ma_resampler_config resampling;
|
|
struct
|
|
{
|
|
const ma_device_id* pDeviceID;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_channel* pChannelMap;
|
|
ma_channel_mix_mode channelMixMode;
|
|
ma_bool32 calculateLFEFromSpatialChannels;
|
|
ma_share_mode shareMode;
|
|
} playback;
|
|
struct
|
|
{
|
|
const ma_device_id* pDeviceID;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_channel* pChannelMap;
|
|
ma_channel_mix_mode channelMixMode;
|
|
ma_bool32 calculateLFEFromSpatialChannels;
|
|
ma_share_mode shareMode;
|
|
} capture;
|
|
|
|
struct
|
|
{
|
|
ma_wasapi_usage usage;
|
|
ma_bool8 noAutoConvertSRC;
|
|
ma_bool8 noDefaultQualitySRC;
|
|
ma_bool8 noAutoStreamRouting;
|
|
ma_bool8 noHardwareOffloading;
|
|
ma_uint32 loopbackProcessID;
|
|
ma_bool8 loopbackProcessExclude;
|
|
} wasapi;
|
|
struct
|
|
{
|
|
ma_bool32 noMMap;
|
|
ma_bool32 noAutoFormat;
|
|
ma_bool32 noAutoChannels;
|
|
ma_bool32 noAutoResample;
|
|
} alsa;
|
|
struct
|
|
{
|
|
const char* pStreamNamePlayback;
|
|
const char* pStreamNameCapture;
|
|
int channelMap;
|
|
} pulse;
|
|
struct
|
|
{
|
|
ma_bool32 allowNominalSampleRateChange;
|
|
} coreaudio;
|
|
struct
|
|
{
|
|
ma_opensl_stream_type streamType;
|
|
ma_opensl_recording_preset recordingPreset;
|
|
ma_bool32 enableCompatibilityWorkarounds;
|
|
} opensl;
|
|
struct
|
|
{
|
|
ma_aaudio_usage usage;
|
|
ma_aaudio_content_type contentType;
|
|
ma_aaudio_input_preset inputPreset;
|
|
ma_aaudio_allowed_capture_policy allowedCapturePolicy;
|
|
ma_bool32 noAutoStartAfterReroute;
|
|
ma_bool32 enableCompatibilityWorkarounds;
|
|
ma_bool32 allowSetBufferCapacity;
|
|
} aaudio;
|
|
};
|
|
|
|
typedef ma_bool32 (* ma_enum_devices_callback_proc)(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData);
|
|
|
|
typedef struct
|
|
{
|
|
const ma_device_id* pDeviceID;
|
|
ma_share_mode shareMode;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_channel channelMap[MA_MAX_CHANNELS];
|
|
ma_uint32 periodSizeInFrames;
|
|
ma_uint32 periodSizeInMilliseconds;
|
|
ma_uint32 periodCount;
|
|
} ma_device_descriptor;
|
|
|
|
struct ma_backend_callbacks
|
|
{
|
|
ma_result (* onContextInit)(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks);
|
|
ma_result (* onContextUninit)(ma_context* pContext);
|
|
ma_result (* onContextEnumerateDevices)(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
|
|
ma_result (* onContextGetDeviceInfo)(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
|
|
ma_result (* onDeviceInit)(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture);
|
|
ma_result (* onDeviceUninit)(ma_device* pDevice);
|
|
ma_result (* onDeviceStart)(ma_device* pDevice);
|
|
ma_result (* onDeviceStop)(ma_device* pDevice);
|
|
ma_result (* onDeviceRead)(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead);
|
|
ma_result (* onDeviceWrite)(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten);
|
|
ma_result (* onDeviceDataLoop)(ma_device* pDevice);
|
|
ma_result (* onDeviceDataLoopWakeup)(ma_device* pDevice);
|
|
ma_result (* onDeviceGetInfo)(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);
|
|
};
|
|
|
|
struct ma_context_config
|
|
{
|
|
ma_log* pLog;
|
|
ma_thread_priority threadPriority;
|
|
size_t threadStackSize;
|
|
void* pUserData;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
struct
|
|
{
|
|
ma_handle hWnd;
|
|
} dsound;
|
|
struct
|
|
{
|
|
ma_bool32 useVerboseDeviceEnumeration;
|
|
} alsa;
|
|
struct
|
|
{
|
|
const char* pApplicationName;
|
|
const char* pServerName;
|
|
ma_bool32 tryAutoSpawn;
|
|
} pulse;
|
|
struct
|
|
{
|
|
ma_ios_session_category sessionCategory;
|
|
ma_uint32 sessionCategoryOptions;
|
|
ma_bool32 noAudioSessionActivate;
|
|
ma_bool32 noAudioSessionDeactivate;
|
|
} coreaudio;
|
|
struct
|
|
{
|
|
const char* pClientName;
|
|
ma_bool32 tryStartServer;
|
|
} jack;
|
|
ma_backend_callbacks custom;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
int code;
|
|
ma_event* pEvent;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
int _unused;
|
|
} quit;
|
|
struct
|
|
{
|
|
ma_device_type deviceType;
|
|
void* pAudioClient;
|
|
void** ppAudioClientService;
|
|
ma_result* pResult;
|
|
} createAudioClient;
|
|
struct
|
|
{
|
|
ma_device* pDevice;
|
|
ma_device_type deviceType;
|
|
} releaseAudioClient;
|
|
} data;
|
|
} ma_context_command__wasapi;
|
|
|
|
struct ma_context
|
|
{
|
|
ma_backend_callbacks callbacks;
|
|
ma_backend backend;
|
|
ma_log* pLog;
|
|
ma_log log;
|
|
ma_thread_priority threadPriority;
|
|
size_t threadStackSize;
|
|
void* pUserData;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_mutex deviceEnumLock;
|
|
ma_mutex deviceInfoLock;
|
|
ma_uint32 deviceInfoCapacity;
|
|
ma_uint32 playbackDeviceInfoCount;
|
|
ma_uint32 captureDeviceInfoCount;
|
|
ma_device_info* pDeviceInfos;
|
|
|
|
union
|
|
{
|
|
#ifdef MA_SUPPORT_WASAPI
|
|
struct
|
|
{
|
|
ma_thread commandThread;
|
|
ma_mutex commandLock;
|
|
ma_semaphore commandSem;
|
|
ma_uint32 commandIndex;
|
|
ma_uint32 commandCount;
|
|
ma_context_command__wasapi commands[4];
|
|
ma_handle hAvrt;
|
|
ma_proc AvSetMmThreadCharacteristicsA;
|
|
ma_proc AvRevertMmThreadcharacteristics;
|
|
ma_handle hMMDevapi;
|
|
ma_proc ActivateAudioInterfaceAsync;
|
|
} wasapi;
|
|
#endif
|
|
#ifdef MA_SUPPORT_DSOUND
|
|
struct
|
|
{
|
|
ma_handle hWnd;
|
|
ma_handle hDSoundDLL;
|
|
ma_proc DirectSoundCreate;
|
|
ma_proc DirectSoundEnumerateA;
|
|
ma_proc DirectSoundCaptureCreate;
|
|
ma_proc DirectSoundCaptureEnumerateA;
|
|
} dsound;
|
|
#endif
|
|
#ifdef MA_SUPPORT_WINMM
|
|
struct
|
|
{
|
|
ma_handle hWinMM;
|
|
ma_proc waveOutGetNumDevs;
|
|
ma_proc waveOutGetDevCapsA;
|
|
ma_proc waveOutOpen;
|
|
ma_proc waveOutClose;
|
|
ma_proc waveOutPrepareHeader;
|
|
ma_proc waveOutUnprepareHeader;
|
|
ma_proc waveOutWrite;
|
|
ma_proc waveOutReset;
|
|
ma_proc waveInGetNumDevs;
|
|
ma_proc waveInGetDevCapsA;
|
|
ma_proc waveInOpen;
|
|
ma_proc waveInClose;
|
|
ma_proc waveInPrepareHeader;
|
|
ma_proc waveInUnprepareHeader;
|
|
ma_proc waveInAddBuffer;
|
|
ma_proc waveInStart;
|
|
ma_proc waveInReset;
|
|
} winmm;
|
|
#endif
|
|
#ifdef MA_SUPPORT_ALSA
|
|
struct
|
|
{
|
|
ma_handle asoundSO;
|
|
ma_proc snd_pcm_open;
|
|
ma_proc snd_pcm_close;
|
|
ma_proc snd_pcm_hw_params_sizeof;
|
|
ma_proc snd_pcm_hw_params_any;
|
|
ma_proc snd_pcm_hw_params_set_format;
|
|
ma_proc snd_pcm_hw_params_set_format_first;
|
|
ma_proc snd_pcm_hw_params_get_format_mask;
|
|
ma_proc snd_pcm_hw_params_set_channels;
|
|
ma_proc snd_pcm_hw_params_set_channels_near;
|
|
ma_proc snd_pcm_hw_params_set_channels_minmax;
|
|
ma_proc snd_pcm_hw_params_set_rate_resample;
|
|
ma_proc snd_pcm_hw_params_set_rate;
|
|
ma_proc snd_pcm_hw_params_set_rate_near;
|
|
ma_proc snd_pcm_hw_params_set_rate_minmax;
|
|
ma_proc snd_pcm_hw_params_set_buffer_size_near;
|
|
ma_proc snd_pcm_hw_params_set_periods_near;
|
|
ma_proc snd_pcm_hw_params_set_access;
|
|
ma_proc snd_pcm_hw_params_get_format;
|
|
ma_proc snd_pcm_hw_params_get_channels;
|
|
ma_proc snd_pcm_hw_params_get_channels_min;
|
|
ma_proc snd_pcm_hw_params_get_channels_max;
|
|
ma_proc snd_pcm_hw_params_get_rate;
|
|
ma_proc snd_pcm_hw_params_get_rate_min;
|
|
ma_proc snd_pcm_hw_params_get_rate_max;
|
|
ma_proc snd_pcm_hw_params_get_buffer_size;
|
|
ma_proc snd_pcm_hw_params_get_periods;
|
|
ma_proc snd_pcm_hw_params_get_access;
|
|
ma_proc snd_pcm_hw_params_test_format;
|
|
ma_proc snd_pcm_hw_params_test_channels;
|
|
ma_proc snd_pcm_hw_params_test_rate;
|
|
ma_proc snd_pcm_hw_params;
|
|
ma_proc snd_pcm_sw_params_sizeof;
|
|
ma_proc snd_pcm_sw_params_current;
|
|
ma_proc snd_pcm_sw_params_get_boundary;
|
|
ma_proc snd_pcm_sw_params_set_avail_min;
|
|
ma_proc snd_pcm_sw_params_set_start_threshold;
|
|
ma_proc snd_pcm_sw_params_set_stop_threshold;
|
|
ma_proc snd_pcm_sw_params;
|
|
ma_proc snd_pcm_format_mask_sizeof;
|
|
ma_proc snd_pcm_format_mask_test;
|
|
ma_proc snd_pcm_get_chmap;
|
|
ma_proc snd_pcm_state;
|
|
ma_proc snd_pcm_prepare;
|
|
ma_proc snd_pcm_start;
|
|
ma_proc snd_pcm_drop;
|
|
ma_proc snd_pcm_drain;
|
|
ma_proc snd_pcm_reset;
|
|
ma_proc snd_device_name_hint;
|
|
ma_proc snd_device_name_get_hint;
|
|
ma_proc snd_card_get_index;
|
|
ma_proc snd_device_name_free_hint;
|
|
ma_proc snd_pcm_mmap_begin;
|
|
ma_proc snd_pcm_mmap_commit;
|
|
ma_proc snd_pcm_recover;
|
|
ma_proc snd_pcm_readi;
|
|
ma_proc snd_pcm_writei;
|
|
ma_proc snd_pcm_avail;
|
|
ma_proc snd_pcm_avail_update;
|
|
ma_proc snd_pcm_wait;
|
|
ma_proc snd_pcm_nonblock;
|
|
ma_proc snd_pcm_info;
|
|
ma_proc snd_pcm_info_sizeof;
|
|
ma_proc snd_pcm_info_get_name;
|
|
ma_proc snd_pcm_poll_descriptors;
|
|
ma_proc snd_pcm_poll_descriptors_count;
|
|
ma_proc snd_pcm_poll_descriptors_revents;
|
|
ma_proc snd_config_update_free_global;
|
|
|
|
ma_mutex internalDeviceEnumLock;
|
|
ma_bool32 useVerboseDeviceEnumeration;
|
|
} alsa;
|
|
#endif
|
|
#ifdef MA_SUPPORT_PULSEAUDIO
|
|
struct
|
|
{
|
|
ma_handle pulseSO;
|
|
ma_proc pa_mainloop_new;
|
|
ma_proc pa_mainloop_free;
|
|
ma_proc pa_mainloop_quit;
|
|
ma_proc pa_mainloop_get_api;
|
|
ma_proc pa_mainloop_iterate;
|
|
ma_proc pa_mainloop_wakeup;
|
|
ma_proc pa_threaded_mainloop_new;
|
|
ma_proc pa_threaded_mainloop_free;
|
|
ma_proc pa_threaded_mainloop_start;
|
|
ma_proc pa_threaded_mainloop_stop;
|
|
ma_proc pa_threaded_mainloop_lock;
|
|
ma_proc pa_threaded_mainloop_unlock;
|
|
ma_proc pa_threaded_mainloop_wait;
|
|
ma_proc pa_threaded_mainloop_signal;
|
|
ma_proc pa_threaded_mainloop_accept;
|
|
ma_proc pa_threaded_mainloop_get_retval;
|
|
ma_proc pa_threaded_mainloop_get_api;
|
|
ma_proc pa_threaded_mainloop_in_thread;
|
|
ma_proc pa_threaded_mainloop_set_name;
|
|
ma_proc pa_context_new;
|
|
ma_proc pa_context_unref;
|
|
ma_proc pa_context_connect;
|
|
ma_proc pa_context_disconnect;
|
|
ma_proc pa_context_set_state_callback;
|
|
ma_proc pa_context_get_state;
|
|
ma_proc pa_context_get_sink_info_list;
|
|
ma_proc pa_context_get_source_info_list;
|
|
ma_proc pa_context_get_sink_info_by_name;
|
|
ma_proc pa_context_get_source_info_by_name;
|
|
ma_proc pa_operation_unref;
|
|
ma_proc pa_operation_get_state;
|
|
ma_proc pa_channel_map_init_extend;
|
|
ma_proc pa_channel_map_valid;
|
|
ma_proc pa_channel_map_compatible;
|
|
ma_proc pa_stream_new;
|
|
ma_proc pa_stream_unref;
|
|
ma_proc pa_stream_connect_playback;
|
|
ma_proc pa_stream_connect_record;
|
|
ma_proc pa_stream_disconnect;
|
|
ma_proc pa_stream_get_state;
|
|
ma_proc pa_stream_get_sample_spec;
|
|
ma_proc pa_stream_get_channel_map;
|
|
ma_proc pa_stream_get_buffer_attr;
|
|
ma_proc pa_stream_set_buffer_attr;
|
|
ma_proc pa_stream_get_device_name;
|
|
ma_proc pa_stream_set_write_callback;
|
|
ma_proc pa_stream_set_read_callback;
|
|
ma_proc pa_stream_set_suspended_callback;
|
|
ma_proc pa_stream_set_moved_callback;
|
|
ma_proc pa_stream_is_suspended;
|
|
ma_proc pa_stream_flush;
|
|
ma_proc pa_stream_drain;
|
|
ma_proc pa_stream_is_corked;
|
|
ma_proc pa_stream_cork;
|
|
ma_proc pa_stream_trigger;
|
|
ma_proc pa_stream_begin_write;
|
|
ma_proc pa_stream_write;
|
|
ma_proc pa_stream_peek;
|
|
ma_proc pa_stream_drop;
|
|
ma_proc pa_stream_writable_size;
|
|
ma_proc pa_stream_readable_size;
|
|
|
|
ma_ptr pMainLoop;
|
|
ma_ptr pPulseContext;
|
|
char* pApplicationName;
|
|
char* pServerName;
|
|
} pulse;
|
|
#endif
|
|
#ifdef MA_SUPPORT_JACK
|
|
struct
|
|
{
|
|
ma_handle jackSO;
|
|
ma_proc jack_client_open;
|
|
ma_proc jack_client_close;
|
|
ma_proc jack_client_name_size;
|
|
ma_proc jack_set_process_callback;
|
|
ma_proc jack_set_buffer_size_callback;
|
|
ma_proc jack_on_shutdown;
|
|
ma_proc jack_get_sample_rate;
|
|
ma_proc jack_get_buffer_size;
|
|
ma_proc jack_get_ports;
|
|
ma_proc jack_activate;
|
|
ma_proc jack_deactivate;
|
|
ma_proc jack_connect;
|
|
ma_proc jack_port_register;
|
|
ma_proc jack_port_name;
|
|
ma_proc jack_port_get_buffer;
|
|
ma_proc jack_free;
|
|
|
|
char* pClientName;
|
|
ma_bool32 tryStartServer;
|
|
} jack;
|
|
#endif
|
|
#ifdef MA_SUPPORT_COREAUDIO
|
|
struct
|
|
{
|
|
ma_handle hCoreFoundation;
|
|
ma_proc CFStringGetCString;
|
|
ma_proc CFRelease;
|
|
|
|
ma_handle hCoreAudio;
|
|
ma_proc AudioObjectGetPropertyData;
|
|
ma_proc AudioObjectGetPropertyDataSize;
|
|
ma_proc AudioObjectSetPropertyData;
|
|
ma_proc AudioObjectAddPropertyListener;
|
|
ma_proc AudioObjectRemovePropertyListener;
|
|
|
|
ma_handle hAudioUnit;
|
|
ma_proc AudioComponentFindNext;
|
|
ma_proc AudioComponentInstanceDispose;
|
|
ma_proc AudioComponentInstanceNew;
|
|
ma_proc AudioOutputUnitStart;
|
|
ma_proc AudioOutputUnitStop;
|
|
ma_proc AudioUnitAddPropertyListener;
|
|
ma_proc AudioUnitGetPropertyInfo;
|
|
ma_proc AudioUnitGetProperty;
|
|
ma_proc AudioUnitSetProperty;
|
|
ma_proc AudioUnitInitialize;
|
|
ma_proc AudioUnitRender;
|
|
|
|
ma_ptr component;
|
|
ma_bool32 noAudioSessionDeactivate;
|
|
} coreaudio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_SNDIO
|
|
struct
|
|
{
|
|
ma_handle sndioSO;
|
|
ma_proc sio_open;
|
|
ma_proc sio_close;
|
|
ma_proc sio_setpar;
|
|
ma_proc sio_getpar;
|
|
ma_proc sio_getcap;
|
|
ma_proc sio_start;
|
|
ma_proc sio_stop;
|
|
ma_proc sio_read;
|
|
ma_proc sio_write;
|
|
ma_proc sio_onmove;
|
|
ma_proc sio_nfds;
|
|
ma_proc sio_pollfd;
|
|
ma_proc sio_revents;
|
|
ma_proc sio_eof;
|
|
ma_proc sio_setvol;
|
|
ma_proc sio_onvol;
|
|
ma_proc sio_initpar;
|
|
} sndio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_AUDIO4
|
|
struct
|
|
{
|
|
int _unused;
|
|
} audio4;
|
|
#endif
|
|
#ifdef MA_SUPPORT_OSS
|
|
struct
|
|
{
|
|
int versionMajor;
|
|
int versionMinor;
|
|
} oss;
|
|
#endif
|
|
#ifdef MA_SUPPORT_AAUDIO
|
|
struct
|
|
{
|
|
ma_handle hAAudio;
|
|
ma_proc AAudio_createStreamBuilder;
|
|
ma_proc AAudioStreamBuilder_delete;
|
|
ma_proc AAudioStreamBuilder_setDeviceId;
|
|
ma_proc AAudioStreamBuilder_setDirection;
|
|
ma_proc AAudioStreamBuilder_setSharingMode;
|
|
ma_proc AAudioStreamBuilder_setFormat;
|
|
ma_proc AAudioStreamBuilder_setChannelCount;
|
|
ma_proc AAudioStreamBuilder_setSampleRate;
|
|
ma_proc AAudioStreamBuilder_setBufferCapacityInFrames;
|
|
ma_proc AAudioStreamBuilder_setFramesPerDataCallback;
|
|
ma_proc AAudioStreamBuilder_setDataCallback;
|
|
ma_proc AAudioStreamBuilder_setErrorCallback;
|
|
ma_proc AAudioStreamBuilder_setPerformanceMode;
|
|
ma_proc AAudioStreamBuilder_setUsage;
|
|
ma_proc AAudioStreamBuilder_setContentType;
|
|
ma_proc AAudioStreamBuilder_setInputPreset;
|
|
ma_proc AAudioStreamBuilder_setAllowedCapturePolicy;
|
|
ma_proc AAudioStreamBuilder_openStream;
|
|
ma_proc AAudioStream_close;
|
|
ma_proc AAudioStream_getState;
|
|
ma_proc AAudioStream_waitForStateChange;
|
|
ma_proc AAudioStream_getFormat;
|
|
ma_proc AAudioStream_getChannelCount;
|
|
ma_proc AAudioStream_getSampleRate;
|
|
ma_proc AAudioStream_getBufferCapacityInFrames;
|
|
ma_proc AAudioStream_getFramesPerDataCallback;
|
|
ma_proc AAudioStream_getFramesPerBurst;
|
|
ma_proc AAudioStream_requestStart;
|
|
ma_proc AAudioStream_requestStop;
|
|
ma_device_job_thread jobThread;
|
|
} aaudio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_OPENSL
|
|
struct
|
|
{
|
|
ma_handle libOpenSLES;
|
|
ma_handle SL_IID_ENGINE;
|
|
ma_handle SL_IID_AUDIOIODEVICECAPABILITIES;
|
|
ma_handle SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
|
ma_handle SL_IID_RECORD;
|
|
ma_handle SL_IID_PLAY;
|
|
ma_handle SL_IID_OUTPUTMIX;
|
|
ma_handle SL_IID_ANDROIDCONFIGURATION;
|
|
ma_proc slCreateEngine;
|
|
} opensl;
|
|
#endif
|
|
#ifdef MA_SUPPORT_WEBAUDIO
|
|
struct
|
|
{
|
|
int _unused;
|
|
} webaudio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_NULL
|
|
struct
|
|
{
|
|
int _unused;
|
|
} null_backend;
|
|
#endif
|
|
};
|
|
|
|
union
|
|
{
|
|
#if defined(MA_WIN32)
|
|
struct
|
|
{
|
|
ma_handle hOle32DLL;
|
|
ma_proc CoInitialize;
|
|
ma_proc CoInitializeEx;
|
|
ma_proc CoUninitialize;
|
|
ma_proc CoCreateInstance;
|
|
ma_proc CoTaskMemFree;
|
|
ma_proc PropVariantClear;
|
|
ma_proc StringFromGUID2;
|
|
|
|
ma_handle hUser32DLL;
|
|
ma_proc GetForegroundWindow;
|
|
ma_proc GetDesktopWindow;
|
|
|
|
ma_handle hAdvapi32DLL;
|
|
ma_proc RegOpenKeyExA;
|
|
ma_proc RegCloseKey;
|
|
ma_proc RegQueryValueExA;
|
|
|
|
long CoInitializeResult;
|
|
} win32;
|
|
#endif
|
|
#ifdef MA_POSIX
|
|
struct
|
|
{
|
|
int _unused;
|
|
} posix;
|
|
#endif
|
|
int _unused;
|
|
};
|
|
};
|
|
|
|
struct ma_device
|
|
{
|
|
ma_context* pContext;
|
|
ma_device_type type;
|
|
ma_uint32 sampleRate;
|
|
ma_atomic_device_state state;
|
|
ma_device_data_proc onData;
|
|
ma_device_notification_proc onNotification;
|
|
ma_stop_proc onStop;
|
|
void* pUserData;
|
|
ma_mutex startStopLock;
|
|
ma_event wakeupEvent;
|
|
ma_event startEvent;
|
|
ma_event stopEvent;
|
|
ma_thread thread;
|
|
ma_result workResult;
|
|
ma_bool8 isOwnerOfContext;
|
|
ma_bool8 noPreSilencedOutputBuffer;
|
|
ma_bool8 noClip;
|
|
ma_bool8 noDisableDenormals;
|
|
ma_bool8 noFixedSizedCallback;
|
|
ma_atomic_float masterVolumeFactor;
|
|
ma_duplex_rb duplexRB;
|
|
struct
|
|
{
|
|
ma_resample_algorithm algorithm;
|
|
ma_resampling_backend_vtable* pBackendVTable;
|
|
void* pBackendUserData;
|
|
struct
|
|
{
|
|
ma_uint32 lpfOrder;
|
|
} linear;
|
|
} resampling;
|
|
struct
|
|
{
|
|
ma_device_id* pID;
|
|
ma_device_id id;
|
|
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
|
|
ma_share_mode shareMode;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_channel channelMap[MA_MAX_CHANNELS];
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
ma_uint32 internalSampleRate;
|
|
ma_channel internalChannelMap[MA_MAX_CHANNELS];
|
|
ma_uint32 internalPeriodSizeInFrames;
|
|
ma_uint32 internalPeriods;
|
|
ma_channel_mix_mode channelMixMode;
|
|
ma_bool32 calculateLFEFromSpatialChannels;
|
|
ma_data_converter converter;
|
|
void* pIntermediaryBuffer;
|
|
ma_uint32 intermediaryBufferCap;
|
|
ma_uint32 intermediaryBufferLen;
|
|
void* pInputCache;
|
|
ma_uint64 inputCacheCap;
|
|
ma_uint64 inputCacheConsumed;
|
|
ma_uint64 inputCacheRemaining;
|
|
} playback;
|
|
struct
|
|
{
|
|
ma_device_id* pID;
|
|
ma_device_id id;
|
|
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
|
|
ma_share_mode shareMode;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_channel channelMap[MA_MAX_CHANNELS];
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
ma_uint32 internalSampleRate;
|
|
ma_channel internalChannelMap[MA_MAX_CHANNELS];
|
|
ma_uint32 internalPeriodSizeInFrames;
|
|
ma_uint32 internalPeriods;
|
|
ma_channel_mix_mode channelMixMode;
|
|
ma_bool32 calculateLFEFromSpatialChannels;
|
|
ma_data_converter converter;
|
|
void* pIntermediaryBuffer;
|
|
ma_uint32 intermediaryBufferCap;
|
|
ma_uint32 intermediaryBufferLen;
|
|
} capture;
|
|
|
|
union
|
|
{
|
|
#ifdef MA_SUPPORT_WASAPI
|
|
struct
|
|
{
|
|
ma_ptr pAudioClientPlayback;
|
|
ma_ptr pAudioClientCapture;
|
|
ma_ptr pRenderClient;
|
|
ma_ptr pCaptureClient;
|
|
ma_ptr pDeviceEnumerator;
|
|
ma_IMMNotificationClient notificationClient;
|
|
ma_handle hEventPlayback;
|
|
ma_handle hEventCapture;
|
|
ma_uint32 actualBufferSizeInFramesPlayback;
|
|
ma_uint32 actualBufferSizeInFramesCapture;
|
|
ma_uint32 originalPeriodSizeInFrames;
|
|
ma_uint32 originalPeriodSizeInMilliseconds;
|
|
ma_uint32 originalPeriods;
|
|
ma_performance_profile originalPerformanceProfile;
|
|
ma_uint32 periodSizeInFramesPlayback;
|
|
ma_uint32 periodSizeInFramesCapture;
|
|
void* pMappedBufferCapture;
|
|
ma_uint32 mappedBufferCaptureCap;
|
|
ma_uint32 mappedBufferCaptureLen;
|
|
void* pMappedBufferPlayback;
|
|
ma_uint32 mappedBufferPlaybackCap;
|
|
ma_uint32 mappedBufferPlaybackLen;
|
|
ma_atomic_bool32 isStartedCapture;
|
|
ma_atomic_bool32 isStartedPlayback;
|
|
ma_uint32 loopbackProcessID;
|
|
ma_bool8 loopbackProcessExclude;
|
|
ma_bool8 noAutoConvertSRC;
|
|
ma_bool8 noDefaultQualitySRC;
|
|
ma_bool8 noHardwareOffloading;
|
|
ma_bool8 allowCaptureAutoStreamRouting;
|
|
ma_bool8 allowPlaybackAutoStreamRouting;
|
|
ma_bool8 isDetachedPlayback;
|
|
ma_bool8 isDetachedCapture;
|
|
ma_wasapi_usage usage;
|
|
void* hAvrtHandle;
|
|
ma_mutex rerouteLock;
|
|
} wasapi;
|
|
#endif
|
|
#ifdef MA_SUPPORT_DSOUND
|
|
struct
|
|
{
|
|
ma_ptr pPlayback;
|
|
ma_ptr pPlaybackPrimaryBuffer;
|
|
ma_ptr pPlaybackBuffer;
|
|
ma_ptr pCapture;
|
|
ma_ptr pCaptureBuffer;
|
|
} dsound;
|
|
#endif
|
|
#ifdef MA_SUPPORT_WINMM
|
|
struct
|
|
{
|
|
ma_handle hDevicePlayback;
|
|
ma_handle hDeviceCapture;
|
|
ma_handle hEventPlayback;
|
|
ma_handle hEventCapture;
|
|
ma_uint32 fragmentSizeInFrames;
|
|
ma_uint32 iNextHeaderPlayback;
|
|
ma_uint32 iNextHeaderCapture;
|
|
ma_uint32 headerFramesConsumedPlayback;
|
|
ma_uint32 headerFramesConsumedCapture;
|
|
ma_uint8* pWAVEHDRPlayback;
|
|
ma_uint8* pWAVEHDRCapture;
|
|
ma_uint8* pIntermediaryBufferPlayback;
|
|
ma_uint8* pIntermediaryBufferCapture;
|
|
ma_uint8* _pHeapData;
|
|
} winmm;
|
|
#endif
|
|
#ifdef MA_SUPPORT_ALSA
|
|
struct
|
|
{
|
|
ma_ptr pPCMPlayback;
|
|
ma_ptr pPCMCapture;
|
|
void* pPollDescriptorsPlayback;
|
|
void* pPollDescriptorsCapture;
|
|
int pollDescriptorCountPlayback;
|
|
int pollDescriptorCountCapture;
|
|
int wakeupfdPlayback;
|
|
int wakeupfdCapture;
|
|
ma_bool8 isUsingMMapPlayback;
|
|
ma_bool8 isUsingMMapCapture;
|
|
} alsa;
|
|
#endif
|
|
#ifdef MA_SUPPORT_PULSEAUDIO
|
|
struct
|
|
{
|
|
ma_ptr pMainLoop;
|
|
ma_ptr pPulseContext;
|
|
ma_ptr pStreamPlayback;
|
|
ma_ptr pStreamCapture;
|
|
} pulse;
|
|
#endif
|
|
#ifdef MA_SUPPORT_JACK
|
|
struct
|
|
{
|
|
ma_ptr pClient;
|
|
ma_ptr* ppPortsPlayback;
|
|
ma_ptr* ppPortsCapture;
|
|
float* pIntermediaryBufferPlayback;
|
|
float* pIntermediaryBufferCapture;
|
|
} jack;
|
|
#endif
|
|
#ifdef MA_SUPPORT_COREAUDIO
|
|
struct
|
|
{
|
|
ma_uint32 deviceObjectIDPlayback;
|
|
ma_uint32 deviceObjectIDCapture;
|
|
ma_ptr audioUnitPlayback;
|
|
ma_ptr audioUnitCapture;
|
|
ma_ptr pAudioBufferList;
|
|
ma_uint32 audioBufferCapInFrames;
|
|
ma_event stopEvent;
|
|
ma_uint32 originalPeriodSizeInFrames;
|
|
ma_uint32 originalPeriodSizeInMilliseconds;
|
|
ma_uint32 originalPeriods;
|
|
ma_performance_profile originalPerformanceProfile;
|
|
ma_bool32 isDefaultPlaybackDevice;
|
|
ma_bool32 isDefaultCaptureDevice;
|
|
ma_bool32 isSwitchingPlaybackDevice;
|
|
ma_bool32 isSwitchingCaptureDevice;
|
|
void* pNotificationHandler;
|
|
} coreaudio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_SNDIO
|
|
struct
|
|
{
|
|
ma_ptr handlePlayback;
|
|
ma_ptr handleCapture;
|
|
ma_bool32 isStartedPlayback;
|
|
ma_bool32 isStartedCapture;
|
|
} sndio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_AUDIO4
|
|
struct
|
|
{
|
|
int fdPlayback;
|
|
int fdCapture;
|
|
} audio4;
|
|
#endif
|
|
#ifdef MA_SUPPORT_OSS
|
|
struct
|
|
{
|
|
int fdPlayback;
|
|
int fdCapture;
|
|
} oss;
|
|
#endif
|
|
#ifdef MA_SUPPORT_AAUDIO
|
|
struct
|
|
{
|
|
ma_ptr pStreamPlayback;
|
|
ma_ptr pStreamCapture;
|
|
ma_mutex rerouteLock;
|
|
ma_atomic_bool32 isTearingDown;
|
|
ma_aaudio_usage usage;
|
|
ma_aaudio_content_type contentType;
|
|
ma_aaudio_input_preset inputPreset;
|
|
ma_aaudio_allowed_capture_policy allowedCapturePolicy;
|
|
ma_bool32 noAutoStartAfterReroute;
|
|
} aaudio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_OPENSL
|
|
struct
|
|
{
|
|
ma_ptr pOutputMixObj;
|
|
ma_ptr pOutputMix;
|
|
ma_ptr pAudioPlayerObj;
|
|
ma_ptr pAudioPlayer;
|
|
ma_ptr pAudioRecorderObj;
|
|
ma_ptr pAudioRecorder;
|
|
ma_ptr pBufferQueuePlayback;
|
|
ma_ptr pBufferQueueCapture;
|
|
ma_bool32 isDrainingCapture;
|
|
ma_bool32 isDrainingPlayback;
|
|
ma_uint32 currentBufferIndexPlayback;
|
|
ma_uint32 currentBufferIndexCapture;
|
|
ma_uint8* pBufferPlayback;
|
|
ma_uint8* pBufferCapture;
|
|
} opensl;
|
|
#endif
|
|
#ifdef MA_SUPPORT_WEBAUDIO
|
|
struct
|
|
{
|
|
int audioContext;
|
|
int audioWorklet;
|
|
float* pIntermediaryBuffer;
|
|
void* pStackBuffer;
|
|
ma_result initResult;
|
|
int deviceIndex;
|
|
} webaudio;
|
|
#endif
|
|
#ifdef MA_SUPPORT_NULL
|
|
struct
|
|
{
|
|
ma_thread deviceThread;
|
|
ma_event operationEvent;
|
|
ma_event operationCompletionEvent;
|
|
ma_semaphore operationSemaphore;
|
|
ma_uint32 operation;
|
|
ma_result operationResult;
|
|
ma_timer timer;
|
|
double priorRunTime;
|
|
ma_uint32 currentPeriodFramesRemainingPlayback;
|
|
ma_uint32 currentPeriodFramesRemainingCapture;
|
|
ma_uint64 lastProcessedFramePlayback;
|
|
ma_uint64 lastProcessedFrameCapture;
|
|
ma_atomic_bool32 isStarted;
|
|
} null_device;
|
|
#endif
|
|
};
|
|
};
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#pragma warning(pop)
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
MA_API ma_context_config ma_context_config_init(void);
|
|
|
|
MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext);
|
|
|
|
MA_API ma_result ma_context_uninit(ma_context* pContext);
|
|
|
|
MA_API size_t ma_context_sizeof(void);
|
|
|
|
MA_API ma_log* ma_context_get_log(ma_context* pContext);
|
|
|
|
MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData);
|
|
|
|
MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount);
|
|
|
|
MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo);
|
|
|
|
MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext);
|
|
|
|
MA_API ma_device_config ma_device_config_init(ma_device_type deviceType);
|
|
|
|
MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice);
|
|
|
|
MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice);
|
|
|
|
MA_API void ma_device_uninit(ma_device* pDevice);
|
|
|
|
MA_API ma_context* ma_device_get_context(ma_device* pDevice);
|
|
|
|
MA_API ma_log* ma_device_get_log(ma_device* pDevice);
|
|
|
|
MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo);
|
|
|
|
MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator);
|
|
|
|
MA_API ma_result ma_device_start(ma_device* pDevice);
|
|
|
|
MA_API ma_result ma_device_stop(ma_device* pDevice);
|
|
|
|
MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice);
|
|
|
|
MA_API ma_device_state ma_device_get_state(const ma_device* pDevice);
|
|
|
|
MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pPlaybackDescriptor, const ma_device_descriptor* pCaptureDescriptor);
|
|
|
|
MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume);
|
|
|
|
MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume);
|
|
|
|
MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB);
|
|
|
|
MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB);
|
|
|
|
MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount);
|
|
|
|
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile);
|
|
|
|
MA_API const char* ma_get_backend_name(ma_backend backend);
|
|
|
|
MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend);
|
|
|
|
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend);
|
|
|
|
MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount);
|
|
|
|
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend);
|
|
|
|
#endif
|
|
|
|
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate);
|
|
|
|
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate);
|
|
|
|
MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
|
|
|
|
MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
|
|
|
|
MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
|
|
MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels);
|
|
static MA_INLINE float* ma_offset_pcm_frames_ptr_f32(float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (float*)ma_offset_pcm_frames_ptr((void*)p, offsetInFrames, ma_format_f32, channels); }
|
|
static MA_INLINE const float* ma_offset_pcm_frames_const_ptr_f32(const float* p, ma_uint64 offsetInFrames, ma_uint32 channels) { return (const float*)ma_offset_pcm_frames_const_ptr((const void*)p, offsetInFrames, ma_format_f32, channels); }
|
|
|
|
MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count);
|
|
MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count);
|
|
MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count);
|
|
MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count);
|
|
MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count);
|
|
MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels);
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor);
|
|
|
|
MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor);
|
|
MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor);
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor);
|
|
MA_API void ma_apply_volume_factor_pcm_frames(void* pFrames, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor);
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains);
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume);
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume);
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume);
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume);
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume);
|
|
MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume);
|
|
|
|
MA_API float ma_volume_linear_to_db(float factor);
|
|
|
|
MA_API float ma_volume_db_to_linear(float gain);
|
|
|
|
MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume);
|
|
|
|
typedef void ma_vfs;
|
|
typedef ma_handle ma_vfs_file;
|
|
|
|
typedef enum
|
|
{
|
|
MA_OPEN_MODE_READ = 0x00000001,
|
|
MA_OPEN_MODE_WRITE = 0x00000002
|
|
} ma_open_mode_flags;
|
|
|
|
typedef enum
|
|
{
|
|
ma_seek_origin_start,
|
|
ma_seek_origin_current,
|
|
ma_seek_origin_end
|
|
} ma_seek_origin;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint64 sizeInBytes;
|
|
} ma_file_info;
|
|
|
|
typedef struct
|
|
{
|
|
ma_result (* onOpen) (ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
|
|
ma_result (* onOpenW)(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
|
|
ma_result (* onClose)(ma_vfs* pVFS, ma_vfs_file file);
|
|
ma_result (* onRead) (ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
|
|
ma_result (* onWrite)(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
|
|
ma_result (* onSeek) (ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
|
|
ma_result (* onTell) (ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
|
|
ma_result (* onInfo) (ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
|
|
} ma_vfs_callbacks;
|
|
|
|
MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
|
|
MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile);
|
|
MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file);
|
|
MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead);
|
|
MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten);
|
|
MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin);
|
|
MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor);
|
|
MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo);
|
|
MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_vfs_callbacks cb;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
} ma_default_vfs;
|
|
|
|
MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef ma_result (* ma_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
|
|
typedef ma_result (* ma_seek_proc)(void* pUserData, ma_int64 offset, ma_seek_origin origin);
|
|
typedef ma_result (* ma_tell_proc)(void* pUserData, ma_int64* pCursor);
|
|
|
|
#if !defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)
|
|
typedef enum
|
|
{
|
|
ma_encoding_format_unknown = 0,
|
|
ma_encoding_format_wav,
|
|
ma_encoding_format_flac,
|
|
ma_encoding_format_mp3,
|
|
ma_encoding_format_vorbis
|
|
} ma_encoding_format;
|
|
#endif
|
|
|
|
#ifndef MA_NO_DECODING
|
|
typedef struct ma_decoder ma_decoder;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format preferredFormat;
|
|
ma_uint32 seekPointCount;
|
|
} ma_decoding_backend_config;
|
|
|
|
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount);
|
|
|
|
typedef struct
|
|
{
|
|
ma_result (* onInit )(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 (* onInitFile )(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);
|
|
ma_result (* onInitFileW )(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);
|
|
ma_result (* onInitMemory)(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend);
|
|
void (* onUninit )(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
} ma_decoding_backend_vtable;
|
|
|
|
typedef ma_result (* ma_decoder_read_proc)(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead);
|
|
typedef ma_result (* ma_decoder_seek_proc)(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin);
|
|
typedef ma_result (* ma_decoder_tell_proc)(ma_decoder* pDecoder, ma_int64* pCursor);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_channel* pChannelMap;
|
|
ma_channel_mix_mode channelMixMode;
|
|
ma_dither_mode ditherMode;
|
|
ma_resampler_config resampling;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_encoding_format encodingFormat;
|
|
ma_uint32 seekPointCount;
|
|
ma_decoding_backend_vtable** ppCustomBackendVTables;
|
|
ma_uint32 customBackendCount;
|
|
void* pCustomBackendUserData;
|
|
} ma_decoder_config;
|
|
|
|
struct ma_decoder
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_data_source* pBackend;
|
|
const ma_decoding_backend_vtable* pBackendVTable;
|
|
void* pBackendUserData;
|
|
ma_decoder_read_proc onRead;
|
|
ma_decoder_seek_proc onSeek;
|
|
ma_decoder_tell_proc onTell;
|
|
void* pUserData;
|
|
ma_uint64 readPointerInPCMFrames;
|
|
ma_format outputFormat;
|
|
ma_uint32 outputChannels;
|
|
ma_uint32 outputSampleRate;
|
|
ma_data_converter converter;
|
|
void* pInputCache;
|
|
ma_uint64 inputCacheCap;
|
|
ma_uint64 inputCacheConsumed;
|
|
ma_uint64 inputCacheRemaining;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
ma_vfs* pVFS;
|
|
ma_vfs_file file;
|
|
} vfs;
|
|
struct
|
|
{
|
|
const ma_uint8* pData;
|
|
size_t dataSize;
|
|
size_t currentReadPos;
|
|
} memory;
|
|
} data;
|
|
};
|
|
|
|
MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate);
|
|
MA_API ma_decoder_config ma_decoder_config_init_default(void);
|
|
|
|
MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
|
|
MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
|
|
MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
|
|
MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
|
|
MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
|
|
MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder);
|
|
|
|
MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder);
|
|
|
|
MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
|
|
MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex);
|
|
|
|
MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
|
|
MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor);
|
|
|
|
MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength);
|
|
|
|
MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames);
|
|
|
|
MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
|
|
MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
|
|
MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut);
|
|
|
|
#endif
|
|
|
|
#ifndef MA_NO_ENCODING
|
|
typedef struct ma_encoder ma_encoder;
|
|
|
|
typedef ma_result (* ma_encoder_write_proc) (ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten);
|
|
typedef ma_result (* ma_encoder_seek_proc) (ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin);
|
|
typedef ma_result (* ma_encoder_init_proc) (ma_encoder* pEncoder);
|
|
typedef void (* ma_encoder_uninit_proc) (ma_encoder* pEncoder);
|
|
typedef ma_result (* ma_encoder_write_pcm_frames_proc)(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
|
|
|
|
typedef struct
|
|
{
|
|
ma_encoding_format encodingFormat;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
} ma_encoder_config;
|
|
|
|
MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
|
|
|
|
struct ma_encoder
|
|
{
|
|
ma_encoder_config config;
|
|
ma_encoder_write_proc onWrite;
|
|
ma_encoder_seek_proc onSeek;
|
|
ma_encoder_init_proc onInit;
|
|
ma_encoder_uninit_proc onUninit;
|
|
ma_encoder_write_pcm_frames_proc onWritePCMFrames;
|
|
void* pUserData;
|
|
void* pInternalEncoder;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
ma_vfs* pVFS;
|
|
ma_vfs_file file;
|
|
} vfs;
|
|
} data;
|
|
};
|
|
|
|
MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
|
|
MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
|
|
MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
|
|
MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
|
|
MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder);
|
|
MA_API void ma_encoder_uninit(ma_encoder* pEncoder);
|
|
MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten);
|
|
|
|
#endif
|
|
|
|
#ifndef MA_NO_GENERATION
|
|
typedef enum
|
|
{
|
|
ma_waveform_type_sine,
|
|
ma_waveform_type_square,
|
|
ma_waveform_type_triangle,
|
|
ma_waveform_type_sawtooth
|
|
} ma_waveform_type;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_waveform_type type;
|
|
double amplitude;
|
|
double frequency;
|
|
} ma_waveform_config;
|
|
|
|
MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_waveform_config config;
|
|
double advance;
|
|
double time;
|
|
} ma_waveform;
|
|
|
|
MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform);
|
|
MA_API void ma_waveform_uninit(ma_waveform* pWaveform);
|
|
MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude);
|
|
MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency);
|
|
MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type);
|
|
MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate);
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
double dutyCycle;
|
|
double amplitude;
|
|
double frequency;
|
|
} ma_pulsewave_config;
|
|
|
|
MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_waveform waveform;
|
|
ma_pulsewave_config config;
|
|
} ma_pulsewave;
|
|
|
|
MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform);
|
|
MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform);
|
|
MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude);
|
|
MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency);
|
|
MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate);
|
|
MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle);
|
|
|
|
typedef enum
|
|
{
|
|
ma_noise_type_white,
|
|
ma_noise_type_pink,
|
|
ma_noise_type_brownian
|
|
} ma_noise_type;
|
|
|
|
typedef struct
|
|
{
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_noise_type type;
|
|
ma_int32 seed;
|
|
double amplitude;
|
|
ma_bool32 duplicateChannels;
|
|
} ma_noise_config;
|
|
|
|
MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude);
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_noise_config config;
|
|
ma_lcg lcg;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
double** bin;
|
|
double* accumulation;
|
|
ma_uint32* counter;
|
|
} pink;
|
|
struct
|
|
{
|
|
double* accumulation;
|
|
} brownian;
|
|
} state;
|
|
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
} ma_noise;
|
|
|
|
MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise);
|
|
MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise);
|
|
MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude);
|
|
MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed);
|
|
MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type);
|
|
|
|
#endif
|
|
|
|
#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_NO_DECODING)
|
|
#define MA_NO_RESOURCE_MANAGER
|
|
#endif
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
typedef struct ma_resource_manager ma_resource_manager;
|
|
typedef struct ma_resource_manager_data_buffer_node ma_resource_manager_data_buffer_node;
|
|
typedef struct ma_resource_manager_data_buffer ma_resource_manager_data_buffer;
|
|
typedef struct ma_resource_manager_data_stream ma_resource_manager_data_stream;
|
|
typedef struct ma_resource_manager_data_source ma_resource_manager_data_source;
|
|
|
|
typedef enum
|
|
{
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM = 0x00000001,
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE = 0x00000002,
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC = 0x00000004,
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT = 0x00000008,
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH = 0x00000010,
|
|
MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING = 0x00000020
|
|
} ma_resource_manager_data_source_flags;
|
|
|
|
typedef struct
|
|
{
|
|
ma_async_notification* pNotification;
|
|
ma_fence* pFence;
|
|
} ma_resource_manager_pipeline_stage_notification;
|
|
|
|
typedef struct
|
|
{
|
|
ma_resource_manager_pipeline_stage_notification init;
|
|
ma_resource_manager_pipeline_stage_notification done;
|
|
} ma_resource_manager_pipeline_notifications;
|
|
|
|
MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void);
|
|
|
|
#if 1
|
|
#define ma_resource_manager_job ma_job
|
|
#define ma_resource_manager_job_init ma_job_init
|
|
#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_FLAG_NON_BLOCKING MA_JOB_QUEUE_FLAG_NON_BLOCKING
|
|
#define ma_resource_manager_job_queue_config ma_job_queue_config
|
|
#define ma_resource_manager_job_queue_config_init ma_job_queue_config_init
|
|
#define ma_resource_manager_job_queue ma_job_queue
|
|
#define ma_resource_manager_job_queue_get_heap_size ma_job_queue_get_heap_size
|
|
#define ma_resource_manager_job_queue_init_preallocated ma_job_queue_init_preallocated
|
|
#define ma_resource_manager_job_queue_init ma_job_queue_init
|
|
#define ma_resource_manager_job_queue_uninit ma_job_queue_uninit
|
|
#define ma_resource_manager_job_queue_post ma_job_queue_post
|
|
#define ma_resource_manager_job_queue_next ma_job_queue_next
|
|
#endif
|
|
|
|
#ifndef MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT
|
|
#define MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT 64
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING = 0x00000001,
|
|
|
|
MA_RESOURCE_MANAGER_FLAG_NO_THREADING = 0x00000002
|
|
} ma_resource_manager_flags;
|
|
|
|
typedef struct
|
|
{
|
|
const char* pFilePath;
|
|
const wchar_t* pFilePathW;
|
|
const ma_resource_manager_pipeline_notifications* pNotifications;
|
|
ma_uint64 initialSeekPointInPCMFrames;
|
|
ma_uint64 rangeBegInPCMFrames;
|
|
ma_uint64 rangeEndInPCMFrames;
|
|
ma_uint64 loopPointBegInPCMFrames;
|
|
ma_uint64 loopPointEndInPCMFrames;
|
|
ma_uint32 flags;
|
|
ma_bool32 isLooping;
|
|
} ma_resource_manager_data_source_config;
|
|
|
|
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void);
|
|
|
|
typedef enum
|
|
{
|
|
ma_resource_manager_data_supply_type_unknown = 0,
|
|
ma_resource_manager_data_supply_type_encoded,
|
|
ma_resource_manager_data_supply_type_decoded,
|
|
ma_resource_manager_data_supply_type_decoded_paged
|
|
} ma_resource_manager_data_supply_type;
|
|
|
|
typedef struct
|
|
{
|
|
MA_ATOMIC(4, ma_resource_manager_data_supply_type) type;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
const void* pData;
|
|
size_t sizeInBytes;
|
|
} encoded;
|
|
struct
|
|
{
|
|
const void* pData;
|
|
ma_uint64 totalFrameCount;
|
|
ma_uint64 decodedFrameCount;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
} decoded;
|
|
struct
|
|
{
|
|
ma_paged_audio_buffer_data data;
|
|
ma_uint64 decodedFrameCount;
|
|
ma_uint32 sampleRate;
|
|
} decodedPaged;
|
|
} backend;
|
|
} ma_resource_manager_data_supply;
|
|
|
|
struct ma_resource_manager_data_buffer_node
|
|
{
|
|
ma_uint32 hashedName32;
|
|
ma_uint32 refCount;
|
|
MA_ATOMIC(4, ma_result) result;
|
|
MA_ATOMIC(4, ma_uint32) executionCounter;
|
|
MA_ATOMIC(4, ma_uint32) executionPointer;
|
|
ma_bool32 isDataOwnedByResourceManager;
|
|
ma_resource_manager_data_supply data;
|
|
ma_resource_manager_data_buffer_node* pParent;
|
|
ma_resource_manager_data_buffer_node* pChildLo;
|
|
ma_resource_manager_data_buffer_node* pChildHi;
|
|
};
|
|
|
|
struct ma_resource_manager_data_buffer
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_buffer_node* pNode;
|
|
ma_uint32 flags;
|
|
MA_ATOMIC(4, ma_uint32) executionCounter;
|
|
MA_ATOMIC(4, ma_uint32) executionPointer;
|
|
ma_uint64 seekTargetInPCMFrames;
|
|
ma_bool32 seekToCursorOnNextRead;
|
|
MA_ATOMIC(4, ma_result) result;
|
|
MA_ATOMIC(4, ma_bool32) isLooping;
|
|
ma_atomic_bool32 isConnectorInitialized;
|
|
union
|
|
{
|
|
ma_decoder decoder;
|
|
ma_audio_buffer buffer;
|
|
ma_paged_audio_buffer pagedBuffer;
|
|
} connector;
|
|
};
|
|
|
|
struct ma_resource_manager_data_stream
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_uint32 flags;
|
|
ma_decoder decoder;
|
|
ma_bool32 isDecoderInitialized;
|
|
ma_uint64 totalLengthInPCMFrames;
|
|
ma_uint32 relativeCursor;
|
|
MA_ATOMIC(8, ma_uint64) absoluteCursor;
|
|
ma_uint32 currentPageIndex;
|
|
MA_ATOMIC(4, ma_uint32) executionCounter;
|
|
MA_ATOMIC(4, ma_uint32) executionPointer;
|
|
|
|
MA_ATOMIC(4, ma_bool32) isLooping;
|
|
|
|
void* pPageData;
|
|
MA_ATOMIC(4, ma_uint32) pageFrameCount[2];
|
|
|
|
MA_ATOMIC(4, ma_result) result;
|
|
MA_ATOMIC(4, ma_bool32) isDecoderAtEnd;
|
|
MA_ATOMIC(4, ma_bool32) isPageValid[2];
|
|
MA_ATOMIC(4, ma_bool32) seekCounter;
|
|
};
|
|
|
|
struct ma_resource_manager_data_source
|
|
{
|
|
union
|
|
{
|
|
ma_resource_manager_data_buffer buffer;
|
|
ma_resource_manager_data_stream stream;
|
|
} backend;
|
|
|
|
ma_uint32 flags;
|
|
MA_ATOMIC(4, ma_uint32) executionCounter;
|
|
MA_ATOMIC(4, ma_uint32) executionPointer;
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_log* pLog;
|
|
ma_format decodedFormat;
|
|
ma_uint32 decodedChannels;
|
|
ma_uint32 decodedSampleRate;
|
|
ma_uint32 jobThreadCount;
|
|
size_t jobThreadStackSize;
|
|
ma_uint32 jobQueueCapacity;
|
|
ma_uint32 flags;
|
|
ma_vfs* pVFS;
|
|
ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
|
|
ma_uint32 customDecodingBackendCount;
|
|
void* pCustomDecodingBackendUserData;
|
|
ma_resampler_config resampling;
|
|
} ma_resource_manager_config;
|
|
|
|
MA_API ma_resource_manager_config ma_resource_manager_config_init(void);
|
|
|
|
struct ma_resource_manager
|
|
{
|
|
ma_resource_manager_config config;
|
|
ma_resource_manager_data_buffer_node* pRootDataBufferNode;
|
|
#ifndef MA_NO_THREADING
|
|
ma_mutex dataBufferBSTLock;
|
|
ma_thread jobThreads[MA_RESOURCE_MANAGER_MAX_JOB_THREAD_COUNT];
|
|
#endif
|
|
ma_job_queue jobQueue;
|
|
ma_default_vfs defaultVFS;
|
|
ma_log log;
|
|
};
|
|
|
|
MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager);
|
|
MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager);
|
|
MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager);
|
|
|
|
MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags);
|
|
MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags);
|
|
MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
|
|
MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
|
|
MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes);
|
|
MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes);
|
|
MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath);
|
|
MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath);
|
|
MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName);
|
|
MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName);
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor);
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength);
|
|
MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping);
|
|
MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer);
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames);
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream);
|
|
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
|
|
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream);
|
|
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream);
|
|
MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor);
|
|
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength);
|
|
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream);
|
|
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping);
|
|
MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream);
|
|
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames);
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor);
|
|
MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength);
|
|
MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping);
|
|
MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource);
|
|
MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames);
|
|
|
|
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob);
|
|
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager);
|
|
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob);
|
|
MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob);
|
|
MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager);
|
|
#endif
|
|
|
|
#ifndef MA_NO_NODE_GRAPH
|
|
#ifndef MA_MAX_NODE_BUS_COUNT
|
|
#define MA_MAX_NODE_BUS_COUNT 254
|
|
#endif
|
|
|
|
#ifndef MA_MAX_NODE_LOCAL_BUS_COUNT
|
|
#define MA_MAX_NODE_LOCAL_BUS_COUNT 2
|
|
#endif
|
|
|
|
#define MA_NODE_BUS_COUNT_UNKNOWN 255
|
|
|
|
typedef struct
|
|
{
|
|
size_t offset;
|
|
size_t sizeInBytes;
|
|
unsigned char _data[1];
|
|
} ma_stack;
|
|
|
|
typedef struct ma_node_graph ma_node_graph;
|
|
typedef void ma_node;
|
|
|
|
typedef enum
|
|
{
|
|
MA_NODE_FLAG_PASSTHROUGH = 0x00000001,
|
|
MA_NODE_FLAG_CONTINUOUS_PROCESSING = 0x00000002,
|
|
MA_NODE_FLAG_ALLOW_NULL_INPUT = 0x00000004,
|
|
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES = 0x00000008,
|
|
MA_NODE_FLAG_SILENT_OUTPUT = 0x00000010
|
|
} ma_node_flags;
|
|
|
|
typedef enum
|
|
{
|
|
ma_node_state_started = 0,
|
|
ma_node_state_stopped = 1
|
|
} ma_node_state;
|
|
|
|
typedef struct
|
|
{
|
|
|
|
void (* onProcess)(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut);
|
|
|
|
ma_result (* onGetRequiredInputFrameCount)(ma_node* pNode, ma_uint32 outputFrameCount, ma_uint32* pInputFrameCount);
|
|
|
|
ma_uint8 inputBusCount;
|
|
|
|
ma_uint8 outputBusCount;
|
|
|
|
ma_uint32 flags;
|
|
} ma_node_vtable;
|
|
|
|
typedef struct
|
|
{
|
|
const ma_node_vtable* vtable;
|
|
ma_node_state initialState;
|
|
ma_uint32 inputBusCount;
|
|
ma_uint32 outputBusCount;
|
|
const ma_uint32* pInputChannels;
|
|
const ma_uint32* pOutputChannels;
|
|
} ma_node_config;
|
|
|
|
MA_API ma_node_config ma_node_config_init(void);
|
|
|
|
typedef struct ma_node_output_bus ma_node_output_bus;
|
|
struct ma_node_output_bus
|
|
{
|
|
ma_node* pNode;
|
|
ma_uint8 outputBusIndex;
|
|
ma_uint8 channels;
|
|
|
|
ma_uint8 inputNodeInputBusIndex;
|
|
MA_ATOMIC(4, ma_uint32) flags;
|
|
MA_ATOMIC(4, ma_uint32) refCount;
|
|
MA_ATOMIC(4, ma_bool32) isAttached;
|
|
MA_ATOMIC(4, ma_spinlock) lock;
|
|
MA_ATOMIC(4, float) volume;
|
|
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pNext;
|
|
MA_ATOMIC(MA_SIZEOF_PTR, ma_node_output_bus*) pPrev;
|
|
MA_ATOMIC(MA_SIZEOF_PTR, ma_node*) pInputNode;
|
|
};
|
|
|
|
typedef struct ma_node_input_bus ma_node_input_bus;
|
|
struct ma_node_input_bus
|
|
{
|
|
ma_node_output_bus head;
|
|
MA_ATOMIC(4, ma_uint32) nextCounter;
|
|
MA_ATOMIC(4, ma_spinlock) lock;
|
|
|
|
ma_uint8 channels;
|
|
};
|
|
|
|
typedef struct ma_node_base ma_node_base;
|
|
struct ma_node_base
|
|
{
|
|
ma_node_graph* pNodeGraph;
|
|
const ma_node_vtable* vtable;
|
|
ma_uint32 inputBusCount;
|
|
ma_uint32 outputBusCount;
|
|
ma_node_input_bus* pInputBuses;
|
|
ma_node_output_bus* pOutputBuses;
|
|
float* pCachedData;
|
|
ma_uint16 cachedDataCapInFramesPerBus;
|
|
|
|
ma_uint16 cachedFrameCountOut;
|
|
ma_uint16 cachedFrameCountIn;
|
|
ma_uint16 consumedFrameCountIn;
|
|
|
|
MA_ATOMIC(4, ma_node_state) state;
|
|
MA_ATOMIC(8, ma_uint64) stateTimes[2];
|
|
MA_ATOMIC(8, ma_uint64) localTime;
|
|
|
|
ma_node_input_bus _inputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
|
|
ma_node_output_bus _outputBuses[MA_MAX_NODE_LOCAL_BUS_COUNT];
|
|
void* _pHeap;
|
|
ma_bool32 _ownsHeap;
|
|
};
|
|
|
|
MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode);
|
|
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode);
|
|
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode);
|
|
MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode);
|
|
MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode);
|
|
MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex);
|
|
MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex);
|
|
MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex);
|
|
MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex);
|
|
MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode);
|
|
MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume);
|
|
MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex);
|
|
MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state);
|
|
MA_API ma_node_state ma_node_get_state(const ma_node* pNode);
|
|
MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime);
|
|
MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state);
|
|
MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime);
|
|
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd);
|
|
MA_API ma_uint64 ma_node_get_time(const ma_node* pNode);
|
|
MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 channels;
|
|
ma_uint32 processingSizeInFrames;
|
|
size_t preMixStackSizeInBytes;
|
|
} ma_node_graph_config;
|
|
|
|
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels);
|
|
|
|
struct ma_node_graph
|
|
{
|
|
ma_node_base base;
|
|
ma_node_base endpoint;
|
|
float* pProcessingCache;
|
|
ma_uint32 processingCacheFramesRemaining;
|
|
ma_uint32 processingSizeInFrames;
|
|
|
|
MA_ATOMIC(4, ma_bool32) isReading;
|
|
|
|
ma_stack* pPreMixStack;
|
|
};
|
|
|
|
MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph);
|
|
MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph);
|
|
MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph);
|
|
MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph);
|
|
MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime);
|
|
MA_API ma_uint32 ma_node_graph_get_processing_size_in_frames(const ma_node_graph* pNodeGraph);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_data_source* pDataSource;
|
|
} ma_data_source_node_config;
|
|
|
|
MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base base;
|
|
ma_data_source* pDataSource;
|
|
} ma_data_source_node;
|
|
|
|
MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode);
|
|
MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping);
|
|
MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_uint32 channels;
|
|
ma_uint32 outputBusCount;
|
|
} ma_splitter_node_config;
|
|
|
|
MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base base;
|
|
} ma_splitter_node;
|
|
|
|
MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode);
|
|
MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_biquad_config biquad;
|
|
} ma_biquad_node_config;
|
|
|
|
MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_biquad biquad;
|
|
} ma_biquad_node;
|
|
|
|
MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode);
|
|
MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode);
|
|
MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_lpf_config lpf;
|
|
} ma_lpf_node_config;
|
|
|
|
MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_lpf lpf;
|
|
} ma_lpf_node;
|
|
|
|
MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode);
|
|
MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode);
|
|
MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_hpf_config hpf;
|
|
} ma_hpf_node_config;
|
|
|
|
MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_hpf hpf;
|
|
} ma_hpf_node;
|
|
|
|
MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode);
|
|
MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode);
|
|
MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_bpf_config bpf;
|
|
} ma_bpf_node_config;
|
|
|
|
MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_bpf bpf;
|
|
} ma_bpf_node;
|
|
|
|
MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode);
|
|
MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode);
|
|
MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_notch_config notch;
|
|
} ma_notch_node_config;
|
|
|
|
MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_notch2 notch;
|
|
} ma_notch_node;
|
|
|
|
MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode);
|
|
MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode);
|
|
MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_peak_config peak;
|
|
} ma_peak_node_config;
|
|
|
|
MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_peak2 peak;
|
|
} ma_peak_node;
|
|
|
|
MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode);
|
|
MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode);
|
|
MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_loshelf_config loshelf;
|
|
} ma_loshelf_node_config;
|
|
|
|
MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_loshelf2 loshelf;
|
|
} ma_loshelf_node;
|
|
|
|
MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode);
|
|
MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode);
|
|
MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_hishelf_config hishelf;
|
|
} ma_hishelf_node_config;
|
|
|
|
MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_hishelf2 hishelf;
|
|
} ma_hishelf_node;
|
|
|
|
MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode);
|
|
MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode);
|
|
MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_config nodeConfig;
|
|
ma_delay_config delay;
|
|
} ma_delay_node_config;
|
|
|
|
MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_delay delay;
|
|
} ma_delay_node;
|
|
|
|
MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode);
|
|
MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value);
|
|
MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode);
|
|
MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value);
|
|
MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode);
|
|
MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value);
|
|
MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode);
|
|
#endif
|
|
|
|
#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
|
|
typedef struct ma_engine ma_engine;
|
|
typedef struct ma_sound ma_sound;
|
|
|
|
typedef enum
|
|
{
|
|
MA_SOUND_FLAG_STREAM = 0x00000001,
|
|
MA_SOUND_FLAG_DECODE = 0x00000002,
|
|
MA_SOUND_FLAG_ASYNC = 0x00000004,
|
|
MA_SOUND_FLAG_WAIT_INIT = 0x00000008,
|
|
MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010,
|
|
MA_SOUND_FLAG_LOOPING = 0x00000020,
|
|
|
|
MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000,
|
|
MA_SOUND_FLAG_NO_PITCH = 0x00002000,
|
|
MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000
|
|
} ma_sound_flags;
|
|
|
|
#ifndef MA_ENGINE_MAX_LISTENERS
|
|
#define MA_ENGINE_MAX_LISTENERS 4
|
|
#endif
|
|
|
|
#define MA_LISTENER_INDEX_CLOSEST ((ma_uint8)-1)
|
|
|
|
typedef enum
|
|
{
|
|
ma_engine_node_type_sound,
|
|
ma_engine_node_type_group
|
|
} ma_engine_node_type;
|
|
|
|
typedef struct
|
|
{
|
|
ma_engine* pEngine;
|
|
ma_engine_node_type type;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 volumeSmoothTimeInPCMFrames;
|
|
ma_mono_expansion_mode monoExpansionMode;
|
|
ma_bool8 isPitchDisabled;
|
|
ma_bool8 isSpatializationDisabled;
|
|
ma_uint8 pinnedListenerIndex;
|
|
ma_resampler_config resampling;
|
|
} ma_engine_node_config;
|
|
|
|
MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags);
|
|
|
|
typedef struct
|
|
{
|
|
ma_node_base baseNode;
|
|
ma_engine* pEngine;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 volumeSmoothTimeInPCMFrames;
|
|
ma_mono_expansion_mode monoExpansionMode;
|
|
ma_fader fader;
|
|
ma_resampler resampler;
|
|
ma_spatializer spatializer;
|
|
ma_panner panner;
|
|
ma_gainer volumeGainer;
|
|
ma_atomic_float volume;
|
|
MA_ATOMIC(4, float) pitch;
|
|
float oldPitch;
|
|
float oldDopplerPitch;
|
|
MA_ATOMIC(4, ma_bool32) isPitchDisabled;
|
|
MA_ATOMIC(4, ma_bool32) isSpatializationDisabled;
|
|
MA_ATOMIC(4, ma_uint32) pinnedListenerIndex;
|
|
|
|
struct
|
|
{
|
|
ma_atomic_float volumeBeg;
|
|
ma_atomic_float volumeEnd;
|
|
ma_atomic_uint64 fadeLengthInFrames;
|
|
ma_atomic_uint64 absoluteGlobalTimeInFrames;
|
|
} fadeSettings;
|
|
|
|
ma_bool8 _ownsHeap;
|
|
void* _pHeap;
|
|
} ma_engine_node;
|
|
|
|
MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes);
|
|
MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode);
|
|
MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode);
|
|
MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
|
|
#define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF
|
|
|
|
typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound);
|
|
|
|
typedef struct
|
|
{
|
|
const char* pFilePath;
|
|
const wchar_t* pFilePathW;
|
|
ma_data_source* pDataSource;
|
|
ma_node* pInitialAttachment;
|
|
ma_uint32 initialAttachmentInputBusIndex;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_mono_expansion_mode monoExpansionMode;
|
|
ma_uint32 flags;
|
|
ma_uint32 volumeSmoothTimeInPCMFrames;
|
|
ma_uint64 initialSeekPointInPCMFrames;
|
|
ma_uint64 rangeBegInPCMFrames;
|
|
ma_uint64 rangeEndInPCMFrames;
|
|
ma_uint64 loopPointBegInPCMFrames;
|
|
ma_uint64 loopPointEndInPCMFrames;
|
|
ma_sound_end_proc endCallback;
|
|
void* pEndCallbackUserData;
|
|
ma_resampler_config pitchResampling;
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
ma_resource_manager_pipeline_notifications initNotifications;
|
|
#endif
|
|
ma_fence* pDoneFence;
|
|
ma_bool32 isLooping;
|
|
} ma_sound_config;
|
|
|
|
MA_API ma_sound_config ma_sound_config_init(void);
|
|
MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine);
|
|
|
|
struct ma_sound
|
|
{
|
|
ma_engine_node engineNode;
|
|
ma_data_source* pDataSource;
|
|
MA_ATOMIC(8, ma_uint64) seekTarget;
|
|
MA_ATOMIC(4, ma_bool32) atEnd;
|
|
ma_sound_end_proc endCallback;
|
|
void* pEndCallbackUserData;
|
|
float* pProcessingCache;
|
|
ma_uint32 processingCacheFramesRemaining;
|
|
ma_uint32 processingCacheCap;
|
|
ma_bool8 ownsDataSource;
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
ma_resource_manager_data_source* pResourceManagerDataSource;
|
|
#endif
|
|
};
|
|
|
|
typedef struct ma_sound_inlined ma_sound_inlined;
|
|
struct ma_sound_inlined
|
|
{
|
|
ma_sound sound;
|
|
ma_sound_inlined* pNext;
|
|
ma_sound_inlined* pPrev;
|
|
};
|
|
|
|
typedef ma_sound_config ma_sound_group_config;
|
|
typedef ma_sound ma_sound_group;
|
|
|
|
MA_API ma_sound_group_config ma_sound_group_config_init(void);
|
|
MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine);
|
|
|
|
typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount);
|
|
|
|
typedef struct
|
|
{
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
ma_resource_manager* pResourceManager;
|
|
#endif
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
ma_context* pContext;
|
|
ma_device* pDevice;
|
|
ma_device_id* pPlaybackDeviceID;
|
|
ma_device_data_proc dataCallback;
|
|
ma_device_notification_proc notificationCallback;
|
|
#endif
|
|
ma_log* pLog;
|
|
ma_uint32 listenerCount;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 periodSizeInFrames;
|
|
ma_uint32 periodSizeInMilliseconds;
|
|
ma_uint32 gainSmoothTimeInFrames;
|
|
ma_uint32 gainSmoothTimeInMilliseconds;
|
|
ma_uint32 defaultVolumeSmoothTimeInPCMFrames;
|
|
ma_uint32 preMixStackSizeInBytes;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_bool32 noAutoStart;
|
|
ma_bool32 noDevice;
|
|
ma_mono_expansion_mode monoExpansionMode;
|
|
ma_vfs* pResourceManagerVFS;
|
|
ma_engine_process_proc onProcess;
|
|
void* pProcessUserData;
|
|
ma_resampler_config resourceManagerResampling;
|
|
ma_resampler_config pitchResampling;
|
|
} ma_engine_config;
|
|
|
|
MA_API ma_engine_config ma_engine_config_init(void);
|
|
|
|
struct ma_engine
|
|
{
|
|
ma_node_graph nodeGraph;
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
ma_resource_manager* pResourceManager;
|
|
#endif
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
ma_device* pDevice;
|
|
#endif
|
|
ma_log* pLog;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 listenerCount;
|
|
ma_spatializer_listener listeners[MA_ENGINE_MAX_LISTENERS];
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_bool8 ownsResourceManager;
|
|
ma_bool8 ownsDevice;
|
|
ma_spinlock inlinedSoundLock;
|
|
ma_sound_inlined* pInlinedSoundHead;
|
|
MA_ATOMIC(4, ma_uint32) inlinedSoundCount;
|
|
ma_uint32 gainSmoothTimeInFrames;
|
|
ma_uint32 defaultVolumeSmoothTimeInPCMFrames;
|
|
ma_mono_expansion_mode monoExpansionMode;
|
|
ma_engine_process_proc onProcess;
|
|
void* pProcessUserData;
|
|
ma_resampler_config pitchResamplingConfig;
|
|
};
|
|
|
|
MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine);
|
|
MA_API void ma_engine_uninit(ma_engine* pEngine);
|
|
MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine);
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine);
|
|
#endif
|
|
MA_API ma_device* ma_engine_get_device(ma_engine* pEngine);
|
|
MA_API ma_log* ma_engine_get_log(ma_engine* pEngine);
|
|
MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine);
|
|
MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine);
|
|
MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine);
|
|
MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime);
|
|
MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime);
|
|
MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine);
|
|
MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime);
|
|
MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine);
|
|
MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine);
|
|
|
|
MA_API ma_result ma_engine_start(ma_engine* pEngine);
|
|
MA_API ma_result ma_engine_stop(ma_engine* pEngine);
|
|
MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume);
|
|
MA_API float ma_engine_get_volume(ma_engine* pEngine);
|
|
MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB);
|
|
MA_API float ma_engine_get_gain_db(ma_engine* pEngine);
|
|
|
|
MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine);
|
|
MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ);
|
|
MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
|
|
MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex);
|
|
MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
|
|
MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex);
|
|
MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
|
|
MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex);
|
|
MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
|
|
MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
|
|
MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z);
|
|
MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex);
|
|
MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled);
|
|
MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex);
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex);
|
|
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup);
|
|
#endif
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);
|
|
MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound);
|
|
MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
|
|
#endif
|
|
MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound);
|
|
MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound);
|
|
MA_API void ma_sound_uninit(ma_sound* pSound);
|
|
MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound);
|
|
MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound);
|
|
MA_API ma_result ma_sound_start(ma_sound* pSound);
|
|
MA_API ma_result ma_sound_stop(ma_sound* pSound);
|
|
MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames);
|
|
MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames);
|
|
MA_API void ma_sound_reset_start_time(ma_sound* pSound);
|
|
MA_API void ma_sound_reset_stop_time(ma_sound* pSound);
|
|
MA_API void ma_sound_reset_fade(ma_sound* pSound);
|
|
MA_API void ma_sound_reset_stop_time_and_fade(ma_sound* pSound);
|
|
MA_API void ma_sound_set_volume(ma_sound* pSound, float volume);
|
|
MA_API float ma_sound_get_volume(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_pan(ma_sound* pSound, float pan);
|
|
MA_API float ma_sound_get_pan(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode);
|
|
MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch);
|
|
MA_API float ma_sound_get_pitch(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled);
|
|
MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex);
|
|
MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound);
|
|
MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound);
|
|
MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z);
|
|
MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z);
|
|
MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z);
|
|
MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel);
|
|
MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning);
|
|
MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff);
|
|
MA_API float ma_sound_get_rolloff(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain);
|
|
MA_API float ma_sound_get_min_gain(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain);
|
|
MA_API float ma_sound_get_max_gain(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance);
|
|
MA_API float ma_sound_get_min_distance(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance);
|
|
MA_API float ma_sound_get_max_distance(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
|
|
MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
|
|
MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor);
|
|
MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor);
|
|
MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
|
|
MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
|
|
MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames);
|
|
MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds);
|
|
MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
|
|
MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
|
|
MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames);
|
|
MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds);
|
|
MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames);
|
|
MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds);
|
|
MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound);
|
|
MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound);
|
|
MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound);
|
|
MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping);
|
|
MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound);
|
|
MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound);
|
|
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds);
|
|
MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor);
|
|
MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength);
|
|
MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor);
|
|
MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength);
|
|
MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData);
|
|
|
|
MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup);
|
|
MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_uninit(ma_sound_group* pGroup);
|
|
MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup);
|
|
MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup);
|
|
MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume);
|
|
MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan);
|
|
MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode);
|
|
MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch);
|
|
MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled);
|
|
MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex);
|
|
MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup);
|
|
MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup);
|
|
MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z);
|
|
MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z);
|
|
MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z);
|
|
MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel);
|
|
MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning);
|
|
MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff);
|
|
MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain);
|
|
MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain);
|
|
MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance);
|
|
MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance);
|
|
MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain);
|
|
MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain);
|
|
MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor);
|
|
MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor);
|
|
MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames);
|
|
MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds);
|
|
MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup);
|
|
MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
|
|
MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
|
|
MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames);
|
|
MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds);
|
|
MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup);
|
|
MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup);
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(Q_CREATOR_RUN) || defined(__INTELLISENSE__) || defined(__CDT_PARSER__)
|
|
#define MINIAUDIO_IMPLEMENTATION
|
|
#endif
|
|
|
|
#if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION)
|
|
#ifndef miniaudio_c
|
|
#define miniaudio_c
|
|
|
|
#include <assert.h>
|
|
#include <limits.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#if !defined(_MSC_VER) && !defined(__DMC__)
|
|
//#include <strings.h>
|
|
// For some reason strcasecmp wasn't available in my compiler
|
|
// https://stackoverflow.com/questions/31127260/strcasecmp-a-non-standard-function
|
|
int strcasecmp(const char *a, const char *b) {
|
|
int ca, cb;
|
|
do {
|
|
ca = * (unsigned char *)a;
|
|
cb = * (unsigned char *)b;
|
|
ca = tolower(toupper(ca));
|
|
cb = tolower(toupper(cb));
|
|
a++;
|
|
b++;
|
|
} while (ca == cb && ca != '\0');
|
|
return ca - cb;
|
|
}
|
|
#include <wchar.h>
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
#include <float.h>
|
|
#endif
|
|
|
|
#if defined(MA_WIN32)
|
|
#include <windows.h>
|
|
|
|
#ifndef STGM_READ
|
|
#define STGM_READ 0x00000000L
|
|
#endif
|
|
#ifndef CLSCTX_ALL
|
|
#define CLSCTX_ALL 23
|
|
#endif
|
|
|
|
typedef struct ma_IUnknown ma_IUnknown;
|
|
#endif
|
|
|
|
#if !defined(MA_WIN32)
|
|
#if !defined(MA_NO_THREADING)
|
|
#include <sched.h>
|
|
#include <pthread.h>
|
|
#endif
|
|
|
|
#include <sys/time.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#if defined(MA_XBOX_NXDK)
|
|
#include <stat.h>
|
|
#else
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef MA_EMSCRIPTEN
|
|
#include <emscripten/emscripten.h>
|
|
#endif
|
|
|
|
#if !defined(MA_64BIT) && !defined(MA_32BIT)
|
|
#ifdef _WIN32
|
|
#ifdef _WIN64
|
|
#define MA_64BIT
|
|
#else
|
|
#define MA_32BIT
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_64BIT) && !defined(MA_32BIT)
|
|
#ifdef __GNUC__
|
|
#ifdef __LP64__
|
|
#define MA_64BIT
|
|
#else
|
|
#define MA_32BIT
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_64BIT) && !defined(MA_32BIT)
|
|
#include <stdint.h>
|
|
#if INTPTR_MAX == INT64_MAX
|
|
#define MA_64BIT
|
|
#else
|
|
#define MA_32BIT
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__arm__) || defined(_M_ARM)
|
|
#define MA_ARM32
|
|
#endif
|
|
#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64)
|
|
#define MA_ARM64
|
|
#endif
|
|
|
|
#if defined(__x86_64__) || defined(_M_X64)
|
|
#define MA_X64
|
|
#elif defined(__i386) || defined(_M_IX86)
|
|
#define MA_X86
|
|
#elif defined(MA_ARM32) || defined(MA_ARM64)
|
|
#define MA_ARM
|
|
#endif
|
|
|
|
#if defined(MA_X64) || defined(MA_X86)
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#if _MSC_VER >= 1400 && !defined(MA_NO_SSE2)
|
|
#define MA_SUPPORT_SSE2
|
|
#endif
|
|
#if _MSC_VER >= 1700 && !defined(MA_NO_AVX2)
|
|
#define MA_SUPPORT_AVX2
|
|
#endif
|
|
#else
|
|
#if defined(__SSE2__) && !defined(MA_NO_SSE2)
|
|
#define MA_SUPPORT_SSE2
|
|
#endif
|
|
#if defined(__AVX2__) && !defined(MA_NO_AVX2)
|
|
#define MA_SUPPORT_AVX2
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
|
|
#if !defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && __has_include(<emmintrin.h>)
|
|
#define MA_SUPPORT_SSE2
|
|
#endif
|
|
#if !defined(MA_SUPPORT_AVX2) && !defined(MA_NO_AVX2) && __has_include(<immintrin.h>)
|
|
#define MA_SUPPORT_AVX2
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(MA_SUPPORT_AVX2) || defined(MA_SUPPORT_AVX)
|
|
#include <immintrin.h>
|
|
#elif defined(MA_SUPPORT_SSE2)
|
|
#include <emmintrin.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(MA_ARM)
|
|
#if !defined(MA_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
|
|
#define MA_SUPPORT_NEON
|
|
#include <arm_neon.h>
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4752)
|
|
#pragma warning(disable:4049)
|
|
#endif
|
|
|
|
#if defined(MA_X64) || defined(MA_X86)
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#if _MSC_VER >= 1400
|
|
#include <intrin.h>
|
|
static MA_INLINE void ma_cpuid(int info[4], int fid)
|
|
{
|
|
__cpuid(info, fid);
|
|
}
|
|
#else
|
|
#define MA_NO_CPUID
|
|
#endif
|
|
|
|
#if _MSC_VER >= 1600 && (defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219)
|
|
static MA_INLINE unsigned long long ma_xgetbv(int reg)
|
|
{
|
|
return _xgetbv(reg);
|
|
}
|
|
#else
|
|
#define MA_NO_XGETBV
|
|
#endif
|
|
#elif (defined(__GNUC__) || defined(__clang__)) && !defined(MA_ANDROID)
|
|
static MA_INLINE void ma_cpuid(int info[4], int fid)
|
|
{
|
|
|
|
#if defined(MA_X86) && defined(__PIC__)
|
|
__asm__ __volatile__ (
|
|
"xchg{l} {%%}ebx, %k1;"
|
|
"cpuid;"
|
|
"xchg{l} {%%}ebx, %k1;"
|
|
: "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
|
|
);
|
|
#else
|
|
__asm__ __volatile__ (
|
|
"cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
|
|
);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE ma_uint64 ma_xgetbv(int reg)
|
|
{
|
|
unsigned int hi;
|
|
unsigned int lo;
|
|
|
|
__asm__ __volatile__ (
|
|
"xgetbv" : "=a"(lo), "=d"(hi) : "c"(reg)
|
|
);
|
|
|
|
return ((ma_uint64)hi << 32) | (ma_uint64)lo;
|
|
}
|
|
#else
|
|
#define MA_NO_CPUID
|
|
#define MA_NO_XGETBV
|
|
#endif
|
|
#else
|
|
#define MA_NO_CPUID
|
|
#define MA_NO_XGETBV
|
|
#endif
|
|
|
|
static MA_INLINE ma_bool32 ma_has_sse2(void)
|
|
{
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
#if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_SSE2)
|
|
#if defined(MA_X64)
|
|
return MA_TRUE;
|
|
#elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
|
|
return MA_TRUE;
|
|
#else
|
|
#if defined(MA_NO_CPUID)
|
|
return MA_FALSE;
|
|
#else
|
|
int info[4];
|
|
ma_cpuid(info, 1);
|
|
return (info[3] & (1 << 26)) != 0;
|
|
#endif
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
static MA_INLINE ma_bool32 ma_has_avx()
|
|
{
|
|
#if defined(MA_SUPPORT_AVX)
|
|
#if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX)
|
|
#if defined(_AVX_) || defined(__AVX__)
|
|
return MA_TRUE;
|
|
#else
|
|
#if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
|
|
return MA_FALSE;
|
|
#else
|
|
int info[4];
|
|
ma_cpuid(info, 1);
|
|
if (((info[2] & (1 << 27)) != 0) && ((info[2] & (1 << 28)) != 0)) {
|
|
ma_uint64 xrc = ma_xgetbv(0);
|
|
if ((xrc & 0x06) == 0x06) {
|
|
return MA_TRUE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
#endif
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE ma_bool32 ma_has_avx2(void)
|
|
{
|
|
#if defined(MA_SUPPORT_AVX2)
|
|
#if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_NO_AVX2)
|
|
#if defined(_AVX2_) || defined(__AVX2__)
|
|
return MA_TRUE;
|
|
#else
|
|
#if defined(MA_NO_CPUID) || defined(MA_NO_XGETBV)
|
|
return MA_FALSE;
|
|
#else
|
|
int info1[4];
|
|
int info7[4];
|
|
ma_cpuid(info1, 1);
|
|
ma_cpuid(info7, 7);
|
|
if (((info1[2] & (1 << 27)) != 0) && ((info7[1] & (1 << 5)) != 0)) {
|
|
ma_uint64 xrc = ma_xgetbv(0);
|
|
if ((xrc & 0x06) == 0x06) {
|
|
return MA_TRUE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
#endif
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE ma_bool32 ma_has_neon(void)
|
|
{
|
|
#if defined(MA_SUPPORT_NEON)
|
|
#if defined(MA_ARM) && !defined(MA_NO_NEON)
|
|
#if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
|
|
#if defined(__has_builtin)
|
|
#define MA_COMPILER_HAS_BUILTIN(x) __has_builtin(x)
|
|
#else
|
|
#define MA_COMPILER_HAS_BUILTIN(x) 0
|
|
#endif
|
|
|
|
#ifndef MA_ASSUME
|
|
#if MA_COMPILER_HAS_BUILTIN(__builtin_assume)
|
|
#define MA_ASSUME(x) __builtin_assume(x)
|
|
#elif MA_COMPILER_HAS_BUILTIN(__builtin_unreachable)
|
|
#define MA_ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
|
|
#elif defined(_MSC_VER)
|
|
#define MA_ASSUME(x) __assume(x)
|
|
#else
|
|
#define MA_ASSUME(x) (void)(x)
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MA_RESTRICT
|
|
#if defined(__clang__) || defined(_MSC_VER) || (defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95)))
|
|
#define MA_RESTRICT __restrict
|
|
#else
|
|
#define MA_RESTRICT
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
|
#define MA_HAS_BYTESWAP16_INTRINSIC
|
|
#define MA_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_HAS_BYTESWAP64_INTRINSIC
|
|
#elif defined(__clang__)
|
|
#if MA_COMPILER_HAS_BUILTIN(__builtin_bswap16)
|
|
#define MA_HAS_BYTESWAP16_INTRINSIC
|
|
#endif
|
|
#if MA_COMPILER_HAS_BUILTIN(__builtin_bswap32)
|
|
#define MA_HAS_BYTESWAP32_INTRINSIC
|
|
#endif
|
|
#if MA_COMPILER_HAS_BUILTIN(__builtin_bswap64)
|
|
#define MA_HAS_BYTESWAP64_INTRINSIC
|
|
#endif
|
|
#elif defined(__GNUC__)
|
|
#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
|
|
#define MA_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_HAS_BYTESWAP64_INTRINSIC
|
|
#endif
|
|
#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
|
#define MA_HAS_BYTESWAP16_INTRINSIC
|
|
#endif
|
|
#endif
|
|
|
|
static MA_INLINE ma_bool32 ma_is_little_endian(void)
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
return MA_TRUE;
|
|
#else
|
|
int n = 1;
|
|
return (*(char*)&n) == 1;
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE ma_bool32 ma_is_big_endian(void)
|
|
{
|
|
return !ma_is_little_endian();
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_swap_endian_uint32(ma_uint32 n)
|
|
{
|
|
#ifdef MA_HAS_BYTESWAP32_INTRINSIC
|
|
#if defined(_MSC_VER)
|
|
return _byteswap_ulong(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT)
|
|
ma_uint32 r;
|
|
__asm__ __volatile__ (
|
|
#if defined(MA_64BIT)
|
|
"rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
|
|
#else
|
|
"rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
|
|
#endif
|
|
);
|
|
return r;
|
|
#else
|
|
return __builtin_bswap32(n);
|
|
#endif
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & 0xFF000000) >> 24) |
|
|
((n & 0x00FF0000) >> 8) |
|
|
((n & 0x0000FF00) << 8) |
|
|
((n & 0x000000FF) << 24);
|
|
#endif
|
|
}
|
|
|
|
#if !defined(MA_EMSCRIPTEN)
|
|
#ifdef MA_WIN32
|
|
static void ma_sleep__win32(ma_uint32 milliseconds)
|
|
{
|
|
Sleep((DWORD)milliseconds);
|
|
}
|
|
#endif
|
|
#ifdef MA_POSIX
|
|
static void ma_sleep__posix(ma_uint32 milliseconds)
|
|
{
|
|
#ifdef MA_EMSCRIPTEN
|
|
(void)milliseconds;
|
|
MA_ASSERT(MA_FALSE);
|
|
#else
|
|
#if (defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L) || defined(MA_SWITCH)
|
|
struct timespec ts;
|
|
ts.tv_sec = milliseconds / 1000;
|
|
ts.tv_nsec = milliseconds % 1000 * 1000000;
|
|
nanosleep(&ts, NULL);
|
|
#else
|
|
struct timeval tv;
|
|
tv.tv_sec = milliseconds / 1000;
|
|
tv.tv_usec = milliseconds % 1000 * 1000;
|
|
select(0, NULL, NULL, NULL, &tv);
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE void ma_sleep(ma_uint32 milliseconds)
|
|
{
|
|
#ifdef MA_WIN32
|
|
ma_sleep__win32(milliseconds);
|
|
#endif
|
|
#ifdef MA_POSIX
|
|
ma_sleep__posix(milliseconds);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE void ma_yield(void)
|
|
{
|
|
#if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
|
|
#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(__clang__)
|
|
#if _MSC_VER >= 1400
|
|
_mm_pause();
|
|
#else
|
|
#if defined(__DMC__)
|
|
__asm nop;
|
|
#else
|
|
__asm pause;
|
|
#endif
|
|
#endif
|
|
#else
|
|
__asm__ __volatile__ ("rep; nop");
|
|
#endif
|
|
#elif (defined(__arm__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7) || defined(_M_ARM64) || (defined(_M_ARM) && _M_ARM >= 7) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6T2__)
|
|
#if defined(_MSC_VER)
|
|
__yield();
|
|
#else
|
|
__asm__ __volatile__ ("yield");
|
|
#endif
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
#define MA_MM_DENORMALS_ZERO_MASK 0x0040
|
|
#define MA_MM_FLUSH_ZERO_MASK 0x8000
|
|
|
|
static MA_INLINE unsigned int ma_disable_denormals(void)
|
|
{
|
|
unsigned int prevState;
|
|
|
|
#if defined(_MSC_VER) && !defined(MA_XBOX_NXDK)
|
|
{
|
|
#if _MSC_VER <= 1200
|
|
{
|
|
prevState = _statusfp();
|
|
_controlfp(prevState | _DN_FLUSH, _MCW_DN);
|
|
}
|
|
#else
|
|
{
|
|
unsigned int unused;
|
|
_controlfp_s(&prevState, 0, 0);
|
|
_controlfp_s(&unused, prevState | _DN_FLUSH, _MCW_DN);
|
|
}
|
|
#endif
|
|
}
|
|
#elif defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
#if defined(MA_SUPPORT_SSE2) && defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__))
|
|
{
|
|
prevState = _mm_getcsr();
|
|
_mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK);
|
|
}
|
|
#else
|
|
{
|
|
prevState = 0;
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
prevState = 0;
|
|
}
|
|
#endif
|
|
|
|
return prevState;
|
|
}
|
|
|
|
static MA_INLINE void ma_restore_denormals(unsigned int prevState)
|
|
{
|
|
#if defined(_MSC_VER) && !defined(MA_XBOX_NXDK)
|
|
{
|
|
#if _MSC_VER <= 1200
|
|
{
|
|
_controlfp(prevState, _MCW_DN);
|
|
}
|
|
#else
|
|
{
|
|
unsigned int unused;
|
|
_controlfp_s(&unused, prevState, _MCW_DN);
|
|
}
|
|
#endif
|
|
}
|
|
#elif defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
#if defined(MA_SUPPORT_SSE2) && defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__))
|
|
{
|
|
_mm_setcsr(prevState);
|
|
}
|
|
#else
|
|
{
|
|
(void)prevState;
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
(void)prevState;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef MA_ANDROID
|
|
#include <sys/system_properties.h>
|
|
|
|
int ma_android_sdk_version()
|
|
{
|
|
char sdkVersion[PROP_VALUE_MAX + 1] = {0, };
|
|
if (__system_property_get("ro.build.version.sdk", sdkVersion)) {
|
|
return atoi(sdkVersion);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#ifndef MA_COINIT_VALUE
|
|
#define MA_COINIT_VALUE 0
|
|
#endif
|
|
|
|
#ifndef MA_FLT_MAX
|
|
#ifdef FLT_MAX
|
|
#define MA_FLT_MAX FLT_MAX
|
|
#else
|
|
#define MA_FLT_MAX 3.402823466e+38F
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MA_PI
|
|
#define MA_PI 3.14159265358979323846264f
|
|
#endif
|
|
#ifndef MA_PI_D
|
|
#define MA_PI_D 3.14159265358979323846264
|
|
#endif
|
|
#ifndef MA_TAU
|
|
#define MA_TAU 6.28318530717958647693f
|
|
#endif
|
|
#ifndef MA_TAU_D
|
|
#define MA_TAU_D 6.28318530717958647693
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_FORMAT
|
|
#define MA_DEFAULT_FORMAT ma_format_f32
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_CHANNELS
|
|
#define MA_DEFAULT_CHANNELS 2
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_SAMPLE_RATE
|
|
#define MA_DEFAULT_SAMPLE_RATE 48000
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_PERIODS
|
|
#define MA_DEFAULT_PERIODS 3
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY
|
|
#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY 10
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE
|
|
#define MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE 100
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_RESAMPLER_LPF_ORDER
|
|
#if MA_MAX_FILTER_ORDER >= 4
|
|
#define MA_DEFAULT_RESAMPLER_LPF_ORDER 4
|
|
#else
|
|
#define MA_DEFAULT_RESAMPLER_LPF_ORDER MA_MAX_FILTER_ORDER
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
#endif
|
|
|
|
static ma_uint32 g_maStandardSampleRatePriorities[] = {
|
|
(ma_uint32)ma_standard_sample_rate_48000,
|
|
(ma_uint32)ma_standard_sample_rate_44100,
|
|
|
|
(ma_uint32)ma_standard_sample_rate_32000,
|
|
(ma_uint32)ma_standard_sample_rate_24000,
|
|
(ma_uint32)ma_standard_sample_rate_22050,
|
|
|
|
(ma_uint32)ma_standard_sample_rate_88200,
|
|
(ma_uint32)ma_standard_sample_rate_96000,
|
|
(ma_uint32)ma_standard_sample_rate_176400,
|
|
(ma_uint32)ma_standard_sample_rate_192000,
|
|
|
|
(ma_uint32)ma_standard_sample_rate_16000,
|
|
(ma_uint32)ma_standard_sample_rate_11025,
|
|
(ma_uint32)ma_standard_sample_rate_8000,
|
|
|
|
(ma_uint32)ma_standard_sample_rate_352800,
|
|
(ma_uint32)ma_standard_sample_rate_384000
|
|
};
|
|
|
|
static MA_INLINE ma_bool32 ma_is_standard_sample_rate(ma_uint32 sampleRate)
|
|
{
|
|
ma_uint32 iSampleRate;
|
|
|
|
for (iSampleRate = 0; iSampleRate < sizeof(g_maStandardSampleRatePriorities) / sizeof(g_maStandardSampleRatePriorities[0]); iSampleRate += 1) {
|
|
if (g_maStandardSampleRatePriorities[iSampleRate] == sampleRate) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
static ma_format g_maFormatPriorities[] = {
|
|
ma_format_s16,
|
|
ma_format_f32,
|
|
|
|
ma_format_s32,
|
|
|
|
ma_format_s24,
|
|
|
|
ma_format_u8
|
|
};
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
MA_API void ma_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
|
|
{
|
|
if (pMajor) {
|
|
*pMajor = MA_VERSION_MAJOR;
|
|
}
|
|
|
|
if (pMinor) {
|
|
*pMinor = MA_VERSION_MINOR;
|
|
}
|
|
|
|
if (pRevision) {
|
|
*pRevision = MA_VERSION_REVISION;
|
|
}
|
|
}
|
|
|
|
MA_API const char* ma_version_string(void)
|
|
{
|
|
return MA_VERSION_STRING;
|
|
}
|
|
|
|
#ifndef MA_ASSERT
|
|
#define MA_ASSERT(condition) assert(condition)
|
|
#endif
|
|
|
|
#ifndef MA_MALLOC
|
|
#define MA_MALLOC(sz) malloc((sz))
|
|
#endif
|
|
#ifndef MA_REALLOC
|
|
#define MA_REALLOC(p, sz) realloc((p), (sz))
|
|
#endif
|
|
#ifndef MA_FREE
|
|
#define MA_FREE(p) free((p))
|
|
#endif
|
|
|
|
static MA_INLINE void ma_zero_memory_default(void* p, size_t sz)
|
|
{
|
|
if (p == NULL) {
|
|
MA_ASSERT(sz == 0);
|
|
return;
|
|
}
|
|
|
|
if (sz > 0) {
|
|
memset(p, 0, sz);
|
|
}
|
|
}
|
|
|
|
#ifndef MA_ZERO_MEMORY
|
|
#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz))
|
|
#endif
|
|
#ifndef MA_COPY_MEMORY
|
|
#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
|
|
#endif
|
|
#ifndef MA_MOVE_MEMORY
|
|
#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
|
|
#endif
|
|
|
|
#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p)))
|
|
|
|
#define ma_countof(x) (sizeof(x) / sizeof(x[0]))
|
|
#define ma_max(x, y) (((x) > (y)) ? (x) : (y))
|
|
#define ma_min(x, y) (((x) < (y)) ? (x) : (y))
|
|
#define ma_abs(x) (((x) > 0) ? (x) : -(x))
|
|
#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi)))
|
|
#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
|
|
#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1))
|
|
#define ma_align_64(x) ma_align(x, 8)
|
|
|
|
#define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels))
|
|
|
|
static MA_INLINE double ma_sind(double x)
|
|
{
|
|
return sin(x);
|
|
}
|
|
|
|
static MA_INLINE double ma_expd(double x)
|
|
{
|
|
return exp(x);
|
|
}
|
|
|
|
static MA_INLINE double ma_logd(double x)
|
|
{
|
|
return log(x);
|
|
}
|
|
|
|
static MA_INLINE double ma_powd(double x, double y)
|
|
{
|
|
return pow(x, y);
|
|
}
|
|
|
|
static MA_INLINE double ma_sqrtd(double x)
|
|
{
|
|
return sqrt(x);
|
|
}
|
|
|
|
static MA_INLINE float ma_rsqrtf(float x)
|
|
{
|
|
#if defined(MA_SUPPORT_SSE2) && !defined(MA_NO_SSE2) && (defined(MA_X64) || (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))
|
|
{
|
|
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
{
|
|
float result;
|
|
__asm__ __volatile__("rsqrtss %1, %0" : "=x"(result) : "x"(x));
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ps1(x)));
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
return 1 / (float)ma_sqrtd(x);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE float ma_sinf(float x)
|
|
{
|
|
return (float)ma_sind((float)x);
|
|
}
|
|
|
|
static MA_INLINE double ma_cosd(double x)
|
|
{
|
|
return ma_sind((MA_PI_D*0.5) - x);
|
|
}
|
|
|
|
static MA_INLINE float ma_cosf(float x)
|
|
{
|
|
return (float)ma_cosd((float)x);
|
|
}
|
|
|
|
static MA_INLINE double ma_log10d(double x)
|
|
{
|
|
return ma_logd(x) * 0.43429448190325182765;
|
|
}
|
|
|
|
static MA_INLINE float ma_powf(float x, float y)
|
|
{
|
|
return (float)ma_powd((double)x, (double)y);
|
|
}
|
|
|
|
static MA_INLINE float ma_log10f(float x)
|
|
{
|
|
return (float)ma_log10d((double)x);
|
|
}
|
|
|
|
static MA_INLINE double ma_degrees_to_radians(double degrees)
|
|
{
|
|
return degrees * 0.01745329252;
|
|
}
|
|
|
|
static MA_INLINE double ma_radians_to_degrees(double radians)
|
|
{
|
|
return radians * 57.295779512896;
|
|
}
|
|
|
|
static MA_INLINE float ma_degrees_to_radians_f(float degrees)
|
|
{
|
|
return degrees * 0.01745329252f;
|
|
}
|
|
|
|
static MA_INLINE float ma_radians_to_degrees_f(float radians)
|
|
{
|
|
return radians * 57.295779512896f;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src)
|
|
{
|
|
size_t i;
|
|
|
|
if (dst == 0) {
|
|
return 22;
|
|
}
|
|
if (dstSizeInBytes == 0) {
|
|
return 34;
|
|
}
|
|
if (src == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
for (i = 0; i < dstSizeInBytes && src[i] != '\0'; ++i) {
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
if (i < dstSizeInBytes) {
|
|
dst[i] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
dst[0] = '\0';
|
|
return 34;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src)
|
|
{
|
|
size_t i;
|
|
|
|
if (dst == 0) {
|
|
return 22;
|
|
}
|
|
if (dstCap == 0) {
|
|
return 34;
|
|
}
|
|
if (src == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
if (i < dstCap) {
|
|
dst[i] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
dst[0] = '\0';
|
|
return 34;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
|
|
{
|
|
size_t maxcount;
|
|
size_t i;
|
|
|
|
if (dst == 0) {
|
|
return 22;
|
|
}
|
|
if (dstSizeInBytes == 0) {
|
|
return 34;
|
|
}
|
|
if (src == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
maxcount = count;
|
|
if (count == ((size_t)-1) || count >= dstSizeInBytes) {
|
|
maxcount = dstSizeInBytes - 1;
|
|
}
|
|
|
|
for (i = 0; i < maxcount && src[i] != '\0'; ++i) {
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
if (src[i] == '\0' || i == count || count == ((size_t)-1)) {
|
|
dst[i] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
dst[0] = '\0';
|
|
return 34;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src)
|
|
{
|
|
char* dstorig;
|
|
|
|
if (dst == 0) {
|
|
return 22;
|
|
}
|
|
if (dstSizeInBytes == 0) {
|
|
return 34;
|
|
}
|
|
if (src == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
dstorig = dst;
|
|
|
|
while (dstSizeInBytes > 0 && dst[0] != '\0') {
|
|
dst += 1;
|
|
dstSizeInBytes -= 1;
|
|
}
|
|
|
|
if (dstSizeInBytes == 0) {
|
|
return 22;
|
|
}
|
|
|
|
while (dstSizeInBytes > 0 && src[0] != '\0') {
|
|
*dst++ = *src++;
|
|
dstSizeInBytes -= 1;
|
|
}
|
|
|
|
if (dstSizeInBytes > 0) {
|
|
dst[0] = '\0';
|
|
} else {
|
|
dstorig[0] = '\0';
|
|
return 34;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count)
|
|
{
|
|
char* dstorig;
|
|
|
|
if (dst == 0) {
|
|
return 22;
|
|
}
|
|
if (dstSizeInBytes == 0) {
|
|
return 34;
|
|
}
|
|
if (src == 0) {
|
|
return 22;
|
|
}
|
|
|
|
dstorig = dst;
|
|
|
|
while (dstSizeInBytes > 0 && dst[0] != '\0') {
|
|
dst += 1;
|
|
dstSizeInBytes -= 1;
|
|
}
|
|
|
|
if (dstSizeInBytes == 0) {
|
|
return 22;
|
|
}
|
|
|
|
if (count == ((size_t)-1)) {
|
|
count = dstSizeInBytes - 1;
|
|
}
|
|
|
|
while (dstSizeInBytes > 0 && src[0] != '\0' && count > 0) {
|
|
*dst++ = *src++;
|
|
dstSizeInBytes -= 1;
|
|
count -= 1;
|
|
}
|
|
|
|
if (dstSizeInBytes > 0) {
|
|
dst[0] = '\0';
|
|
} else {
|
|
dstorig[0] = '\0';
|
|
return 34;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix)
|
|
{
|
|
int sign;
|
|
unsigned int valueU;
|
|
char* dstEnd;
|
|
|
|
if (dst == NULL || dstSizeInBytes == 0) {
|
|
return 22;
|
|
}
|
|
if (radix < 2 || radix > 36) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
sign = (value < 0 && radix == 10) ? -1 : 1;
|
|
|
|
if (value < 0) {
|
|
valueU = -value;
|
|
} else {
|
|
valueU = value;
|
|
}
|
|
|
|
dstEnd = dst;
|
|
do
|
|
{
|
|
int remainder = valueU % radix;
|
|
if (remainder > 9) {
|
|
*dstEnd = (char)((remainder - 10) + 'a');
|
|
} else {
|
|
*dstEnd = (char)(remainder + '0');
|
|
}
|
|
|
|
dstEnd += 1;
|
|
dstSizeInBytes -= 1;
|
|
valueU /= radix;
|
|
} while (dstSizeInBytes > 0 && valueU > 0);
|
|
|
|
if (dstSizeInBytes == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
if (sign < 0) {
|
|
*dstEnd++ = '-';
|
|
dstSizeInBytes -= 1;
|
|
}
|
|
|
|
if (dstSizeInBytes == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
*dstEnd = '\0';
|
|
|
|
dstEnd -= 1;
|
|
while (dst < dstEnd) {
|
|
char temp = *dst;
|
|
*dst = *dstEnd;
|
|
*dstEnd = temp;
|
|
|
|
dst += 1;
|
|
dstEnd -= 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2)
|
|
{
|
|
if (str1 == str2) return 0;
|
|
|
|
if (str1 == NULL) return -1;
|
|
if (str2 == NULL) return 1;
|
|
|
|
for (;;) {
|
|
if (str1[0] == '\0') {
|
|
break;
|
|
}
|
|
if (str1[0] != str2[0]) {
|
|
break;
|
|
}
|
|
|
|
str1 += 1;
|
|
str2 += 1;
|
|
}
|
|
|
|
return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0];
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_wcscmp(const wchar_t* str1, const wchar_t* str2)
|
|
{
|
|
if (str1 == str2) return 0;
|
|
|
|
if (str1 == NULL) return -1;
|
|
if (str2 == NULL) return 1;
|
|
|
|
for (;;) {
|
|
if (str1[0] == L'\0') {
|
|
break;
|
|
}
|
|
if (str1[0] != str2[0]) {
|
|
break;
|
|
}
|
|
|
|
str1 += 1;
|
|
str2 += 1;
|
|
}
|
|
|
|
return ((unsigned short*)str1)[0] - ((unsigned short*)str2)[0];
|
|
}
|
|
|
|
MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB)
|
|
{
|
|
int result;
|
|
|
|
result = ma_strncpy_s(dst, dstSize, srcA, (size_t)-1);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_strncat_s(dst, dstSize, srcB, (size_t)-1);
|
|
if (result != 0) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE size_t ma_wcslen(const wchar_t* str)
|
|
{
|
|
const wchar_t* end;
|
|
|
|
if (str == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
end = str;
|
|
while (end[0] != '\0') {
|
|
end += 1;
|
|
}
|
|
|
|
return end - str;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
size_t sz;
|
|
char* dst;
|
|
|
|
if (src == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
sz = strlen(src)+1;
|
|
dst = (char*)ma_malloc(sz, pAllocationCallbacks);
|
|
if (dst == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ma_strcpy_s(dst, sz, src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
size_t sz = ma_wcslen(src)+1;
|
|
wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks);
|
|
if (dst == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ma_wcscpy_s(dst, sz, src);
|
|
|
|
return dst;
|
|
}
|
|
|
|
#include <errno.h>
|
|
static ma_result ma_result_from_errno(int e)
|
|
{
|
|
if (e == 0) {
|
|
return MA_SUCCESS;
|
|
}
|
|
#ifdef EPERM
|
|
else if (e == EPERM) { return MA_INVALID_OPERATION; }
|
|
#endif
|
|
#ifdef ENOENT
|
|
else if (e == ENOENT) { return MA_DOES_NOT_EXIST; }
|
|
#endif
|
|
#ifdef ESRCH
|
|
else if (e == ESRCH) { return MA_DOES_NOT_EXIST; }
|
|
#endif
|
|
#ifdef EINTR
|
|
else if (e == EINTR) { return MA_INTERRUPT; }
|
|
#endif
|
|
#ifdef EIO
|
|
else if (e == EIO) { return MA_IO_ERROR; }
|
|
#endif
|
|
#ifdef ENXIO
|
|
else if (e == ENXIO) { return MA_DOES_NOT_EXIST; }
|
|
#endif
|
|
#ifdef E2BIG
|
|
else if (e == E2BIG) { return MA_INVALID_ARGS; }
|
|
#endif
|
|
#ifdef ENOEXEC
|
|
else if (e == ENOEXEC) { return MA_INVALID_FILE; }
|
|
#endif
|
|
#ifdef EBADF
|
|
else if (e == EBADF) { return MA_INVALID_FILE; }
|
|
#endif
|
|
#ifdef ECHILD
|
|
else if (e == ECHILD) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EAGAIN
|
|
else if (e == EAGAIN) { return MA_UNAVAILABLE; }
|
|
#endif
|
|
#ifdef ENOMEM
|
|
else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; }
|
|
#endif
|
|
#ifdef EACCES
|
|
else if (e == EACCES) { return MA_ACCESS_DENIED; }
|
|
#endif
|
|
#ifdef EFAULT
|
|
else if (e == EFAULT) { return MA_BAD_ADDRESS; }
|
|
#endif
|
|
#ifdef ENOTBLK
|
|
else if (e == ENOTBLK) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBUSY
|
|
else if (e == EBUSY) { return MA_BUSY; }
|
|
#endif
|
|
#ifdef EEXIST
|
|
else if (e == EEXIST) { return MA_ALREADY_EXISTS; }
|
|
#endif
|
|
#ifdef EXDEV
|
|
else if (e == EXDEV) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENODEV
|
|
else if (e == ENODEV) { return MA_DOES_NOT_EXIST; }
|
|
#endif
|
|
#ifdef ENOTDIR
|
|
else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; }
|
|
#endif
|
|
#ifdef EISDIR
|
|
else if (e == EISDIR) { return MA_IS_DIRECTORY; }
|
|
#endif
|
|
#ifdef EINVAL
|
|
else if (e == EINVAL) { return MA_INVALID_ARGS; }
|
|
#endif
|
|
#ifdef ENFILE
|
|
else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; }
|
|
#endif
|
|
#ifdef EMFILE
|
|
else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; }
|
|
#endif
|
|
#ifdef ENOTTY
|
|
else if (e == ENOTTY) { return MA_INVALID_OPERATION; }
|
|
#endif
|
|
#ifdef ETXTBSY
|
|
else if (e == ETXTBSY) { return MA_BUSY; }
|
|
#endif
|
|
#ifdef EFBIG
|
|
else if (e == EFBIG) { return MA_TOO_BIG; }
|
|
#endif
|
|
#ifdef ENOSPC
|
|
else if (e == ENOSPC) { return MA_NO_SPACE; }
|
|
#endif
|
|
#ifdef ESPIPE
|
|
else if (e == ESPIPE) { return MA_BAD_SEEK; }
|
|
#endif
|
|
#ifdef EROFS
|
|
else if (e == EROFS) { return MA_ACCESS_DENIED; }
|
|
#endif
|
|
#ifdef EMLINK
|
|
else if (e == EMLINK) { return MA_TOO_MANY_LINKS; }
|
|
#endif
|
|
#ifdef EPIPE
|
|
else if (e == EPIPE) { return MA_BAD_PIPE; }
|
|
#endif
|
|
#ifdef EDOM
|
|
else if (e == EDOM) { return MA_OUT_OF_RANGE; }
|
|
#endif
|
|
#ifdef ERANGE
|
|
else if (e == ERANGE) { return MA_OUT_OF_RANGE; }
|
|
#endif
|
|
#ifdef EDEADLK
|
|
else if (e == EDEADLK) { return MA_DEADLOCK; }
|
|
#endif
|
|
#ifdef ENAMETOOLONG
|
|
else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; }
|
|
#endif
|
|
#ifdef ENOLCK
|
|
else if (e == ENOLCK) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOSYS
|
|
else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; }
|
|
#endif
|
|
#ifdef ENOTEMPTY
|
|
else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; }
|
|
#endif
|
|
#ifdef ELOOP
|
|
else if (e == ELOOP) { return MA_TOO_MANY_LINKS; }
|
|
#endif
|
|
#ifdef ENOMSG
|
|
else if (e == ENOMSG) { return MA_NO_MESSAGE; }
|
|
#endif
|
|
#ifdef EIDRM
|
|
else if (e == EIDRM) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ECHRNG
|
|
else if (e == ECHRNG) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EL2NSYNC
|
|
else if (e == EL2NSYNC) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EL3HLT
|
|
else if (e == EL3HLT) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EL3RST
|
|
else if (e == EL3RST) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ELNRNG
|
|
else if (e == ELNRNG) { return MA_OUT_OF_RANGE; }
|
|
#endif
|
|
#ifdef EUNATCH
|
|
else if (e == EUNATCH) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOCSI
|
|
else if (e == ENOCSI) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EL2HLT
|
|
else if (e == EL2HLT) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBADE
|
|
else if (e == EBADE) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBADR
|
|
else if (e == EBADR) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EXFULL
|
|
else if (e == EXFULL) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOANO
|
|
else if (e == ENOANO) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBADRQC
|
|
else if (e == EBADRQC) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBADSLT
|
|
else if (e == EBADSLT) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBFONT
|
|
else if (e == EBFONT) { return MA_INVALID_FILE; }
|
|
#endif
|
|
#ifdef ENOSTR
|
|
else if (e == ENOSTR) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENODATA
|
|
else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; }
|
|
#endif
|
|
#ifdef ETIME
|
|
else if (e == ETIME) { return MA_TIMEOUT; }
|
|
#endif
|
|
#ifdef ENOSR
|
|
else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; }
|
|
#endif
|
|
#ifdef ENONET
|
|
else if (e == ENONET) { return MA_NO_NETWORK; }
|
|
#endif
|
|
#ifdef ENOPKG
|
|
else if (e == ENOPKG) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EREMOTE
|
|
else if (e == EREMOTE) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOLINK
|
|
else if (e == ENOLINK) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EADV
|
|
else if (e == EADV) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ESRMNT
|
|
else if (e == ESRMNT) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ECOMM
|
|
else if (e == ECOMM) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EPROTO
|
|
else if (e == EPROTO) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EMULTIHOP
|
|
else if (e == EMULTIHOP) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EDOTDOT
|
|
else if (e == EDOTDOT) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EBADMSG
|
|
else if (e == EBADMSG) { return MA_BAD_MESSAGE; }
|
|
#endif
|
|
#ifdef EOVERFLOW
|
|
else if (e == EOVERFLOW) { return MA_TOO_BIG; }
|
|
#endif
|
|
#ifdef ENOTUNIQ
|
|
else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; }
|
|
#endif
|
|
#ifdef EBADFD
|
|
else if (e == EBADFD) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EREMCHG
|
|
else if (e == EREMCHG) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ELIBACC
|
|
else if (e == ELIBACC) { return MA_ACCESS_DENIED; }
|
|
#endif
|
|
#ifdef ELIBBAD
|
|
else if (e == ELIBBAD) { return MA_INVALID_FILE; }
|
|
#endif
|
|
#ifdef ELIBSCN
|
|
else if (e == ELIBSCN) { return MA_INVALID_FILE; }
|
|
#endif
|
|
#ifdef ELIBMAX
|
|
else if (e == ELIBMAX) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ELIBEXEC
|
|
else if (e == ELIBEXEC) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EILSEQ
|
|
else if (e == EILSEQ) { return MA_INVALID_DATA; }
|
|
#endif
|
|
#ifdef ERESTART
|
|
else if (e == ERESTART) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ESTRPIPE
|
|
else if (e == ESTRPIPE) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EUSERS
|
|
else if (e == EUSERS) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOTSOCK
|
|
else if (e == ENOTSOCK) { return MA_NOT_SOCKET; }
|
|
#endif
|
|
#ifdef EDESTADDRREQ
|
|
else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; }
|
|
#endif
|
|
#ifdef EMSGSIZE
|
|
else if (e == EMSGSIZE) { return MA_TOO_BIG; }
|
|
#endif
|
|
#ifdef EPROTOTYPE
|
|
else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; }
|
|
#endif
|
|
#ifdef ENOPROTOOPT
|
|
else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; }
|
|
#endif
|
|
#ifdef EPROTONOSUPPORT
|
|
else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; }
|
|
#endif
|
|
#ifdef ESOCKTNOSUPPORT
|
|
else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; }
|
|
#endif
|
|
#ifdef EOPNOTSUPP
|
|
else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; }
|
|
#endif
|
|
#ifdef EPFNOSUPPORT
|
|
else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; }
|
|
#endif
|
|
#ifdef EAFNOSUPPORT
|
|
else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; }
|
|
#endif
|
|
#ifdef EADDRINUSE
|
|
else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; }
|
|
#endif
|
|
#ifdef EADDRNOTAVAIL
|
|
else if (e == EADDRNOTAVAIL) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENETDOWN
|
|
else if (e == ENETDOWN) { return MA_NO_NETWORK; }
|
|
#endif
|
|
#ifdef ENETUNREACH
|
|
else if (e == ENETUNREACH) { return MA_NO_NETWORK; }
|
|
#endif
|
|
#ifdef ENETRESET
|
|
else if (e == ENETRESET) { return MA_NO_NETWORK; }
|
|
#endif
|
|
#ifdef ECONNABORTED
|
|
else if (e == ECONNABORTED) { return MA_NO_NETWORK; }
|
|
#endif
|
|
#ifdef ECONNRESET
|
|
else if (e == ECONNRESET) { return MA_CONNECTION_RESET; }
|
|
#endif
|
|
#ifdef ENOBUFS
|
|
else if (e == ENOBUFS) { return MA_NO_SPACE; }
|
|
#endif
|
|
#ifdef EISCONN
|
|
else if (e == EISCONN) { return MA_ALREADY_CONNECTED; }
|
|
#endif
|
|
#ifdef ENOTCONN
|
|
else if (e == ENOTCONN) { return MA_NOT_CONNECTED; }
|
|
#endif
|
|
#ifdef ESHUTDOWN
|
|
else if (e == ESHUTDOWN) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ETOOMANYREFS
|
|
else if (e == ETOOMANYREFS) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ETIMEDOUT
|
|
else if (e == ETIMEDOUT) { return MA_TIMEOUT; }
|
|
#endif
|
|
#ifdef ECONNREFUSED
|
|
else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; }
|
|
#endif
|
|
#ifdef EHOSTDOWN
|
|
else if (e == EHOSTDOWN) { return MA_NO_HOST; }
|
|
#endif
|
|
#ifdef EHOSTUNREACH
|
|
else if (e == EHOSTUNREACH) { return MA_NO_HOST; }
|
|
#endif
|
|
#ifdef EALREADY
|
|
else if (e == EALREADY) { return MA_IN_PROGRESS; }
|
|
#endif
|
|
#ifdef EINPROGRESS
|
|
else if (e == EINPROGRESS) { return MA_IN_PROGRESS; }
|
|
#endif
|
|
#ifdef ESTALE
|
|
else if (e == ESTALE) { return MA_INVALID_FILE; }
|
|
#endif
|
|
#ifdef EUCLEAN
|
|
else if (e == EUCLEAN) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOTNAM
|
|
else if (e == ENOTNAM) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENAVAIL
|
|
else if (e == ENAVAIL) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EISNAM
|
|
else if (e == EISNAM) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EREMOTEIO
|
|
else if (e == EREMOTEIO) { return MA_IO_ERROR; }
|
|
#endif
|
|
#ifdef EDQUOT
|
|
else if (e == EDQUOT) { return MA_NO_SPACE; }
|
|
#endif
|
|
#ifdef ENOMEDIUM
|
|
else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; }
|
|
#endif
|
|
#ifdef EMEDIUMTYPE
|
|
else if (e == EMEDIUMTYPE) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ECANCELED
|
|
else if (e == ECANCELED) { return MA_CANCELLED; }
|
|
#endif
|
|
#ifdef ENOKEY
|
|
else if (e == ENOKEY) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EKEYEXPIRED
|
|
else if (e == EKEYEXPIRED) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EKEYREVOKED
|
|
else if (e == EKEYREVOKED) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EKEYREJECTED
|
|
else if (e == EKEYREJECTED) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EOWNERDEAD
|
|
else if (e == EOWNERDEAD) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ENOTRECOVERABLE
|
|
else if (e == ENOTRECOVERABLE) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef ERFKILL
|
|
else if (e == ERFKILL) { return MA_ERROR; }
|
|
#endif
|
|
#ifdef EHWPOISON
|
|
else if (e == EHWPOISON) { return MA_ERROR; }
|
|
#endif
|
|
else {
|
|
return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
|
|
{
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
|
errno_t err;
|
|
#endif
|
|
|
|
if (ppFile != NULL) {
|
|
*ppFile = NULL;
|
|
}
|
|
|
|
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if (defined(_MSC_VER) && _MSC_VER >= 1400) && !defined(MA_XBOX_NXDK)
|
|
err = fopen_s(ppFile, pFilePath, pOpenMode);
|
|
if (err != 0) {
|
|
return ma_result_from_errno(err);
|
|
}
|
|
#else
|
|
#if defined(_WIN32) || defined(__APPLE__)
|
|
*ppFile = fopen(pFilePath, pOpenMode);
|
|
#else
|
|
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
|
|
*ppFile = fopen64(pFilePath, pOpenMode);
|
|
#else
|
|
*ppFile = fopen(pFilePath, pOpenMode);
|
|
#endif
|
|
#endif
|
|
if (*ppFile == NULL) {
|
|
ma_result result = ma_result_from_errno(errno);
|
|
if (result == MA_SUCCESS) {
|
|
result = MA_ERROR;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if defined(_WIN32) && !defined(MA_XBOX_NXDK)
|
|
#if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
|
|
#define MA_HAS_WFOPEN
|
|
#endif
|
|
#endif
|
|
|
|
MA_API ma_result ma_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (ppFile != NULL) {
|
|
*ppFile = NULL;
|
|
}
|
|
|
|
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_HAS_WFOPEN)
|
|
{
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
|
{
|
|
errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
|
|
if (err != 0) {
|
|
return ma_result_from_errno(err);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
*ppFile = _wfopen(pFilePath, pOpenMode);
|
|
if (*ppFile == NULL) {
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
(void)pAllocationCallbacks;
|
|
}
|
|
#elif !defined(MA_XBOX_NXDK) && !defined(MA_DOS)
|
|
{
|
|
mbstate_t mbs;
|
|
size_t lenMB;
|
|
const wchar_t* pFilePathTemp = pFilePath;
|
|
char* pFilePathMB = NULL;
|
|
char pOpenModeMB[32] = {0};
|
|
|
|
MA_ZERO_OBJECT(&mbs);
|
|
lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
|
|
if (lenMB == (size_t)-1) {
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
pFilePathMB = (char*)ma_malloc(lenMB + 1, pAllocationCallbacks);
|
|
if (pFilePathMB == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pFilePathTemp = pFilePath;
|
|
MA_ZERO_OBJECT(&mbs);
|
|
wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
|
|
|
|
{
|
|
size_t i = 0;
|
|
for (;;) {
|
|
if (pOpenMode[i] == 0) {
|
|
pOpenModeMB[i] = '\0';
|
|
break;
|
|
}
|
|
|
|
pOpenModeMB[i] = (char)pOpenMode[i];
|
|
i += 1;
|
|
}
|
|
}
|
|
|
|
*ppFile = fopen(pFilePathMB, pOpenModeMB);
|
|
|
|
ma_free(pFilePathMB, pAllocationCallbacks);
|
|
}
|
|
#else
|
|
{
|
|
*ppFile = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (*ppFile == NULL) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_copy_memory_64(void* dst, const void* src, ma_uint64 sizeInBytes)
|
|
{
|
|
#if MA_SIZE_MAX > 0xFFFFFFFF
|
|
MA_COPY_MEMORY(dst, src, (size_t)sizeInBytes);
|
|
#else
|
|
while (sizeInBytes > 0) {
|
|
ma_uint64 bytesToCopyNow = sizeInBytes;
|
|
if (bytesToCopyNow > MA_SIZE_MAX) {
|
|
bytesToCopyNow = MA_SIZE_MAX;
|
|
}
|
|
|
|
MA_COPY_MEMORY(dst, src, (size_t)bytesToCopyNow);
|
|
|
|
sizeInBytes -= bytesToCopyNow;
|
|
dst = ( void*)(( ma_uint8*)dst + bytesToCopyNow);
|
|
src = (const void*)((const ma_uint8*)src + bytesToCopyNow);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_zero_memory_64(void* dst, ma_uint64 sizeInBytes)
|
|
{
|
|
#if MA_SIZE_MAX > 0xFFFFFFFF
|
|
MA_ZERO_MEMORY(dst, (size_t)sizeInBytes);
|
|
#else
|
|
while (sizeInBytes > 0) {
|
|
ma_uint64 bytesToZeroNow = sizeInBytes;
|
|
if (bytesToZeroNow > MA_SIZE_MAX) {
|
|
bytesToZeroNow = MA_SIZE_MAX;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(dst, (size_t)bytesToZeroNow);
|
|
|
|
sizeInBytes -= bytesToZeroNow;
|
|
dst = (void*)((ma_uint8*)dst + bytesToZeroNow);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE unsigned int ma_next_power_of_2(unsigned int x)
|
|
{
|
|
x--;
|
|
x |= x >> 1;
|
|
x |= x >> 2;
|
|
x |= x >> 4;
|
|
x |= x >> 8;
|
|
x |= x >> 16;
|
|
x++;
|
|
|
|
return x;
|
|
}
|
|
|
|
static MA_INLINE unsigned int ma_prev_power_of_2(unsigned int x)
|
|
{
|
|
return ma_next_power_of_2(x) >> 1;
|
|
}
|
|
|
|
static MA_INLINE unsigned int ma_round_to_power_of_2(unsigned int x)
|
|
{
|
|
unsigned int prev = ma_prev_power_of_2(x);
|
|
unsigned int next = ma_next_power_of_2(x);
|
|
if ((next - x) > (x - prev)) {
|
|
return prev;
|
|
} else {
|
|
return next;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE unsigned int ma_count_set_bits(unsigned int x)
|
|
{
|
|
unsigned int count = 0;
|
|
while (x != 0) {
|
|
if (x & 1) {
|
|
count += 1;
|
|
}
|
|
|
|
x = x >> 1;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static void* ma__malloc_default(size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_MALLOC(sz);
|
|
}
|
|
|
|
static void* ma__realloc_default(void* p, size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_REALLOC(p, sz);
|
|
}
|
|
|
|
static void ma__free_default(void* p, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
MA_FREE(p);
|
|
}
|
|
|
|
static ma_allocation_callbacks ma_allocation_callbacks_init_default(void)
|
|
{
|
|
ma_allocation_callbacks callbacks;
|
|
callbacks.pUserData = NULL;
|
|
callbacks.onMalloc = ma__malloc_default;
|
|
callbacks.onRealloc = ma__realloc_default;
|
|
callbacks.onFree = ma__free_default;
|
|
|
|
return callbacks;
|
|
}
|
|
|
|
static ma_result ma_allocation_callbacks_init_copy(ma_allocation_callbacks* pDst, const ma_allocation_callbacks* pSrc)
|
|
{
|
|
if (pDst == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSrc == NULL) {
|
|
*pDst = ma_allocation_callbacks_init_default();
|
|
} else {
|
|
if (pSrc->pUserData == NULL && pSrc->onFree == NULL && pSrc->onMalloc == NULL && pSrc->onRealloc == NULL) {
|
|
*pDst = ma_allocation_callbacks_init_default();
|
|
} else {
|
|
if (pSrc->onFree == NULL || (pSrc->onMalloc == NULL && pSrc->onRealloc == NULL)) {
|
|
return MA_INVALID_ARGS;
|
|
} else {
|
|
*pDst = *pSrc;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#ifndef ma_va_copy
|
|
#if !defined(_MSC_VER) || _MSC_VER >= 1800
|
|
#if (defined(__GNUC__) && __GNUC__ < 3)
|
|
#define ma_va_copy(dst, src) ((dst) = (src))
|
|
#else
|
|
#define ma_va_copy(dst, src) va_copy((dst), (src))
|
|
#endif
|
|
#else
|
|
#define ma_va_copy(dst, src) ((dst) = (src))
|
|
#endif
|
|
#endif
|
|
|
|
MA_API const char* ma_log_level_to_string(ma_uint32 logLevel)
|
|
{
|
|
switch (logLevel)
|
|
{
|
|
case MA_LOG_LEVEL_DEBUG: return "DEBUG";
|
|
case MA_LOG_LEVEL_INFO: return "INFO";
|
|
case MA_LOG_LEVEL_WARNING: return "WARNING";
|
|
case MA_LOG_LEVEL_ERROR: return "ERROR";
|
|
default: return "ERROR";
|
|
}
|
|
}
|
|
|
|
#if defined(MA_DEBUG_OUTPUT)
|
|
#if defined(MA_ANDROID)
|
|
#include <android/log.h>
|
|
#endif
|
|
|
|
#ifndef MA_ANDROID_LOG_TAG
|
|
#define MA_ANDROID_LOG_TAG "miniaudio"
|
|
#endif
|
|
|
|
void ma_log_callback_debug(void* pUserData, ma_uint32 level, const char* pMessage)
|
|
{
|
|
(void)pUserData;
|
|
|
|
#if defined(MA_ANDROID)
|
|
{
|
|
__android_log_print(ANDROID_LOG_DEBUG, MA_ANDROID_LOG_TAG, "%s: %s", ma_log_level_to_string(level), pMessage);
|
|
}
|
|
#else
|
|
{
|
|
printf("%s: %s", ma_log_level_to_string(level), pMessage);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_log_callback ma_log_callback_init(ma_log_callback_proc onLog, void* pUserData)
|
|
{
|
|
ma_log_callback callback;
|
|
|
|
MA_ZERO_OBJECT(&callback);
|
|
callback.onLog = onLog;
|
|
callback.pUserData = pUserData;
|
|
|
|
return callback;
|
|
}
|
|
|
|
MA_API ma_result ma_log_init(const ma_allocation_callbacks* pAllocationCallbacks, ma_log* pLog)
|
|
{
|
|
if (pLog == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pLog);
|
|
ma_allocation_callbacks_init_copy(&pLog->allocationCallbacks, pAllocationCallbacks);
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_result result = ma_mutex_init(&pLog->lock);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_DEBUG_OUTPUT)
|
|
{
|
|
ma_log_register_callback(pLog, ma_log_callback_init(ma_log_callback_debug, NULL));
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_log_uninit(ma_log* pLog)
|
|
{
|
|
if (pLog == NULL) {
|
|
return;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
ma_mutex_uninit(&pLog->lock);
|
|
#endif
|
|
}
|
|
|
|
static void ma_log_lock(ma_log* pLog)
|
|
{
|
|
#ifndef MA_NO_THREADING
|
|
ma_mutex_lock(&pLog->lock);
|
|
#else
|
|
(void)pLog;
|
|
#endif
|
|
}
|
|
|
|
static void ma_log_unlock(ma_log* pLog)
|
|
{
|
|
#ifndef MA_NO_THREADING
|
|
ma_mutex_unlock(&pLog->lock);
|
|
#else
|
|
(void)pLog;
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_log_register_callback(ma_log* pLog, ma_log_callback callback)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
|
|
if (pLog == NULL || callback.onLog == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_log_lock(pLog);
|
|
{
|
|
if (pLog->callbackCount == ma_countof(pLog->callbacks)) {
|
|
result = MA_OUT_OF_MEMORY;
|
|
} else {
|
|
pLog->callbacks[pLog->callbackCount] = callback;
|
|
pLog->callbackCount += 1;
|
|
}
|
|
}
|
|
ma_log_unlock(pLog);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_log_unregister_callback(ma_log* pLog, ma_log_callback callback)
|
|
{
|
|
if (pLog == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_log_lock(pLog);
|
|
{
|
|
ma_uint32 iLog;
|
|
for (iLog = 0; iLog < pLog->callbackCount; ) {
|
|
if (pLog->callbacks[iLog].onLog == callback.onLog) {
|
|
ma_uint32 jLog;
|
|
for (jLog = iLog; jLog < pLog->callbackCount-1; jLog += 1) {
|
|
pLog->callbacks[jLog] = pLog->callbacks[jLog + 1];
|
|
}
|
|
|
|
pLog->callbackCount -= 1;
|
|
} else {
|
|
iLog += 1;
|
|
}
|
|
}
|
|
}
|
|
ma_log_unlock(pLog);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_log_post(ma_log* pLog, ma_uint32 level, const char* pMessage)
|
|
{
|
|
if (pLog == NULL || pMessage == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_log_lock(pLog);
|
|
{
|
|
ma_uint32 iLog;
|
|
for (iLog = 0; iLog < pLog->callbackCount; iLog += 1) {
|
|
if (pLog->callbacks[iLog].onLog) {
|
|
pLog->callbacks[iLog].onLog(pLog->callbacks[iLog].pUserData, level, pMessage);
|
|
}
|
|
}
|
|
}
|
|
ma_log_unlock(pLog);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
static int ma_vscprintf(const ma_allocation_callbacks* pAllocationCallbacks, const char* format, va_list args)
|
|
{
|
|
#if _MSC_VER > 1200
|
|
return _vscprintf(format, args);
|
|
#else
|
|
int result;
|
|
char* pTempBuffer = NULL;
|
|
size_t tempBufferCap = 1024;
|
|
|
|
if (format == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
for (;;) {
|
|
char* pNewTempBuffer = (char*)ma_realloc(pTempBuffer, tempBufferCap, pAllocationCallbacks);
|
|
if (pNewTempBuffer == NULL) {
|
|
ma_free(pTempBuffer, pAllocationCallbacks);
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
pTempBuffer = pNewTempBuffer;
|
|
|
|
result = _vsnprintf(pTempBuffer, tempBufferCap, format, args);
|
|
ma_free(pTempBuffer, NULL);
|
|
|
|
if (result != -1) {
|
|
break;
|
|
}
|
|
|
|
tempBufferCap *= 2;
|
|
}
|
|
|
|
return result;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat, va_list args)
|
|
{
|
|
if (pLog == NULL || pFormat == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || ((!defined(_MSC_VER) || _MSC_VER >= 1900) && !defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) || (defined(__cplusplus) && __cplusplus >= 201103L)
|
|
{
|
|
ma_result result;
|
|
int length;
|
|
char pFormattedMessageStack[1024];
|
|
char* pFormattedMessageHeap = NULL;
|
|
va_list args2;
|
|
|
|
ma_va_copy(args2, args);
|
|
{
|
|
length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args2);
|
|
}
|
|
va_end(args2);
|
|
|
|
if (length < 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if ((size_t)length < sizeof(pFormattedMessageStack)) {
|
|
result = ma_log_post(pLog, level, pFormattedMessageStack);
|
|
} else {
|
|
pFormattedMessageHeap = (char*)ma_malloc(length + 1, &pLog->allocationCallbacks);
|
|
if (pFormattedMessageHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
length = vsnprintf(pFormattedMessageHeap, length + 1, pFormat, args);
|
|
if (length < 0) {
|
|
ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
result = ma_log_post(pLog, level, pFormattedMessageHeap);
|
|
ma_free(pFormattedMessageHeap, &pLog->allocationCallbacks);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1200
|
|
{
|
|
ma_result result;
|
|
int formattedLen;
|
|
char* pFormattedMessage = NULL;
|
|
va_list args2;
|
|
|
|
ma_va_copy(args2, args);
|
|
{
|
|
formattedLen = ma_vscprintf(&pLog->allocationCallbacks, pFormat, args2);
|
|
}
|
|
va_end(args2);
|
|
|
|
if (formattedLen <= 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pFormattedMessage = (char*)ma_malloc(formattedLen + 1, &pLog->allocationCallbacks);
|
|
if (pFormattedMessage == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
#if _MSC_VER >= 1400
|
|
{
|
|
vsprintf_s(pFormattedMessage, formattedLen + 1, pFormat, args);
|
|
}
|
|
#else
|
|
{
|
|
vsprintf(pFormattedMessage, pFormat, args);
|
|
}
|
|
#endif
|
|
|
|
result = ma_log_post(pLog, level, pFormattedMessage);
|
|
ma_free(pFormattedMessage, &pLog->allocationCallbacks);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
(void)level;
|
|
(void)args;
|
|
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_log_postf(ma_log* pLog, ma_uint32 level, const char* pFormat, ...)
|
|
{
|
|
ma_result result;
|
|
va_list args;
|
|
|
|
if (pLog == NULL || pFormat == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
va_start(args, pFormat);
|
|
{
|
|
result = ma_log_postv(pLog, level, pFormat, args);
|
|
}
|
|
va_end(args);
|
|
|
|
return result;
|
|
}
|
|
|
|
static MA_INLINE ma_uint8 ma_clip_u8(ma_int32 x)
|
|
{
|
|
return (ma_uint8)(ma_clamp(x, -128, 127) + 128);
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_clip_s16(ma_int32 x)
|
|
{
|
|
return (ma_int16)ma_clamp(x, -32768, 32767);
|
|
}
|
|
|
|
static MA_INLINE ma_int64 ma_clip_s24(ma_int64 x)
|
|
{
|
|
return (ma_int64)ma_clamp(x, -8388608, 8388607);
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_clip_s32(ma_int64 x)
|
|
{
|
|
ma_int64 clipMin;
|
|
ma_int64 clipMax;
|
|
clipMin = -((ma_int64)2147483647 + 1);
|
|
clipMax = (ma_int64)2147483647;
|
|
|
|
return (ma_int32)ma_clamp(x, clipMin, clipMax);
|
|
}
|
|
|
|
static MA_INLINE float ma_clip_f32(float x)
|
|
{
|
|
if (x < -1) return -1;
|
|
if (x > +1) return +1;
|
|
return x;
|
|
}
|
|
|
|
static MA_INLINE float ma_mix_f32(float x, float y, float a)
|
|
{
|
|
return x*(1-a) + y*a;
|
|
}
|
|
static MA_INLINE float ma_mix_f32_fast(float x, float y, float a)
|
|
{
|
|
float r0 = (y - x);
|
|
float r1 = r0*a;
|
|
return x + r1;
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE __m128 ma_mix_f32_fast__sse2(__m128 x, __m128 y, __m128 a)
|
|
{
|
|
return _mm_add_ps(x, _mm_mul_ps(_mm_sub_ps(y, x), a));
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_AVX2)
|
|
static MA_INLINE __m256 ma_mix_f32_fast__avx2(__m256 x, __m256 y, __m256 a)
|
|
{
|
|
return _mm256_add_ps(x, _mm256_mul_ps(_mm256_sub_ps(y, x), a));
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE float32x4_t ma_mix_f32_fast__neon(float32x4_t x, float32x4_t y, float32x4_t a)
|
|
{
|
|
return vaddq_f32(x, vmulq_f32(vsubq_f32(y, x), a));
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE double ma_mix_f64(double x, double y, double a)
|
|
{
|
|
return x*(1-a) + y*a;
|
|
}
|
|
static MA_INLINE double ma_mix_f64_fast(double x, double y, double a)
|
|
{
|
|
return x + (y - x)*a;
|
|
}
|
|
|
|
static MA_INLINE float ma_scale_to_range_f32(float x, float lo, float hi)
|
|
{
|
|
return lo + x*(hi-lo);
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_gcf_u32(ma_uint32 a, ma_uint32 b)
|
|
{
|
|
for (;;) {
|
|
if (b == 0) {
|
|
break;
|
|
} else {
|
|
ma_uint32 t = a;
|
|
a = b;
|
|
b = t % a;
|
|
}
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
static ma_uint32 ma_ffs_32(ma_uint32 x)
|
|
{
|
|
ma_uint32 i;
|
|
|
|
for (i = 0; i < 32; i += 1) {
|
|
if ((x & (1U << i)) != 0) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_float_to_fixed_16(float x)
|
|
{
|
|
return (ma_int16)(x * (1 << 8));
|
|
}
|
|
|
|
#ifndef MA_DEFAULT_LCG_SEED
|
|
#define MA_DEFAULT_LCG_SEED 4321
|
|
#endif
|
|
|
|
#define MA_LCG_M 2147483647
|
|
#define MA_LCG_A 48271
|
|
#define MA_LCG_C 0
|
|
|
|
static ma_lcg g_maLCG = {MA_DEFAULT_LCG_SEED};
|
|
|
|
static MA_INLINE void ma_lcg_seed(ma_lcg* pLCG, ma_int32 seed)
|
|
{
|
|
MA_ASSERT(pLCG != NULL);
|
|
pLCG->state = seed;
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_lcg_rand_s32(ma_lcg* pLCG)
|
|
{
|
|
pLCG->state = (MA_LCG_A * pLCG->state + MA_LCG_C) % MA_LCG_M;
|
|
return pLCG->state;
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_lcg_rand_u32(ma_lcg* pLCG)
|
|
{
|
|
return (ma_uint32)ma_lcg_rand_s32(pLCG);
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_lcg_rand_s16(ma_lcg* pLCG)
|
|
{
|
|
return (ma_int16)(ma_lcg_rand_s32(pLCG) & 0xFFFF);
|
|
}
|
|
|
|
static MA_INLINE double ma_lcg_rand_f64(ma_lcg* pLCG)
|
|
{
|
|
return ma_lcg_rand_s32(pLCG) / (double)0x7FFFFFFF;
|
|
}
|
|
|
|
static MA_INLINE float ma_lcg_rand_f32(ma_lcg* pLCG)
|
|
{
|
|
return (float)ma_lcg_rand_f64(pLCG);
|
|
}
|
|
|
|
static MA_INLINE float ma_lcg_rand_range_f32(ma_lcg* pLCG, float lo, float hi)
|
|
{
|
|
return ma_scale_to_range_f32(ma_lcg_rand_f32(pLCG), lo, hi);
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_lcg_rand_range_s32(ma_lcg* pLCG, ma_int32 lo, ma_int32 hi)
|
|
{
|
|
if (lo == hi) {
|
|
return lo;
|
|
}
|
|
|
|
return lo + ma_lcg_rand_u32(pLCG) / (0xFFFFFFFF / (hi - lo + 1) + 1);
|
|
}
|
|
|
|
#if 0
|
|
static MA_INLINE void ma_seed(ma_int32 seed)
|
|
{
|
|
ma_lcg_seed(&g_maLCG, seed);
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_rand_s32(void)
|
|
{
|
|
return ma_lcg_rand_s32(&g_maLCG);
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_rand_u32(void)
|
|
{
|
|
return ma_lcg_rand_u32(&g_maLCG);
|
|
}
|
|
|
|
static MA_INLINE double ma_rand_f64(void)
|
|
{
|
|
return ma_lcg_rand_f64(&g_maLCG);
|
|
}
|
|
|
|
static MA_INLINE float ma_rand_f32(void)
|
|
{
|
|
return ma_lcg_rand_f32(&g_maLCG);
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE float ma_rand_range_f32(float lo, float hi)
|
|
{
|
|
return ma_lcg_rand_range_f32(&g_maLCG, lo, hi);
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_rand_range_s32(ma_int32 lo, ma_int32 hi)
|
|
{
|
|
return ma_lcg_rand_range_s32(&g_maLCG, lo, hi);
|
|
}
|
|
|
|
static MA_INLINE float ma_dither_f32_rectangle(float ditherMin, float ditherMax)
|
|
{
|
|
return ma_rand_range_f32(ditherMin, ditherMax);
|
|
}
|
|
|
|
static MA_INLINE float ma_dither_f32_triangle(float ditherMin, float ditherMax)
|
|
{
|
|
float a = ma_rand_range_f32(ditherMin, 0);
|
|
float b = ma_rand_range_f32(0, ditherMax);
|
|
return a + b;
|
|
}
|
|
|
|
static MA_INLINE float ma_dither_f32(ma_dither_mode ditherMode, float ditherMin, float ditherMax)
|
|
{
|
|
if (ditherMode == ma_dither_mode_rectangle) {
|
|
return ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
}
|
|
if (ditherMode == ma_dither_mode_triangle) {
|
|
return ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 ditherMin, ma_int32 ditherMax)
|
|
{
|
|
if (ditherMode == ma_dither_mode_rectangle) {
|
|
ma_int32 a = ma_rand_range_s32(ditherMin, ditherMax);
|
|
return a;
|
|
}
|
|
if (ditherMode == ma_dither_mode_triangle) {
|
|
ma_int32 a = ma_rand_range_s32(ditherMin, 0);
|
|
ma_int32 b = ma_rand_range_s32(0, ditherMax);
|
|
return a + b;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef ma_atomic_h
|
|
#define ma_atomic_h
|
|
#if defined(__cplusplus)
|
|
extern "C" {
|
|
#endif
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wlong-long"
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic ignored "-Wc++11-long-long"
|
|
#endif
|
|
#endif
|
|
typedef int ma_atomic_memory_order;
|
|
#if !defined(MA_ATOMIC_MODERN_MSVC) && \
|
|
!defined(MA_ATOMIC_LEGACY_MSVC) && \
|
|
!defined(MA_ATOMIC_LEGACY_MSVC_ASM) && \
|
|
!defined(MA_ATOMIC_MODERN_GCC) && \
|
|
!defined(MA_ATOMIC_LEGACY_GCC) && \
|
|
!defined(MA_ATOMIC_LEGACY_GCC_ASM)
|
|
#if defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__) || defined(__BORLANDC__)
|
|
#if (defined(_MSC_VER) && _MSC_VER > 1600)
|
|
#define MA_ATOMIC_MODERN_MSVC
|
|
#else
|
|
#if defined(MA_X64)
|
|
#define MA_ATOMIC_LEGACY_MSVC
|
|
#else
|
|
#define MA_ATOMIC_LEGACY_MSVC_ASM
|
|
#endif
|
|
#endif
|
|
#elif (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) || defined(__clang__)
|
|
#define MA_ATOMIC_MODERN_GCC
|
|
#else
|
|
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
|
|
#define MA_ATOMIC_LEGACY_GCC
|
|
#else
|
|
#define MA_ATOMIC_LEGACY_GCC_ASM
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC)
|
|
#include <intrin.h>
|
|
#define ma_atomic_memory_order_relaxed 1
|
|
#define ma_atomic_memory_order_consume 2
|
|
#define ma_atomic_memory_order_acquire 3
|
|
#define ma_atomic_memory_order_release 4
|
|
#define ma_atomic_memory_order_acq_rel 5
|
|
#define ma_atomic_memory_order_seq_cst 6
|
|
#define MA_ATOMIC_MSVC_ARM_INTRINSIC_NORETURN(dst, src, order, intrin, ma_atomicType, msvcType) \
|
|
switch (order) \
|
|
{ \
|
|
case ma_atomic_memory_order_relaxed: \
|
|
{ \
|
|
intrin##_nf((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
case ma_atomic_memory_order_consume: \
|
|
case ma_atomic_memory_order_acquire: \
|
|
{ \
|
|
intrin##_acq((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
case ma_atomic_memory_order_release: \
|
|
{ \
|
|
intrin##_rel((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
case ma_atomic_memory_order_acq_rel: \
|
|
case ma_atomic_memory_order_seq_cst: \
|
|
default: \
|
|
{ \
|
|
intrin((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
}
|
|
#define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \
|
|
ma_atomicType result; \
|
|
switch (order) \
|
|
{ \
|
|
case ma_atomic_memory_order_relaxed: \
|
|
{ \
|
|
result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
case ma_atomic_memory_order_consume: \
|
|
case ma_atomic_memory_order_acquire: \
|
|
{ \
|
|
result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
case ma_atomic_memory_order_release: \
|
|
{ \
|
|
result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
case ma_atomic_memory_order_acq_rel: \
|
|
case ma_atomic_memory_order_seq_cst: \
|
|
default: \
|
|
{ \
|
|
result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \
|
|
} break; \
|
|
} \
|
|
return result;
|
|
typedef ma_uint32 ma_atomic_flag;
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, 1, order, _InterlockedExchange, ma_atomic_flag, long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_atomic_flag)_InterlockedExchange((volatile long*)dst, (long)1);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC_NORETURN(dst, 0, order, _InterlockedExchange, ma_atomic_flag, long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
_InterlockedExchange((volatile long*)dst, (long)0);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
(void)order;
|
|
return (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, 0, 0);
|
|
}
|
|
#endif
|
|
#if defined(MA_ATOMIC_LEGACY_MSVC_ASM)
|
|
#define ma_atomic_memory_order_relaxed 1
|
|
#define ma_atomic_memory_order_consume 2
|
|
#define ma_atomic_memory_order_acquire 3
|
|
#define ma_atomic_memory_order_release 4
|
|
#define ma_atomic_memory_order_acq_rel 5
|
|
#define ma_atomic_memory_order_seq_cst 6
|
|
typedef ma_uint32 ma_atomic_flag;
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_flag result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov eax, 1
|
|
xchg [ecx], eax
|
|
mov result, eax
|
|
}
|
|
return result;
|
|
}
|
|
static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov dword ptr [esi], 0
|
|
}
|
|
} else {
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, 0
|
|
xchg [esi], eax
|
|
}
|
|
}
|
|
}
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_flag result = 0;
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, [esi]
|
|
mov result, eax
|
|
}
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, [esi]
|
|
lock add dword ptr [esp], 0
|
|
mov result, eax
|
|
}
|
|
} else {
|
|
__asm {
|
|
lock add dword ptr [esp], 0
|
|
mov esi, dst
|
|
mov eax, [esi]
|
|
mov result, eax
|
|
lock add dword ptr [esp], 0
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#endif
|
|
#if defined(MA_ATOMIC_MODERN_GCC)
|
|
#define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED
|
|
#define ma_atomic_memory_order_consume __ATOMIC_CONSUME
|
|
#define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE
|
|
#define ma_atomic_memory_order_release __ATOMIC_RELEASE
|
|
#define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL
|
|
#define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST
|
|
typedef ma_uint32 ma_atomic_flag;
|
|
#define ma_atomic_flag_test_and_set_explicit(dst, order) __atomic_exchange_n(dst, 1, order)
|
|
#define ma_atomic_flag_clear_explicit(dst, order) __atomic_store_n(dst, 0, order)
|
|
#define ma_atomic_flag_load_explicit(dst, order) __atomic_load_n(dst, order)
|
|
#endif
|
|
#if defined(MA_ATOMIC_LEGACY_GCC)
|
|
#define ma_atomic_memory_order_relaxed 1
|
|
#define ma_atomic_memory_order_consume 2
|
|
#define ma_atomic_memory_order_acquire 3
|
|
#define ma_atomic_memory_order_release 4
|
|
#define ma_atomic_memory_order_acq_rel 5
|
|
#define ma_atomic_memory_order_seq_cst 6
|
|
typedef ma_uint32 ma_atomic_flag;
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
if (order > ma_atomic_memory_order_acquire) {
|
|
__sync_synchronize();
|
|
}
|
|
return __sync_lock_test_and_set(dst, 1);
|
|
}
|
|
static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
if (order > ma_atomic_memory_order_release) {
|
|
__sync_synchronize();
|
|
}
|
|
__sync_lock_release(dst);
|
|
}
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
(void)order;
|
|
return __sync_val_compare_and_swap((ma_atomic_flag*)dst, 0, 0);
|
|
}
|
|
#endif
|
|
#if defined(MA_ATOMIC_LEGACY_GCC_ASM)
|
|
#define ma_atomic_memory_order_relaxed 1
|
|
#define ma_atomic_memory_order_consume 2
|
|
#define ma_atomic_memory_order_acquire 3
|
|
#define ma_atomic_memory_order_release 4
|
|
#define ma_atomic_memory_order_acq_rel 5
|
|
#define ma_atomic_memory_order_seq_cst 6
|
|
#if defined(MA_X86)
|
|
#define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory")
|
|
#elif defined(MA_X64)
|
|
#define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory")
|
|
#else
|
|
#error Unsupported architecture.
|
|
#endif
|
|
#define MA_ATOMIC_XCHG_GCC_X86(instructionSizeSuffix, result, dst, src) \
|
|
__asm__ __volatile__( \
|
|
"xchg"instructionSizeSuffix" %0, %1" \
|
|
: "=r"(result), \
|
|
"=m"(*dst) \
|
|
: "0"(src), \
|
|
"m"(*dst) \
|
|
: "memory" \
|
|
)
|
|
#define MA_ATOMIC_LOAD_RELAXED_GCC_X86(instructionSizeSuffix, result, dst) \
|
|
__asm__ __volatile__( \
|
|
"mov"instructionSizeSuffix" %1, %0" \
|
|
: "=r"(result) \
|
|
: "m"(*dst) \
|
|
)
|
|
#define MA_ATOMIC_LOAD_RELEASE_GCC_X86(instructionSizeSuffix, result, dst) \
|
|
ma_atomic_thread_fence(ma_atomic_memory_order_release); \
|
|
__asm__ __volatile__( \
|
|
"mov"instructionSizeSuffix" %1, %0" \
|
|
: "=r"(result) \
|
|
: "m"(*dst) \
|
|
: "memory" \
|
|
)
|
|
#define MA_ATOMIC_LOAD_SEQ_CST_GCC_X86(instructionSizeSuffix, result, dst) \
|
|
ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst); \
|
|
__asm__ __volatile__( \
|
|
"mov"instructionSizeSuffix" %1, %0" \
|
|
: "=r"(result) \
|
|
: "m"(*dst) \
|
|
: "memory" \
|
|
); \
|
|
ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst)
|
|
typedef ma_uint32 ma_atomic_flag;
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_test_and_set_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_flag result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
(void)order;
|
|
MA_ATOMIC_XCHG_GCC_X86("l", result, dst, 1);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
static MA_INLINE void ma_atomic_flag_clear_explicit(volatile ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm__ __volatile__(
|
|
"movl $0, %0"
|
|
: "=m"(*dst)
|
|
);
|
|
} else if (order == ma_atomic_memory_order_release) {
|
|
__asm__ __volatile__(
|
|
"movl $0, %0"
|
|
: "=m"(*dst)
|
|
:
|
|
: "memory"
|
|
);
|
|
} else {
|
|
ma_atomic_flag tmp = 0;
|
|
__asm__ __volatile__(
|
|
"xchgl %0, %1"
|
|
: "=r"(tmp),
|
|
"=m"(*dst)
|
|
: "0"(tmp),
|
|
"m"(*dst)
|
|
: "memory"
|
|
);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_atomic_flag ma_atomic_flag_load_explicit(volatile const ma_atomic_flag* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
ma_atomic_flag result;
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst);
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst);
|
|
} else {
|
|
MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst);
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
#define ma_atomic_flag_test_and_set(dst) ma_atomic_flag_test_and_set_explicit(dst, ma_atomic_memory_order_acquire)
|
|
#define ma_atomic_flag_clear(dst) ma_atomic_flag_clear_explicit(dst, ma_atomic_memory_order_release)
|
|
typedef ma_atomic_flag ma_atomic_spinlock;
|
|
static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock)
|
|
{
|
|
for (;;) {
|
|
if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) {
|
|
break;
|
|
}
|
|
while (ma_atomic_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {
|
|
}
|
|
}
|
|
}
|
|
static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock)
|
|
{
|
|
ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release);
|
|
}
|
|
ma_atomic_spinlock ma_atomic_global_lock;
|
|
#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC_ASM) || defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM)
|
|
#if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i486__)) || (defined(_M_IX86) && _M_IX86 >= 400)))
|
|
#if defined(MA_ATOMIC_LEGACY_MSVC) && defined(MA_X64)
|
|
#else
|
|
#define MA_ATOMIC_IS_LOCK_FREE_8 1
|
|
#define MA_ATOMIC_IS_LOCK_FREE_16 1
|
|
#endif
|
|
#define MA_ATOMIC_IS_LOCK_FREE_32 1
|
|
#if defined(MA_X64) || (defined(MA_X86) && ((defined(__GNUC__) && defined(__i586__)) || (defined(_M_IX86) && _M_IX86 >= 500)))
|
|
#define MA_ATOMIC_IS_LOCK_FREE_64 1
|
|
#else
|
|
#endif
|
|
#else
|
|
#endif
|
|
#if defined(MA_ARM32) || defined(MA_ARM64)
|
|
#define MA_ATOMIC_IS_LOCK_FREE_8 1
|
|
#define MA_ATOMIC_IS_LOCK_FREE_16 1
|
|
#define MA_ATOMIC_IS_LOCK_FREE_32 1
|
|
#if defined(MA_ARM64) || defined(__ARM_ARCH_7A__) || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) || defined(__ARM_ARCH_6ZK__)
|
|
#define MA_ATOMIC_IS_LOCK_FREE_64 1
|
|
#endif
|
|
#endif
|
|
#if defined(MA_ATOMIC_PPC32) || defined(MA_ATOMIC_PPC64)
|
|
#if (defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 7))) && !defined(__clang__)
|
|
#else
|
|
#define MA_ATOMIC_IS_LOCK_FREE_8 1
|
|
#define MA_ATOMIC_IS_LOCK_FREE_16 1
|
|
#endif
|
|
#define MA_ATOMIC_IS_LOCK_FREE_32 1
|
|
#if defined(MA_ATOMIC_PPC64)
|
|
#define MA_ATOMIC_IS_LOCK_FREE_64 1
|
|
#endif
|
|
#endif
|
|
static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr)
|
|
{
|
|
(void)ptr;
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr)
|
|
{
|
|
(void)ptr;
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr)
|
|
{
|
|
(void)ptr;
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr)
|
|
{
|
|
(void)ptr;
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
return 1;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
#define MA_ATOMIC_COMPARE_AND_SWAP_LOCK(sizeInBits, dst, expected, replacement) \
|
|
ma_uint##sizeInBits result; \
|
|
ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
|
|
{ \
|
|
result = *dst; \
|
|
if (result == expected) { \
|
|
*dst = replacement; \
|
|
} \
|
|
} \
|
|
ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
|
|
return result
|
|
#define MA_ATOMIC_LOAD_EXPLICIT_LOCK(sizeInBits, ptr, order) \
|
|
ma_uint##sizeInBits result; \
|
|
ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
|
|
{ \
|
|
result = *ptr; \
|
|
(void)order; \
|
|
} \
|
|
ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
|
|
return result
|
|
#define MA_ATOMIC_STORE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \
|
|
ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
|
|
{ \
|
|
*dst = src; \
|
|
(void)order; \
|
|
} \
|
|
ma_atomic_spinlock_unlock(&ma_atomic_global_lock)
|
|
#define MA_ATOMIC_STORE_EXPLICIT_CAS(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits oldValue; \
|
|
do { \
|
|
oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
|
|
} while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \
|
|
(void)order
|
|
#define MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits result; \
|
|
ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
|
|
{ \
|
|
result = *dst; \
|
|
*dst = src; \
|
|
(void)order; \
|
|
} \
|
|
ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
|
|
return result
|
|
#define MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits oldValue; \
|
|
do { \
|
|
oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
|
|
} while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, src) != oldValue); \
|
|
(void)order; \
|
|
return oldValue
|
|
#define MA_ATOMIC_FETCH_ADD_LOCK(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits result; \
|
|
ma_atomic_spinlock_lock(&ma_atomic_global_lock); \
|
|
{ \
|
|
result = *dst; \
|
|
*dst += src; \
|
|
(void)order; \
|
|
} \
|
|
ma_atomic_spinlock_unlock(&ma_atomic_global_lock); \
|
|
return result
|
|
#define MA_ATOMIC_FETCH_ADD_CAS(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits oldValue; \
|
|
ma_uint##sizeInBits newValue; \
|
|
do { \
|
|
oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
|
|
newValue = oldValue + src; \
|
|
} while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
|
|
(void)order; \
|
|
return oldValue
|
|
#define MA_ATOMIC_FETCH_AND_CAS(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits oldValue; \
|
|
ma_uint##sizeInBits newValue; \
|
|
do { \
|
|
oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
|
|
newValue = (ma_uint##sizeInBits)(oldValue & src); \
|
|
} while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
|
|
(void)order; \
|
|
return oldValue
|
|
#define MA_ATOMIC_FETCH_OR_CAS(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits oldValue; \
|
|
ma_uint##sizeInBits newValue; \
|
|
do { \
|
|
oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
|
|
newValue = (ma_uint##sizeInBits)(oldValue | src); \
|
|
} while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
|
|
(void)order; \
|
|
return oldValue
|
|
#define MA_ATOMIC_FETCH_XOR_CAS(sizeInBits, dst, src, order) \
|
|
ma_uint##sizeInBits oldValue; \
|
|
ma_uint##sizeInBits newValue; \
|
|
do { \
|
|
oldValue = ma_atomic_load_explicit_##sizeInBits(dst, ma_atomic_memory_order_relaxed); \
|
|
newValue = (ma_uint##sizeInBits)(oldValue ^ src); \
|
|
} while (ma_atomic_compare_and_swap_##sizeInBits(dst, oldValue, newValue) != oldValue); \
|
|
(void)order; \
|
|
return oldValue
|
|
#if defined(MA_ATOMIC_MODERN_MSVC) || defined(MA_ATOMIC_LEGACY_MSVC)
|
|
#define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, replacement, order, intrin, ma_atomicType, msvcType) \
|
|
ma_atomicType result; \
|
|
switch (order) \
|
|
{ \
|
|
case ma_atomic_memory_order_relaxed: \
|
|
{ \
|
|
result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
|
|
} break; \
|
|
case ma_atomic_memory_order_consume: \
|
|
case ma_atomic_memory_order_acquire: \
|
|
{ \
|
|
result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
|
|
} break; \
|
|
case ma_atomic_memory_order_release: \
|
|
{ \
|
|
result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
|
|
} break; \
|
|
case ma_atomic_memory_order_acq_rel: \
|
|
case ma_atomic_memory_order_seq_cst: \
|
|
default: \
|
|
{ \
|
|
result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)replacement); \
|
|
} break; \
|
|
} \
|
|
return result;
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
#define ma_atomic_compare_and_swap_8( dst, expected, replacement) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)replacement, (char)expected)
|
|
#else
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
#define ma_atomic_compare_and_swap_16(dst, expected, replacement) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)replacement, (short)expected)
|
|
#else
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
#define ma_atomic_compare_and_swap_32(dst, expected, replacement) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)replacement, (long)expected)
|
|
#else
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
#define ma_atomic_compare_and_swap_64(dst, expected, replacement) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)replacement, (ma_int64)expected)
|
|
#else
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
#if defined(MA_32BIT)
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order);
|
|
}
|
|
#else
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
#if defined(MA_32BIT)
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order);
|
|
}
|
|
#else
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long);
|
|
}
|
|
#else
|
|
{
|
|
(void)order;
|
|
return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order);
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order);
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order);
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order);
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ARM)
|
|
{
|
|
MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
#define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order)
|
|
#define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order)
|
|
#define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order)
|
|
#define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order)
|
|
#if defined(MA_X64)
|
|
#define ma_atomic_thread_fence(order) __faststorefence(), (void)order
|
|
#elif defined(MA_ARM64)
|
|
#define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order
|
|
#else
|
|
static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order)
|
|
{
|
|
volatile ma_uint32 barrier = 0;
|
|
ma_atomic_fetch_add_explicit_32(&barrier, 0, order);
|
|
}
|
|
#endif
|
|
#define ma_atomic_signal_fence(order) _ReadWriteBarrier(), (void)order
|
|
#endif
|
|
#if defined(MA_ATOMIC_LEGACY_MSVC_ASM)
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
ma_uint8 result = 0;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov al, expected
|
|
mov dl, replacement
|
|
lock cmpxchg [ecx], dl
|
|
mov result, al
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
ma_uint16 result = 0;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov ax, expected
|
|
mov dx, replacement
|
|
lock cmpxchg [ecx], dx
|
|
mov result, ax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
ma_uint32 result = 0;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov eax, expected
|
|
mov edx, replacement
|
|
lock cmpxchg [ecx], edx
|
|
mov result, eax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
ma_uint32 resultEAX = 0;
|
|
ma_uint32 resultEDX = 0;
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, dword ptr expected
|
|
mov edx, dword ptr expected + 4
|
|
mov ebx, dword ptr replacement
|
|
mov ecx, dword ptr replacement + 4
|
|
lock cmpxchg8b qword ptr [esi]
|
|
mov resultEAX, eax
|
|
mov resultEDX, edx
|
|
}
|
|
return ((ma_uint64)resultEDX << 32) | resultEAX;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
ma_uint8 result = 0;
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov al, [esi]
|
|
mov result, al
|
|
}
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov al, [esi]
|
|
lock add dword ptr [esp], 0
|
|
mov result, al
|
|
}
|
|
} else {
|
|
__asm {
|
|
lock add dword ptr [esp], 0
|
|
mov esi, dst
|
|
mov al, [esi]
|
|
mov result, al
|
|
lock add dword ptr [esp], 0
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
ma_uint16 result = 0;
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov ax, [esi]
|
|
mov result, ax
|
|
}
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov ax, [esi]
|
|
lock add dword ptr [esp], 0
|
|
mov result, ax
|
|
}
|
|
} else {
|
|
__asm {
|
|
lock add dword ptr [esp], 0
|
|
mov esi, dst
|
|
mov ax, [esi]
|
|
mov result, ax
|
|
lock add dword ptr [esp], 0
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
ma_uint32 result = 0;
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, [esi]
|
|
mov result, eax
|
|
}
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, [esi]
|
|
lock add dword ptr [esp], 0
|
|
mov result, eax
|
|
}
|
|
} else {
|
|
__asm {
|
|
lock add dword ptr [esp], 0
|
|
mov esi, dst
|
|
mov eax, [esi]
|
|
mov result, eax
|
|
lock add dword ptr [esp], 0
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order)
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0);
|
|
}
|
|
static MA_INLINE void __stdcall ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov al, src
|
|
mov [esi], al
|
|
}
|
|
} else {
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
__asm {
|
|
mov esi, dst
|
|
mov al, src
|
|
xchg [esi], al
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
static MA_INLINE void __stdcall ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov ax, src
|
|
mov [esi], ax
|
|
}
|
|
} else {
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
__asm {
|
|
mov esi, dst
|
|
mov ax, src
|
|
xchg [esi], ax
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
static MA_INLINE void __stdcall ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, src
|
|
mov [esi], eax
|
|
}
|
|
} else {
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
__asm {
|
|
mov esi, dst
|
|
mov eax, src
|
|
xchg [esi], eax
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
static MA_INLINE void __stdcall ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
ma_uint8 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov al, src
|
|
lock xchg [ecx], al
|
|
mov result, al
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
ma_uint16 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov ax, src
|
|
lock xchg [ecx], ax
|
|
mov result, ax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
ma_uint32 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov eax, src
|
|
xchg [ecx], eax
|
|
mov result, eax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
ma_uint8 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov al, src
|
|
lock xadd [ecx], al
|
|
mov result, al
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
ma_uint16 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov ax, src
|
|
lock xadd [ecx], ax
|
|
mov result, ax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
ma_uint32 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov eax, src
|
|
lock xadd [ecx], eax
|
|
mov result, eax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
ma_uint8 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov al, src
|
|
neg al
|
|
lock xadd [ecx], al
|
|
mov result, al
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
ma_uint16 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov ax, src
|
|
neg ax
|
|
lock xadd [ecx], ax
|
|
mov result, ax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
ma_uint32 result = 0;
|
|
(void)order;
|
|
__asm {
|
|
mov ecx, dst
|
|
mov eax, src
|
|
neg eax
|
|
lock xadd [ecx], eax
|
|
mov result, eax
|
|
}
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_CAS(64, dst, (ma_uint64)(-(ma_int64)src), order);
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
|
|
}
|
|
static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order)
|
|
{
|
|
(void)order;
|
|
__asm {
|
|
lock add dword ptr [esp], 0
|
|
}
|
|
}
|
|
#define ma_atomic_signal_fence(order) __asm {}; (void)order
|
|
#endif
|
|
#if defined(MA_ATOMIC_MODERN_GCC)
|
|
#define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE
|
|
#define ma_atomic_thread_fence(order) __atomic_thread_fence(order)
|
|
#define ma_atomic_signal_fence(order) __atomic_signal_fence(order)
|
|
#define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr)
|
|
#define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr)
|
|
#define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr)
|
|
#define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr)
|
|
#define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order)
|
|
#define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order)
|
|
#define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order)
|
|
#define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order)
|
|
#define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order)
|
|
#define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order)
|
|
#define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order)
|
|
#define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order)
|
|
#define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order)
|
|
#define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order)
|
|
#define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order)
|
|
#define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order)
|
|
#define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 0, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, replacement, 1, successOrder, failureOrder)
|
|
#define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order)
|
|
#define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order)
|
|
#define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order)
|
|
#define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order)
|
|
#define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order)
|
|
#define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order)
|
|
#define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order)
|
|
#define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order)
|
|
#define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order)
|
|
#define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order)
|
|
#define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order)
|
|
#define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order)
|
|
#define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order)
|
|
#define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order)
|
|
#define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order)
|
|
#define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order)
|
|
#define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order)
|
|
#define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order)
|
|
#define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order)
|
|
#define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order)
|
|
static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
|
|
{
|
|
__atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
|
return expected;
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
|
|
{
|
|
__atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
|
return expected;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
|
|
{
|
|
__atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
|
return expected;
|
|
}
|
|
#if defined(__clang__)
|
|
#pragma clang diagnostic push
|
|
#if __clang_major__ >= 8
|
|
#pragma clang diagnostic ignored "-Watomic-alignment"
|
|
#endif
|
|
#endif
|
|
static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
|
|
{
|
|
__atomic_compare_exchange_n(dst, &expected, replacement, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
|
|
return expected;
|
|
}
|
|
#if defined(__clang__)
|
|
#pragma clang diagnostic pop
|
|
#endif
|
|
#endif
|
|
#if defined(MA_ATOMIC_LEGACY_GCC) || defined(MA_ATOMIC_LEGACY_GCC_ASM)
|
|
#define ma_atomic_signal_fence(order) __asm__ __volatile__("":::"memory")
|
|
#if defined(MA_ATOMIC_LEGACY_GCC)
|
|
#define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order
|
|
static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
return __sync_val_compare_and_swap(dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
return __sync_val_compare_and_swap(dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
return __sync_val_compare_and_swap(dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
return __sync_val_compare_and_swap(dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, ptr, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
if (order > ma_atomic_memory_order_acquire) {
|
|
__sync_synchronize();
|
|
}
|
|
return __sync_lock_test_and_set(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
if (order > ma_atomic_memory_order_acquire) {
|
|
__sync_synchronize();
|
|
}
|
|
return __sync_lock_test_and_set(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
if (order > ma_atomic_memory_order_acquire) {
|
|
__sync_synchronize();
|
|
}
|
|
return __sync_lock_test_and_set(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
if (order > ma_atomic_memory_order_acquire) {
|
|
__sync_synchronize();
|
|
}
|
|
return __sync_lock_test_and_set(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
#define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order)
|
|
#define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order)
|
|
#define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order)
|
|
#define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order)
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_add(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_add(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_add(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_add(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_sub(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(8, dst, (ma_uint8)(-(ma_int8)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_sub(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(16, dst, (ma_uint16)(-(ma_int16)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_sub(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(32, dst, (ma_uint32)(-(ma_int32)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_sub(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(64, dst, (ma_uint64)(-(ma_int64)src), order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_and(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_and(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_and(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_and(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_or(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_or(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_or(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_or(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_xor(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_xor(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_xor(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64)
|
|
{
|
|
(void)order;
|
|
return __sync_fetch_and_xor(dst, src);
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
#elif defined(MA_ATOMIC_LEGACY_GCC_ASM)
|
|
#define MA_ATOMIC_CMPXCHG_GCC_X86(instructionSizeSuffix, result, dst, expected, replacement) \
|
|
__asm__ __volatile__( \
|
|
"lock; cmpxchg"instructionSizeSuffix" %2, %1" \
|
|
: "=a"(result), \
|
|
"=m"(*dst) \
|
|
: "r"(replacement), \
|
|
"0"(expected), \
|
|
"m"(*dst) \
|
|
: "cc", "memory")
|
|
#define MA_ATOMIC_XADD_GCC_X86(instructionSizeSuffix, result, dst, src) \
|
|
__asm__ __volatile__( \
|
|
"lock; xadd"instructionSizeSuffix" %0, %1" \
|
|
: "=a"(result), \
|
|
"=m"(*dst) \
|
|
: "0"(src), \
|
|
"m"(*dst) \
|
|
: "cc", "memory")
|
|
static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint8 result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_CMPXCHG_GCC_X86("b", result, dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(8, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint16 result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_CMPXCHG_GCC_X86("w", result, dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(16, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint32 result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_CMPXCHG_GCC_X86("l", result, dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(32, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 replacement)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint64 result;
|
|
#if defined(MA_X86)
|
|
{
|
|
ma_uint32 resultEAX;
|
|
ma_uint32 resultEDX;
|
|
__asm__ __volatile__(
|
|
"pushl %%ebx\n"
|
|
"movl %4, %%ebx\n"
|
|
"lock cmpxchg8b (%%edi)\n"
|
|
"popl %%ebx\n"
|
|
: "=a"(resultEAX),
|
|
"=d"(resultEDX)
|
|
: "a"((ma_uint32)(expected & 0xFFFFFFFF)),
|
|
"d"((ma_uint32)(expected >> 32)),
|
|
"r"((ma_uint32)(replacement & 0xFFFFFFFF)),
|
|
"c"((ma_uint32)(replacement >> 32)),
|
|
"D"(dst)
|
|
: "memory", "cc");
|
|
result = ((ma_uint64)resultEDX << 32) | resultEAX;
|
|
}
|
|
#elif defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_CMPXCHG_GCC_X86("q", result, dst, expected, replacement);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_COMPARE_AND_SWAP_LOCK(64, dst, expected, replacement);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint8 result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
MA_ATOMIC_LOAD_RELAXED_GCC_X86("b", result, dst);
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
MA_ATOMIC_LOAD_RELEASE_GCC_X86("b", result, dst);
|
|
} else {
|
|
MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("b", result, dst);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(8, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint16 result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
MA_ATOMIC_LOAD_RELAXED_GCC_X86("w", result, dst);
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
MA_ATOMIC_LOAD_RELEASE_GCC_X86("w", result, dst);
|
|
} else {
|
|
MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("w", result, dst);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(16, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint32 result;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
MA_ATOMIC_LOAD_RELAXED_GCC_X86("l", result, dst);
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
MA_ATOMIC_LOAD_RELEASE_GCC_X86("l", result, dst);
|
|
} else {
|
|
MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("l", result, dst);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(32, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* dst, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint64 result;
|
|
#if defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
MA_ATOMIC_LOAD_RELAXED_GCC_X86("q", result, dst);
|
|
} else if (order <= ma_atomic_memory_order_release) {
|
|
MA_ATOMIC_LOAD_RELEASE_GCC_X86("q", result, dst);
|
|
} else {
|
|
MA_ATOMIC_LOAD_SEQ_CST_GCC_X86("q", result, dst);
|
|
}
|
|
}
|
|
#elif defined(MA_X86)
|
|
{
|
|
(void)order;
|
|
return ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, 0, 0);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_LOAD_EXPLICIT_LOCK(64, dst, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint8 result;
|
|
(void)order;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_XCHG_GCC_X86("b", result, dst, src);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint16 result;
|
|
(void)order;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_XCHG_GCC_X86("w", result, dst, src);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint32 result;
|
|
(void)order;
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_XCHG_GCC_X86("l", result, dst, src);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
ma_uint64 result;
|
|
(void)order;
|
|
#if defined(MA_X86)
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_CAS(64, dst, src, order);
|
|
}
|
|
#elif defined(MA_X64)
|
|
{
|
|
MA_ATOMIC_XCHG_GCC_X86("q", result, dst, src);
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_EXCHANGE_EXPLICIT_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm__ __volatile__ (
|
|
"movb %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
);
|
|
} else {
|
|
__asm__ __volatile__ (
|
|
"xchgb %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
: "memory"
|
|
);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm__ __volatile__ (
|
|
"movw %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
);
|
|
} else {
|
|
__asm__ __volatile__ (
|
|
"xchgw %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
: "memory"
|
|
);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm__ __volatile__ (
|
|
"movl %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
);
|
|
} else {
|
|
__asm__ __volatile__ (
|
|
"xchgl %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
: "memory"
|
|
);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X64)
|
|
{
|
|
if (order == ma_atomic_memory_order_relaxed) {
|
|
__asm__ __volatile__ (
|
|
"movq %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
);
|
|
} else {
|
|
__asm__ __volatile__ (
|
|
"xchgq %1, %0"
|
|
: "=m"(*dst)
|
|
: "r"(src)
|
|
: "memory"
|
|
);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_CAS(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_STORE_EXPLICIT_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_8) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
ma_uint8 result;
|
|
(void)order;
|
|
MA_ATOMIC_XADD_GCC_X86("b", result, dst, src);
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(8, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_16) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
ma_uint16 result;
|
|
(void)order;
|
|
MA_ATOMIC_XADD_GCC_X86("w", result, dst, src);
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(16, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_32) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
{
|
|
ma_uint32 result;
|
|
(void)order;
|
|
MA_ATOMIC_XADD_GCC_X86("l", result, dst, src);
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(32, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
#if defined(MA_ATOMIC_IS_LOCK_FREE_64) && (defined(MA_X86) || defined(MA_X64))
|
|
{
|
|
#if defined(MA_X86)
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_CAS(64, dst, src, order);
|
|
}
|
|
#elif defined(MA_X64)
|
|
{
|
|
ma_uint64 result;
|
|
MA_ATOMIC_XADD_GCC_X86("q", result, dst, src);
|
|
(void)order;
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
#error Unsupported architecture.
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
{
|
|
MA_ATOMIC_FETCH_ADD_LOCK(64, dst, src, order);
|
|
}
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_8(dst, (ma_uint8)(-(ma_int8)src), order);
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_16(dst, (ma_uint16)(-(ma_int16)src), order);
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_32(dst, (ma_uint32)(-(ma_int32)src), order);
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
return ma_atomic_fetch_add_explicit_64(dst, (ma_uint64)(-(ma_int64)src), order);
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(8, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(16, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(32, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_AND_CAS(64, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(8, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(16, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(32, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_OR_CAS(64, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(8, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(16, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(32, dst, src, order);
|
|
}
|
|
static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order)
|
|
{
|
|
MA_ATOMIC_FETCH_XOR_CAS(64, dst, src, order);
|
|
}
|
|
#else
|
|
#error Unsupported compiler.
|
|
#endif
|
|
#endif
|
|
#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE)
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_uint8 result;
|
|
(void)successOrder;
|
|
(void)failureOrder;
|
|
result = ma_atomic_compare_and_swap_8(dst, *expected, replacement);
|
|
if (result == *expected) {
|
|
return 1;
|
|
} else {
|
|
*expected = result;
|
|
return 0;
|
|
}
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_uint16 result;
|
|
(void)successOrder;
|
|
(void)failureOrder;
|
|
result = ma_atomic_compare_and_swap_16(dst, *expected, replacement);
|
|
if (result == *expected) {
|
|
return 1;
|
|
} else {
|
|
*expected = result;
|
|
return 0;
|
|
}
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_uint32 result;
|
|
(void)successOrder;
|
|
(void)failureOrder;
|
|
result = ma_atomic_compare_and_swap_32(dst, *expected, replacement);
|
|
if (result == *expected) {
|
|
return 1;
|
|
} else {
|
|
*expected = result;
|
|
return 0;
|
|
}
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_uint64 result;
|
|
(void)successOrder;
|
|
(void)failureOrder;
|
|
result = ma_atomic_compare_and_swap_64(dst, *expected, replacement);
|
|
if (result == *expected) {
|
|
return 1;
|
|
} else {
|
|
*expected = result;
|
|
return 0;
|
|
}
|
|
}
|
|
#define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, successOrder, failureOrder)
|
|
#endif
|
|
#if defined(MA_64BIT)
|
|
static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr)
|
|
{
|
|
return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr);
|
|
}
|
|
static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order)
|
|
{
|
|
return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order);
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order);
|
|
}
|
|
static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
|
|
{
|
|
return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement)
|
|
{
|
|
return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)replacement);
|
|
}
|
|
#elif defined(MA_32BIT)
|
|
static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr)
|
|
{
|
|
return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr);
|
|
}
|
|
static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order)
|
|
{
|
|
return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order);
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order);
|
|
}
|
|
static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order)
|
|
{
|
|
return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* replacement)
|
|
{
|
|
return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)replacement);
|
|
}
|
|
#else
|
|
#error Unsupported architecture.
|
|
#endif
|
|
#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_ptr(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_ptr(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_16( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_32( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_64( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order)
|
|
#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order)
|
|
#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order)
|
|
#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order)
|
|
#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)replacement, successOrder, failureOrder)
|
|
#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)replacement, successOrder, failureOrder)
|
|
#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order)
|
|
#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order)
|
|
#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order)
|
|
#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order)
|
|
#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_i8( dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_i16(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_i32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_i64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_i8( dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_i16(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_i32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_i64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired)
|
|
#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired)
|
|
#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired)
|
|
#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired)
|
|
typedef union
|
|
{
|
|
ma_uint32 i;
|
|
float f;
|
|
} ma_atomic_if32;
|
|
typedef union
|
|
{
|
|
ma_uint64 i;
|
|
double f;
|
|
} ma_atomic_if64;
|
|
#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order)
|
|
#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order)
|
|
static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
}
|
|
static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
}
|
|
static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_atomic_if32 d;
|
|
d.f = replacement;
|
|
return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_atomic_if64 d;
|
|
d.f = replacement;
|
|
return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_atomic_if32 d;
|
|
d.f = replacement;
|
|
return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double replacement, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder)
|
|
{
|
|
ma_atomic_if64 d;
|
|
d.f = replacement;
|
|
return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder);
|
|
}
|
|
static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 x;
|
|
x.f = src;
|
|
r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order);
|
|
return r.f;
|
|
}
|
|
#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_f32(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_strong_f64(dst, expected, replacement) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_f32(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_compare_exchange_weak_f64(dst, expected, replacement) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, replacement, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst)
|
|
#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst)
|
|
static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float replacement)
|
|
{
|
|
ma_atomic_if32 r;
|
|
ma_atomic_if32 e, d;
|
|
e.f = expected;
|
|
d.f = replacement;
|
|
r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i);
|
|
return r.f;
|
|
}
|
|
static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double replacement)
|
|
{
|
|
ma_atomic_if64 r;
|
|
ma_atomic_if64 e, d;
|
|
e.f = expected;
|
|
d.f = replacement;
|
|
r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i);
|
|
return r.f;
|
|
}
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#if defined(__cplusplus)
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \
|
|
} \
|
|
static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \
|
|
{ \
|
|
ma_atomic_store_##c89TypeExtension(&x->value, value); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \
|
|
} \
|
|
static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \
|
|
{ \
|
|
return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \
|
|
} \
|
|
static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \
|
|
{ \
|
|
return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \
|
|
} \
|
|
|
|
#define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \
|
|
static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \
|
|
{ \
|
|
return ma_atomic_load_ptr((void**)&x->value); \
|
|
} \
|
|
static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \
|
|
{ \
|
|
ma_atomic_store_ptr((void**)&x->value, (void*)value); \
|
|
} \
|
|
static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \
|
|
{ \
|
|
return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \
|
|
} \
|
|
static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \
|
|
{ \
|
|
return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \
|
|
} \
|
|
static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \
|
|
{ \
|
|
return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \
|
|
} \
|
|
|
|
MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32)
|
|
MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32)
|
|
MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64)
|
|
MA_ATOMIC_SAFE_TYPE_IMPL(f32, float)
|
|
MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32)
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state)
|
|
#endif
|
|
|
|
MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn)
|
|
{
|
|
ma_uint64 outputFrameCount;
|
|
ma_uint64 preliminaryInputFrameCountFromFrac;
|
|
ma_uint64 preliminaryInputFrameCount;
|
|
|
|
if (sampleRateIn == 0 || sampleRateOut == 0 || frameCountIn == 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (sampleRateOut == sampleRateIn) {
|
|
return frameCountIn;
|
|
}
|
|
|
|
outputFrameCount = (frameCountIn * sampleRateOut) / sampleRateIn;
|
|
|
|
preliminaryInputFrameCountFromFrac = (outputFrameCount * (sampleRateIn / sampleRateOut)) / sampleRateOut;
|
|
preliminaryInputFrameCount = (outputFrameCount * (sampleRateIn % sampleRateOut)) + preliminaryInputFrameCountFromFrac;
|
|
|
|
if (preliminaryInputFrameCount <= frameCountIn) {
|
|
outputFrameCount += 1;
|
|
}
|
|
|
|
return outputFrameCount;
|
|
}
|
|
|
|
#ifndef MA_DATA_CONVERTER_STACK_BUFFER_SIZE
|
|
#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
|
|
#endif
|
|
|
|
#if defined(MA_WIN32)
|
|
static ma_result ma_result_from_GetLastError(DWORD error)
|
|
{
|
|
switch (error)
|
|
{
|
|
case ERROR_SUCCESS: return MA_SUCCESS;
|
|
case ERROR_PATH_NOT_FOUND: return MA_DOES_NOT_EXIST;
|
|
case ERROR_TOO_MANY_OPEN_FILES: return MA_TOO_MANY_OPEN_FILES;
|
|
case ERROR_NOT_ENOUGH_MEMORY: return MA_OUT_OF_MEMORY;
|
|
case ERROR_DISK_FULL: return MA_NO_SPACE;
|
|
case ERROR_HANDLE_EOF: return MA_AT_END;
|
|
case ERROR_NEGATIVE_SEEK: return MA_BAD_SEEK;
|
|
case ERROR_INVALID_PARAMETER: return MA_INVALID_ARGS;
|
|
case ERROR_ACCESS_DENIED: return MA_ACCESS_DENIED;
|
|
case ERROR_SEM_TIMEOUT: return MA_TIMEOUT;
|
|
case ERROR_FILE_NOT_FOUND: return MA_DOES_NOT_EXIST;
|
|
default: break;
|
|
}
|
|
|
|
return MA_ERROR;
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, ma_bool32 yield)
|
|
{
|
|
if (pSpinlock == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (;;) {
|
|
if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) {
|
|
break;
|
|
}
|
|
|
|
while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) {
|
|
if (yield) {
|
|
ma_yield();
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spinlock_lock(volatile ma_spinlock* pSpinlock)
|
|
{
|
|
return ma_spinlock_lock_ex(pSpinlock, MA_TRUE);
|
|
}
|
|
|
|
MA_API ma_result ma_spinlock_lock_noyield(volatile ma_spinlock* pSpinlock)
|
|
{
|
|
return ma_spinlock_lock_ex(pSpinlock, MA_FALSE);
|
|
}
|
|
|
|
MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock)
|
|
{
|
|
if (pSpinlock == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
#if defined(MA_POSIX)
|
|
#define MA_THREADCALL
|
|
typedef void* ma_thread_result;
|
|
#elif defined(MA_WIN32)
|
|
#define MA_THREADCALL WINAPI
|
|
typedef unsigned long ma_thread_result;
|
|
#endif
|
|
|
|
typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData);
|
|
|
|
#ifdef MA_POSIX
|
|
static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
|
|
{
|
|
int result;
|
|
pthread_attr_t* pAttr = NULL;
|
|
|
|
#if !defined(MA_EMSCRIPTEN) && !defined(MA_3DS) && !defined(MA_SWITCH)
|
|
pthread_attr_t attr;
|
|
if (pthread_attr_init(&attr) == 0) {
|
|
int scheduler = -1;
|
|
|
|
pAttr = &attr;
|
|
|
|
#if !defined(MA_BEOS)
|
|
{
|
|
if (priority == ma_thread_priority_idle) {
|
|
#ifdef SCHED_IDLE
|
|
if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) {
|
|
scheduler = SCHED_IDLE;
|
|
}
|
|
#endif
|
|
} else if (priority == ma_thread_priority_realtime) {
|
|
#ifdef SCHED_FIFO
|
|
if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) {
|
|
scheduler = SCHED_FIFO;
|
|
}
|
|
#endif
|
|
#ifdef MA_LINUX
|
|
} else {
|
|
scheduler = sched_getscheduler(0);
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(_POSIX_THREAD_ATTR_STACKSIZE) && _POSIX_THREAD_ATTR_STACKSIZE >= 0
|
|
{
|
|
if (stackSize > 0) {
|
|
pthread_attr_setstacksize(&attr, stackSize);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
(void)stackSize;
|
|
}
|
|
#endif
|
|
|
|
if (scheduler != -1) {
|
|
int priorityMin = sched_get_priority_min(scheduler);
|
|
int priorityMax = sched_get_priority_max(scheduler);
|
|
int priorityStep = (priorityMax - priorityMin) / 7;
|
|
|
|
struct sched_param sched;
|
|
if (priorityMin != -1 && priorityMax != -1 && pthread_attr_getschedparam(&attr, &sched) == 0) {
|
|
if (priority == ma_thread_priority_idle) {
|
|
sched.sched_priority = priorityMin;
|
|
} else if (priority == ma_thread_priority_realtime) {
|
|
#if defined(MA_PTHREAD_REALTIME_THREAD_PRIORITY)
|
|
{
|
|
sched.sched_priority = MA_PTHREAD_REALTIME_THREAD_PRIORITY;
|
|
}
|
|
#else
|
|
{
|
|
sched.sched_priority = priorityMax;
|
|
}
|
|
#endif
|
|
} else {
|
|
sched.sched_priority += ((int)priority + 5) * priorityStep;
|
|
}
|
|
|
|
if (sched.sched_priority < priorityMin) {
|
|
sched.sched_priority = priorityMin;
|
|
}
|
|
if (sched.sched_priority > priorityMax) {
|
|
sched.sched_priority = priorityMax;
|
|
}
|
|
|
|
if (pthread_attr_setschedparam(&attr, &sched) == 0) {
|
|
#if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
|
|
{
|
|
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
(void)priority;
|
|
(void)stackSize;
|
|
#endif
|
|
|
|
result = pthread_create((pthread_t*)pThread, pAttr, entryProc, pData);
|
|
|
|
if (pAttr != NULL) {
|
|
pthread_attr_destroy(pAttr);
|
|
}
|
|
|
|
if (result != 0) {
|
|
|
|
#ifndef MA_NO_PTHREAD_REALTIME_PRIORITY_FALLBACK
|
|
{
|
|
if(result == EPERM && priority == ma_thread_priority_realtime) {
|
|
return ma_thread_create__posix(pThread, ma_thread_priority_normal, stackSize, entryProc, pData);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return ma_result_from_errno(result);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_thread_wait__posix(ma_thread* pThread)
|
|
{
|
|
pthread_join((pthread_t)*pThread, NULL);
|
|
}
|
|
|
|
static ma_result ma_mutex_init__posix(ma_mutex* pMutex)
|
|
{
|
|
int result;
|
|
|
|
if (pMutex == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pMutex);
|
|
|
|
result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL);
|
|
if (result != 0) {
|
|
return ma_result_from_errno(result);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_mutex_uninit__posix(ma_mutex* pMutex)
|
|
{
|
|
pthread_mutex_destroy((pthread_mutex_t*)pMutex);
|
|
}
|
|
|
|
static void ma_mutex_lock__posix(ma_mutex* pMutex)
|
|
{
|
|
pthread_mutex_lock((pthread_mutex_t*)pMutex);
|
|
}
|
|
|
|
static void ma_mutex_unlock__posix(ma_mutex* pMutex)
|
|
{
|
|
pthread_mutex_unlock((pthread_mutex_t*)pMutex);
|
|
}
|
|
|
|
static ma_result ma_event_init__posix(ma_event* pEvent)
|
|
{
|
|
int result;
|
|
|
|
result = pthread_mutex_init((pthread_mutex_t*)&pEvent->lock, NULL);
|
|
if (result != 0) {
|
|
return ma_result_from_errno(result);
|
|
}
|
|
|
|
result = pthread_cond_init((pthread_cond_t*)&pEvent->cond, NULL);
|
|
if (result != 0) {
|
|
pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
|
|
return ma_result_from_errno(result);
|
|
}
|
|
|
|
pEvent->value = 0;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_event_uninit__posix(ma_event* pEvent)
|
|
{
|
|
pthread_cond_destroy((pthread_cond_t*)&pEvent->cond);
|
|
pthread_mutex_destroy((pthread_mutex_t*)&pEvent->lock);
|
|
}
|
|
|
|
static ma_result ma_event_wait__posix(ma_event* pEvent)
|
|
{
|
|
pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
|
|
{
|
|
while (pEvent->value == 0) {
|
|
pthread_cond_wait((pthread_cond_t*)&pEvent->cond, (pthread_mutex_t*)&pEvent->lock);
|
|
}
|
|
pEvent->value = 0;
|
|
}
|
|
pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_event_signal__posix(ma_event* pEvent)
|
|
{
|
|
pthread_mutex_lock((pthread_mutex_t*)&pEvent->lock);
|
|
{
|
|
pEvent->value = 1;
|
|
pthread_cond_signal((pthread_cond_t*)&pEvent->cond);
|
|
}
|
|
pthread_mutex_unlock((pthread_mutex_t*)&pEvent->lock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_semaphore_init__posix(int initialValue, ma_semaphore* pSemaphore)
|
|
{
|
|
int result;
|
|
|
|
if (pSemaphore == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pSemaphore->value = initialValue;
|
|
|
|
result = pthread_mutex_init((pthread_mutex_t*)&pSemaphore->lock, NULL);
|
|
if (result != 0) {
|
|
return ma_result_from_errno(result);
|
|
}
|
|
|
|
result = pthread_cond_init((pthread_cond_t*)&pSemaphore->cond, NULL);
|
|
if (result != 0) {
|
|
pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
|
|
return ma_result_from_errno(result);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_semaphore_uninit__posix(ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
return;
|
|
}
|
|
|
|
pthread_cond_destroy((pthread_cond_t*)&pSemaphore->cond);
|
|
pthread_mutex_destroy((pthread_mutex_t*)&pSemaphore->lock);
|
|
}
|
|
|
|
static ma_result ma_semaphore_wait__posix(ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
|
|
{
|
|
while (pSemaphore->value == 0) {
|
|
pthread_cond_wait((pthread_cond_t*)&pSemaphore->cond, (pthread_mutex_t*)&pSemaphore->lock);
|
|
}
|
|
|
|
pSemaphore->value -= 1;
|
|
}
|
|
pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pthread_mutex_lock((pthread_mutex_t*)&pSemaphore->lock);
|
|
{
|
|
pSemaphore->value += 1;
|
|
pthread_cond_signal((pthread_cond_t*)&pSemaphore->cond);
|
|
}
|
|
pthread_mutex_unlock((pthread_mutex_t*)&pSemaphore->lock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#elif defined(MA_WIN32)
|
|
static int ma_thread_priority_to_win32(ma_thread_priority priority)
|
|
{
|
|
switch (priority) {
|
|
case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE;
|
|
case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST;
|
|
case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL;
|
|
case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL;
|
|
case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL;
|
|
case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST;
|
|
case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL;
|
|
default: return THREAD_PRIORITY_NORMAL;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData)
|
|
{
|
|
DWORD threadID;
|
|
|
|
*pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID);
|
|
if (*pThread == NULL) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority));
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_thread_wait__win32(ma_thread* pThread)
|
|
{
|
|
WaitForSingleObject((HANDLE)*pThread, INFINITE);
|
|
CloseHandle((HANDLE)*pThread);
|
|
}
|
|
|
|
static ma_result ma_mutex_init__win32(ma_mutex* pMutex)
|
|
{
|
|
*pMutex = CreateEventA(NULL, FALSE, TRUE, NULL);
|
|
if (*pMutex == NULL) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_mutex_uninit__win32(ma_mutex* pMutex)
|
|
{
|
|
CloseHandle((HANDLE)*pMutex);
|
|
}
|
|
|
|
static void ma_mutex_lock__win32(ma_mutex* pMutex)
|
|
{
|
|
WaitForSingleObject((HANDLE)*pMutex, INFINITE);
|
|
}
|
|
|
|
static void ma_mutex_unlock__win32(ma_mutex* pMutex)
|
|
{
|
|
SetEvent((HANDLE)*pMutex);
|
|
}
|
|
|
|
static ma_result ma_event_init__win32(ma_event* pEvent)
|
|
{
|
|
*pEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
if (*pEvent == NULL) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_event_uninit__win32(ma_event* pEvent)
|
|
{
|
|
CloseHandle((HANDLE)*pEvent);
|
|
}
|
|
|
|
static ma_result ma_event_wait__win32(ma_event* pEvent)
|
|
{
|
|
DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE);
|
|
if (result == WAIT_OBJECT_0) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
if (result == WAIT_TIMEOUT) {
|
|
return MA_TIMEOUT;
|
|
}
|
|
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
static ma_result ma_event_signal__win32(ma_event* pEvent)
|
|
{
|
|
BOOL result = SetEvent((HANDLE)*pEvent);
|
|
if (result == 0) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore)
|
|
{
|
|
*pSemaphore = CreateSemaphore(NULL, (LONG)initialValue, LONG_MAX, NULL);
|
|
if (*pSemaphore == NULL) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore)
|
|
{
|
|
CloseHandle((HANDLE)*pSemaphore);
|
|
}
|
|
|
|
static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore)
|
|
{
|
|
DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE);
|
|
if (result == WAIT_OBJECT_0) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
if (result == WAIT_TIMEOUT) {
|
|
return MA_TIMEOUT;
|
|
}
|
|
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore)
|
|
{
|
|
BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL);
|
|
if (result == 0) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
ma_thread_entry_proc entryProc;
|
|
void* pData;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
} ma_thread_proxy_data;
|
|
|
|
static ma_thread_result MA_THREADCALL ma_thread_entry_proxy(void* pData)
|
|
{
|
|
ma_thread_proxy_data* pProxyData = (ma_thread_proxy_data*)pData;
|
|
ma_thread_entry_proc entryProc;
|
|
void* pEntryProcData;
|
|
ma_thread_result result;
|
|
|
|
#if defined(MA_ON_THREAD_ENTRY)
|
|
MA_ON_THREAD_ENTRY
|
|
#endif
|
|
|
|
entryProc = pProxyData->entryProc;
|
|
pEntryProcData = pProxyData->pData;
|
|
|
|
ma_free(pProxyData, &pProxyData->allocationCallbacks);
|
|
|
|
result = entryProc(pEntryProcData);
|
|
|
|
#if defined(MA_ON_THREAD_EXIT)
|
|
MA_ON_THREAD_EXIT
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_result result;
|
|
ma_thread_proxy_data* pProxyData;
|
|
|
|
if (pThread == NULL || entryProc == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pProxyData = (ma_thread_proxy_data*)ma_malloc(sizeof(*pProxyData), pAllocationCallbacks);
|
|
if (pProxyData == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
#if defined(MA_THREAD_DEFAULT_STACK_SIZE)
|
|
if (stackSize == 0) {
|
|
stackSize = MA_THREAD_DEFAULT_STACK_SIZE;
|
|
}
|
|
#endif
|
|
|
|
pProxyData->entryProc = entryProc;
|
|
pProxyData->pData = pData;
|
|
ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks);
|
|
|
|
#if defined(MA_POSIX)
|
|
result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
|
|
#elif defined(MA_WIN32)
|
|
result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData);
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pProxyData, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_thread_wait(ma_thread* pThread)
|
|
{
|
|
if (pThread == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
ma_thread_wait__posix(pThread);
|
|
#elif defined(MA_WIN32)
|
|
ma_thread_wait__win32(pThread);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mutex_init(ma_mutex* pMutex)
|
|
{
|
|
if (pMutex == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_mutex_init__posix(pMutex);
|
|
#elif defined(MA_WIN32)
|
|
return ma_mutex_init__win32(pMutex);
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_mutex_uninit(ma_mutex* pMutex)
|
|
{
|
|
if (pMutex == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
ma_mutex_uninit__posix(pMutex);
|
|
#elif defined(MA_WIN32)
|
|
ma_mutex_uninit__win32(pMutex);
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_mutex_lock(ma_mutex* pMutex)
|
|
{
|
|
if (pMutex == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
ma_mutex_lock__posix(pMutex);
|
|
#elif defined(MA_WIN32)
|
|
ma_mutex_lock__win32(pMutex);
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_mutex_unlock(ma_mutex* pMutex)
|
|
{
|
|
if (pMutex == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
ma_mutex_unlock__posix(pMutex);
|
|
#elif defined(MA_WIN32)
|
|
ma_mutex_unlock__win32(pMutex);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_event_init(ma_event* pEvent)
|
|
{
|
|
if (pEvent == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_event_init__posix(pEvent);
|
|
#elif defined(MA_WIN32)
|
|
return ma_event_init__win32(pEvent);
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
static ma_result ma_event_alloc_and_init(ma_event** ppEvent, ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_result result;
|
|
ma_event* pEvent;
|
|
|
|
if (ppEvent == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*ppEvent = NULL;
|
|
|
|
pEvent = ma_malloc(sizeof(*pEvent), pAllocationCallbacks);
|
|
if (pEvent == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_event_init(pEvent);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pEvent, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppEvent = pEvent;
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_event_uninit(ma_event* pEvent)
|
|
{
|
|
if (pEvent == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
ma_event_uninit__posix(pEvent);
|
|
#elif defined(MA_WIN32)
|
|
ma_event_uninit__win32(pEvent);
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
static void ma_event_uninit_and_free(ma_event* pEvent, ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pEvent == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_event_uninit(pEvent);
|
|
ma_free(pEvent, pAllocationCallbacks);
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_result ma_event_wait(ma_event* pEvent)
|
|
{
|
|
if (pEvent == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_event_wait__posix(pEvent);
|
|
#elif defined(MA_WIN32)
|
|
return ma_event_wait__win32(pEvent);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_event_signal(ma_event* pEvent)
|
|
{
|
|
if (pEvent == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_event_signal__posix(pEvent);
|
|
#elif defined(MA_WIN32)
|
|
return ma_event_signal__win32(pEvent);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_semaphore_init__posix(initialValue, pSemaphore);
|
|
#elif defined(MA_WIN32)
|
|
return ma_semaphore_init__win32(initialValue, pSemaphore);
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
ma_semaphore_uninit__posix(pSemaphore);
|
|
#elif defined(MA_WIN32)
|
|
ma_semaphore_uninit__win32(pSemaphore);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_semaphore_wait__posix(pSemaphore);
|
|
#elif defined(MA_WIN32)
|
|
return ma_semaphore_wait__win32(pSemaphore);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore)
|
|
{
|
|
if (pSemaphore == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_POSIX)
|
|
return ma_semaphore_release__posix(pSemaphore);
|
|
#elif defined(MA_WIN32)
|
|
return ma_semaphore_release__win32(pSemaphore);
|
|
#endif
|
|
}
|
|
#else
|
|
#ifndef MA_NO_DEVICE_IO
|
|
#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO";
|
|
#endif
|
|
#endif
|
|
|
|
#define MA_FENCE_COUNTER_MAX 0x7FFFFFFF
|
|
|
|
MA_API ma_result ma_fence_init(ma_fence* pFence)
|
|
{
|
|
if (pFence == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFence);
|
|
pFence->counter = 0;
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_event_init(&pFence->e);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_fence_uninit(ma_fence* pFence)
|
|
{
|
|
if (pFence == NULL) {
|
|
return;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_event_uninit(&pFence->e);
|
|
}
|
|
#endif
|
|
|
|
MA_ZERO_OBJECT(pFence);
|
|
}
|
|
|
|
MA_API ma_result ma_fence_acquire(ma_fence* pFence)
|
|
{
|
|
if (pFence == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (;;) {
|
|
ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter);
|
|
ma_uint32 newCounter = oldCounter + 1;
|
|
|
|
if (newCounter > MA_FENCE_COUNTER_MAX) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_OUT_OF_RANGE;
|
|
}
|
|
|
|
if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
|
|
return MA_SUCCESS;
|
|
} else {
|
|
if (oldCounter == MA_FENCE_COUNTER_MAX) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_OUT_OF_RANGE;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
MA_API ma_result ma_fence_release(ma_fence* pFence)
|
|
{
|
|
if (pFence == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (;;) {
|
|
ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter);
|
|
ma_uint32 newCounter = oldCounter - 1;
|
|
|
|
if (oldCounter == 0) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
if (newCounter == 0) {
|
|
ma_event_signal(&pFence->e);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
} else {
|
|
if (oldCounter == 0) {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
MA_API ma_result ma_fence_wait(ma_fence* pFence)
|
|
{
|
|
if (pFence == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (;;) {
|
|
ma_uint32 counter;
|
|
|
|
counter = ma_atomic_load_32(&pFence->counter);
|
|
if (counter == 0) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_event_wait(&pFence->e);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
MA_API ma_result ma_async_notification_signal(ma_async_notification* pNotification)
|
|
{
|
|
ma_async_notification_callbacks* pNotificationCallbacks = (ma_async_notification_callbacks*)pNotification;
|
|
|
|
if (pNotification == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pNotificationCallbacks->onSignal == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
pNotificationCallbacks->onSignal(pNotification);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
static void ma_async_notification_poll__on_signal(ma_async_notification* pNotification)
|
|
{
|
|
((ma_async_notification_poll*)pNotification)->signalled = MA_TRUE;
|
|
}
|
|
|
|
MA_API ma_result ma_async_notification_poll_init(ma_async_notification_poll* pNotificationPoll)
|
|
{
|
|
if (pNotificationPoll == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pNotificationPoll->cb.onSignal = ma_async_notification_poll__on_signal;
|
|
pNotificationPoll->signalled = MA_FALSE;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_async_notification_poll_is_signalled(const ma_async_notification_poll* pNotificationPoll)
|
|
{
|
|
if (pNotificationPoll == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return pNotificationPoll->signalled;
|
|
}
|
|
|
|
static void ma_async_notification_event__on_signal(ma_async_notification* pNotification)
|
|
{
|
|
ma_async_notification_event_signal((ma_async_notification_event*)pNotification);
|
|
}
|
|
|
|
MA_API ma_result ma_async_notification_event_init(ma_async_notification_event* pNotificationEvent)
|
|
{
|
|
if (pNotificationEvent == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pNotificationEvent->cb.onSignal = ma_async_notification_event__on_signal;
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_event_init(&pNotificationEvent->e);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_async_notification_event_uninit(ma_async_notification_event* pNotificationEvent)
|
|
{
|
|
if (pNotificationEvent == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_event_uninit(&pNotificationEvent->e);
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_async_notification_event_wait(ma_async_notification_event* pNotificationEvent)
|
|
{
|
|
if (pNotificationEvent == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
return ma_event_wait(&pNotificationEvent->e);
|
|
}
|
|
#else
|
|
{
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_async_notification_event_signal(ma_async_notification_event* pNotificationEvent)
|
|
{
|
|
if (pNotificationEvent == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
return ma_event_signal(&pNotificationEvent->e);
|
|
}
|
|
#else
|
|
{
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_slot_allocator_config ma_slot_allocator_config_init(ma_uint32 capacity)
|
|
{
|
|
ma_slot_allocator_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.capacity = capacity;
|
|
|
|
return config;
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_slot_allocator_calculate_group_capacity(ma_uint32 slotCapacity)
|
|
{
|
|
ma_uint32 cap = slotCapacity / 32;
|
|
if ((slotCapacity % 32) != 0) {
|
|
cap += 1;
|
|
}
|
|
|
|
return cap;
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_slot_allocator_group_capacity(const ma_slot_allocator* pAllocator)
|
|
{
|
|
return ma_slot_allocator_calculate_group_capacity(pAllocator->capacity);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t groupsOffset;
|
|
size_t slotsOffset;
|
|
} ma_slot_allocator_heap_layout;
|
|
|
|
static ma_result ma_slot_allocator_get_heap_layout(const ma_slot_allocator_config* pConfig, ma_slot_allocator_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->capacity == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->groupsOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(ma_slot_allocator_calculate_group_capacity(pConfig->capacity) * sizeof(ma_slot_allocator_group));
|
|
|
|
pHeapLayout->slotsOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_uint32));
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_slot_allocator_get_heap_size(const ma_slot_allocator_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_slot_allocator_heap_layout layout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_slot_allocator_get_heap_layout(pConfig, &layout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = layout.sizeInBytes;
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_slot_allocator_init_preallocated(const ma_slot_allocator_config* pConfig, void* pHeap, ma_slot_allocator* pAllocator)
|
|
{
|
|
ma_result result;
|
|
ma_slot_allocator_heap_layout heapLayout;
|
|
|
|
if (pAllocator == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pAllocator);
|
|
|
|
if (pHeap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_slot_allocator_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pAllocator->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pAllocator->pGroups = (ma_slot_allocator_group*)ma_offset_ptr(pHeap, heapLayout.groupsOffset);
|
|
pAllocator->pSlots = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.slotsOffset);
|
|
pAllocator->capacity = pConfig->capacity;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_slot_allocator_init(const ma_slot_allocator_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_slot_allocator* pAllocator)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_slot_allocator_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_slot_allocator_init_preallocated(pConfig, pHeap, pAllocator);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pAllocator->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_slot_allocator_uninit(ma_slot_allocator* pAllocator, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocator == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pAllocator->_ownsHeap) {
|
|
ma_free(pAllocator->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint64* pSlot)
|
|
{
|
|
ma_uint32 iAttempt;
|
|
const ma_uint32 maxAttempts = 2;
|
|
|
|
if (pAllocator == NULL || pSlot == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iAttempt = 0; iAttempt < maxAttempts; iAttempt += 1) {
|
|
ma_uint32 iGroup;
|
|
for (iGroup = 0; iGroup < ma_slot_allocator_group_capacity(pAllocator); iGroup += 1) {
|
|
for (;;) {
|
|
ma_uint32 oldBitfield;
|
|
ma_uint32 newBitfield;
|
|
ma_uint32 bitOffset;
|
|
|
|
oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield);
|
|
|
|
if (oldBitfield == 0xFFFFFFFF) {
|
|
break;
|
|
}
|
|
|
|
bitOffset = ma_ffs_32(~oldBitfield);
|
|
MA_ASSERT(bitOffset < 32);
|
|
|
|
newBitfield = oldBitfield | (1 << bitOffset);
|
|
|
|
if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
|
|
ma_uint32 slotIndex;
|
|
|
|
ma_atomic_fetch_add_32(&pAllocator->count, 1);
|
|
|
|
slotIndex = (iGroup << 5) + bitOffset;
|
|
if (slotIndex >= pAllocator->capacity) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pAllocator->pSlots[slotIndex] += 1;
|
|
|
|
*pSlot = (((ma_uint64)pAllocator->pSlots[slotIndex] << 32) | slotIndex);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pAllocator->count < pAllocator->capacity) {
|
|
ma_yield();
|
|
} else {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 slot)
|
|
{
|
|
ma_uint32 iGroup;
|
|
ma_uint32 iBit;
|
|
|
|
if (pAllocator == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
iGroup = (ma_uint32)((slot & 0xFFFFFFFF) >> 5);
|
|
iBit = (ma_uint32)((slot & 0xFFFFFFFF) & 31);
|
|
|
|
if (iGroup >= ma_slot_allocator_group_capacity(pAllocator)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(iBit < 32);
|
|
|
|
while (ma_atomic_load_32(&pAllocator->count) > 0) {
|
|
ma_uint32 oldBitfield;
|
|
ma_uint32 newBitfield;
|
|
|
|
oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield);
|
|
newBitfield = oldBitfield & ~(1 << iBit);
|
|
|
|
#if defined(MA_DEBUG_OUTPUT)
|
|
{
|
|
if ((oldBitfield & (1 << iBit)) == 0) {
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) {
|
|
ma_atomic_fetch_sub_32(&pAllocator->count, 1);
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
#define MA_JOB_ID_NONE ~((ma_uint64)0)
|
|
#define MA_JOB_SLOT_NONE (ma_uint16)(~0)
|
|
|
|
static MA_INLINE ma_uint32 ma_job_extract_refcount(ma_uint64 toc)
|
|
{
|
|
return (ma_uint32)(toc >> 32);
|
|
}
|
|
|
|
static MA_INLINE ma_uint16 ma_job_extract_slot(ma_uint64 toc)
|
|
{
|
|
return (ma_uint16)(toc & 0x0000FFFF);
|
|
}
|
|
|
|
#if 0
|
|
static MA_INLINE ma_uint16 ma_job_extract_code(ma_uint64 toc)
|
|
{
|
|
return (ma_uint16)((toc & 0xFFFF0000) >> 16);
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE ma_uint64 ma_job_toc_to_allocation(ma_uint64 toc)
|
|
{
|
|
return ((ma_uint64)ma_job_extract_refcount(toc) << 32) | (ma_uint64)ma_job_extract_slot(toc);
|
|
}
|
|
|
|
static MA_INLINE ma_uint64 ma_job_set_refcount(ma_uint64 toc, ma_uint32 refcount)
|
|
{
|
|
toc = toc & ~((ma_uint64)0xFFFFFFFF << 32);
|
|
toc = toc | ((ma_uint64)refcount << 32);
|
|
|
|
return toc;
|
|
}
|
|
|
|
MA_API ma_job ma_job_init(ma_uint16 code)
|
|
{
|
|
ma_job job;
|
|
|
|
MA_ZERO_OBJECT(&job);
|
|
job.toc.breakup.code = code;
|
|
job.toc.breakup.slot = MA_JOB_SLOT_NONE;
|
|
job.next = MA_JOB_ID_NONE;
|
|
|
|
return job;
|
|
}
|
|
|
|
static ma_result ma_job_process__noop(ma_job* pJob);
|
|
static ma_result ma_job_process__quit(ma_job* pJob);
|
|
static ma_result ma_job_process__custom(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob);
|
|
static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob);
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob);
|
|
#endif
|
|
|
|
static ma_job_proc g_jobVTable[MA_JOB_TYPE_COUNT] =
|
|
{
|
|
ma_job_process__quit,
|
|
ma_job_process__custom,
|
|
|
|
ma_job_process__resource_manager__load_data_buffer_node,
|
|
ma_job_process__resource_manager__free_data_buffer_node,
|
|
ma_job_process__resource_manager__page_data_buffer_node,
|
|
ma_job_process__resource_manager__load_data_buffer,
|
|
ma_job_process__resource_manager__free_data_buffer,
|
|
ma_job_process__resource_manager__load_data_stream,
|
|
ma_job_process__resource_manager__free_data_stream,
|
|
ma_job_process__resource_manager__page_data_stream,
|
|
ma_job_process__resource_manager__seek_data_stream,
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
ma_job_process__device__aaudio_reroute
|
|
#endif
|
|
};
|
|
|
|
MA_API ma_result ma_job_process(ma_job* pJob)
|
|
{
|
|
if (pJob == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pJob->toc.breakup.code >= MA_JOB_TYPE_COUNT) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return g_jobVTable[pJob->toc.breakup.code](pJob);
|
|
}
|
|
|
|
static ma_result ma_job_process__noop(ma_job* pJob)
|
|
{
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
(void)pJob;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_job_process__quit(ma_job* pJob)
|
|
{
|
|
return ma_job_process__noop(pJob);
|
|
}
|
|
|
|
static ma_result ma_job_process__custom(ma_job* pJob)
|
|
{
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
if (pJob->data.custom.proc == NULL) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
return pJob->data.custom.proc(pJob);
|
|
}
|
|
|
|
MA_API ma_job_queue_config ma_job_queue_config_init(ma_uint32 flags, ma_uint32 capacity)
|
|
{
|
|
ma_job_queue_config config;
|
|
|
|
config.flags = flags;
|
|
config.capacity = capacity;
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t allocatorOffset;
|
|
size_t jobsOffset;
|
|
} ma_job_queue_heap_layout;
|
|
|
|
static ma_result ma_job_queue_get_heap_layout(const ma_job_queue_config* pConfig, ma_job_queue_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->capacity == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
{
|
|
ma_slot_allocator_config allocatorConfig;
|
|
size_t allocatorHeapSizeInBytes;
|
|
|
|
allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
|
|
result = ma_slot_allocator_get_heap_size(&allocatorConfig, &allocatorHeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->allocatorOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += allocatorHeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->jobsOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(pConfig->capacity * sizeof(ma_job));
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_job_queue_get_heap_size(const ma_job_queue_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_job_queue_heap_layout layout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_job_queue_get_heap_layout(pConfig, &layout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = layout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_job_queue_init_preallocated(const ma_job_queue_config* pConfig, void* pHeap, ma_job_queue* pQueue)
|
|
{
|
|
ma_result result;
|
|
ma_job_queue_heap_layout heapLayout;
|
|
ma_slot_allocator_config allocatorConfig;
|
|
|
|
if (pQueue == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pQueue);
|
|
|
|
result = ma_job_queue_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pQueue->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pQueue->flags = pConfig->flags;
|
|
pQueue->capacity = pConfig->capacity;
|
|
pQueue->pJobs = (ma_job*)ma_offset_ptr(pHeap, heapLayout.jobsOffset);
|
|
|
|
allocatorConfig = ma_slot_allocator_config_init(pConfig->capacity);
|
|
result = ma_slot_allocator_init_preallocated(&allocatorConfig, ma_offset_ptr(pHeap, heapLayout.allocatorOffset), &pQueue->allocator);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_semaphore_init(0, &pQueue->sem);
|
|
}
|
|
#else
|
|
{
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ma_slot_allocator_alloc(&pQueue->allocator, &pQueue->head);
|
|
pQueue->pJobs[ma_job_extract_slot(pQueue->head)].next = MA_JOB_ID_NONE;
|
|
pQueue->tail = pQueue->head;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_job_queue_init(const ma_job_queue_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_job_queue* pQueue)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_job_queue_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_job_queue_init_preallocated(pConfig, pHeap, pQueue);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pQueue->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pQueue == NULL) {
|
|
return;
|
|
}
|
|
|
|
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_semaphore_uninit(&pQueue->sem);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ma_slot_allocator_uninit(&pQueue->allocator, pAllocationCallbacks);
|
|
|
|
if (pQueue->_ownsHeap) {
|
|
ma_free(pQueue->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired)
|
|
{
|
|
return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected;
|
|
}
|
|
|
|
MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 slot;
|
|
ma_uint64 tail;
|
|
ma_uint64 next;
|
|
|
|
if (pQueue == NULL || pJob == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_slot_allocator_alloc(&pQueue->allocator, &slot);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
MA_ASSERT(ma_job_extract_slot(slot) < pQueue->capacity);
|
|
|
|
pQueue->pJobs[ma_job_extract_slot(slot)] = *pJob;
|
|
pQueue->pJobs[ma_job_extract_slot(slot)].toc.allocation = slot;
|
|
pQueue->pJobs[ma_job_extract_slot(slot)].toc.breakup.code = pJob->toc.breakup.code;
|
|
pQueue->pJobs[ma_job_extract_slot(slot)].next = MA_JOB_ID_NONE;
|
|
|
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
|
ma_spinlock_lock(&pQueue->lock);
|
|
#endif
|
|
{
|
|
for (;;) {
|
|
tail = ma_atomic_load_64(&pQueue->tail);
|
|
next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next);
|
|
|
|
if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) {
|
|
if (ma_job_extract_slot(next) == 0xFFFF) {
|
|
if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) {
|
|
break;
|
|
}
|
|
} else {
|
|
ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
|
|
}
|
|
}
|
|
}
|
|
ma_job_queue_cas(&pQueue->tail, tail, slot);
|
|
}
|
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
|
ma_spinlock_unlock(&pQueue->lock);
|
|
#endif
|
|
|
|
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_semaphore_release(&pQueue->sem);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob)
|
|
{
|
|
ma_uint64 head;
|
|
ma_uint64 tail;
|
|
ma_uint64 next;
|
|
|
|
if (pQueue == NULL || pJob == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pQueue->flags & MA_JOB_QUEUE_FLAG_NON_BLOCKING) == 0) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_semaphore_wait(&pQueue->sem);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
|
ma_spinlock_lock(&pQueue->lock);
|
|
#endif
|
|
{
|
|
|
|
for (;;) {
|
|
head = ma_atomic_load_64(&pQueue->head);
|
|
tail = ma_atomic_load_64(&pQueue->tail);
|
|
next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next);
|
|
|
|
if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) {
|
|
if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) {
|
|
if (ma_job_extract_slot(next) == 0xFFFF) {
|
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
|
ma_spinlock_unlock(&pQueue->lock);
|
|
#endif
|
|
return MA_NO_DATA_AVAILABLE;
|
|
}
|
|
ma_job_queue_cas(&pQueue->tail, tail, ma_job_extract_slot(next));
|
|
} else {
|
|
*pJob = pQueue->pJobs[ma_job_extract_slot(next)];
|
|
if (ma_job_queue_cas(&pQueue->head, head, ma_job_extract_slot(next))) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE
|
|
ma_spinlock_unlock(&pQueue->lock);
|
|
#endif
|
|
|
|
ma_slot_allocator_free(&pQueue->allocator, head);
|
|
|
|
if (pJob->toc.breakup.code == MA_JOB_TYPE_QUIT) {
|
|
ma_job_queue_post(pQueue, pJob);
|
|
return MA_CANCELLED;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
#if defined(MA_EMSCRIPTEN) || defined(MA_ORBIS) || defined(MA_PROSPERO) || defined(MA_SWITCH) || defined(MA_DOS)
|
|
#define MA_NO_RUNTIME_LINKING
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef MA_POSIX
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
#include <dlfcn.h>
|
|
#endif
|
|
#endif
|
|
|
|
MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename)
|
|
{
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
{
|
|
ma_handle handle;
|
|
|
|
ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename);
|
|
|
|
#ifdef MA_WIN32
|
|
#if !defined(MA_WIN32_UWP) || !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP)))
|
|
handle = (ma_handle)LoadLibraryA(filename);
|
|
#else
|
|
WCHAR filenameW[4096];
|
|
if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) {
|
|
handle = NULL;
|
|
} else {
|
|
handle = (ma_handle)LoadPackagedLibrary(filenameW, 0);
|
|
}
|
|
#endif
|
|
#else
|
|
handle = (ma_handle)dlopen(filename, RTLD_NOW);
|
|
#endif
|
|
|
|
if (handle == NULL) {
|
|
ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename);
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
#else
|
|
{
|
|
(void)pLog;
|
|
(void)filename;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_dlclose(ma_log* pLog, ma_handle handle)
|
|
{
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
{
|
|
#ifdef MA_WIN32
|
|
{
|
|
FreeLibrary((HMODULE)handle);
|
|
}
|
|
#else
|
|
{
|
|
#if !defined(MA_ANDROID) || (defined(__ANDROID_API__) && __ANDROID_API__ >= 28)
|
|
{
|
|
dlclose((void*)handle);
|
|
}
|
|
#else
|
|
{
|
|
(void)handle;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
(void)pLog;
|
|
}
|
|
#else
|
|
{
|
|
(void)pLog;
|
|
(void)handle;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol)
|
|
{
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
{
|
|
ma_proc proc;
|
|
|
|
ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol);
|
|
|
|
#ifdef _WIN32
|
|
{
|
|
proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol);
|
|
}
|
|
#else
|
|
{
|
|
#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#endif
|
|
proc = (ma_proc)dlsym((void*)handle, symbol);
|
|
#if (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) || defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
if (proc == NULL) {
|
|
ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol);
|
|
}
|
|
|
|
(void)pLog;
|
|
return proc;
|
|
}
|
|
#else
|
|
{
|
|
(void)pLog;
|
|
(void)handle;
|
|
(void)symbol;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef MA_APPLE
|
|
#include <AvailabilityMacros.h>
|
|
#endif
|
|
|
|
#ifndef MA_NO_DEVICE_IO
|
|
|
|
#if defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
|
|
#include <mach/mach_time.h>
|
|
#endif
|
|
|
|
#ifdef MA_POSIX
|
|
#include <sys/types.h>
|
|
#endif
|
|
|
|
#ifndef MA_AAUDIO_MIN_ANDROID_SDK_VERSION
|
|
#define MA_AAUDIO_MIN_ANDROID_SDK_VERSION 27
|
|
#endif
|
|
|
|
MA_API void ma_device_info_add_native_data_format(ma_device_info* pDeviceInfo, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags)
|
|
{
|
|
if (pDeviceInfo == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats)) {
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_backend backend;
|
|
const char* pName;
|
|
} ma_backend_info;
|
|
|
|
static ma_backend_info gBackendInfo[] =
|
|
{
|
|
{ma_backend_wasapi, "WASAPI"},
|
|
{ma_backend_dsound, "DirectSound"},
|
|
{ma_backend_winmm, "WinMM"},
|
|
{ma_backend_coreaudio, "Core Audio"},
|
|
{ma_backend_sndio, "sndio"},
|
|
{ma_backend_audio4, "audio(4)"},
|
|
{ma_backend_oss, "OSS"},
|
|
{ma_backend_pulseaudio, "PulseAudio"},
|
|
{ma_backend_alsa, "ALSA"},
|
|
{ma_backend_jack, "JACK"},
|
|
{ma_backend_aaudio, "AAudio"},
|
|
{ma_backend_opensl, "OpenSL|ES"},
|
|
{ma_backend_webaudio, "Web Audio"},
|
|
{ma_backend_custom, "Custom"},
|
|
{ma_backend_null, "Null"}
|
|
};
|
|
|
|
MA_API const char* ma_get_backend_name(ma_backend backend)
|
|
{
|
|
if (backend < 0 || backend >= (int)ma_countof(gBackendInfo)) {
|
|
return "Unknown";
|
|
}
|
|
|
|
return gBackendInfo[backend].pName;
|
|
}
|
|
|
|
MA_API ma_result ma_get_backend_from_name(const char* pBackendName, ma_backend* pBackend)
|
|
{
|
|
size_t iBackend;
|
|
|
|
if (pBackendName == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iBackend = 0; iBackend < ma_countof(gBackendInfo); iBackend += 1) {
|
|
if (ma_strcmp(pBackendName, gBackendInfo[iBackend].pName) == 0) {
|
|
if (pBackend != NULL) {
|
|
*pBackend = gBackendInfo[iBackend].backend;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_is_backend_enabled(ma_backend backend)
|
|
{
|
|
switch (backend)
|
|
{
|
|
case ma_backend_wasapi:
|
|
#if defined(MA_HAS_WASAPI)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_dsound:
|
|
#if defined(MA_HAS_DSOUND)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_winmm:
|
|
#if defined(MA_HAS_WINMM)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_coreaudio:
|
|
#if defined(MA_HAS_COREAUDIO)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_sndio:
|
|
#if defined(MA_HAS_SNDIO)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_audio4:
|
|
#if defined(MA_HAS_AUDIO4)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_oss:
|
|
#if defined(MA_HAS_OSS)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_pulseaudio:
|
|
#if defined(MA_HAS_PULSEAUDIO)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_alsa:
|
|
#if defined(MA_HAS_ALSA)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_jack:
|
|
#if defined(MA_HAS_JACK)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_aaudio:
|
|
#if defined(MA_HAS_AAUDIO)
|
|
#if defined(MA_ANDROID)
|
|
{
|
|
return ma_android_sdk_version() >= MA_AAUDIO_MIN_ANDROID_SDK_VERSION;
|
|
}
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_opensl:
|
|
#if defined(MA_HAS_OPENSL)
|
|
#if defined(MA_ANDROID)
|
|
{
|
|
return ma_android_sdk_version() >= 9;
|
|
}
|
|
#else
|
|
return MA_TRUE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_webaudio:
|
|
#if defined(MA_HAS_WEBAUDIO)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_custom:
|
|
#if defined(MA_HAS_CUSTOM)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
case ma_backend_null:
|
|
#if defined(MA_HAS_NULL)
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
|
|
default: return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_get_enabled_backends(ma_backend* pBackends, size_t backendCap, size_t* pBackendCount)
|
|
{
|
|
size_t backendCount;
|
|
size_t iBackend;
|
|
ma_result result = MA_SUCCESS;
|
|
|
|
if (pBackendCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
backendCount = 0;
|
|
|
|
for (iBackend = 0; iBackend <= ma_backend_null; iBackend += 1) {
|
|
ma_backend backend = (ma_backend)iBackend;
|
|
|
|
if (ma_is_backend_enabled(backend)) {
|
|
if (backendCount == backendCap) {
|
|
result = MA_NO_SPACE;
|
|
break;
|
|
} else {
|
|
pBackends[backendCount] = backend;
|
|
backendCount += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pBackendCount != NULL) {
|
|
*pBackendCount = backendCount;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend)
|
|
{
|
|
switch (backend)
|
|
{
|
|
case ma_backend_wasapi: return MA_TRUE;
|
|
case ma_backend_dsound: return MA_FALSE;
|
|
case ma_backend_winmm: return MA_FALSE;
|
|
case ma_backend_coreaudio: return MA_FALSE;
|
|
case ma_backend_sndio: return MA_FALSE;
|
|
case ma_backend_audio4: return MA_FALSE;
|
|
case ma_backend_oss: return MA_FALSE;
|
|
case ma_backend_pulseaudio: return MA_FALSE;
|
|
case ma_backend_alsa: return MA_FALSE;
|
|
case ma_backend_jack: return MA_FALSE;
|
|
case ma_backend_aaudio: return MA_FALSE;
|
|
case ma_backend_opensl: return MA_FALSE;
|
|
case ma_backend_webaudio: return MA_FALSE;
|
|
case ma_backend_custom: return MA_FALSE;
|
|
case ma_backend_null: return MA_FALSE;
|
|
default: return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
#if defined(MA_WIN32) && !defined(MA_XBOX)
|
|
#define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001)
|
|
#define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002)
|
|
#define MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE ((HRESULT)0x88890003)
|
|
#define MA_AUDCLNT_E_DEVICE_INVALIDATED ((HRESULT)0x88890004)
|
|
#define MA_AUDCLNT_E_NOT_STOPPED ((HRESULT)0x88890005)
|
|
#define MA_AUDCLNT_E_BUFFER_TOO_LARGE ((HRESULT)0x88890006)
|
|
#define MA_AUDCLNT_E_OUT_OF_ORDER ((HRESULT)0x88890007)
|
|
#define MA_AUDCLNT_E_UNSUPPORTED_FORMAT ((HRESULT)0x88890008)
|
|
#define MA_AUDCLNT_E_INVALID_SIZE ((HRESULT)0x88890009)
|
|
#define MA_AUDCLNT_E_DEVICE_IN_USE ((HRESULT)0x8889000A)
|
|
#define MA_AUDCLNT_E_BUFFER_OPERATION_PENDING ((HRESULT)0x8889000B)
|
|
#define MA_AUDCLNT_E_THREAD_NOT_REGISTERED ((HRESULT)0x8889000C)
|
|
#define MA_AUDCLNT_E_NO_SINGLE_PROCESS ((HRESULT)0x8889000D)
|
|
#define MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED ((HRESULT)0x8889000E)
|
|
#define MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED ((HRESULT)0x8889000F)
|
|
#define MA_AUDCLNT_E_SERVICE_NOT_RUNNING ((HRESULT)0x88890010)
|
|
#define MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED ((HRESULT)0x88890011)
|
|
#define MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY ((HRESULT)0x88890012)
|
|
#define MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL ((HRESULT)0x88890013)
|
|
#define MA_AUDCLNT_E_EVENTHANDLE_NOT_SET ((HRESULT)0x88890014)
|
|
#define MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE ((HRESULT)0x88890015)
|
|
#define MA_AUDCLNT_E_BUFFER_SIZE_ERROR ((HRESULT)0x88890016)
|
|
#define MA_AUDCLNT_E_CPUUSAGE_EXCEEDED ((HRESULT)0x88890017)
|
|
#define MA_AUDCLNT_E_BUFFER_ERROR ((HRESULT)0x88890018)
|
|
#define MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED ((HRESULT)0x88890019)
|
|
#define MA_AUDCLNT_E_INVALID_DEVICE_PERIOD ((HRESULT)0x88890020)
|
|
#define MA_AUDCLNT_E_INVALID_STREAM_FLAG ((HRESULT)0x88890021)
|
|
#define MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE ((HRESULT)0x88890022)
|
|
#define MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES ((HRESULT)0x88890023)
|
|
#define MA_AUDCLNT_E_OFFLOAD_MODE_ONLY ((HRESULT)0x88890024)
|
|
#define MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY ((HRESULT)0x88890025)
|
|
#define MA_AUDCLNT_E_RESOURCES_INVALIDATED ((HRESULT)0x88890026)
|
|
#define MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED ((HRESULT)0x88890027)
|
|
#define MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED ((HRESULT)0x88890028)
|
|
#define MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED ((HRESULT)0x88890029)
|
|
#define MA_AUDCLNT_E_HEADTRACKING_ENABLED ((HRESULT)0x88890030)
|
|
#define MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED ((HRESULT)0x88890040)
|
|
#define MA_AUDCLNT_S_BUFFER_EMPTY ((HRESULT)0x08890001)
|
|
#define MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED ((HRESULT)0x08890002)
|
|
#define MA_AUDCLNT_S_POSITION_STALLED ((HRESULT)0x08890003)
|
|
|
|
#define MA_DS_OK ((HRESULT)0)
|
|
#define MA_DS_NO_VIRTUALIZATION ((HRESULT)0x0878000A)
|
|
#define MA_DSERR_ALLOCATED ((HRESULT)0x8878000A)
|
|
#define MA_DSERR_CONTROLUNAVAIL ((HRESULT)0x8878001E)
|
|
#define MA_DSERR_INVALIDPARAM ((HRESULT)0x80070057)
|
|
#define MA_DSERR_INVALIDCALL ((HRESULT)0x88780032)
|
|
#define MA_DSERR_GENERIC ((HRESULT)0x80004005)
|
|
#define MA_DSERR_PRIOLEVELNEEDED ((HRESULT)0x88780046)
|
|
#define MA_DSERR_OUTOFMEMORY ((HRESULT)0x8007000E)
|
|
#define MA_DSERR_BADFORMAT ((HRESULT)0x88780064)
|
|
#define MA_DSERR_UNSUPPORTED ((HRESULT)0x80004001)
|
|
#define MA_DSERR_NODRIVER ((HRESULT)0x88780078)
|
|
#define MA_DSERR_ALREADYINITIALIZED ((HRESULT)0x88780082)
|
|
#define MA_DSERR_NOAGGREGATION ((HRESULT)0x80040110)
|
|
#define MA_DSERR_BUFFERLOST ((HRESULT)0x88780096)
|
|
#define MA_DSERR_OTHERAPPHASPRIO ((HRESULT)0x887800A0)
|
|
#define MA_DSERR_UNINITIALIZED ((HRESULT)0x887800AA)
|
|
#define MA_DSERR_NOINTERFACE ((HRESULT)0x80004002)
|
|
#define MA_DSERR_ACCESSDENIED ((HRESULT)0x80070005)
|
|
#define MA_DSERR_BUFFERTOOSMALL ((HRESULT)0x887800B4)
|
|
#define MA_DSERR_DS8_REQUIRED ((HRESULT)0x887800BE)
|
|
#define MA_DSERR_SENDLOOP ((HRESULT)0x887800C8)
|
|
#define MA_DSERR_BADSENDBUFFERGUID ((HRESULT)0x887800D2)
|
|
#define MA_DSERR_OBJECTNOTFOUND ((HRESULT)0x88781161)
|
|
#define MA_DSERR_FXUNAVAILABLE ((HRESULT)0x887800DC)
|
|
|
|
static ma_result ma_result_from_HRESULT(HRESULT hr)
|
|
{
|
|
switch (hr)
|
|
{
|
|
case NOERROR: return MA_SUCCESS;
|
|
|
|
case E_POINTER: return MA_INVALID_ARGS;
|
|
case E_UNEXPECTED: return MA_ERROR;
|
|
case E_NOTIMPL: return MA_NOT_IMPLEMENTED;
|
|
case E_OUTOFMEMORY: return MA_OUT_OF_MEMORY;
|
|
case E_INVALIDARG: return MA_INVALID_ARGS;
|
|
case E_NOINTERFACE: return MA_API_NOT_FOUND;
|
|
case E_HANDLE: return MA_INVALID_ARGS;
|
|
case E_ABORT: return MA_ERROR;
|
|
case E_FAIL: return MA_ERROR;
|
|
case E_ACCESSDENIED: return MA_ACCESS_DENIED;
|
|
|
|
case MA_AUDCLNT_E_NOT_INITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
|
|
case MA_AUDCLNT_E_ALREADY_INITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
|
|
case MA_AUDCLNT_E_WRONG_ENDPOINT_TYPE: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_DEVICE_INVALIDATED: return MA_UNAVAILABLE;
|
|
case MA_AUDCLNT_E_NOT_STOPPED: return MA_DEVICE_NOT_STOPPED;
|
|
case MA_AUDCLNT_E_BUFFER_TOO_LARGE: return MA_TOO_BIG;
|
|
case MA_AUDCLNT_E_OUT_OF_ORDER: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_UNSUPPORTED_FORMAT: return MA_FORMAT_NOT_SUPPORTED;
|
|
case MA_AUDCLNT_E_INVALID_SIZE: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_DEVICE_IN_USE: return MA_BUSY;
|
|
case MA_AUDCLNT_E_BUFFER_OPERATION_PENDING: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_THREAD_NOT_REGISTERED: return MA_DOES_NOT_EXIST;
|
|
case MA_AUDCLNT_E_NO_SINGLE_PROCESS: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED: return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
case MA_AUDCLNT_E_ENDPOINT_CREATE_FAILED: return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
case MA_AUDCLNT_E_SERVICE_NOT_RUNNING: return MA_NOT_CONNECTED;
|
|
case MA_AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_EXCLUSIVE_MODE_ONLY: return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
case MA_AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_EVENTHANDLE_NOT_SET: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_INCORRECT_BUFFER_SIZE: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_BUFFER_SIZE_ERROR: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_CPUUSAGE_EXCEEDED: return MA_ERROR;
|
|
case MA_AUDCLNT_E_BUFFER_ERROR: return MA_ERROR;
|
|
case MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_INVALID_DEVICE_PERIOD: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_INVALID_STREAM_FLAG: return MA_INVALID_ARGS;
|
|
case MA_AUDCLNT_E_ENDPOINT_OFFLOAD_NOT_CAPABLE: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_OUT_OF_OFFLOAD_RESOURCES: return MA_OUT_OF_MEMORY;
|
|
case MA_AUDCLNT_E_OFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_NONOFFLOAD_MODE_ONLY: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_RESOURCES_INVALIDATED: return MA_INVALID_DATA;
|
|
case MA_AUDCLNT_E_RAW_MODE_UNSUPPORTED: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_ENGINE_PERIODICITY_LOCKED: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_ENGINE_FORMAT_LOCKED: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_HEADTRACKING_ENABLED: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_E_HEADTRACKING_UNSUPPORTED: return MA_INVALID_OPERATION;
|
|
case MA_AUDCLNT_S_BUFFER_EMPTY: return MA_NO_SPACE;
|
|
case MA_AUDCLNT_S_THREAD_ALREADY_REGISTERED: return MA_ALREADY_EXISTS;
|
|
case MA_AUDCLNT_S_POSITION_STALLED: return MA_ERROR;
|
|
|
|
case MA_DS_NO_VIRTUALIZATION: return MA_SUCCESS;
|
|
case MA_DSERR_ALLOCATED: return MA_ALREADY_IN_USE;
|
|
case MA_DSERR_CONTROLUNAVAIL: return MA_INVALID_OPERATION;
|
|
case MA_DSERR_INVALIDCALL: return MA_INVALID_OPERATION;
|
|
case MA_DSERR_PRIOLEVELNEEDED: return MA_INVALID_OPERATION;
|
|
case MA_DSERR_BADFORMAT: return MA_FORMAT_NOT_SUPPORTED;
|
|
case MA_DSERR_NODRIVER: return MA_FAILED_TO_INIT_BACKEND;
|
|
case MA_DSERR_ALREADYINITIALIZED: return MA_DEVICE_ALREADY_INITIALIZED;
|
|
case MA_DSERR_NOAGGREGATION: return MA_ERROR;
|
|
case MA_DSERR_BUFFERLOST: return MA_UNAVAILABLE;
|
|
case MA_DSERR_OTHERAPPHASPRIO: return MA_ACCESS_DENIED;
|
|
case MA_DSERR_UNINITIALIZED: return MA_DEVICE_NOT_INITIALIZED;
|
|
case MA_DSERR_BUFFERTOOSMALL: return MA_NO_SPACE;
|
|
case MA_DSERR_DS8_REQUIRED: return MA_INVALID_OPERATION;
|
|
case MA_DSERR_SENDLOOP: return MA_DEADLOCK;
|
|
case MA_DSERR_BADSENDBUFFERGUID: return MA_INVALID_ARGS;
|
|
case MA_DSERR_OBJECTNOTFOUND: return MA_NO_DEVICE;
|
|
case MA_DSERR_FXUNAVAILABLE: return MA_UNAVAILABLE;
|
|
|
|
default: return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
#define MA_VT_LPWSTR 31
|
|
#define MA_VT_BLOB 65
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4201)
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic ignored "-Wc11-extensions"
|
|
#endif
|
|
#endif
|
|
typedef struct
|
|
{
|
|
WORD vt;
|
|
WORD wReserved1;
|
|
WORD wReserved2;
|
|
WORD wReserved3;
|
|
union
|
|
{
|
|
struct
|
|
{
|
|
ULONG cbSize;
|
|
BYTE* pBlobData;
|
|
} blob;
|
|
WCHAR* pwszVal;
|
|
char pad[16];
|
|
};
|
|
} MA_PROPVARIANT;
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#pragma warning(pop)
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved);
|
|
typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit);
|
|
typedef void (WINAPI * MA_PFN_CoUninitialize)(void);
|
|
typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv);
|
|
typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv);
|
|
typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar);
|
|
typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax);
|
|
|
|
typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void);
|
|
typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void);
|
|
|
|
#if defined(MA_WIN32_DESKTOP)
|
|
typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult);
|
|
typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey);
|
|
typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData);
|
|
#endif
|
|
|
|
static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
|
static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
|
|
|
|
MA_API size_t ma_strlen_WCHAR(const WCHAR* str)
|
|
{
|
|
size_t len = 0;
|
|
while (str[len] != '\0') {
|
|
len += 1;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2)
|
|
{
|
|
while (*s1 != '\0' && *s1 == *s2) {
|
|
s1 += 1;
|
|
s2 += 1;
|
|
}
|
|
|
|
return *s1 - *s2;
|
|
}
|
|
|
|
MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src)
|
|
{
|
|
size_t i;
|
|
|
|
if (dst == 0) {
|
|
return 22;
|
|
}
|
|
if (dstCap == 0) {
|
|
return 34;
|
|
}
|
|
if (src == 0) {
|
|
dst[0] = '\0';
|
|
return 22;
|
|
}
|
|
|
|
for (i = 0; i < dstCap && src[i] != '\0'; ++i) {
|
|
dst[i] = src[i];
|
|
}
|
|
|
|
if (i < dstCap) {
|
|
dst[i] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
dst[0] = '\0';
|
|
return 34;
|
|
}
|
|
#endif
|
|
|
|
#define MA_DEFAULT_PLAYBACK_DEVICE_NAME "Default Playback Device"
|
|
#define MA_DEFAULT_CAPTURE_DEVICE_NAME "Default Capture Device"
|
|
|
|
#if defined(MA_WIN32) && !defined(MA_POSIX)
|
|
static LARGE_INTEGER g_ma_TimerFrequency;
|
|
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
|
|
{
|
|
LARGE_INTEGER counter;
|
|
|
|
if (g_ma_TimerFrequency.QuadPart == 0) {
|
|
QueryPerformanceFrequency(&g_ma_TimerFrequency);
|
|
}
|
|
|
|
QueryPerformanceCounter(&counter);
|
|
pTimer->counter = counter.QuadPart;
|
|
}
|
|
|
|
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
|
|
{
|
|
LARGE_INTEGER counter;
|
|
if (!QueryPerformanceCounter(&counter)) {
|
|
return 0;
|
|
}
|
|
|
|
return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
|
|
}
|
|
#elif defined(MA_APPLE) && (MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
|
|
static ma_uint64 g_ma_TimerFrequency = 0;
|
|
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
|
|
{
|
|
mach_timebase_info_data_t baseTime;
|
|
mach_timebase_info(&baseTime);
|
|
g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;
|
|
|
|
pTimer->counter = mach_absolute_time();
|
|
}
|
|
|
|
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
|
|
{
|
|
ma_uint64 newTimeCounter = mach_absolute_time();
|
|
ma_uint64 oldTimeCounter = pTimer->counter;
|
|
|
|
return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
|
|
}
|
|
#elif defined(MA_EMSCRIPTEN)
|
|
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
|
|
{
|
|
pTimer->counterD = emscripten_get_now();
|
|
}
|
|
|
|
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
|
|
{
|
|
return (emscripten_get_now() - pTimer->counterD) / 1000;
|
|
}
|
|
#else
|
|
#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
|
|
#if defined(CLOCK_MONOTONIC)
|
|
#define MA_CLOCK_ID CLOCK_MONOTONIC
|
|
#else
|
|
#define MA_CLOCK_ID CLOCK_REALTIME
|
|
#endif
|
|
|
|
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
|
|
{
|
|
struct timespec newTime;
|
|
clock_gettime(MA_CLOCK_ID, &newTime);
|
|
|
|
pTimer->counter = ((ma_int64)newTime.tv_sec * 1000000000) + newTime.tv_nsec;
|
|
}
|
|
|
|
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
|
|
{
|
|
ma_uint64 newTimeCounter;
|
|
ma_uint64 oldTimeCounter;
|
|
|
|
struct timespec newTime;
|
|
clock_gettime(MA_CLOCK_ID, &newTime);
|
|
|
|
newTimeCounter = ((ma_uint64)newTime.tv_sec * 1000000000) + newTime.tv_nsec;
|
|
oldTimeCounter = pTimer->counter;
|
|
|
|
return (newTimeCounter - oldTimeCounter) / 1000000000.0;
|
|
}
|
|
#else
|
|
static MA_INLINE void ma_timer_init(ma_timer* pTimer)
|
|
{
|
|
struct timeval newTime;
|
|
gettimeofday(&newTime, NULL);
|
|
|
|
pTimer->counter = ((ma_int64)newTime.tv_sec * 1000000) + newTime.tv_usec;
|
|
}
|
|
|
|
static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
|
|
{
|
|
ma_uint64 newTimeCounter;
|
|
ma_uint64 oldTimeCounter;
|
|
|
|
struct timeval newTime;
|
|
gettimeofday(&newTime, NULL);
|
|
|
|
newTimeCounter = ((ma_uint64)newTime.tv_sec * 1000000) + newTime.tv_usec;
|
|
oldTimeCounter = pTimer->counter;
|
|
|
|
return (newTimeCounter - oldTimeCounter) / 1000000.0;
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#if 0
|
|
static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn)
|
|
{
|
|
ma_uint32 closestRate = 0;
|
|
ma_uint32 closestDiff = 0xFFFFFFFF;
|
|
size_t iStandardRate;
|
|
|
|
for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
|
|
ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
|
|
ma_uint32 diff;
|
|
|
|
if (sampleRateIn > standardRate) {
|
|
diff = sampleRateIn - standardRate;
|
|
} else {
|
|
diff = standardRate - sampleRateIn;
|
|
}
|
|
|
|
if (diff == 0) {
|
|
return standardRate;
|
|
}
|
|
|
|
if (closestDiff > diff) {
|
|
closestDiff = diff;
|
|
closestRate = standardRate;
|
|
}
|
|
}
|
|
|
|
return closestRate;
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE unsigned int ma_device_disable_denormals(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (!pDevice->noDisableDenormals) {
|
|
return ma_disable_denormals();
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_device_restore_denormals(ma_device* pDevice, unsigned int prevState)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (!pDevice->noDisableDenormals) {
|
|
ma_restore_denormals(prevState);
|
|
} else {
|
|
(void)prevState;
|
|
}
|
|
}
|
|
|
|
static ma_device_notification ma_device_notification_init(ma_device* pDevice, ma_device_notification_type type)
|
|
{
|
|
ma_device_notification notification;
|
|
|
|
MA_ZERO_OBJECT(¬ification);
|
|
notification.pDevice = pDevice;
|
|
notification.type = type;
|
|
|
|
return notification;
|
|
}
|
|
|
|
static void ma_device__on_notification(ma_device_notification notification)
|
|
{
|
|
MA_ASSERT(notification.pDevice != NULL);
|
|
|
|
if (notification.pDevice->onNotification != NULL) {
|
|
notification.pDevice->onNotification(¬ification);
|
|
}
|
|
|
|
if (notification.pDevice->onStop != NULL && notification.type == ma_device_notification_type_stopped) {
|
|
notification.pDevice->onStop(notification.pDevice);
|
|
}
|
|
}
|
|
|
|
static void ma_device__on_notification_started(ma_device* pDevice)
|
|
{
|
|
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started));
|
|
}
|
|
|
|
static void ma_device__on_notification_stopped(ma_device* pDevice)
|
|
{
|
|
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped));
|
|
}
|
|
|
|
#if !defined(MA_EMSCRIPTEN)
|
|
static void ma_device__on_notification_rerouted(ma_device* pDevice)
|
|
{
|
|
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted));
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_EMSCRIPTEN)
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
void EMSCRIPTEN_KEEPALIVE ma_device__on_notification_unlocked(ma_device* pDevice)
|
|
{
|
|
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_unlocked));
|
|
}
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pDevice->onData != NULL);
|
|
|
|
if (!pDevice->noPreSilencedOutputBuffer && pFramesOut != NULL) {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
|
|
}
|
|
|
|
pDevice->onData(pDevice, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
static void ma_device__on_data(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_stopping) {
|
|
return;
|
|
}
|
|
|
|
if (pDevice->noFixedSizedCallback) {
|
|
ma_device__on_data_inner(pDevice, pFramesOut, pFramesIn, frameCount);
|
|
} else {
|
|
ma_uint32 totalFramesProcessed = 0;
|
|
|
|
while (totalFramesProcessed < frameCount) {
|
|
ma_uint32 totalFramesRemaining = frameCount - totalFramesProcessed;
|
|
ma_uint32 framesToProcessThisIteration = 0;
|
|
|
|
if (pFramesIn != NULL) {
|
|
if (pDevice->capture.intermediaryBufferLen < pDevice->capture.intermediaryBufferCap) {
|
|
framesToProcessThisIteration = totalFramesRemaining;
|
|
if (framesToProcessThisIteration > pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen) {
|
|
framesToProcessThisIteration = pDevice->capture.intermediaryBufferCap - pDevice->capture.intermediaryBufferLen;
|
|
}
|
|
|
|
ma_copy_pcm_frames(
|
|
ma_offset_pcm_frames_ptr(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferLen, pDevice->capture.format, pDevice->capture.channels),
|
|
ma_offset_pcm_frames_const_ptr(pFramesIn, totalFramesProcessed, pDevice->capture.format, pDevice->capture.channels),
|
|
framesToProcessThisIteration,
|
|
pDevice->capture.format, pDevice->capture.channels);
|
|
|
|
pDevice->capture.intermediaryBufferLen += framesToProcessThisIteration;
|
|
}
|
|
|
|
if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
} else {
|
|
ma_device__on_data_inner(pDevice, NULL, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
|
|
|
|
pDevice->capture.intermediaryBufferLen = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
if (pDevice->playback.intermediaryBufferLen > 0) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
} else {
|
|
framesToProcessThisIteration = totalFramesRemaining;
|
|
if (framesToProcessThisIteration > pDevice->playback.intermediaryBufferLen) {
|
|
framesToProcessThisIteration = pDevice->playback.intermediaryBufferLen;
|
|
}
|
|
}
|
|
|
|
ma_copy_pcm_frames(
|
|
ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, pDevice->playback.format, pDevice->playback.channels),
|
|
ma_offset_pcm_frames_ptr(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap - pDevice->playback.intermediaryBufferLen, pDevice->playback.format, pDevice->playback.channels),
|
|
framesToProcessThisIteration,
|
|
pDevice->playback.format, pDevice->playback.channels);
|
|
|
|
pDevice->playback.intermediaryBufferLen -= framesToProcessThisIteration;
|
|
}
|
|
|
|
if (pDevice->playback.intermediaryBufferLen == 0) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
} else {
|
|
ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, NULL, pDevice->playback.intermediaryBufferCap);
|
|
|
|
pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->capture.intermediaryBufferLen == pDevice->capture.intermediaryBufferCap) {
|
|
ma_device__on_data_inner(pDevice, pDevice->playback.pIntermediaryBuffer, pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap);
|
|
|
|
pDevice->playback.intermediaryBufferLen = pDevice->playback.intermediaryBufferCap;
|
|
pDevice->capture.intermediaryBufferLen = 0;
|
|
}
|
|
}
|
|
|
|
totalFramesProcessed += framesToProcessThisIteration;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_device__handle_data_callback(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
|
{
|
|
float masterVolumeFactor;
|
|
|
|
ma_device_get_master_volume(pDevice, &masterVolumeFactor);
|
|
|
|
if (pDevice->onData) {
|
|
unsigned int prevDenormalState = ma_device_disable_denormals(pDevice);
|
|
{
|
|
if (pFramesIn != NULL && masterVolumeFactor != 1) {
|
|
ma_uint8 tempFramesIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 bpfCapture = ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
|
|
ma_uint32 bpfPlayback = ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
|
|
ma_uint32 totalFramesProcessed = 0;
|
|
while (totalFramesProcessed < frameCount) {
|
|
ma_uint32 framesToProcessThisIteration = frameCount - totalFramesProcessed;
|
|
if (framesToProcessThisIteration > sizeof(tempFramesIn)/bpfCapture) {
|
|
framesToProcessThisIteration = sizeof(tempFramesIn)/bpfCapture;
|
|
}
|
|
|
|
ma_copy_and_apply_volume_factor_pcm_frames(tempFramesIn, ma_offset_ptr(pFramesIn, totalFramesProcessed*bpfCapture), framesToProcessThisIteration, pDevice->capture.format, pDevice->capture.channels, masterVolumeFactor);
|
|
|
|
ma_device__on_data(pDevice, ma_offset_ptr(pFramesOut, totalFramesProcessed*bpfPlayback), tempFramesIn, framesToProcessThisIteration);
|
|
|
|
totalFramesProcessed += framesToProcessThisIteration;
|
|
}
|
|
} else {
|
|
ma_device__on_data(pDevice, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
if (masterVolumeFactor != 1) {
|
|
if (pFramesIn == NULL) {
|
|
ma_apply_volume_factor_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels, masterVolumeFactor);
|
|
}
|
|
}
|
|
|
|
if (!pDevice->noClip && pDevice->playback.format == ma_format_f32) {
|
|
ma_clip_samples_f32((float*)pFramesOut, (const float*)pFramesOut, frameCount * pDevice->playback.channels);
|
|
}
|
|
}
|
|
}
|
|
ma_device_restore_denormals(pDevice, prevDenormalState);
|
|
} else {
|
|
if (pFramesOut != NULL) {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, pDevice->playback.format, pDevice->playback.channels);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_device__read_frames_from_client(ma_device* pDevice, ma_uint32 frameCount, void* pFramesOut)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(frameCount > 0);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
|
|
if (pDevice->playback.converter.isPassthrough) {
|
|
ma_device__handle_data_callback(pDevice, pFramesOut, NULL, frameCount);
|
|
} else {
|
|
ma_result result;
|
|
ma_uint64 totalFramesReadOut;
|
|
void* pRunningFramesOut;
|
|
|
|
totalFramesReadOut = 0;
|
|
pRunningFramesOut = pFramesOut;
|
|
|
|
if (pDevice->playback.pInputCache != NULL) {
|
|
while (totalFramesReadOut < frameCount) {
|
|
ma_uint64 framesToReadThisIterationIn;
|
|
ma_uint64 framesToReadThisIterationOut;
|
|
|
|
if (pDevice->playback.inputCacheRemaining > 0) {
|
|
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
|
|
framesToReadThisIterationIn = framesToReadThisIterationOut;
|
|
if (framesToReadThisIterationIn > pDevice->playback.inputCacheRemaining) {
|
|
framesToReadThisIterationIn = pDevice->playback.inputCacheRemaining;
|
|
}
|
|
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
pDevice->playback.inputCacheConsumed += framesToReadThisIterationIn;
|
|
pDevice->playback.inputCacheRemaining -= framesToReadThisIterationIn;
|
|
|
|
totalFramesReadOut += framesToReadThisIterationOut;
|
|
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
|
|
if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pDevice->playback.inputCacheRemaining == 0) {
|
|
ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, NULL, (ma_uint32)pDevice->playback.inputCacheCap);
|
|
|
|
pDevice->playback.inputCacheConsumed = 0;
|
|
pDevice->playback.inputCacheRemaining = pDevice->playback.inputCacheCap;
|
|
}
|
|
}
|
|
} else {
|
|
while (totalFramesReadOut < frameCount) {
|
|
ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
|
|
ma_uint64 framesToReadThisIterationIn;
|
|
ma_uint64 framesReadThisIterationIn;
|
|
ma_uint64 framesToReadThisIterationOut;
|
|
ma_uint64 framesReadThisIterationOut;
|
|
ma_uint64 requiredInputFrameCount;
|
|
|
|
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
|
|
framesToReadThisIterationIn = framesToReadThisIterationOut;
|
|
if (framesToReadThisIterationIn > intermediaryBufferCap) {
|
|
framesToReadThisIterationIn = intermediaryBufferCap;
|
|
}
|
|
|
|
ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, framesToReadThisIterationOut, &requiredInputFrameCount);
|
|
if (framesToReadThisIterationIn > requiredInputFrameCount) {
|
|
framesToReadThisIterationIn = requiredInputFrameCount;
|
|
}
|
|
|
|
ma_device__handle_data_callback(pDevice, pIntermediaryBuffer, NULL, (ma_uint32)framesToReadThisIterationIn);
|
|
|
|
framesReadThisIterationIn = framesToReadThisIterationIn;
|
|
framesReadThisIterationOut = framesToReadThisIterationOut;
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
totalFramesReadOut += framesReadThisIterationOut;
|
|
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
|
|
if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_device__send_frames_to_client(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(frameCountInDeviceFormat > 0);
|
|
MA_ASSERT(pFramesInDeviceFormat != NULL);
|
|
|
|
if (pDevice->capture.converter.isPassthrough) {
|
|
ma_device__handle_data_callback(pDevice, NULL, pFramesInDeviceFormat, frameCountInDeviceFormat);
|
|
} else {
|
|
ma_result result;
|
|
ma_uint8 pFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 framesInClientFormatCap = sizeof(pFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
|
|
ma_uint64 totalDeviceFramesProcessed = 0;
|
|
ma_uint64 totalClientFramesProcessed = 0;
|
|
const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
|
|
|
|
for (;;) {
|
|
ma_uint64 deviceFramesProcessedThisIteration;
|
|
ma_uint64 clientFramesProcessedThisIteration;
|
|
|
|
deviceFramesProcessedThisIteration = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
|
|
clientFramesProcessedThisIteration = framesInClientFormatCap;
|
|
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &deviceFramesProcessedThisIteration, pFramesInClientFormat, &clientFramesProcessedThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (clientFramesProcessedThisIteration > 0) {
|
|
ma_device__handle_data_callback(pDevice, NULL, pFramesInClientFormat, (ma_uint32)clientFramesProcessedThisIteration);
|
|
}
|
|
|
|
pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, deviceFramesProcessedThisIteration * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
|
|
totalDeviceFramesProcessed += deviceFramesProcessedThisIteration;
|
|
totalClientFramesProcessed += clientFramesProcessedThisIteration;
|
|
|
|
(void)totalClientFramesProcessed;
|
|
|
|
if (deviceFramesProcessedThisIteration == 0 && clientFramesProcessedThisIteration == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static ma_result ma_device__handle_duplex_callback_capture(ma_device* pDevice, ma_uint32 frameCountInDeviceFormat, const void* pFramesInDeviceFormat, ma_pcm_rb* pRB)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 totalDeviceFramesProcessed = 0;
|
|
const void* pRunningFramesInDeviceFormat = pFramesInDeviceFormat;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(frameCountInDeviceFormat > 0);
|
|
MA_ASSERT(pFramesInDeviceFormat != NULL);
|
|
MA_ASSERT(pRB != NULL);
|
|
|
|
for (;;) {
|
|
ma_uint32 framesToProcessInDeviceFormat = (frameCountInDeviceFormat - totalDeviceFramesProcessed);
|
|
ma_uint32 framesToProcessInClientFormat = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
|
|
ma_uint64 framesProcessedInDeviceFormat;
|
|
ma_uint64 framesProcessedInClientFormat;
|
|
void* pFramesInClientFormat;
|
|
|
|
result = ma_pcm_rb_acquire_write(pRB, &framesToProcessInClientFormat, &pFramesInClientFormat);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to acquire capture PCM frames from ring buffer.");
|
|
break;
|
|
}
|
|
|
|
if (framesToProcessInClientFormat == 0) {
|
|
if (ma_pcm_rb_pointer_distance(pRB) == (ma_int32)ma_pcm_rb_get_subbuffer_size(pRB)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
framesProcessedInDeviceFormat = framesToProcessInDeviceFormat;
|
|
framesProcessedInClientFormat = framesToProcessInClientFormat;
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningFramesInDeviceFormat, &framesProcessedInDeviceFormat, pFramesInClientFormat, &framesProcessedInClientFormat);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
result = ma_pcm_rb_commit_write(pRB, (ma_uint32)framesProcessedInClientFormat);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to commit capture PCM frames to ring buffer.");
|
|
break;
|
|
}
|
|
|
|
pRunningFramesInDeviceFormat = ma_offset_ptr(pRunningFramesInDeviceFormat, framesProcessedInDeviceFormat * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
|
|
totalDeviceFramesProcessed += (ma_uint32)framesProcessedInDeviceFormat;
|
|
|
|
if (framesProcessedInClientFormat == 0 && framesProcessedInDeviceFormat == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device__handle_duplex_callback_playback(ma_device* pDevice, ma_uint32 frameCount, void* pFramesInInternalFormat, ma_pcm_rb* pRB)
|
|
{
|
|
ma_result result;
|
|
ma_uint8 silentInputFrames[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 totalFramesReadOut = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(frameCount > 0);
|
|
MA_ASSERT(pFramesInInternalFormat != NULL);
|
|
MA_ASSERT(pRB != NULL);
|
|
MA_ASSERT(pDevice->playback.pInputCache != NULL);
|
|
|
|
MA_ZERO_MEMORY(silentInputFrames, sizeof(silentInputFrames));
|
|
|
|
while (totalFramesReadOut < frameCount && ma_device_is_started(pDevice)) {
|
|
if (pDevice->playback.inputCacheRemaining > 0) {
|
|
ma_uint64 framesConvertedIn = pDevice->playback.inputCacheRemaining;
|
|
ma_uint64 framesConvertedOut = (frameCount - totalFramesReadOut);
|
|
ma_data_converter_process_pcm_frames(&pDevice->playback.converter, ma_offset_pcm_frames_ptr(pDevice->playback.pInputCache, pDevice->playback.inputCacheConsumed, pDevice->playback.format, pDevice->playback.channels), &framesConvertedIn, pFramesInInternalFormat, &framesConvertedOut);
|
|
|
|
pDevice->playback.inputCacheConsumed += framesConvertedIn;
|
|
pDevice->playback.inputCacheRemaining -= framesConvertedIn;
|
|
|
|
totalFramesReadOut += (ma_uint32)framesConvertedOut;
|
|
pFramesInInternalFormat = ma_offset_ptr(pFramesInInternalFormat, framesConvertedOut * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
}
|
|
|
|
if (totalFramesReadOut < frameCount && pDevice->playback.inputCacheRemaining == 0) {
|
|
ma_uint32 inputFrameCount;
|
|
void* pInputFrames;
|
|
|
|
inputFrameCount = (ma_uint32)pDevice->playback.inputCacheCap;
|
|
result = ma_pcm_rb_acquire_read(pRB, &inputFrameCount, &pInputFrames);
|
|
if (result == MA_SUCCESS) {
|
|
if (inputFrameCount > 0) {
|
|
ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, pInputFrames, inputFrameCount);
|
|
} else {
|
|
if (ma_pcm_rb_pointer_distance(pRB) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
inputFrameCount = (ma_uint32)ma_min(pDevice->playback.inputCacheCap, sizeof(silentInputFrames) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels));
|
|
ma_device__handle_data_callback(pDevice, pDevice->playback.pInputCache, silentInputFrames, inputFrameCount);
|
|
}
|
|
|
|
pDevice->playback.inputCacheConsumed = 0;
|
|
pDevice->playback.inputCacheRemaining = inputFrameCount;
|
|
|
|
result = ma_pcm_rb_commit_read(pRB, inputFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state newState)
|
|
{
|
|
ma_atomic_device_state_set(&pDevice->state, newState);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_get_format_priority_index(ma_format format)
|
|
{
|
|
ma_uint32 i;
|
|
for (i = 0; i < ma_countof(g_maFormatPriorities); ++i) {
|
|
if (g_maFormatPriorities[i] == format) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return (ma_uint32)-1;
|
|
}
|
|
|
|
static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType);
|
|
|
|
static ma_bool32 ma_device_descriptor_is_valid(const ma_device_descriptor* pDeviceDescriptor)
|
|
{
|
|
if (pDeviceDescriptor == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (pDeviceDescriptor->format == ma_format_unknown) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (pDeviceDescriptor->channels == 0 || pDeviceDescriptor->channels > MA_MAX_CHANNELS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (pDeviceDescriptor->sampleRate == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static ma_result ma_device_audio_thread__default_read_write(ma_device* pDevice)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_bool32 exitLoop = MA_FALSE;
|
|
ma_uint8 capturedDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint8 playbackDeviceData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 capturedDeviceDataCapInFrames = 0;
|
|
ma_uint32 playbackDeviceDataCapInFrames = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
|
if (pDevice->pContext->callbacks.onDeviceRead == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
capturedDeviceDataCapInFrames = sizeof(capturedDeviceData) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->pContext->callbacks.onDeviceWrite == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
playbackDeviceDataCapInFrames = sizeof(playbackDeviceData) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
}
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started && !exitLoop) {
|
|
switch (pDevice->type) {
|
|
case ma_device_type_duplex:
|
|
{
|
|
ma_uint32 totalCapturedDeviceFramesProcessed = 0;
|
|
ma_uint32 capturedDevicePeriodSizeInFrames = ma_min(pDevice->capture.internalPeriodSizeInFrames, pDevice->playback.internalPeriodSizeInFrames);
|
|
|
|
while (totalCapturedDeviceFramesProcessed < capturedDevicePeriodSizeInFrames) {
|
|
ma_uint32 capturedDeviceFramesRemaining;
|
|
ma_uint32 capturedDeviceFramesProcessed;
|
|
ma_uint32 capturedDeviceFramesToProcess;
|
|
ma_uint32 capturedDeviceFramesToTryProcessing = capturedDevicePeriodSizeInFrames - totalCapturedDeviceFramesProcessed;
|
|
if (capturedDeviceFramesToTryProcessing > capturedDeviceDataCapInFrames) {
|
|
capturedDeviceFramesToTryProcessing = capturedDeviceDataCapInFrames;
|
|
}
|
|
|
|
result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, capturedDeviceFramesToTryProcessing, &capturedDeviceFramesToProcess);
|
|
if (result != MA_SUCCESS) {
|
|
exitLoop = MA_TRUE;
|
|
break;
|
|
}
|
|
|
|
capturedDeviceFramesRemaining = capturedDeviceFramesToProcess;
|
|
capturedDeviceFramesProcessed = 0;
|
|
|
|
for (;;) {
|
|
ma_uint8 capturedClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint8 playbackClientData[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 capturedClientDataCapInFrames = sizeof(capturedClientData) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
|
|
ma_uint32 playbackClientDataCapInFrames = sizeof(playbackClientData) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
|
|
ma_uint64 capturedClientFramesToProcessThisIteration = ma_min(capturedClientDataCapInFrames, playbackClientDataCapInFrames);
|
|
ma_uint64 capturedDeviceFramesToProcessThisIteration = capturedDeviceFramesRemaining;
|
|
ma_uint8* pRunningCapturedDeviceFrames = ma_offset_ptr(capturedDeviceData, capturedDeviceFramesProcessed * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
|
|
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningCapturedDeviceFrames, &capturedDeviceFramesToProcessThisIteration, capturedClientData, &capturedClientFramesToProcessThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (capturedClientFramesToProcessThisIteration == 0) {
|
|
break;
|
|
}
|
|
|
|
ma_device__handle_data_callback(pDevice, playbackClientData, capturedClientData, (ma_uint32)capturedClientFramesToProcessThisIteration);
|
|
|
|
capturedDeviceFramesProcessed += (ma_uint32)capturedDeviceFramesToProcessThisIteration;
|
|
capturedDeviceFramesRemaining -= (ma_uint32)capturedDeviceFramesToProcessThisIteration;
|
|
|
|
for (;;) {
|
|
ma_uint64 convertedClientFrameCount = capturedClientFramesToProcessThisIteration;
|
|
ma_uint64 convertedDeviceFrameCount = playbackDeviceDataCapInFrames;
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, playbackClientData, &convertedClientFrameCount, playbackDeviceData, &convertedDeviceFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, (ma_uint32)convertedDeviceFrameCount, NULL);
|
|
if (result != MA_SUCCESS) {
|
|
exitLoop = MA_TRUE;
|
|
break;
|
|
}
|
|
|
|
capturedClientFramesToProcessThisIteration -= (ma_uint32)convertedClientFrameCount;
|
|
if (capturedClientFramesToProcessThisIteration == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
exitLoop = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (capturedDeviceFramesProcessed == 0) {
|
|
break;
|
|
}
|
|
|
|
totalCapturedDeviceFramesProcessed += capturedDeviceFramesProcessed;
|
|
}
|
|
} break;
|
|
|
|
case ma_device_type_capture:
|
|
case ma_device_type_loopback:
|
|
{
|
|
ma_uint32 periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
|
|
ma_uint32 framesReadThisPeriod = 0;
|
|
while (framesReadThisPeriod < periodSizeInFrames) {
|
|
ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesReadThisPeriod;
|
|
ma_uint32 framesProcessed;
|
|
ma_uint32 framesToReadThisIteration = framesRemainingInPeriod;
|
|
if (framesToReadThisIteration > capturedDeviceDataCapInFrames) {
|
|
framesToReadThisIteration = capturedDeviceDataCapInFrames;
|
|
}
|
|
|
|
result = pDevice->pContext->callbacks.onDeviceRead(pDevice, capturedDeviceData, framesToReadThisIteration, &framesProcessed);
|
|
if (result != MA_SUCCESS) {
|
|
exitLoop = MA_TRUE;
|
|
break;
|
|
}
|
|
|
|
if (framesProcessed == 0) {
|
|
break;
|
|
}
|
|
|
|
ma_device__send_frames_to_client(pDevice, framesProcessed, capturedDeviceData);
|
|
|
|
framesReadThisPeriod += framesProcessed;
|
|
}
|
|
} break;
|
|
|
|
case ma_device_type_playback:
|
|
{
|
|
ma_uint32 periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
|
|
ma_uint32 framesWrittenThisPeriod = 0;
|
|
while (framesWrittenThisPeriod < periodSizeInFrames) {
|
|
ma_uint32 framesRemainingInPeriod = periodSizeInFrames - framesWrittenThisPeriod;
|
|
ma_uint32 framesProcessed;
|
|
ma_uint32 framesToWriteThisIteration = framesRemainingInPeriod;
|
|
if (framesToWriteThisIteration > playbackDeviceDataCapInFrames) {
|
|
framesToWriteThisIteration = playbackDeviceDataCapInFrames;
|
|
}
|
|
|
|
ma_device__read_frames_from_client(pDevice, framesToWriteThisIteration, playbackDeviceData);
|
|
|
|
result = pDevice->pContext->callbacks.onDeviceWrite(pDevice, playbackDeviceData, framesToWriteThisIteration, &framesProcessed);
|
|
if (result != MA_SUCCESS) {
|
|
exitLoop = MA_TRUE;
|
|
break;
|
|
}
|
|
|
|
if (framesProcessed == 0) {
|
|
break;
|
|
}
|
|
|
|
framesWrittenThisPeriod += framesProcessed;
|
|
}
|
|
} break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifdef MA_HAS_NULL
|
|
|
|
#define MA_DEVICE_OP_NONE__NULL 0
|
|
#define MA_DEVICE_OP_START__NULL 1
|
|
#define MA_DEVICE_OP_SUSPEND__NULL 2
|
|
#define MA_DEVICE_OP_KILL__NULL 3
|
|
|
|
static ma_thread_result MA_THREADCALL ma_device_thread__null(void* pData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
for (;;) {
|
|
ma_uint32 operation;
|
|
|
|
ma_event_wait(&pDevice->null_device.operationEvent);
|
|
|
|
operation = pDevice->null_device.operation;
|
|
|
|
if (operation == MA_DEVICE_OP_START__NULL) {
|
|
ma_timer_init(&pDevice->null_device.timer);
|
|
|
|
pDevice->null_device.operationResult = MA_SUCCESS;
|
|
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
|
|
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
|
|
continue;
|
|
}
|
|
|
|
if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
|
|
pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
|
|
ma_timer_init(&pDevice->null_device.timer);
|
|
|
|
pDevice->null_device.operationResult = MA_SUCCESS;
|
|
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
|
|
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
|
|
continue;
|
|
}
|
|
|
|
if (operation == MA_DEVICE_OP_KILL__NULL) {
|
|
pDevice->null_device.operationResult = MA_SUCCESS;
|
|
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
|
|
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
|
|
break;
|
|
}
|
|
|
|
if (operation == MA_DEVICE_OP_NONE__NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
pDevice->null_device.operationResult = MA_INVALID_OPERATION;
|
|
ma_event_signal(&pDevice->null_device.operationCompletionEvent);
|
|
ma_semaphore_release(&pDevice->null_device.operationSemaphore);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return (ma_thread_result)0;
|
|
}
|
|
|
|
static ma_result ma_device_do_operation__null(ma_device* pDevice, ma_uint32 operation)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_semaphore_wait(&pDevice->null_device.operationSemaphore);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDevice->null_device.operation = operation;
|
|
|
|
if (ma_event_signal(&pDevice->null_device.operationEvent) != MA_SUCCESS) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (ma_event_wait(&pDevice->null_device.operationCompletionEvent) != MA_SUCCESS) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return pDevice->null_device.operationResult;
|
|
}
|
|
|
|
static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
|
|
{
|
|
ma_uint32 internalSampleRate;
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
internalSampleRate = pDevice->capture.internalSampleRate;
|
|
} else {
|
|
internalSampleRate = pDevice->playback.internalSampleRate;
|
|
}
|
|
|
|
return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_bool32 cbResult = MA_TRUE;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Playback Device", (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), "NULL Capture Device", (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
|
|
(void)cbResult;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__null(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDeviceID != NULL && pDeviceID->nullbackend != 0) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Playback Device", (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), "NULL Capture Device", (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
|
|
pDeviceInfo->nativeDataFormats[0].channels = 0;
|
|
pDeviceInfo->nativeDataFormats[0].sampleRate = 0;
|
|
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__null(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_device_do_operation__null(pDevice, MA_DEVICE_OP_KILL__NULL);
|
|
|
|
ma_thread_wait(&pDevice->null_device.deviceThread);
|
|
|
|
ma_semaphore_uninit(&pDevice->null_device.operationSemaphore);
|
|
ma_event_uninit(&pDevice->null_device.operationCompletionEvent);
|
|
ma_event_uninit(&pDevice->null_device.operationEvent);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__null(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->null_device);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
pDescriptorCapture->format = (pDescriptorCapture->format != ma_format_unknown) ? pDescriptorCapture->format : MA_DEFAULT_FORMAT;
|
|
pDescriptorCapture->channels = (pDescriptorCapture->channels != 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
|
|
pDescriptorCapture->sampleRate = (pDescriptorCapture->sampleRate != 0) ? pDescriptorCapture->sampleRate : MA_DEFAULT_SAMPLE_RATE;
|
|
|
|
if (pDescriptorCapture->channelMap[0] == MA_CHANNEL_NONE) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
|
|
}
|
|
|
|
pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
pDescriptorPlayback->format = (pDescriptorPlayback->format != ma_format_unknown) ? pDescriptorPlayback->format : MA_DEFAULT_FORMAT;
|
|
pDescriptorPlayback->channels = (pDescriptorPlayback->channels != 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
|
|
pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0) ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;
|
|
|
|
if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
|
|
}
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
|
|
}
|
|
|
|
result = ma_event_init(&pDevice->null_device.operationEvent);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_semaphore_init(1, &pDevice->null_device.operationSemaphore);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_thread_create(&pDevice->null_device.deviceThread, pDevice->pContext->threadPriority, 0, ma_device_thread__null, pDevice, &pDevice->pContext->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__null(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_device_do_operation__null(pDevice, MA_DEVICE_OP_START__NULL);
|
|
|
|
ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_TRUE);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__null(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_device_do_operation__null(pDevice, MA_DEVICE_OP_SUSPEND__NULL);
|
|
|
|
ma_atomic_bool32_set(&pDevice->null_device.isStarted, MA_FALSE);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_bool32 ma_device_is_started__null(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
return ma_atomic_bool32_get(&pDevice->null_device.isStarted);
|
|
}
|
|
|
|
static ma_result ma_device_write__null(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 totalPCMFramesProcessed;
|
|
ma_bool32 wasStartedOnEntry;
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
wasStartedOnEntry = ma_device_is_started__null(pDevice);
|
|
|
|
totalPCMFramesProcessed = 0;
|
|
while (totalPCMFramesProcessed < frameCount) {
|
|
ma_uint64 targetFrame;
|
|
|
|
if (pDevice->null_device.currentPeriodFramesRemainingPlayback > 0) {
|
|
ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
|
|
ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingPlayback;
|
|
if (framesToProcess > framesRemaining) {
|
|
framesToProcess = framesRemaining;
|
|
}
|
|
|
|
(void)pPCMFrames;
|
|
|
|
pDevice->null_device.currentPeriodFramesRemainingPlayback -= framesToProcess;
|
|
totalPCMFramesProcessed += framesToProcess;
|
|
}
|
|
|
|
if (pDevice->null_device.currentPeriodFramesRemainingPlayback == 0) {
|
|
pDevice->null_device.currentPeriodFramesRemainingPlayback = 0;
|
|
|
|
if (!ma_device_is_started__null(pDevice) && !wasStartedOnEntry) {
|
|
result = ma_device_start__null(pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(totalPCMFramesProcessed <= frameCount);
|
|
if (totalPCMFramesProcessed == frameCount) {
|
|
break;
|
|
}
|
|
|
|
targetFrame = pDevice->null_device.lastProcessedFramePlayback;
|
|
for (;;) {
|
|
ma_uint64 currentFrame;
|
|
|
|
if (!ma_device_is_started__null(pDevice)) {
|
|
break;
|
|
}
|
|
|
|
currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
|
|
if (currentFrame >= targetFrame) {
|
|
break;
|
|
}
|
|
|
|
ma_sleep(10);
|
|
}
|
|
|
|
pDevice->null_device.lastProcessedFramePlayback += pDevice->playback.internalPeriodSizeInFrames;
|
|
pDevice->null_device.currentPeriodFramesRemainingPlayback = pDevice->playback.internalPeriodSizeInFrames;
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = totalPCMFramesProcessed;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_read__null(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 totalPCMFramesProcessed;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
totalPCMFramesProcessed = 0;
|
|
while (totalPCMFramesProcessed < frameCount) {
|
|
ma_uint64 targetFrame;
|
|
|
|
if (pDevice->null_device.currentPeriodFramesRemainingCapture > 0) {
|
|
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
ma_uint32 framesRemaining = (frameCount - totalPCMFramesProcessed);
|
|
ma_uint32 framesToProcess = pDevice->null_device.currentPeriodFramesRemainingCapture;
|
|
if (framesToProcess > framesRemaining) {
|
|
framesToProcess = framesRemaining;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(ma_offset_ptr(pPCMFrames, totalPCMFramesProcessed*bpf), framesToProcess*bpf);
|
|
|
|
pDevice->null_device.currentPeriodFramesRemainingCapture -= framesToProcess;
|
|
totalPCMFramesProcessed += framesToProcess;
|
|
}
|
|
|
|
if (pDevice->null_device.currentPeriodFramesRemainingCapture == 0) {
|
|
pDevice->null_device.currentPeriodFramesRemainingCapture = 0;
|
|
}
|
|
|
|
MA_ASSERT(totalPCMFramesProcessed <= frameCount);
|
|
if (totalPCMFramesProcessed == frameCount) {
|
|
break;
|
|
}
|
|
|
|
targetFrame = pDevice->null_device.lastProcessedFrameCapture + pDevice->capture.internalPeriodSizeInFrames;
|
|
for (;;) {
|
|
ma_uint64 currentFrame;
|
|
|
|
if (!ma_device_is_started__null(pDevice)) {
|
|
break;
|
|
}
|
|
|
|
currentFrame = ma_device_get_total_run_time_in_frames__null(pDevice);
|
|
if (currentFrame >= targetFrame) {
|
|
break;
|
|
}
|
|
|
|
ma_sleep(10);
|
|
}
|
|
|
|
pDevice->null_device.lastProcessedFrameCapture += pDevice->capture.internalPeriodSizeInFrames;
|
|
pDevice->null_device.currentPeriodFramesRemainingCapture = pDevice->capture.internalPeriodSizeInFrames;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalPCMFramesProcessed;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__null(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_null);
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__null(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
(void)pContext;
|
|
|
|
pCallbacks->onContextInit = ma_context_init__null;
|
|
pCallbacks->onContextUninit = ma_context_uninit__null;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__null;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__null;
|
|
pCallbacks->onDeviceInit = ma_device_init__null;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__null;
|
|
pCallbacks->onDeviceStart = ma_device_start__null;
|
|
pCallbacks->onDeviceStop = ma_device_stop__null;
|
|
pCallbacks->onDeviceRead = ma_device_read__null;
|
|
pCallbacks->onDeviceWrite = ma_device_write__null;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_WIN32) && !defined(MA_XBOX)
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
#define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved))
|
|
#define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)()
|
|
#define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv)
|
|
#define ma_CoTaskMemFree(pContext, pv) ((MA_PFN_CoTaskMemFree)pContext->win32.CoTaskMemFree)(pv)
|
|
#define ma_PropVariantClear(pContext, pvar) ((MA_PFN_PropVariantClear)pContext->win32.PropVariantClear)(pvar)
|
|
#else
|
|
#define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) CoInitializeEx(pvReserved, dwCoInit)
|
|
#define ma_CoUninitialize(pContext) CoUninitialize()
|
|
#define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) CoCreateInstance(rclsid, pUnkOuter, dwClsContext, riid, ppv)
|
|
#define ma_CoTaskMemFree(pContext, pv) CoTaskMemFree(pv)
|
|
#define ma_PropVariantClear(pContext, pvar) PropVariantClear(pvar)
|
|
#endif
|
|
|
|
#if !defined(MAXULONG_PTR) && !defined(__WATCOMC__) && !defined(MA_XBOX_NXDK)
|
|
typedef size_t DWORD_PTR;
|
|
#endif
|
|
|
|
#if !defined(WAVE_FORMAT_1M08)
|
|
#define WAVE_FORMAT_1M08 0x00000001
|
|
#define WAVE_FORMAT_1S08 0x00000002
|
|
#define WAVE_FORMAT_1M16 0x00000004
|
|
#define WAVE_FORMAT_1S16 0x00000008
|
|
#define WAVE_FORMAT_2M08 0x00000010
|
|
#define WAVE_FORMAT_2S08 0x00000020
|
|
#define WAVE_FORMAT_2M16 0x00000040
|
|
#define WAVE_FORMAT_2S16 0x00000080
|
|
#define WAVE_FORMAT_4M08 0x00000100
|
|
#define WAVE_FORMAT_4S08 0x00000200
|
|
#define WAVE_FORMAT_4M16 0x00000400
|
|
#define WAVE_FORMAT_4S16 0x00000800
|
|
#endif
|
|
|
|
#if !defined(WAVE_FORMAT_44M08)
|
|
#define WAVE_FORMAT_44M08 0x00000100
|
|
#define WAVE_FORMAT_44S08 0x00000200
|
|
#define WAVE_FORMAT_44M16 0x00000400
|
|
#define WAVE_FORMAT_44S16 0x00000800
|
|
#define WAVE_FORMAT_48M08 0x00001000
|
|
#define WAVE_FORMAT_48S08 0x00002000
|
|
#define WAVE_FORMAT_48M16 0x00004000
|
|
#define WAVE_FORMAT_48S16 0x00008000
|
|
#define WAVE_FORMAT_96M08 0x00010000
|
|
#define WAVE_FORMAT_96S08 0x00020000
|
|
#define WAVE_FORMAT_96M16 0x00040000
|
|
#define WAVE_FORMAT_96S16 0x00080000
|
|
#endif
|
|
|
|
#ifndef SPEAKER_FRONT_LEFT
|
|
#define SPEAKER_FRONT_LEFT 0x1
|
|
#define SPEAKER_FRONT_RIGHT 0x2
|
|
#define SPEAKER_FRONT_CENTER 0x4
|
|
#define SPEAKER_LOW_FREQUENCY 0x8
|
|
#define SPEAKER_BACK_LEFT 0x10
|
|
#define SPEAKER_BACK_RIGHT 0x20
|
|
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
|
|
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
|
|
#define SPEAKER_BACK_CENTER 0x100
|
|
#define SPEAKER_SIDE_LEFT 0x200
|
|
#define SPEAKER_SIDE_RIGHT 0x400
|
|
#define SPEAKER_TOP_CENTER 0x800
|
|
#define SPEAKER_TOP_FRONT_LEFT 0x1000
|
|
#define SPEAKER_TOP_FRONT_CENTER 0x2000
|
|
#define SPEAKER_TOP_FRONT_RIGHT 0x4000
|
|
#define SPEAKER_TOP_BACK_LEFT 0x8000
|
|
#define SPEAKER_TOP_BACK_CENTER 0x10000
|
|
#define SPEAKER_TOP_BACK_RIGHT 0x20000
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
WORD wFormatTag;
|
|
WORD nChannels;
|
|
DWORD nSamplesPerSec;
|
|
DWORD nAvgBytesPerSec;
|
|
WORD nBlockAlign;
|
|
WORD wBitsPerSample;
|
|
WORD cbSize;
|
|
} MA_WAVEFORMATEX;
|
|
|
|
typedef struct
|
|
{
|
|
WORD wFormatTag;
|
|
WORD nChannels;
|
|
DWORD nSamplesPerSec;
|
|
DWORD nAvgBytesPerSec;
|
|
WORD nBlockAlign;
|
|
WORD wBitsPerSample;
|
|
WORD cbSize;
|
|
union
|
|
{
|
|
WORD wValidBitsPerSample;
|
|
WORD wSamplesPerBlock;
|
|
WORD wReserved;
|
|
} Samples;
|
|
DWORD dwChannelMask;
|
|
GUID SubFormat;
|
|
} MA_WAVEFORMATEXTENSIBLE;
|
|
|
|
#ifndef WAVE_FORMAT_EXTENSIBLE
|
|
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
|
#endif
|
|
|
|
#ifndef WAVE_FORMAT_PCM
|
|
#define WAVE_FORMAT_PCM 1
|
|
#endif
|
|
|
|
#ifndef WAVE_FORMAT_IEEE_FLOAT
|
|
#define WAVE_FORMAT_IEEE_FLOAT 0x0003
|
|
#endif
|
|
|
|
static ma_uint8 ma_channel_id_to_ma__win32(DWORD id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
|
|
case SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
|
|
case SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
|
|
case SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
|
|
case SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
|
|
case SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
|
|
case SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
|
|
case SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
|
|
case SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
|
|
case SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
|
|
case SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
|
|
case SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
|
|
case SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
|
|
case SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
|
|
case SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
|
|
case SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
|
|
case SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
|
|
case SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static DWORD ma_channel_id_to_win32(DWORD id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case MA_CHANNEL_MONO: return SPEAKER_FRONT_CENTER;
|
|
case MA_CHANNEL_FRONT_LEFT: return SPEAKER_FRONT_LEFT;
|
|
case MA_CHANNEL_FRONT_RIGHT: return SPEAKER_FRONT_RIGHT;
|
|
case MA_CHANNEL_FRONT_CENTER: return SPEAKER_FRONT_CENTER;
|
|
case MA_CHANNEL_LFE: return SPEAKER_LOW_FREQUENCY;
|
|
case MA_CHANNEL_BACK_LEFT: return SPEAKER_BACK_LEFT;
|
|
case MA_CHANNEL_BACK_RIGHT: return SPEAKER_BACK_RIGHT;
|
|
case MA_CHANNEL_FRONT_LEFT_CENTER: return SPEAKER_FRONT_LEFT_OF_CENTER;
|
|
case MA_CHANNEL_FRONT_RIGHT_CENTER: return SPEAKER_FRONT_RIGHT_OF_CENTER;
|
|
case MA_CHANNEL_BACK_CENTER: return SPEAKER_BACK_CENTER;
|
|
case MA_CHANNEL_SIDE_LEFT: return SPEAKER_SIDE_LEFT;
|
|
case MA_CHANNEL_SIDE_RIGHT: return SPEAKER_SIDE_RIGHT;
|
|
case MA_CHANNEL_TOP_CENTER: return SPEAKER_TOP_CENTER;
|
|
case MA_CHANNEL_TOP_FRONT_LEFT: return SPEAKER_TOP_FRONT_LEFT;
|
|
case MA_CHANNEL_TOP_FRONT_CENTER: return SPEAKER_TOP_FRONT_CENTER;
|
|
case MA_CHANNEL_TOP_FRONT_RIGHT: return SPEAKER_TOP_FRONT_RIGHT;
|
|
case MA_CHANNEL_TOP_BACK_LEFT: return SPEAKER_TOP_BACK_LEFT;
|
|
case MA_CHANNEL_TOP_BACK_CENTER: return SPEAKER_TOP_BACK_CENTER;
|
|
case MA_CHANNEL_TOP_BACK_RIGHT: return SPEAKER_TOP_BACK_RIGHT;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static DWORD ma_channel_map_to_channel_mask__win32(const ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
DWORD dwChannelMask = 0;
|
|
ma_uint32 iChannel;
|
|
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
dwChannelMask |= ma_channel_id_to_win32(pChannelMap[iChannel]);
|
|
}
|
|
|
|
return dwChannelMask;
|
|
}
|
|
|
|
static void ma_channel_mask_to_channel_map__win32(DWORD dwChannelMask, ma_uint32 channels, ma_channel* pChannelMap)
|
|
{
|
|
if (dwChannelMask == 0) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channels, channels);
|
|
} else {
|
|
if (channels == 1 && (dwChannelMask & SPEAKER_FRONT_CENTER) != 0) {
|
|
pChannelMap[0] = MA_CHANNEL_MONO;
|
|
} else {
|
|
ma_uint32 iChannel = 0;
|
|
ma_uint32 iBit;
|
|
|
|
for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
|
|
DWORD bitValue = (dwChannelMask & (1UL << iBit));
|
|
if (bitValue != 0) {
|
|
pChannelMap[iChannel] = ma_channel_id_to_ma__win32(bitValue);
|
|
iChannel += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
static ma_bool32 ma_is_guid_equal(const void* a, const void* b)
|
|
{
|
|
return IsEqualGUID(*(const GUID*)a, *(const GUID*)b);
|
|
}
|
|
#else
|
|
#define ma_is_guid_equal(a, b) IsEqualGUID((const GUID*)a, (const GUID*)b)
|
|
#endif
|
|
|
|
static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid)
|
|
{
|
|
static GUID nullguid = {0x00000000, 0x0000, 0x0000, {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
|
return ma_is_guid_equal(guid, &nullguid);
|
|
}
|
|
|
|
static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF)
|
|
{
|
|
MA_ASSERT(pWF != NULL);
|
|
|
|
if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF;
|
|
if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) {
|
|
if (pWFEX->Samples.wValidBitsPerSample == 32) {
|
|
return ma_format_s32;
|
|
}
|
|
if (pWFEX->Samples.wValidBitsPerSample == 24) {
|
|
if (pWFEX->wBitsPerSample == 32) {
|
|
return ma_format_s32;
|
|
}
|
|
if (pWFEX->wBitsPerSample == 24) {
|
|
return ma_format_s24;
|
|
}
|
|
}
|
|
if (pWFEX->Samples.wValidBitsPerSample == 16) {
|
|
return ma_format_s16;
|
|
}
|
|
if (pWFEX->Samples.wValidBitsPerSample == 8) {
|
|
return ma_format_u8;
|
|
}
|
|
}
|
|
if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
|
|
if (pWFEX->Samples.wValidBitsPerSample == 32) {
|
|
return ma_format_f32;
|
|
}
|
|
}
|
|
} else {
|
|
if (pWF->wFormatTag == WAVE_FORMAT_PCM) {
|
|
if (pWF->wBitsPerSample == 32) {
|
|
return ma_format_s32;
|
|
}
|
|
if (pWF->wBitsPerSample == 24) {
|
|
return ma_format_s24;
|
|
}
|
|
if (pWF->wBitsPerSample == 16) {
|
|
return ma_format_s16;
|
|
}
|
|
if (pWF->wBitsPerSample == 8) {
|
|
return ma_format_u8;
|
|
}
|
|
}
|
|
if (pWF->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
|
|
if (pWF->wBitsPerSample == 32) {
|
|
return ma_format_f32;
|
|
}
|
|
if (pWF->wBitsPerSample == 64) {
|
|
}
|
|
}
|
|
}
|
|
|
|
return ma_format_unknown;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_WASAPI
|
|
#if 0
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4091)
|
|
#endif
|
|
#include <audioclient.h>
|
|
#include <mmdeviceapi.h>
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(pop)
|
|
#endif
|
|
#endif
|
|
|
|
static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType);
|
|
|
|
#define MA_WIN32_WINNT_VISTA 0x0600
|
|
#define MA_VER_MINORVERSION 0x01
|
|
#define MA_VER_MAJORVERSION 0x02
|
|
#define MA_VER_SERVICEPACKMAJOR 0x20
|
|
#define MA_VER_GREATER_EQUAL 0x03
|
|
|
|
typedef struct {
|
|
DWORD dwOSVersionInfoSize;
|
|
DWORD dwMajorVersion;
|
|
DWORD dwMinorVersion;
|
|
DWORD dwBuildNumber;
|
|
DWORD dwPlatformId;
|
|
WCHAR szCSDVersion[128];
|
|
WORD wServicePackMajor;
|
|
WORD wServicePackMinor;
|
|
WORD wSuiteMask;
|
|
BYTE wProductType;
|
|
BYTE wReserved;
|
|
} ma_OSVERSIONINFOEXW;
|
|
|
|
typedef BOOL (WINAPI * ma_PFNVerifyVersionInfoW) (ma_OSVERSIONINFOEXW* lpVersionInfo, DWORD dwTypeMask, DWORDLONG dwlConditionMask);
|
|
typedef ULONGLONG (WINAPI * ma_PFNVerSetConditionMask)(ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask);
|
|
|
|
#ifndef PROPERTYKEY_DEFINED
|
|
#define PROPERTYKEY_DEFINED
|
|
#ifndef __WATCOMC__
|
|
typedef struct
|
|
{
|
|
GUID fmtid;
|
|
DWORD pid;
|
|
} PROPERTYKEY;
|
|
#endif
|
|
#endif
|
|
|
|
static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp)
|
|
{
|
|
MA_ZERO_OBJECT(pProp);
|
|
}
|
|
|
|
static const PROPERTYKEY MA_PKEY_Device_FriendlyName = {{0xA45C254E, 0xDF1C, 0x4EFD, {0x80, 0x20, 0x67, 0xD1, 0x46, 0xA8, 0x50, 0xE0}}, 14};
|
|
static const PROPERTYKEY MA_PKEY_AudioEngine_DeviceFormat = {{0xF19F064D, 0x82C, 0x4E27, {0xBC, 0x73, 0x68, 0x82, 0xA1, 0xBB, 0x8E, 0x4C}}, 0};
|
|
|
|
static const IID MA_IID_IUnknown = {0x00000000, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
|
|
#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
|
|
static const IID MA_IID_IAgileObject = {0x94EA2B94, 0xE9CC, 0x49E0, {0xC0, 0xFF, 0xEE, 0x64, 0xCA, 0x8F, 0x5B, 0x90}};
|
|
#endif
|
|
|
|
static const IID MA_IID_IAudioClient = {0x1CB9AD4C, 0xDBFA, 0x4C32, {0xB1, 0x78, 0xC2, 0xF5, 0x68, 0xA7, 0x03, 0xB2}};
|
|
static const IID MA_IID_IAudioClient2 = {0x726778CD, 0xF60A, 0x4EDA, {0x82, 0xDE, 0xE4, 0x76, 0x10, 0xCD, 0x78, 0xAA}};
|
|
static const IID MA_IID_IAudioClient3 = {0x7ED4EE07, 0x8E67, 0x4CD4, {0x8C, 0x1A, 0x2B, 0x7A, 0x59, 0x87, 0xAD, 0x42}};
|
|
static const IID MA_IID_IAudioRenderClient = {0xF294ACFC, 0x3146, 0x4483, {0xA7, 0xBF, 0xAD, 0xDC, 0xA7, 0xC2, 0x60, 0xE2}};
|
|
static const IID MA_IID_IAudioCaptureClient = {0xC8ADBD64, 0xE71E, 0x48A0, {0xA4, 0xDE, 0x18, 0x5C, 0x39, 0x5C, 0xD3, 0x17}};
|
|
static const IID MA_IID_IMMNotificationClient = {0x7991EEC9, 0x7E89, 0x4D85, {0x83, 0x90, 0x6C, 0x70, 0x3C, 0xEC, 0x60, 0xC0}};
|
|
#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
|
|
static const IID MA_IID_DEVINTERFACE_AUDIO_RENDER = {0xE6327CAD, 0xDCEC, 0x4949, {0xAE, 0x8A, 0x99, 0x1E, 0x97, 0x6A, 0x79, 0xD2}};
|
|
static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, 0x33FA, 0x4800, {0x96, 0x70, 0x1C, 0xD4, 0x74, 0x97, 0x2C, 0x3F}};
|
|
static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}};
|
|
#endif
|
|
|
|
static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}};
|
|
static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}};
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
#define MA_MM_DEVICE_STATE_ACTIVE 1
|
|
#define MA_MM_DEVICE_STATE_DISABLED 2
|
|
#define MA_MM_DEVICE_STATE_NOTPRESENT 4
|
|
#define MA_MM_DEVICE_STATE_UNPLUGGED 8
|
|
|
|
typedef struct ma_IMMDeviceEnumerator ma_IMMDeviceEnumerator;
|
|
typedef struct ma_IMMDeviceCollection ma_IMMDeviceCollection;
|
|
typedef struct ma_IMMDevice ma_IMMDevice;
|
|
#else
|
|
typedef struct ma_IActivateAudioInterfaceCompletionHandler ma_IActivateAudioInterfaceCompletionHandler;
|
|
typedef struct ma_IActivateAudioInterfaceAsyncOperation ma_IActivateAudioInterfaceAsyncOperation;
|
|
#endif
|
|
typedef struct ma_IPropertyStore ma_IPropertyStore;
|
|
typedef struct ma_IAudioClient ma_IAudioClient;
|
|
typedef struct ma_IAudioClient2 ma_IAudioClient2;
|
|
typedef struct ma_IAudioClient3 ma_IAudioClient3;
|
|
typedef struct ma_IAudioRenderClient ma_IAudioRenderClient;
|
|
typedef struct ma_IAudioCaptureClient ma_IAudioCaptureClient;
|
|
|
|
typedef ma_int64 MA_REFERENCE_TIME;
|
|
|
|
#define MA_AUDCLNT_STREAMFLAGS_CROSSPROCESS 0x00010000
|
|
#define MA_AUDCLNT_STREAMFLAGS_LOOPBACK 0x00020000
|
|
#define MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK 0x00040000
|
|
#define MA_AUDCLNT_STREAMFLAGS_NOPERSIST 0x00080000
|
|
#define MA_AUDCLNT_STREAMFLAGS_RATEADJUST 0x00100000
|
|
#define MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
|
|
#define MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
|
|
#define MA_AUDCLNT_SESSIONFLAGS_EXPIREWHENUNOWNED 0x10000000
|
|
#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDE 0x20000000
|
|
#define MA_AUDCLNT_SESSIONFLAGS_DISPLAY_HIDEWHENEXPIRED 0x40000000
|
|
|
|
#define MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY 1
|
|
#define MA_AUDCLNT_BUFFERFLAGS_SILENT 2
|
|
#define MA_AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR 4
|
|
|
|
typedef enum
|
|
{
|
|
ma_eRender = 0,
|
|
ma_eCapture = 1,
|
|
ma_eAll = 2
|
|
} ma_EDataFlow;
|
|
|
|
typedef enum
|
|
{
|
|
ma_eConsole = 0,
|
|
ma_eMultimedia = 1,
|
|
ma_eCommunications = 2
|
|
} ma_ERole;
|
|
|
|
typedef enum
|
|
{
|
|
MA_AUDCLNT_SHAREMODE_SHARED,
|
|
MA_AUDCLNT_SHAREMODE_EXCLUSIVE
|
|
} MA_AUDCLNT_SHAREMODE;
|
|
|
|
typedef enum
|
|
{
|
|
MA_AudioCategory_Other = 0
|
|
} MA_AUDIO_STREAM_CATEGORY;
|
|
|
|
typedef enum
|
|
{
|
|
MA_AUDCLNT_STREAMOPTIONS_NONE,
|
|
MA_AUDCLNT_STREAMOPTIONS_RAW,
|
|
MA_AUDCLNT_STREAMOPTIONS_MATCH_FORMAT,
|
|
MA_AUDCLNT_STREAMOPTIONS_AMBISONICS,
|
|
MA_AUDCLNT_STREAMOPTIONS_POST_VOLUME_LOOPBACK
|
|
} MA_AUDCLNT_STREAMOPTIONS;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 cbSize;
|
|
BOOL bIsOffload;
|
|
MA_AUDIO_STREAM_CATEGORY eCategory;
|
|
MA_AUDCLNT_STREAMOPTIONS Options;
|
|
} ma_AudioClientProperties;
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IUnknown* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IUnknown* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IUnknown* pThis);
|
|
} ma_IUnknownVtbl;
|
|
struct ma_IUnknown
|
|
{
|
|
ma_IUnknownVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IUnknown_QueryInterface(ma_IUnknown* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IUnknown_AddRef(ma_IUnknown* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMNotificationClient* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState);
|
|
HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);
|
|
HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID);
|
|
HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID);
|
|
HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key);
|
|
} ma_IMMNotificationClientVtbl;
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceEnumerator* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceEnumerator* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices);
|
|
HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint);
|
|
HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice);
|
|
HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
|
|
HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient);
|
|
} ma_IMMDeviceEnumeratorVtbl;
|
|
struct ma_IMMDeviceEnumerator
|
|
{
|
|
ma_IMMDeviceEnumeratorVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IMMDeviceEnumerator_QueryInterface(ma_IMMDeviceEnumerator* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IMMDeviceEnumerator_AddRef(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDeviceCollection* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDeviceCollection* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IMMDeviceCollection* pThis, UINT* pDevices);
|
|
HRESULT (STDMETHODCALLTYPE * Item) (ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice);
|
|
} ma_IMMDeviceCollectionVtbl;
|
|
struct ma_IMMDeviceCollection
|
|
{
|
|
ma_IMMDeviceCollectionVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IMMDeviceCollection_QueryInterface(ma_IMMDeviceCollection* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IMMDeviceCollection_AddRef(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IMMDeviceCollection_Release(ma_IMMDeviceCollection* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceCollection_GetCount(ma_IMMDeviceCollection* pThis, UINT* pDevices) { return pThis->lpVtbl->GetCount(pThis, pDevices); }
|
|
static MA_INLINE HRESULT ma_IMMDeviceCollection_Item(ma_IMMDeviceCollection* pThis, UINT nDevice, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->Item(pThis, nDevice, ppDevice); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IMMDevice* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IMMDevice* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface);
|
|
HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties);
|
|
HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID);
|
|
HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState);
|
|
} ma_IMMDeviceVtbl;
|
|
struct ma_IMMDevice
|
|
{
|
|
ma_IMMDeviceVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); }
|
|
static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); }
|
|
static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); }
|
|
static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); }
|
|
#else
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IActivateAudioInterfaceAsyncOperation* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetActivateResult)(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface);
|
|
} ma_IActivateAudioInterfaceAsyncOperationVtbl;
|
|
struct ma_IActivateAudioInterfaceAsyncOperation
|
|
{
|
|
ma_IActivateAudioInterfaceAsyncOperationVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_QueryInterface(ma_IActivateAudioInterfaceAsyncOperation* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_AddRef(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IActivateAudioInterfaceAsyncOperation_Release(ma_IActivateAudioInterfaceAsyncOperation* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(ma_IActivateAudioInterfaceAsyncOperation* pThis, HRESULT *pActivateResult, ma_IUnknown** ppActivatedInterface) { return pThis->lpVtbl->GetActivateResult(pThis, pActivateResult, ppActivatedInterface); }
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IPropertyStore* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IPropertyStore* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount);
|
|
HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey);
|
|
HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar);
|
|
HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar);
|
|
HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis);
|
|
} ma_IPropertyStoreVtbl;
|
|
struct ma_IPropertyStore
|
|
{
|
|
ma_IPropertyStoreVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IPropertyStore_QueryInterface(ma_IPropertyStore* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); }
|
|
static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); }
|
|
static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); }
|
|
static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); }
|
|
static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
|
|
HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames);
|
|
HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency);
|
|
HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames);
|
|
HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
|
|
HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
|
|
HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
|
|
HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient* pThis, HANDLE eventHandle);
|
|
HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient* pThis, const IID* const riid, void** pp);
|
|
} ma_IAudioClientVtbl;
|
|
struct ma_IAudioClient
|
|
{
|
|
ma_IAudioClientVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_Reset(ma_IAudioClient* pThis) { return pThis->lpVtbl->Reset(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_SetEventHandle(ma_IAudioClient* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
|
|
static MA_INLINE HRESULT ma_IAudioClient_GetService(ma_IAudioClient* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient2* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
|
|
HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames);
|
|
HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency);
|
|
HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames);
|
|
HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
|
|
HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
|
|
HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
|
|
HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient2* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient2* pThis, HANDLE eventHandle);
|
|
HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient2* pThis, const IID* const riid, void** pp);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
|
|
HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties);
|
|
HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
|
|
} ma_IAudioClient2Vtbl;
|
|
struct ma_IAudioClient2
|
|
{
|
|
ma_IAudioClient2Vtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_Reset(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Reset(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
|
|
static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioClient3* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
|
|
HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames);
|
|
HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency);
|
|
HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames);
|
|
HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch);
|
|
HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat);
|
|
HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod);
|
|
HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Reset) (ma_IAudioClient3* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * SetEventHandle) (ma_IAudioClient3* pThis, HANDLE eventHandle);
|
|
HRESULT (STDMETHODCALLTYPE * GetService) (ma_IAudioClient3* pThis, const IID* const riid, void** pp);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable);
|
|
HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties);
|
|
HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames);
|
|
HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames);
|
|
HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid);
|
|
} ma_IAudioClient3Vtbl;
|
|
struct ma_IAudioClient3
|
|
{
|
|
ma_IAudioClient3Vtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_Reset(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Reset(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis, HANDLE eventHandle) { return pThis->lpVtbl->SetEventHandle(pThis, eventHandle); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); }
|
|
static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioRenderClient* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioRenderClient* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData);
|
|
HRESULT (STDMETHODCALLTYPE * ReleaseBuffer)(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags);
|
|
} ma_IAudioRenderClientVtbl;
|
|
struct ma_IAudioRenderClient
|
|
{
|
|
ma_IAudioRenderClientVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IAudioRenderClient_QueryInterface(ma_IAudioRenderClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IAudioRenderClient_AddRef(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IAudioRenderClient_Release(ma_IAudioRenderClient* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioRenderClient_GetBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesRequested, BYTE** ppData) { return pThis->lpVtbl->GetBuffer(pThis, numFramesRequested, ppData); }
|
|
static MA_INLINE HRESULT ma_IAudioRenderClient_ReleaseBuffer(ma_IAudioRenderClient* pThis, ma_uint32 numFramesWritten, DWORD dwFlags) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesWritten, dwFlags); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IAudioCaptureClient* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioCaptureClient* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetBuffer) (ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition);
|
|
HRESULT (STDMETHODCALLTYPE * ReleaseBuffer) (ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead);
|
|
HRESULT (STDMETHODCALLTYPE * GetNextPacketSize)(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket);
|
|
} ma_IAudioCaptureClientVtbl;
|
|
struct ma_IAudioCaptureClient
|
|
{
|
|
ma_IAudioCaptureClientVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IAudioCaptureClient_QueryInterface(ma_IAudioCaptureClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IAudioCaptureClient_AddRef(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IAudioCaptureClient_Release(ma_IAudioCaptureClient* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IAudioCaptureClient_GetBuffer(ma_IAudioCaptureClient* pThis, BYTE** ppData, ma_uint32* pNumFramesToRead, DWORD* pFlags, ma_uint64* pDevicePosition, ma_uint64* pQPCPosition) { return pThis->lpVtbl->GetBuffer(pThis, ppData, pNumFramesToRead, pFlags, pDevicePosition, pQPCPosition); }
|
|
static MA_INLINE HRESULT ma_IAudioCaptureClient_ReleaseBuffer(ma_IAudioCaptureClient* pThis, ma_uint32 numFramesRead) { return pThis->lpVtbl->ReleaseBuffer(pThis, numFramesRead); }
|
|
static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptureClient* pThis, ma_uint32* pNumFramesInNextPacket) { return pThis->lpVtbl->GetNextPacketSize(pThis, pNumFramesInNextPacket); }
|
|
|
|
#if defined(MA_WIN32_UWP)
|
|
typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation);
|
|
#endif
|
|
|
|
typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex);
|
|
typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle);
|
|
|
|
#if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK)
|
|
typedef struct ma_completion_handler_uwp ma_completion_handler_uwp;
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_completion_handler_uwp* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_completion_handler_uwp* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * ActivateCompleted)(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation);
|
|
} ma_completion_handler_uwp_vtbl;
|
|
struct ma_completion_handler_uwp
|
|
{
|
|
ma_completion_handler_uwp_vtbl* lpVtbl;
|
|
MA_ATOMIC(4, ma_uint32) counter;
|
|
HANDLE hEvent;
|
|
};
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_completion_handler_uwp* pThis, const IID* const riid, void** ppObject)
|
|
{
|
|
if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IActivateAudioInterfaceCompletionHandler) && !ma_is_guid_equal(riid, &MA_IID_IAgileObject)) {
|
|
*ppObject = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
*ppObject = (void*)pThis;
|
|
((ma_completion_handler_uwp_vtbl*)pThis->lpVtbl)->AddRef(pThis);
|
|
return S_OK;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis)
|
|
{
|
|
return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis)
|
|
{
|
|
ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1;
|
|
if (newRefCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (ULONG)newRefCount;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_ActivateCompleted(ma_completion_handler_uwp* pThis, ma_IActivateAudioInterfaceAsyncOperation* pActivateOperation)
|
|
{
|
|
(void)pActivateOperation;
|
|
SetEvent(pThis->hEvent);
|
|
return S_OK;
|
|
}
|
|
|
|
static ma_completion_handler_uwp_vtbl g_maCompletionHandlerVtblInstance = {
|
|
ma_completion_handler_uwp_QueryInterface,
|
|
ma_completion_handler_uwp_AddRef,
|
|
ma_completion_handler_uwp_Release,
|
|
ma_completion_handler_uwp_ActivateCompleted
|
|
};
|
|
|
|
static ma_result ma_completion_handler_uwp_init(ma_completion_handler_uwp* pHandler)
|
|
{
|
|
MA_ASSERT(pHandler != NULL);
|
|
MA_ZERO_OBJECT(pHandler);
|
|
|
|
pHandler->lpVtbl = &g_maCompletionHandlerVtblInstance;
|
|
pHandler->counter = 1;
|
|
pHandler->hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
if (pHandler->hEvent == NULL) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler)
|
|
{
|
|
if (pHandler->hEvent != NULL) {
|
|
CloseHandle(pHandler->hEvent);
|
|
}
|
|
}
|
|
|
|
static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler)
|
|
{
|
|
WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE);
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMNotificationClient* pThis, const IID* const riid, void** ppObject)
|
|
{
|
|
if (!ma_is_guid_equal(riid, &MA_IID_IUnknown) && !ma_is_guid_equal(riid, &MA_IID_IMMNotificationClient)) {
|
|
*ppObject = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
*ppObject = (void*)pThis;
|
|
((ma_IMMNotificationClientVtbl*)pThis->lpVtbl)->AddRef(pThis);
|
|
return S_OK;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis)
|
|
{
|
|
return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1;
|
|
}
|
|
|
|
static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis)
|
|
{
|
|
ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1;
|
|
if (newRefCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (ULONG)newRefCount;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState)
|
|
{
|
|
ma_bool32 isThisDevice = MA_FALSE;
|
|
ma_bool32 isCapture = MA_FALSE;
|
|
ma_bool32 isPlayback = MA_FALSE;
|
|
|
|
#ifdef MA_DEBUG_OUTPUT
|
|
#endif
|
|
|
|
if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) {
|
|
isCapture = MA_TRUE;
|
|
if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) {
|
|
isThisDevice = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) {
|
|
isPlayback = MA_TRUE;
|
|
if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) {
|
|
isThisDevice = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isThisDevice) {
|
|
if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) == 0) {
|
|
if (ma_device_get_state(pThis->pDevice) == ma_device_state_started) {
|
|
if (isPlayback) {
|
|
pThis->pDevice->wasapi.isDetachedPlayback = MA_TRUE;
|
|
}
|
|
if (isCapture) {
|
|
pThis->pDevice->wasapi.isDetachedCapture = MA_TRUE;
|
|
}
|
|
|
|
ma_device_stop(pThis->pDevice);
|
|
}
|
|
}
|
|
|
|
if ((dwNewState & MA_MM_DEVICE_STATE_ACTIVE) != 0) {
|
|
ma_bool8 tryRestartingDevice = MA_FALSE;
|
|
|
|
if (isPlayback) {
|
|
if (pThis->pDevice->wasapi.isDetachedPlayback) {
|
|
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
|
|
ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
|
|
tryRestartingDevice = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isCapture) {
|
|
if (pThis->pDevice->wasapi.isDetachedCapture) {
|
|
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
|
|
ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
|
|
tryRestartingDevice = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (tryRestartingDevice) {
|
|
if (pThis->pDevice->wasapi.isDetachedPlayback == MA_FALSE && pThis->pDevice->wasapi.isDetachedCapture == MA_FALSE) {
|
|
ma_device_start(pThis->pDevice);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)
|
|
{
|
|
#ifdef MA_DEBUG_OUTPUT
|
|
#endif
|
|
|
|
(void)pThis;
|
|
(void)pDeviceID;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID)
|
|
{
|
|
#ifdef MA_DEBUG_OUTPUT
|
|
#endif
|
|
|
|
(void)pThis;
|
|
(void)pDeviceID;
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID)
|
|
{
|
|
#ifdef MA_DEBUG_OUTPUT
|
|
#endif
|
|
|
|
(void)role;
|
|
|
|
if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) ||
|
|
(pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) ||
|
|
(pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) {
|
|
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because dataFlow does match device type.\n");
|
|
return S_OK;
|
|
}
|
|
|
|
if (pThis->pDevice->type == ma_device_type_loopback) {
|
|
dataFlow = ma_eCapture;
|
|
}
|
|
|
|
if ((dataFlow == ma_eRender && pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting == MA_FALSE) ||
|
|
(dataFlow == ma_eCapture && pThis->pDevice->wasapi.allowCaptureAutoStreamRouting == MA_FALSE)) {
|
|
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because automatic stream routing has been disabled by the device config.\n");
|
|
return S_OK;
|
|
}
|
|
|
|
if ((dataFlow == ma_eRender && pThis->pDevice->playback.shareMode == ma_share_mode_exclusive) ||
|
|
(dataFlow == ma_eCapture && pThis->pDevice->capture.shareMode == ma_share_mode_exclusive)) {
|
|
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device shared mode is exclusive.\n");
|
|
return S_OK;
|
|
}
|
|
|
|
{
|
|
ma_uint32 previousState = ma_device_get_state(pThis->pDevice);
|
|
ma_bool8 restartDevice = MA_FALSE;
|
|
|
|
if (previousState == ma_device_state_uninitialized || previousState == ma_device_state_starting) {
|
|
ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting abandoned because the device is in the process of starting.\n");
|
|
return S_OK;
|
|
}
|
|
|
|
if (previousState == ma_device_state_started) {
|
|
ma_device_stop(pThis->pDevice);
|
|
restartDevice = MA_TRUE;
|
|
}
|
|
|
|
if (pDefaultDeviceID != NULL) {
|
|
ma_mutex_lock(&pThis->pDevice->wasapi.rerouteLock);
|
|
{
|
|
if (dataFlow == ma_eRender) {
|
|
ma_device_reroute__wasapi(pThis->pDevice, ma_device_type_playback);
|
|
|
|
if (pThis->pDevice->wasapi.isDetachedPlayback) {
|
|
pThis->pDevice->wasapi.isDetachedPlayback = MA_FALSE;
|
|
|
|
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedCapture) {
|
|
restartDevice = MA_FALSE;
|
|
}
|
|
else {
|
|
restartDevice = MA_TRUE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
ma_device_reroute__wasapi(pThis->pDevice, (pThis->pDevice->type == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture);
|
|
|
|
if (pThis->pDevice->wasapi.isDetachedCapture) {
|
|
pThis->pDevice->wasapi.isDetachedCapture = MA_FALSE;
|
|
|
|
if (pThis->pDevice->type == ma_device_type_duplex && pThis->pDevice->wasapi.isDetachedPlayback) {
|
|
restartDevice = MA_FALSE;
|
|
}
|
|
else {
|
|
restartDevice = MA_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ma_mutex_unlock(&pThis->pDevice->wasapi.rerouteLock);
|
|
|
|
if (restartDevice) {
|
|
ma_device_start(pThis->pDevice);
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key)
|
|
{
|
|
#ifdef MA_DEBUG_OUTPUT
|
|
#endif
|
|
|
|
(void)pThis;
|
|
(void)pDeviceID;
|
|
(void)key;
|
|
return S_OK;
|
|
}
|
|
|
|
static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = {
|
|
ma_IMMNotificationClient_QueryInterface,
|
|
ma_IMMNotificationClient_AddRef,
|
|
ma_IMMNotificationClient_Release,
|
|
ma_IMMNotificationClient_OnDeviceStateChanged,
|
|
ma_IMMNotificationClient_OnDeviceAdded,
|
|
ma_IMMNotificationClient_OnDeviceRemoved,
|
|
ma_IMMNotificationClient_OnDefaultDeviceChanged,
|
|
ma_IMMNotificationClient_OnPropertyValueChanged
|
|
};
|
|
#endif
|
|
|
|
static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage)
|
|
{
|
|
switch (usage)
|
|
{
|
|
case ma_wasapi_usage_default: return NULL;
|
|
case ma_wasapi_usage_games: return "Games";
|
|
case ma_wasapi_usage_pro_audio: return "Pro Audio";
|
|
default: break;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
typedef ma_IMMDevice ma_WASAPIDeviceInterface;
|
|
#else
|
|
typedef ma_IUnknown ma_WASAPIDeviceInterface;
|
|
#endif
|
|
|
|
#define MA_CONTEXT_COMMAND_QUIT__WASAPI 1
|
|
#define MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI 2
|
|
#define MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI 3
|
|
|
|
static ma_context_command__wasapi ma_context_init_command__wasapi(int code)
|
|
{
|
|
ma_context_command__wasapi cmd;
|
|
|
|
MA_ZERO_OBJECT(&cmd);
|
|
cmd.code = code;
|
|
|
|
return cmd;
|
|
}
|
|
|
|
static ma_result ma_context_post_command__wasapi(ma_context* pContext, const ma_context_command__wasapi* pCmd)
|
|
{
|
|
ma_result result;
|
|
ma_bool32 isUsingLocalEvent = MA_FALSE;
|
|
ma_event localEvent;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pCmd != NULL);
|
|
|
|
if (pCmd->pEvent == NULL) {
|
|
isUsingLocalEvent = MA_TRUE;
|
|
|
|
result = ma_event_init(&localEvent);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
ma_mutex_lock(&pContext->wasapi.commandLock);
|
|
{
|
|
ma_uint32 index;
|
|
|
|
while (pContext->wasapi.commandCount == ma_countof(pContext->wasapi.commands)) {
|
|
ma_yield();
|
|
}
|
|
|
|
index = (pContext->wasapi.commandIndex + pContext->wasapi.commandCount) % ma_countof(pContext->wasapi.commands);
|
|
pContext->wasapi.commands[index] = *pCmd;
|
|
pContext->wasapi.commands[index].pEvent = &localEvent;
|
|
pContext->wasapi.commandCount += 1;
|
|
|
|
ma_semaphore_release(&pContext->wasapi.commandSem);
|
|
}
|
|
ma_mutex_unlock(&pContext->wasapi.commandLock);
|
|
|
|
if (isUsingLocalEvent) {
|
|
ma_event_wait(&localEvent);
|
|
ma_event_uninit(&localEvent);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_next_command__wasapi(ma_context* pContext, ma_context_command__wasapi* pCmd)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pCmd != NULL);
|
|
|
|
result = ma_semaphore_wait(&pContext->wasapi.commandSem);
|
|
if (result == MA_SUCCESS) {
|
|
ma_mutex_lock(&pContext->wasapi.commandLock);
|
|
{
|
|
*pCmd = pContext->wasapi.commands[pContext->wasapi.commandIndex];
|
|
pContext->wasapi.commandIndex = (pContext->wasapi.commandIndex + 1) % ma_countof(pContext->wasapi.commands);
|
|
pContext->wasapi.commandCount -= 1;
|
|
}
|
|
ma_mutex_unlock(&pContext->wasapi.commandLock);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_thread_result MA_THREADCALL ma_context_command_thread__wasapi(void* pUserData)
|
|
{
|
|
ma_result result;
|
|
ma_context* pContext = (ma_context*)pUserData;
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
for (;;) {
|
|
ma_context_command__wasapi cmd;
|
|
result = ma_context_next_command__wasapi(pContext, &cmd);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
switch (cmd.code)
|
|
{
|
|
case MA_CONTEXT_COMMAND_QUIT__WASAPI:
|
|
{
|
|
} break;
|
|
|
|
case MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI:
|
|
{
|
|
if (cmd.data.createAudioClient.deviceType == ma_device_type_playback) {
|
|
*cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioRenderClient, cmd.data.createAudioClient.ppAudioClientService));
|
|
} else {
|
|
*cmd.data.createAudioClient.pResult = ma_result_from_HRESULT(ma_IAudioClient_GetService((ma_IAudioClient*)cmd.data.createAudioClient.pAudioClient, &MA_IID_IAudioCaptureClient, cmd.data.createAudioClient.ppAudioClientService));
|
|
}
|
|
} break;
|
|
|
|
case MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI:
|
|
{
|
|
if (cmd.data.releaseAudioClient.deviceType == ma_device_type_playback) {
|
|
if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback != NULL) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback);
|
|
cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientPlayback = NULL;
|
|
}
|
|
}
|
|
|
|
if (cmd.data.releaseAudioClient.deviceType == ma_device_type_capture) {
|
|
if (cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture != NULL) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture);
|
|
cmd.data.releaseAudioClient.pDevice->wasapi.pAudioClientCapture = NULL;
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
} break;
|
|
}
|
|
|
|
if (cmd.pEvent != NULL) {
|
|
ma_event_signal(cmd.pEvent);
|
|
}
|
|
|
|
if (cmd.code == MA_CONTEXT_COMMAND_QUIT__WASAPI) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (ma_thread_result)0;
|
|
}
|
|
|
|
static ma_result ma_device_create_IAudioClient_service__wasapi(ma_context* pContext, ma_device_type deviceType, ma_IAudioClient* pAudioClient, void** ppAudioClientService)
|
|
{
|
|
ma_result result;
|
|
ma_result cmdResult;
|
|
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_CREATE_IAUDIOCLIENT__WASAPI);
|
|
cmd.data.createAudioClient.deviceType = deviceType;
|
|
cmd.data.createAudioClient.pAudioClient = (void*)pAudioClient;
|
|
cmd.data.createAudioClient.ppAudioClientService = ppAudioClientService;
|
|
cmd.data.createAudioClient.pResult = &cmdResult;
|
|
|
|
result = ma_context_post_command__wasapi(pContext, &cmd);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return *cmd.data.createAudioClient.pResult;
|
|
}
|
|
|
|
#if 0
|
|
static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevice, ma_device_type deviceType)
|
|
{
|
|
ma_result result;
|
|
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_RELEASE_IAUDIOCLIENT__WASAPI);
|
|
cmd.data.releaseAudioClient.pDevice = pDevice;
|
|
cmd.data.releaseAudioClient.deviceType = deviceType;
|
|
|
|
result = ma_context_post_command__wasapi(pDevice->pContext, &cmd);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo)
|
|
{
|
|
MA_ASSERT(pWF != NULL);
|
|
MA_ASSERT(pInfo != NULL);
|
|
|
|
if (pInfo->nativeDataFormatCount >= ma_countof(pInfo->nativeDataFormats)) {
|
|
return;
|
|
}
|
|
|
|
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].format = ma_format_from_WAVEFORMATEX(pWF);
|
|
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].channels = pWF->nChannels;
|
|
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].sampleRate = pWF->nSamplesPerSec;
|
|
pInfo->nativeDataFormats[pInfo->nativeDataFormatCount].flags = (shareMode == ma_share_mode_exclusive) ? MA_DATA_FORMAT_FLAG_EXCLUSIVE_MODE : 0;
|
|
pInfo->nativeDataFormatCount += 1;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo)
|
|
{
|
|
HRESULT hr;
|
|
MA_WAVEFORMATEX* pWF = NULL;
|
|
|
|
MA_ASSERT(pAudioClient != NULL);
|
|
MA_ASSERT(pInfo != NULL);
|
|
|
|
hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF);
|
|
if (SUCCEEDED(hr)) {
|
|
ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo);
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve mix format for device info retrieval.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
{
|
|
ma_IPropertyStore *pProperties;
|
|
|
|
hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties);
|
|
if (SUCCEEDED(hr)) {
|
|
MA_PROPVARIANT var;
|
|
ma_PropVariantInit(&var);
|
|
|
|
hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var);
|
|
if (SUCCEEDED(hr)) {
|
|
pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData;
|
|
|
|
hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pWF, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_exclusive, pInfo);
|
|
} else {
|
|
ma_uint32 channels = pWF->nChannels;
|
|
ma_channel defaultChannelMap[MA_MAX_CHANNELS];
|
|
MA_WAVEFORMATEXTENSIBLE wf;
|
|
ma_bool32 found;
|
|
ma_uint32 iFormat;
|
|
|
|
if (channels > MA_MAX_CHANNELS) {
|
|
channels = MA_MAX_CHANNELS;
|
|
}
|
|
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels);
|
|
|
|
MA_ZERO_OBJECT(&wf);
|
|
wf.cbSize = sizeof(wf);
|
|
wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
wf.nChannels = (WORD)channels;
|
|
wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels);
|
|
|
|
found = MA_FALSE;
|
|
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
|
|
ma_format format = g_maFormatPriorities[iFormat];
|
|
ma_uint32 iSampleRate;
|
|
|
|
wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
|
|
wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
|
|
wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
|
|
wf.Samples.wValidBitsPerSample = wf.wBitsPerSample;
|
|
if (format == ma_format_f32) {
|
|
wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
} else {
|
|
wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
|
|
}
|
|
|
|
for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) {
|
|
wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate];
|
|
|
|
hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo);
|
|
found = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ma_PropVariantClear(pContext, &var);
|
|
|
|
if (!found) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to find suitable device format for device info retrieval.");
|
|
}
|
|
}
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to retrieve device format for device info retrieval.");
|
|
}
|
|
|
|
ma_IPropertyStore_Release(pProperties);
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "[WASAPI] Failed to open property store for device info retrieval.");
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
(void)pMMDevice;
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
static ma_EDataFlow ma_device_type_to_EDataFlow(ma_device_type deviceType)
|
|
{
|
|
if (deviceType == ma_device_type_playback) {
|
|
return ma_eRender;
|
|
} else if (deviceType == ma_device_type_capture) {
|
|
return ma_eCapture;
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return ma_eRender;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator** ppDeviceEnumerator)
|
|
{
|
|
HRESULT hr;
|
|
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppDeviceEnumerator != NULL);
|
|
|
|
*ppDeviceEnumerator = NULL;
|
|
|
|
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
*ppDeviceEnumerator = pDeviceEnumerator;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType)
|
|
{
|
|
HRESULT hr;
|
|
ma_IMMDevice* pMMDefaultDevice = NULL;
|
|
WCHAR* pDefaultDeviceID = NULL;
|
|
ma_EDataFlow dataFlow;
|
|
ma_ERole role;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDeviceEnumerator != NULL);
|
|
|
|
(void)pContext;
|
|
|
|
dataFlow = ma_device_type_to_EDataFlow(deviceType);
|
|
|
|
role = ma_eConsole;
|
|
|
|
hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, dataFlow, role, &pMMDefaultDevice);
|
|
if (FAILED(hr)) {
|
|
return NULL;
|
|
}
|
|
|
|
hr = ma_IMMDevice_GetId(pMMDefaultDevice, &pDefaultDeviceID);
|
|
|
|
ma_IMMDevice_Release(pMMDefaultDevice);
|
|
pMMDefaultDevice = NULL;
|
|
|
|
if (FAILED(hr)) {
|
|
return NULL;
|
|
}
|
|
|
|
return pDefaultDeviceID;
|
|
}
|
|
|
|
static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType)
|
|
{
|
|
ma_result result;
|
|
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
|
WCHAR* pDefaultDeviceID = NULL;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
result = ma_context_create_IMMDeviceEnumerator__wasapi(pContext, &pDeviceEnumerator);
|
|
if (result != MA_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
|
|
pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
|
|
|
|
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
|
|
return pDefaultDeviceID;
|
|
}
|
|
|
|
static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_IMMDevice** ppMMDevice)
|
|
{
|
|
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
|
HRESULT hr;
|
|
HRESULT CoInitializeResult;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppMMDevice != NULL);
|
|
|
|
CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
|
|
{
|
|
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
|
}
|
|
if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) { ma_CoUninitialize(pContext); }
|
|
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if (pDeviceID == NULL) {
|
|
hr = ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(pDeviceEnumerator, (deviceType == ma_device_type_capture) ? ma_eCapture : ma_eRender, ma_eConsole, ppMMDevice);
|
|
} else {
|
|
hr = ma_IMMDeviceEnumerator_GetDevice(pDeviceEnumerator, pDeviceID->wasapi, ppMMDevice);
|
|
}
|
|
|
|
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve IMMDevice.\n");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID)
|
|
{
|
|
WCHAR* pDeviceIDString;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pDeviceID != NULL);
|
|
|
|
hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString);
|
|
if (SUCCEEDED(hr)) {
|
|
size_t idlen = ma_strlen_WCHAR(pDeviceIDString);
|
|
if (idlen+1 > ma_countof(pDeviceID->wasapi)) {
|
|
ma_CoTaskMemFree(pContext, pDeviceIDString);
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_ERROR;
|
|
}
|
|
|
|
MA_COPY_MEMORY(pDeviceID->wasapi, pDeviceIDString, idlen * sizeof(wchar_t));
|
|
pDeviceID->wasapi[idlen] = '\0';
|
|
|
|
ma_CoTaskMemFree(pContext, pDeviceIDString);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
return MA_ERROR;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo)
|
|
{
|
|
ma_result result;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pMMDevice != NULL);
|
|
MA_ASSERT(pInfo != NULL);
|
|
|
|
result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id);
|
|
if (result == MA_SUCCESS) {
|
|
if (pDefaultDeviceID != NULL) {
|
|
if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) {
|
|
pInfo->isDefault = MA_TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
ma_IPropertyStore *pProperties;
|
|
hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties);
|
|
if (SUCCEEDED(hr)) {
|
|
MA_PROPVARIANT var;
|
|
|
|
ma_PropVariantInit(&var);
|
|
hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var);
|
|
if (SUCCEEDED(hr)) {
|
|
WideCharToMultiByte(CP_UTF8, 0, var.pwszVal, -1, pInfo->name, sizeof(pInfo->name), 0, FALSE);
|
|
ma_PropVariantClear(pContext, &var);
|
|
}
|
|
|
|
ma_IPropertyStore_Release(pProperties);
|
|
}
|
|
}
|
|
|
|
if (!onlySimpleInfo) {
|
|
ma_IAudioClient* pAudioClient;
|
|
hr = ma_IMMDevice_Activate(pMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient);
|
|
if (SUCCEEDED(hr)) {
|
|
result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, pMMDevice, pAudioClient, pInfo);
|
|
|
|
ma_IAudioClient_Release(pAudioClient);
|
|
return result;
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate audio client for device info retrieval.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
UINT deviceCount;
|
|
HRESULT hr;
|
|
ma_uint32 iDevice;
|
|
WCHAR* pDefaultDeviceID = NULL;
|
|
ma_IMMDeviceCollection* pDeviceCollection = NULL;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
pDefaultDeviceID = ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(pContext, pDeviceEnumerator, deviceType);
|
|
|
|
hr = ma_IMMDeviceEnumerator_EnumAudioEndpoints(pDeviceEnumerator, ma_device_type_to_EDataFlow(deviceType), MA_MM_DEVICE_STATE_ACTIVE, &pDeviceCollection);
|
|
if (SUCCEEDED(hr)) {
|
|
hr = ma_IMMDeviceCollection_GetCount(pDeviceCollection, &deviceCount);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to get device count.\n");
|
|
result = ma_result_from_HRESULT(hr);
|
|
goto done;
|
|
}
|
|
|
|
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
|
ma_device_info deviceInfo;
|
|
ma_IMMDevice* pMMDevice;
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
hr = ma_IMMDeviceCollection_Item(pDeviceCollection, iDevice, &pMMDevice);
|
|
if (SUCCEEDED(hr)) {
|
|
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_TRUE, &deviceInfo);
|
|
|
|
ma_IMMDevice_Release(pMMDevice);
|
|
if (result == MA_SUCCESS) {
|
|
ma_bool32 cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
|
|
if (cbResult == MA_FALSE) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (pDefaultDeviceID != NULL) {
|
|
ma_CoTaskMemFree(pContext, pDefaultDeviceID);
|
|
pDefaultDeviceID = NULL;
|
|
}
|
|
|
|
if (pDeviceCollection != NULL) {
|
|
ma_IMMDeviceCollection_Release(pDeviceCollection);
|
|
pDeviceCollection = NULL;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice)
|
|
{
|
|
ma_result result;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppAudioClient != NULL);
|
|
MA_ASSERT(ppMMDevice != NULL);
|
|
|
|
result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, ppMMDevice);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
hr = ma_IMMDevice_Activate(*ppMMDevice, &MA_IID_IAudioClient, CLSCTX_ALL, pActivationParams, (void**)ppAudioClient);
|
|
if (FAILED(hr)) {
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface)
|
|
{
|
|
ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL;
|
|
ma_completion_handler_uwp completionHandler;
|
|
IID iid;
|
|
WCHAR* iidStr;
|
|
HRESULT hr;
|
|
ma_result result;
|
|
HRESULT activateResult;
|
|
ma_IUnknown* pActivatedInterface;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppAudioClient != NULL);
|
|
|
|
if (pDeviceID != NULL) {
|
|
iidStr = (WCHAR*)pDeviceID->wasapi;
|
|
} else {
|
|
if (deviceType == ma_device_type_capture) {
|
|
iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE;
|
|
} else {
|
|
iid = MA_IID_DEVINTERFACE_AUDIO_RENDER;
|
|
}
|
|
|
|
#if defined(__cplusplus)
|
|
hr = StringFromIID(iid, &iidStr);
|
|
#else
|
|
hr = StringFromIID(&iid, &iidStr);
|
|
#endif
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to convert device IID to string for ActivateAudioInterfaceAsync(). Out of memory.\n");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
result = ma_completion_handler_uwp_init(&completionHandler);
|
|
if (result != MA_SUCCESS) {
|
|
ma_CoTaskMemFree(pContext, iidStr);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for waiting for ActivateAudioInterfaceAsync().\n");
|
|
return result;
|
|
}
|
|
|
|
hr = ((MA_PFN_ActivateAudioInterfaceAsync)pContext->wasapi.ActivateAudioInterfaceAsync)(iidStr, &MA_IID_IAudioClient, pActivationParams, (ma_IActivateAudioInterfaceCompletionHandler*)&completionHandler, (ma_IActivateAudioInterfaceAsyncOperation**)&pAsyncOp);
|
|
if (FAILED(hr)) {
|
|
ma_completion_handler_uwp_uninit(&completionHandler);
|
|
ma_CoTaskMemFree(pContext, iidStr);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] ActivateAudioInterfaceAsync() failed.\n");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if (pDeviceID == NULL) {
|
|
ma_CoTaskMemFree(pContext, iidStr);
|
|
}
|
|
|
|
ma_completion_handler_uwp_wait(&completionHandler);
|
|
ma_completion_handler_uwp_uninit(&completionHandler);
|
|
|
|
hr = ma_IActivateAudioInterfaceAsyncOperation_GetActivateResult(pAsyncOp, &activateResult, &pActivatedInterface);
|
|
ma_IActivateAudioInterfaceAsyncOperation_Release(pAsyncOp);
|
|
|
|
if (FAILED(hr) || FAILED(activateResult)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to activate device.\n");
|
|
return FAILED(hr) ? ma_result_from_HRESULT(hr) : ma_result_from_HRESULT(activateResult);
|
|
}
|
|
|
|
hr = ma_IUnknown_QueryInterface(pActivatedInterface, &MA_IID_IAudioClient, (void**)ppAudioClient);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to query IAudioClient interface.\n");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if (ppActivatedInterface) {
|
|
*ppActivatedInterface = pActivatedInterface;
|
|
} else {
|
|
ma_IUnknown_Release(pActivatedInterface);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
typedef enum
|
|
{
|
|
MA_AUDIOCLIENT_ACTIVATION_TYPE_DEFAULT,
|
|
MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK
|
|
} MA_AUDIOCLIENT_ACTIVATION_TYPE;
|
|
|
|
typedef enum
|
|
{
|
|
MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE,
|
|
MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE
|
|
} MA_PROCESS_LOOPBACK_MODE;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD TargetProcessId;
|
|
MA_PROCESS_LOOPBACK_MODE ProcessLoopbackMode;
|
|
} MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS;
|
|
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#pragma warning(push)
|
|
#pragma warning(disable:4201)
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
|
#if defined(__clang__)
|
|
#pragma GCC diagnostic ignored "-Wc11-extensions"
|
|
#endif
|
|
#endif
|
|
typedef struct
|
|
{
|
|
MA_AUDIOCLIENT_ACTIVATION_TYPE ActivationType;
|
|
union
|
|
{
|
|
MA_AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS ProcessLoopbackParams;
|
|
};
|
|
} MA_AUDIOCLIENT_ACTIVATION_PARAMS;
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#pragma warning(pop)
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
|
|
#define MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK L"VAD\\Process_Loopback"
|
|
|
|
static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_uint32 loopbackProcessID, ma_bool32 loopbackProcessExclude, ma_IAudioClient** ppAudioClient, ma_WASAPIDeviceInterface** ppDeviceInterface)
|
|
{
|
|
ma_result result;
|
|
ma_bool32 usingProcessLoopback = MA_FALSE;
|
|
MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams;
|
|
MA_PROPVARIANT activationParams;
|
|
MA_PROPVARIANT* pActivationParams = NULL;
|
|
ma_device_id virtualDeviceID;
|
|
|
|
if (deviceType == ma_device_type_loopback && loopbackProcessID != 0 && pDeviceID == NULL) {
|
|
usingProcessLoopback = MA_TRUE;
|
|
}
|
|
|
|
if (usingProcessLoopback) {
|
|
MA_ZERO_OBJECT(&audioclientActivationParams);
|
|
audioclientActivationParams.ActivationType = MA_AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK;
|
|
audioclientActivationParams.ProcessLoopbackParams.ProcessLoopbackMode = (loopbackProcessExclude) ? MA_PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE : MA_PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE;
|
|
audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID;
|
|
|
|
ma_PropVariantInit(&activationParams);
|
|
activationParams.vt = MA_VT_BLOB;
|
|
activationParams.blob.cbSize = sizeof(audioclientActivationParams);
|
|
activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams;
|
|
pActivationParams = &activationParams;
|
|
|
|
MA_COPY_MEMORY(virtualDeviceID.wasapi, MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK, (ma_wcslen(MA_VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK) + 1) * sizeof(wchar_t));
|
|
pDeviceID = &virtualDeviceID;
|
|
} else {
|
|
pActivationParams = NULL;
|
|
}
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
result = ma_context_get_IAudioClient_Desktop__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
|
|
#else
|
|
result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, pActivationParams, ppAudioClient, ppDeviceInterface);
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
if (usingProcessLoopback) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Loopback mode requested to %s process ID %u, but initialization failed. Support for this feature begins with Windows 10 Build 20348. Confirm your version of Windows or consider not using process-specific loopback.\n", (loopbackProcessExclude) ? "exclude" : "include", loopbackProcessID);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
HRESULT hr;
|
|
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
|
|
|
hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_playback, callback, pUserData);
|
|
ma_context_enumerate_devices_by_type__wasapi(pContext, pDeviceEnumerator, ma_device_type_capture, callback, pUserData);
|
|
|
|
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
|
|
#else
|
|
|
|
if (callback) {
|
|
ma_bool32 cbResult = MA_TRUE;
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
ma_result result;
|
|
ma_IMMDevice* pMMDevice = NULL;
|
|
WCHAR* pDefaultDeviceID = NULL;
|
|
|
|
result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDefaultDeviceID = ma_context_get_default_device_id__wasapi(pContext, deviceType);
|
|
|
|
result = ma_context_get_device_info_from_MMDevice__wasapi(pContext, pMMDevice, pDefaultDeviceID, MA_FALSE, pDeviceInfo);
|
|
|
|
if (pDefaultDeviceID != NULL) {
|
|
ma_CoTaskMemFree(pContext, pDefaultDeviceID);
|
|
pDefaultDeviceID = NULL;
|
|
}
|
|
|
|
ma_IMMDevice_Release(pMMDevice);
|
|
|
|
return result;
|
|
#else
|
|
ma_IAudioClient* pAudioClient;
|
|
ma_result result;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
result = ma_context_get_IAudioClient_UWP__wasapi(pContext, deviceType, pDeviceID, NULL, &pAudioClient, NULL);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_context_get_device_info_from_IAudioClient__wasapi(pContext, NULL, pAudioClient, pDeviceInfo);
|
|
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
|
|
ma_IAudioClient_Release(pAudioClient);
|
|
return result;
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_device_uninit__wasapi(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
{
|
|
if (pDevice->wasapi.pDeviceEnumerator) {
|
|
((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator)->lpVtbl->UnregisterEndpointNotificationCallback((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator, &pDevice->wasapi.notificationClient);
|
|
ma_IMMDeviceEnumerator_Release((ma_IMMDeviceEnumerator*)pDevice->wasapi.pDeviceEnumerator);
|
|
}
|
|
|
|
ma_mutex_uninit(&pDevice->wasapi.rerouteLock);
|
|
}
|
|
#endif
|
|
|
|
if (pDevice->wasapi.pRenderClient) {
|
|
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_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
|
|
}
|
|
if (pDevice->wasapi.pCaptureClient) {
|
|
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_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
|
|
}
|
|
|
|
if (pDevice->wasapi.pAudioClientPlayback) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
}
|
|
if (pDevice->wasapi.pAudioClientCapture) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
}
|
|
|
|
if (pDevice->wasapi.hEventPlayback) {
|
|
CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback);
|
|
}
|
|
if (pDevice->wasapi.hEventCapture) {
|
|
CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_format formatIn;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 sampleRateIn;
|
|
ma_channel channelMapIn[MA_MAX_CHANNELS];
|
|
ma_uint32 periodSizeInFramesIn;
|
|
ma_uint32 periodSizeInMillisecondsIn;
|
|
ma_uint32 periodsIn;
|
|
ma_share_mode shareMode;
|
|
ma_performance_profile performanceProfile;
|
|
ma_bool32 noAutoConvertSRC;
|
|
ma_bool32 noDefaultQualitySRC;
|
|
ma_bool32 noHardwareOffloading;
|
|
ma_uint32 loopbackProcessID;
|
|
ma_bool32 loopbackProcessExclude;
|
|
|
|
ma_IAudioClient* pAudioClient;
|
|
ma_IAudioRenderClient* pRenderClient;
|
|
ma_IAudioCaptureClient* pCaptureClient;
|
|
ma_format formatOut;
|
|
ma_uint32 channelsOut;
|
|
ma_uint32 sampleRateOut;
|
|
ma_channel channelMapOut[MA_MAX_CHANNELS];
|
|
ma_uint32 periodSizeInFramesOut;
|
|
ma_uint32 periodsOut;
|
|
ma_bool32 usingAudioClient3;
|
|
char deviceName[256];
|
|
ma_device_id id;
|
|
} ma_device_init_internal_data__wasapi;
|
|
|
|
static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__wasapi* pData)
|
|
{
|
|
HRESULT hr;
|
|
ma_result result = MA_SUCCESS;
|
|
const char* errorMsg = "";
|
|
MA_AUDCLNT_SHAREMODE shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
|
|
DWORD streamFlags = 0;
|
|
MA_REFERENCE_TIME periodDurationInMicroseconds;
|
|
ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE;
|
|
MA_WAVEFORMATEXTENSIBLE wf;
|
|
ma_WASAPIDeviceInterface* pDeviceInterface = NULL;
|
|
ma_IAudioClient2* pAudioClient2;
|
|
ma_uint32 nativeSampleRate;
|
|
ma_bool32 usingProcessLoopback = MA_FALSE;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pData != NULL);
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL;
|
|
|
|
pData->pAudioClient = NULL;
|
|
pData->pRenderClient = NULL;
|
|
pData->pCaptureClient = NULL;
|
|
|
|
streamFlags = MA_AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
|
|
if (!pData->noAutoConvertSRC && pData->sampleRateIn != 0 && pData->shareMode != ma_share_mode_exclusive) {
|
|
streamFlags |= MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
|
|
}
|
|
if (!pData->noDefaultQualitySRC && pData->sampleRateIn != 0 && (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) != 0) {
|
|
streamFlags |= MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
|
|
}
|
|
if (deviceType == ma_device_type_loopback) {
|
|
streamFlags |= MA_AUDCLNT_STREAMFLAGS_LOOPBACK;
|
|
}
|
|
|
|
result = ma_context_get_IAudioClient__wasapi(pContext, deviceType, pDeviceID, pData->loopbackProcessID, pData->loopbackProcessExclude, &pData->pAudioClient, &pDeviceInterface);
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&wf);
|
|
|
|
if (!pData->noHardwareOffloading) {
|
|
hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient2, (void**)&pAudioClient2);
|
|
if (SUCCEEDED(hr)) {
|
|
BOOL isHardwareOffloadingSupported = 0;
|
|
hr = ma_IAudioClient2_IsOffloadCapable(pAudioClient2, MA_AudioCategory_Other, &isHardwareOffloadingSupported);
|
|
if (SUCCEEDED(hr) && isHardwareOffloadingSupported) {
|
|
ma_AudioClientProperties clientProperties;
|
|
MA_ZERO_OBJECT(&clientProperties);
|
|
clientProperties.cbSize = sizeof(clientProperties);
|
|
clientProperties.bIsOffload = 1;
|
|
clientProperties.eCategory = MA_AudioCategory_Other;
|
|
ma_IAudioClient2_SetClientProperties(pAudioClient2, &clientProperties);
|
|
}
|
|
|
|
pAudioClient2->lpVtbl->Release(pAudioClient2);
|
|
}
|
|
}
|
|
|
|
result = MA_FORMAT_NOT_SUPPORTED;
|
|
if (pData->shareMode == ma_share_mode_exclusive) {
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
ma_IPropertyStore* pStore = NULL;
|
|
hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore);
|
|
if (SUCCEEDED(hr)) {
|
|
MA_PROPVARIANT prop;
|
|
ma_PropVariantInit(&prop);
|
|
hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop);
|
|
if (SUCCEEDED(hr)) {
|
|
MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData;
|
|
hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
|
|
}
|
|
|
|
ma_PropVariantClear(pContext, &prop);
|
|
}
|
|
|
|
ma_IPropertyStore_Release(pStore);
|
|
}
|
|
#else
|
|
|
|
hr = S_FALSE;
|
|
#endif
|
|
|
|
if (hr == S_OK) {
|
|
shareMode = MA_AUDCLNT_SHAREMODE_EXCLUSIVE;
|
|
result = MA_SUCCESS;
|
|
} else {
|
|
result = MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL;
|
|
hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat);
|
|
if (hr != S_OK) {
|
|
if (usingProcessLoopback) {
|
|
wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
|
wf.nChannels = 2;
|
|
wf.nSamplesPerSec = 44100;
|
|
wf.wBitsPerSample = 32;
|
|
wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8;
|
|
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
|
|
wf.cbSize = sizeof(MA_WAVEFORMATEX);
|
|
|
|
result = MA_SUCCESS;
|
|
} else {
|
|
result = MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
} else {
|
|
if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE));
|
|
} else {
|
|
size_t cbSize = pNativeFormat->cbSize;
|
|
if (cbSize == 0) {
|
|
cbSize = sizeof(MA_WAVEFORMATEX);
|
|
}
|
|
|
|
if (cbSize > sizeof(wf)) {
|
|
cbSize = sizeof(wf);
|
|
}
|
|
|
|
MA_COPY_MEMORY(&wf, pNativeFormat, cbSize);
|
|
}
|
|
|
|
result = MA_SUCCESS;
|
|
}
|
|
|
|
ma_CoTaskMemFree(pContext, pNativeFormat);
|
|
|
|
shareMode = MA_AUDCLNT_SHAREMODE_SHARED;
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
errorMsg = "[WASAPI] Failed to find best device mix format.";
|
|
goto done;
|
|
}
|
|
|
|
nativeSampleRate = wf.nSamplesPerSec;
|
|
if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) {
|
|
wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE;
|
|
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
|
|
}
|
|
|
|
pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf);
|
|
if (pData->formatOut == ma_format_unknown) {
|
|
if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
|
|
result = MA_SHARE_MODE_NOT_SUPPORTED;
|
|
} else {
|
|
result = MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
errorMsg = "[WASAPI] Native format not supported.";
|
|
goto done;
|
|
}
|
|
|
|
pData->channelsOut = wf.nChannels;
|
|
pData->sampleRateOut = wf.nSamplesPerSec;
|
|
|
|
if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) {
|
|
ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut);
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
|
|
}
|
|
|
|
pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS;
|
|
pData->periodSizeInFramesOut = pData->periodSizeInFramesIn;
|
|
if (pData->periodSizeInFramesOut == 0) {
|
|
if (pData->periodSizeInMillisecondsIn == 0) {
|
|
if (pData->performanceProfile == ma_performance_profile_low_latency) {
|
|
pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec);
|
|
} else {
|
|
pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec);
|
|
}
|
|
} else {
|
|
pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec);
|
|
}
|
|
}
|
|
|
|
periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec;
|
|
|
|
if (shareMode == MA_AUDCLNT_SHAREMODE_EXCLUSIVE) {
|
|
MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
|
|
|
|
hr = E_FAIL;
|
|
for (;;) {
|
|
hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
|
|
if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) {
|
|
if (bufferDuration > 500*10000) {
|
|
break;
|
|
} else {
|
|
if (bufferDuration == 0) {
|
|
break;
|
|
}
|
|
|
|
bufferDuration = bufferDuration * 2;
|
|
continue;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hr == MA_AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) {
|
|
ma_uint32 bufferSizeInFrames;
|
|
hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
|
|
if (SUCCEEDED(hr)) {
|
|
bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5);
|
|
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
hr = ma_IMMDevice_Activate(pDeviceInterface, &MA_IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pData->pAudioClient);
|
|
#else
|
|
hr = ma_IUnknown_QueryInterface(pDeviceInterface, &MA_IID_IAudioClient, (void**)&pData->pAudioClient);
|
|
#endif
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
if (hr == E_ACCESSDENIED) {
|
|
errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Access denied.", result = MA_ACCESS_DENIED;
|
|
} else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
|
|
errorMsg = "[WASAPI] Failed to initialize device in exclusive mode. Device in use.", result = MA_BUSY;
|
|
} else {
|
|
errorMsg = "[WASAPI] Failed to initialize device in exclusive mode."; result = ma_result_from_HRESULT(hr);
|
|
}
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (shareMode == MA_AUDCLNT_SHAREMODE_SHARED) {
|
|
|
|
#ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE
|
|
{
|
|
if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) {
|
|
ma_IAudioClient3* pAudioClient3 = NULL;
|
|
hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3);
|
|
if (SUCCEEDED(hr)) {
|
|
ma_uint32 defaultPeriodInFrames;
|
|
ma_uint32 fundamentalPeriodInFrames;
|
|
ma_uint32 minPeriodInFrames;
|
|
ma_uint32 maxPeriodInFrames;
|
|
hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames);
|
|
if (SUCCEEDED(hr)) {
|
|
ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut;
|
|
ma_uint32 actualPeriodInFrames = desiredPeriodInFrames;
|
|
|
|
actualPeriodInFrames = actualPeriodInFrames / fundamentalPeriodInFrames;
|
|
actualPeriodInFrames = actualPeriodInFrames * fundamentalPeriodInFrames;
|
|
|
|
actualPeriodInFrames = ma_clamp(actualPeriodInFrames, minPeriodInFrames, maxPeriodInFrames);
|
|
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Trying IAudioClient3_InitializeSharedAudioStream(actualPeriodInFrames=%d)\n", actualPeriodInFrames);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " defaultPeriodInFrames=%d\n", defaultPeriodInFrames);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " fundamentalPeriodInFrames=%d\n", fundamentalPeriodInFrames);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " minPeriodInFrames=%d\n", minPeriodInFrames);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " maxPeriodInFrames=%d\n", maxPeriodInFrames);
|
|
|
|
if (actualPeriodInFrames >= desiredPeriodInFrames) {
|
|
hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL);
|
|
if (SUCCEEDED(hr)) {
|
|
wasInitializedUsingIAudioClient3 = MA_TRUE;
|
|
pData->periodSizeInFramesOut = actualPeriodInFrames;
|
|
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Using IAudioClient3\n");
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " periodSizeInFramesOut=%d\n", pData->periodSizeInFramesOut);
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_InitializeSharedAudioStream failed. Falling back to IAudioClient.\n");
|
|
}
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because the desired period size is larger than the maximum supported by IAudioClient3.\n");
|
|
}
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] IAudioClient3_GetSharedModeEnginePeriod failed. Falling back to IAudioClient.\n");
|
|
}
|
|
|
|
ma_IAudioClient3_Release(pAudioClient3);
|
|
pAudioClient3 = NULL;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[WASAPI] Not using IAudioClient3 because MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE is enabled.\n");
|
|
}
|
|
#endif
|
|
|
|
if (!wasInitializedUsingIAudioClient3) {
|
|
MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10;
|
|
hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL);
|
|
if (FAILED(hr)) {
|
|
if (hr == E_ACCESSDENIED) {
|
|
errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED;
|
|
} else if (hr == MA_AUDCLNT_E_DEVICE_IN_USE) {
|
|
errorMsg = "[WASAPI] Failed to initialize device. Device in use.", result = MA_BUSY;
|
|
} else {
|
|
errorMsg = "[WASAPI] Failed to initialize device.", result = ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!wasInitializedUsingIAudioClient3) {
|
|
ma_uint32 bufferSizeInFrames = 0;
|
|
hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames);
|
|
if (FAILED(hr)) {
|
|
errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr);
|
|
goto done;
|
|
}
|
|
|
|
if (usingProcessLoopback) {
|
|
bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000);
|
|
}
|
|
|
|
pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut;
|
|
}
|
|
|
|
pData->usingAudioClient3 = wasInitializedUsingIAudioClient3;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pRenderClient);
|
|
} else {
|
|
result = ma_device_create_IAudioClient_service__wasapi(pContext, deviceType, (ma_IAudioClient*)pData->pAudioClient, (void**)&pData->pCaptureClient);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
errorMsg = "[WASAPI] Failed to get audio client service.";
|
|
goto done;
|
|
}
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
{
|
|
ma_IPropertyStore *pProperties;
|
|
hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties);
|
|
if (SUCCEEDED(hr)) {
|
|
MA_PROPVARIANT varName;
|
|
ma_PropVariantInit(&varName);
|
|
hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName);
|
|
if (SUCCEEDED(hr)) {
|
|
WideCharToMultiByte(CP_UTF8, 0, varName.pwszVal, -1, pData->deviceName, sizeof(pData->deviceName), 0, FALSE);
|
|
ma_PropVariantClear(pContext, &varName);
|
|
}
|
|
|
|
ma_IPropertyStore_Release(pProperties);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
{
|
|
ma_context_get_device_id_from_MMDevice__wasapi(pContext, pDeviceInterface, &pData->id);
|
|
}
|
|
#else
|
|
{
|
|
}
|
|
#endif
|
|
|
|
done:
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
if (pDeviceInterface != NULL) {
|
|
ma_IMMDevice_Release(pDeviceInterface);
|
|
}
|
|
#else
|
|
if (pDeviceInterface != NULL) {
|
|
ma_IUnknown_Release(pDeviceInterface);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
if (pData->pRenderClient) {
|
|
ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pData->pRenderClient);
|
|
pData->pRenderClient = NULL;
|
|
}
|
|
if (pData->pCaptureClient) {
|
|
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pData->pCaptureClient);
|
|
pData->pCaptureClient = NULL;
|
|
}
|
|
if (pData->pAudioClient) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient);
|
|
pData->pAudioClient = NULL;
|
|
}
|
|
|
|
if (errorMsg != NULL && errorMsg[0] != '\0') {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "%s\n", errorMsg);
|
|
}
|
|
|
|
return result;
|
|
} else {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type deviceType)
|
|
{
|
|
ma_device_init_internal_data__wasapi data;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
|
|
if (pDevice->wasapi.pCaptureClient) {
|
|
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
|
|
pDevice->wasapi.pCaptureClient = NULL;
|
|
}
|
|
|
|
if (pDevice->wasapi.pAudioClientCapture) {
|
|
pDevice->wasapi.pAudioClientCapture = NULL;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
if (pDevice->wasapi.pRenderClient) {
|
|
ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
|
|
pDevice->wasapi.pRenderClient = NULL;
|
|
}
|
|
|
|
if (pDevice->wasapi.pAudioClientPlayback) {
|
|
pDevice->wasapi.pAudioClientPlayback = NULL;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
data.formatIn = pDevice->playback.format;
|
|
data.channelsIn = pDevice->playback.channels;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
|
|
data.shareMode = pDevice->playback.shareMode;
|
|
} else {
|
|
data.formatIn = pDevice->capture.format;
|
|
data.channelsIn = pDevice->capture.channels;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
|
|
data.shareMode = pDevice->capture.shareMode;
|
|
}
|
|
|
|
data.sampleRateIn = pDevice->sampleRate;
|
|
data.periodSizeInFramesIn = pDevice->wasapi.originalPeriodSizeInFrames;
|
|
data.periodSizeInMillisecondsIn = pDevice->wasapi.originalPeriodSizeInMilliseconds;
|
|
data.periodsIn = pDevice->wasapi.originalPeriods;
|
|
data.performanceProfile = pDevice->wasapi.originalPerformanceProfile;
|
|
data.noAutoConvertSRC = pDevice->wasapi.noAutoConvertSRC;
|
|
data.noDefaultQualitySRC = pDevice->wasapi.noDefaultQualitySRC;
|
|
data.noHardwareOffloading = pDevice->wasapi.noHardwareOffloading;
|
|
data.loopbackProcessID = pDevice->wasapi.loopbackProcessID;
|
|
data.loopbackProcessExclude = pDevice->wasapi.loopbackProcessExclude;
|
|
result = ma_device_init_internal__wasapi(pDevice->pContext, deviceType, NULL, &data);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_loopback) {
|
|
pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
|
|
pDevice->wasapi.pCaptureClient = data.pCaptureClient;
|
|
|
|
pDevice->capture.internalFormat = data.formatOut;
|
|
pDevice->capture.internalChannels = data.channelsOut;
|
|
pDevice->capture.internalSampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDevice->capture.internalPeriods = data.periodsOut;
|
|
ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName);
|
|
|
|
ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
|
|
|
|
pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
|
|
ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
|
|
|
|
ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
|
|
pDevice->wasapi.pRenderClient = data.pRenderClient;
|
|
|
|
pDevice->playback.internalFormat = data.formatOut;
|
|
pDevice->playback.internalChannels = data.channelsOut;
|
|
pDevice->playback.internalSampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDevice->playback.internalPeriods = data.periodsOut;
|
|
ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName);
|
|
|
|
ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
|
|
|
|
pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
|
|
ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
|
|
|
|
ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
HRESULT hr;
|
|
ma_IMMDeviceEnumerator* pDeviceEnumerator;
|
|
#endif
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->wasapi);
|
|
pDevice->wasapi.usage = pConfig->wasapi.usage;
|
|
pDevice->wasapi.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
|
|
pDevice->wasapi.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
|
|
pDevice->wasapi.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
|
|
pDevice->wasapi.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
|
|
pDevice->wasapi.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback && pConfig->playback.shareMode == ma_share_mode_exclusive) {
|
|
return MA_INVALID_DEVICE_CONFIG;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
|
ma_device_init_internal_data__wasapi data;
|
|
data.formatIn = pDescriptorCapture->format;
|
|
data.channelsIn = pDescriptorCapture->channels;
|
|
data.sampleRateIn = pDescriptorCapture->sampleRate;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
|
|
data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
|
|
data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
|
|
data.periodsIn = pDescriptorCapture->periodCount;
|
|
data.shareMode = pDescriptorCapture->shareMode;
|
|
data.performanceProfile = pConfig->performanceProfile;
|
|
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
|
|
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
|
|
data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
|
|
data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
|
|
data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
|
|
|
|
result = ma_device_init_internal__wasapi(pDevice->pContext, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_loopback : ma_device_type_capture, pDescriptorCapture->pDeviceID, &data);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDevice->wasapi.pAudioClientCapture = data.pAudioClient;
|
|
pDevice->wasapi.pCaptureClient = data.pCaptureClient;
|
|
pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
|
|
pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
|
|
pDevice->wasapi.originalPeriods = pDescriptorCapture->periodCount;
|
|
pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
|
|
|
|
pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL);
|
|
if (pDevice->wasapi.hEventCapture == NULL) {
|
|
result = ma_result_from_GetLastError(GetLastError());
|
|
|
|
if (pDevice->wasapi.pCaptureClient != NULL) {
|
|
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
|
|
pDevice->wasapi.pCaptureClient = NULL;
|
|
}
|
|
if (pDevice->wasapi.pAudioClientCapture != NULL) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
pDevice->wasapi.pAudioClientCapture = NULL;
|
|
}
|
|
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture.");
|
|
return result;
|
|
}
|
|
ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture);
|
|
|
|
pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut;
|
|
ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture);
|
|
|
|
ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi);
|
|
|
|
pDescriptorCapture->format = data.formatOut;
|
|
pDescriptorCapture->channels = data.channelsOut;
|
|
pDescriptorCapture->sampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDescriptorCapture->periodCount = data.periodsOut;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_device_init_internal_data__wasapi data;
|
|
data.formatIn = pDescriptorPlayback->format;
|
|
data.channelsIn = pDescriptorPlayback->channels;
|
|
data.sampleRateIn = pDescriptorPlayback->sampleRate;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
|
|
data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
|
|
data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
|
|
data.periodsIn = pDescriptorPlayback->periodCount;
|
|
data.shareMode = pDescriptorPlayback->shareMode;
|
|
data.performanceProfile = pConfig->performanceProfile;
|
|
data.noAutoConvertSRC = pConfig->wasapi.noAutoConvertSRC;
|
|
data.noDefaultQualitySRC = pConfig->wasapi.noDefaultQualitySRC;
|
|
data.noHardwareOffloading = pConfig->wasapi.noHardwareOffloading;
|
|
data.loopbackProcessID = pConfig->wasapi.loopbackProcessID;
|
|
data.loopbackProcessExclude = pConfig->wasapi.loopbackProcessExclude;
|
|
|
|
result = ma_device_init_internal__wasapi(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data);
|
|
if (result != MA_SUCCESS) {
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
if (pDevice->wasapi.pCaptureClient != NULL) {
|
|
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
|
|
pDevice->wasapi.pCaptureClient = NULL;
|
|
}
|
|
if (pDevice->wasapi.pAudioClientCapture != NULL) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
pDevice->wasapi.pAudioClientCapture = NULL;
|
|
}
|
|
|
|
CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
|
|
pDevice->wasapi.hEventCapture = NULL;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pDevice->wasapi.pAudioClientPlayback = data.pAudioClient;
|
|
pDevice->wasapi.pRenderClient = data.pRenderClient;
|
|
pDevice->wasapi.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
|
|
pDevice->wasapi.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
|
|
pDevice->wasapi.originalPeriods = pDescriptorPlayback->periodCount;
|
|
pDevice->wasapi.originalPerformanceProfile = pConfig->performanceProfile;
|
|
|
|
pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL);
|
|
if (pDevice->wasapi.hEventPlayback == NULL) {
|
|
result = ma_result_from_GetLastError(GetLastError());
|
|
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
if (pDevice->wasapi.pCaptureClient != NULL) {
|
|
ma_IAudioCaptureClient_Release((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient);
|
|
pDevice->wasapi.pCaptureClient = NULL;
|
|
}
|
|
if (pDevice->wasapi.pAudioClientCapture != NULL) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
pDevice->wasapi.pAudioClientCapture = NULL;
|
|
}
|
|
|
|
CloseHandle((HANDLE)pDevice->wasapi.hEventCapture);
|
|
pDevice->wasapi.hEventCapture = NULL;
|
|
}
|
|
|
|
if (pDevice->wasapi.pRenderClient != NULL) {
|
|
ma_IAudioRenderClient_Release((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient);
|
|
pDevice->wasapi.pRenderClient = NULL;
|
|
}
|
|
if (pDevice->wasapi.pAudioClientPlayback != NULL) {
|
|
ma_IAudioClient_Release((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
pDevice->wasapi.pAudioClientPlayback = NULL;
|
|
}
|
|
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback.");
|
|
return result;
|
|
}
|
|
ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback);
|
|
|
|
pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut;
|
|
ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback);
|
|
|
|
ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi);
|
|
|
|
pDescriptorPlayback->format = data.formatOut;
|
|
pDescriptorPlayback->channels = data.channelsOut;
|
|
pDescriptorPlayback->sampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDescriptorPlayback->periodCount = data.periodsOut;
|
|
}
|
|
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
if (pConfig->wasapi.noAutoStreamRouting == MA_FALSE) {
|
|
if ((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) && pConfig->capture.pDeviceID == NULL) {
|
|
pDevice->wasapi.allowCaptureAutoStreamRouting = MA_TRUE;
|
|
}
|
|
if ((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.pDeviceID == NULL) {
|
|
pDevice->wasapi.allowPlaybackAutoStreamRouting = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
ma_mutex_init(&pDevice->wasapi.rerouteLock);
|
|
|
|
hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__wasapi(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
pDevice->wasapi.notificationClient.lpVtbl = (void*)&g_maNotificationCientVtbl;
|
|
pDevice->wasapi.notificationClient.counter = 1;
|
|
pDevice->wasapi.notificationClient.pDevice = pDevice;
|
|
|
|
hr = pDeviceEnumerator->lpVtbl->RegisterEndpointNotificationCallback(pDeviceEnumerator, &pDevice->wasapi.notificationClient);
|
|
if (SUCCEEDED(hr)) {
|
|
pDevice->wasapi.pDeviceEnumerator = (ma_ptr)pDeviceEnumerator;
|
|
} else {
|
|
ma_IMMDeviceEnumerator_Release(pDeviceEnumerator);
|
|
}
|
|
#endif
|
|
|
|
ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_FALSE);
|
|
ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device__get_available_frames__wasapi(ma_device* pDevice, ma_IAudioClient* pAudioClient, ma_uint32* pFrameCount)
|
|
{
|
|
ma_uint32 paddingFramesCount;
|
|
HRESULT hr;
|
|
ma_share_mode shareMode;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pFrameCount != NULL);
|
|
|
|
*pFrameCount = 0;
|
|
|
|
if ((ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientPlayback && (ma_ptr)pAudioClient != pDevice->wasapi.pAudioClientCapture) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
shareMode = ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) ? pDevice->playback.shareMode : pDevice->capture.shareMode;
|
|
if (shareMode == ma_share_mode_shared) {
|
|
hr = ma_IAudioClient_GetCurrentPadding(pAudioClient, &paddingFramesCount);
|
|
if (FAILED(hr)) {
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
|
|
*pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback - paddingFramesCount;
|
|
} else {
|
|
*pFrameCount = paddingFramesCount;
|
|
}
|
|
} else {
|
|
if ((ma_ptr)pAudioClient == pDevice->wasapi.pAudioClientPlayback) {
|
|
*pFrameCount = pDevice->wasapi.actualBufferSizeInFramesPlayback;
|
|
} else {
|
|
*pFrameCount = pDevice->wasapi.actualBufferSizeInFramesCapture;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_reroute__wasapi(ma_device* pDevice, ma_device_type deviceType)
|
|
{
|
|
ma_result result;
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== CHANGING DEVICE ===\n");
|
|
|
|
result = ma_device_reinit__wasapi(pDevice, deviceType);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WASAPI] Reinitializing device after route change failed.\n");
|
|
return result;
|
|
}
|
|
|
|
ma_device__post_init_setup(pDevice, deviceType);
|
|
ma_device__on_notification_rerouted(pDevice);
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "=== DEVICE CHANGED ===\n");
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (pDevice->pContext->wasapi.hAvrt) {
|
|
const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage);
|
|
if (pTaskName) {
|
|
DWORD idx = 0;
|
|
pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
|
hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr);
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
ma_atomic_bool32_set(&pDevice->wasapi.isStartedCapture, MA_TRUE);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr);
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_TRUE);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__wasapi(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_mutex_lock(&pDevice->wasapi.rerouteLock);
|
|
{
|
|
result = ma_device_start__wasapi_nolock(pDevice);
|
|
}
|
|
ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->wasapi.hAvrtHandle) {
|
|
((MA_PFN_AvRevertMmThreadCharacteristics)pDevice->pContext->wasapi.AvRevertMmThreadcharacteristics)((HANDLE)pDevice->wasapi.hAvrtHandle);
|
|
pDevice->wasapi.hAvrtHandle = NULL;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
|
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.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
hr = ma_IAudioClient_Reset((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to reset internal capture device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (ma_atomic_bool32_get(&pDevice->wasapi.isStartedPlayback)) {
|
|
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 prevFramesAvailablePlayback = (ma_uint32)-1;
|
|
ma_uint32 framesAvailablePlayback;
|
|
for (;;) {
|
|
result = ma_device__get_available_frames__wasapi(pDevice, (ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &framesAvailablePlayback);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (framesAvailablePlayback >= pDevice->wasapi.actualBufferSizeInFramesPlayback) {
|
|
break;
|
|
}
|
|
|
|
if (framesAvailablePlayback == prevFramesAvailablePlayback) {
|
|
break;
|
|
}
|
|
prevFramesAvailablePlayback = framesAvailablePlayback;
|
|
|
|
ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
|
|
WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime);
|
|
}
|
|
}
|
|
}
|
|
|
|
hr = ma_IAudioClient_Stop((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to stop internal playback device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
{
|
|
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);
|
|
}
|
|
|
|
ma_atomic_bool32_set(&pDevice->wasapi.isStartedPlayback, MA_FALSE);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__wasapi(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_mutex_lock(&pDevice->wasapi.rerouteLock);
|
|
{
|
|
result = ma_device_stop__wasapi_nolock(pDevice);
|
|
}
|
|
ma_mutex_unlock(&pDevice->wasapi.rerouteLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifndef MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS
|
|
#define MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS 5000
|
|
#endif
|
|
|
|
static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 totalFramesProcessed = 0;
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
|
|
ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
|
|
|
|
if (pDevice->wasapi.pMappedBufferCapture != NULL) {
|
|
ma_uint32 framesToProcessNow = framesRemaining;
|
|
if (framesToProcessNow > pDevice->wasapi.mappedBufferCaptureLen) {
|
|
framesToProcessNow = pDevice->wasapi.mappedBufferCaptureLen;
|
|
}
|
|
|
|
ma_copy_pcm_frames(
|
|
ma_offset_pcm_frames_ptr(pFrames, totalFramesProcessed, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
|
|
ma_offset_pcm_frames_const_ptr(pDevice->wasapi.pMappedBufferCapture, pDevice->wasapi.mappedBufferCaptureCap - pDevice->wasapi.mappedBufferCaptureLen, pDevice->capture.internalFormat, pDevice->capture.internalChannels),
|
|
framesToProcessNow,
|
|
pDevice->capture.internalFormat, pDevice->capture.internalChannels
|
|
);
|
|
|
|
totalFramesProcessed += framesToProcessNow;
|
|
pDevice->wasapi.mappedBufferCaptureLen -= framesToProcessNow;
|
|
|
|
if (pDevice->wasapi.mappedBufferCaptureLen == 0) {
|
|
ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
|
|
pDevice->wasapi.pMappedBufferCapture = NULL;
|
|
pDevice->wasapi.mappedBufferCaptureCap = 0;
|
|
}
|
|
} else {
|
|
HRESULT hr;
|
|
DWORD flags = 0;
|
|
|
|
hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
|
|
if (hr == S_OK) {
|
|
pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
|
|
|
|
#if defined(MA_DEBUG_OUTPUT)
|
|
{
|
|
if (flags != 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Capture Flags: %ld\n", flags);
|
|
|
|
if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity (possible overrun). Attempting recovery. mappedBufferCaptureCap=%d\n", pDevice->wasapi.mappedBufferCaptureCap);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
|
|
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
|
|
ma_uint32 i;
|
|
ma_uint32 periodCount = (pDevice->wasapi.actualBufferSizeInFramesCapture / pDevice->wasapi.periodSizeInFramesCapture);
|
|
ma_uint32 iterationCount = periodCount / 2;
|
|
if ((periodCount % 2) > 0) {
|
|
iterationCount += 1;
|
|
}
|
|
|
|
for (i = 0; i < iterationCount; i += 1) {
|
|
hr = ma_IAudioCaptureClient_ReleaseBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, pDevice->wasapi.mappedBufferCaptureCap);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_ReleaseBuffer() failed with %ld.\n", hr);
|
|
break;
|
|
}
|
|
|
|
flags = 0;
|
|
hr = ma_IAudioCaptureClient_GetBuffer((ma_IAudioCaptureClient*)pDevice->wasapi.pCaptureClient, (BYTE**)&pDevice->wasapi.pMappedBufferCapture, &pDevice->wasapi.mappedBufferCaptureCap, &flags, NULL, NULL);
|
|
if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || FAILED(hr)) {
|
|
pDevice->wasapi.pMappedBufferCapture = NULL;
|
|
pDevice->wasapi.mappedBufferCaptureCap = 0;
|
|
pDevice->wasapi.mappedBufferCaptureLen = 0;
|
|
|
|
if (hr == MA_AUDCLNT_S_BUFFER_EMPTY) {
|
|
if ((flags & MA_AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) != 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied, and data discontinuity still reported.\n");
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: Buffer emptied.\n");
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Data discontinuity recovery: IAudioCaptureClient_GetBuffer() failed with %ld.\n", hr);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pDevice->wasapi.pMappedBufferCapture != NULL) {
|
|
pDevice->wasapi.mappedBufferCaptureLen = pDevice->wasapi.mappedBufferCaptureCap;
|
|
}
|
|
}
|
|
}
|
|
|
|
continue;
|
|
} else {
|
|
if (hr == MA_AUDCLNT_S_BUFFER_EMPTY || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
|
|
|
|
DWORD timeoutInMilliseconds = MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS;
|
|
if (pDevice->type == ma_device_type_loopback) {
|
|
timeoutInMilliseconds = 10;
|
|
}
|
|
|
|
if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) {
|
|
if (pDevice->type == ma_device_type_loopback) {
|
|
continue;
|
|
} else {
|
|
result = MA_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr);
|
|
result = ma_result_from_HRESULT(hr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (totalFramesProcessed < frameCount && 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;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesProcessed;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 totalFramesProcessed = 0;
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started && totalFramesProcessed < frameCount) {
|
|
ma_uint32 framesRemaining = frameCount - totalFramesProcessed;
|
|
|
|
if (pDevice->wasapi.pMappedBufferPlayback != NULL) {
|
|
ma_uint32 framesToProcessNow = framesRemaining;
|
|
if (framesToProcessNow > (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen)) {
|
|
framesToProcessNow = (pDevice->wasapi.mappedBufferPlaybackCap - pDevice->wasapi.mappedBufferPlaybackLen);
|
|
}
|
|
|
|
ma_copy_pcm_frames(
|
|
ma_offset_pcm_frames_ptr(pDevice->wasapi.pMappedBufferPlayback, pDevice->wasapi.mappedBufferPlaybackLen, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
|
|
ma_offset_pcm_frames_const_ptr(pFrames, totalFramesProcessed, pDevice->playback.internalFormat, pDevice->playback.internalChannels),
|
|
framesToProcessNow,
|
|
pDevice->playback.internalFormat, pDevice->playback.internalChannels
|
|
);
|
|
|
|
totalFramesProcessed += framesToProcessNow;
|
|
pDevice->wasapi.mappedBufferPlaybackLen += framesToProcessNow;
|
|
|
|
if (pDevice->wasapi.mappedBufferPlaybackLen == pDevice->wasapi.mappedBufferPlaybackCap) {
|
|
ma_IAudioRenderClient_ReleaseBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, pDevice->wasapi.mappedBufferPlaybackCap, 0);
|
|
pDevice->wasapi.pMappedBufferPlayback = NULL;
|
|
pDevice->wasapi.mappedBufferPlaybackCap = 0;
|
|
pDevice->wasapi.mappedBufferPlaybackLen = 0;
|
|
|
|
if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
|
|
if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
|
|
result = MA_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
HRESULT hr;
|
|
ma_uint32 bufferSizeInFrames;
|
|
|
|
if (pDevice->playback.shareMode == ma_share_mode_exclusive) {
|
|
bufferSizeInFrames = pDevice->wasapi.actualBufferSizeInFramesPlayback;
|
|
} else {
|
|
bufferSizeInFrames = pDevice->wasapi.periodSizeInFramesPlayback;
|
|
}
|
|
|
|
hr = ma_IAudioRenderClient_GetBuffer((ma_IAudioRenderClient*)pDevice->wasapi.pRenderClient, bufferSizeInFrames, (BYTE**)&pDevice->wasapi.pMappedBufferPlayback);
|
|
if (hr == S_OK) {
|
|
pDevice->wasapi.mappedBufferPlaybackCap = bufferSizeInFrames;
|
|
pDevice->wasapi.mappedBufferPlaybackLen = 0;
|
|
} else {
|
|
if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) {
|
|
if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) {
|
|
result = MA_ERROR;
|
|
break;
|
|
}
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from playback device in preparation for writing to the device. HRESULT = %d. Stopping device.\n", (int)hr);
|
|
result = ma_result_from_HRESULT(hr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = totalFramesProcessed;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
|
SetEvent((HANDLE)pDevice->wasapi.hEventCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
SetEvent((HANDLE)pDevice->wasapi.hEventPlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__wasapi(ma_context* pContext)
|
|
{
|
|
ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI);
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_wasapi);
|
|
|
|
ma_context_post_command__wasapi(pContext, &cmd);
|
|
ma_thread_wait(&pContext->wasapi.commandThread);
|
|
|
|
if (pContext->wasapi.hAvrt) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);
|
|
pContext->wasapi.hAvrt = NULL;
|
|
}
|
|
|
|
#if defined(MA_WIN32_UWP)
|
|
{
|
|
if (pContext->wasapi.hMMDevapi) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);
|
|
pContext->wasapi.hMMDevapi = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ma_semaphore_uninit(&pContext->wasapi.commandSem);
|
|
ma_mutex_uninit(&pContext->wasapi.commandLock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
#ifdef MA_WIN32_DESKTOP
|
|
|
|
{
|
|
ma_OSVERSIONINFOEXW osvi;
|
|
ma_handle kernel32DLL;
|
|
ma_PFNVerifyVersionInfoW _VerifyVersionInfoW;
|
|
ma_PFNVerSetConditionMask _VerSetConditionMask;
|
|
|
|
kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll");
|
|
if (kernel32DLL == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
_VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW");
|
|
_VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask");
|
|
if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), kernel32DLL);
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&osvi);
|
|
osvi.dwOSVersionInfoSize = sizeof(osvi);
|
|
osvi.dwMajorVersion = ((MA_WIN32_WINNT_VISTA >> 8) & 0xFF);
|
|
osvi.dwMinorVersion = ((MA_WIN32_WINNT_VISTA >> 0) & 0xFF);
|
|
osvi.wServicePackMajor = 1;
|
|
if (_VerifyVersionInfoW(&osvi, MA_VER_MAJORVERSION | MA_VER_MINORVERSION | MA_VER_SERVICEPACKMAJOR, _VerSetConditionMask(_VerSetConditionMask(_VerSetConditionMask(0, MA_VER_MAJORVERSION, MA_VER_GREATER_EQUAL), MA_VER_MINORVERSION, MA_VER_GREATER_EQUAL), MA_VER_SERVICEPACKMAJOR, MA_VER_GREATER_EQUAL))) {
|
|
result = MA_SUCCESS;
|
|
} else {
|
|
result = MA_NO_BACKEND;
|
|
}
|
|
|
|
ma_dlclose(ma_context_get_log(pContext), kernel32DLL);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&pContext->wasapi);
|
|
|
|
#if defined(MA_WIN32_UWP)
|
|
{
|
|
pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll");
|
|
if (pContext->wasapi.hMMDevapi) {
|
|
pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync");
|
|
if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi);
|
|
return MA_NO_BACKEND;
|
|
}
|
|
} else {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll");
|
|
if (pContext->wasapi.hAvrt) {
|
|
pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA");
|
|
pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics");
|
|
|
|
if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) {
|
|
pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL;
|
|
pContext->wasapi.AvRevertMmThreadcharacteristics = NULL;
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt);
|
|
pContext->wasapi.hAvrt = NULL;
|
|
}
|
|
}
|
|
|
|
{
|
|
result = ma_mutex_init(&pContext->wasapi.commandLock);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_semaphore_init(0, &pContext->wasapi.commandSem);
|
|
if (result != MA_SUCCESS) {
|
|
ma_mutex_uninit(&pContext->wasapi.commandLock);
|
|
return result;
|
|
}
|
|
|
|
result = ma_thread_create(&pContext->wasapi.commandThread, ma_thread_priority_normal, 0, ma_context_command_thread__wasapi, pContext, &pContext->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
ma_semaphore_uninit(&pContext->wasapi.commandSem);
|
|
ma_mutex_uninit(&pContext->wasapi.commandLock);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
pCallbacks->onContextInit = ma_context_init__wasapi;
|
|
pCallbacks->onContextUninit = ma_context_uninit__wasapi;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__wasapi;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__wasapi;
|
|
pCallbacks->onDeviceInit = ma_device_init__wasapi;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__wasapi;
|
|
pCallbacks->onDeviceStart = ma_device_start__wasapi;
|
|
pCallbacks->onDeviceStop = ma_device_stop__wasapi;
|
|
pCallbacks->onDeviceRead = ma_device_read__wasapi;
|
|
pCallbacks->onDeviceWrite = ma_device_write__wasapi;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__wasapi;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_DSOUND
|
|
|
|
#define MA_DSSCL_NORMAL 1
|
|
#define MA_DSSCL_PRIORITY 2
|
|
#define MA_DSSCL_EXCLUSIVE 3
|
|
#define MA_DSSCL_WRITEPRIMARY 4
|
|
|
|
#define MA_DSCAPS_PRIMARYMONO 0x00000001
|
|
#define MA_DSCAPS_PRIMARYSTEREO 0x00000002
|
|
#define MA_DSCAPS_PRIMARY8BIT 0x00000004
|
|
#define MA_DSCAPS_PRIMARY16BIT 0x00000008
|
|
#define MA_DSCAPS_CONTINUOUSRATE 0x00000010
|
|
#define MA_DSCAPS_EMULDRIVER 0x00000020
|
|
#define MA_DSCAPS_CERTIFIED 0x00000040
|
|
#define MA_DSCAPS_SECONDARYMONO 0x00000100
|
|
#define MA_DSCAPS_SECONDARYSTEREO 0x00000200
|
|
#define MA_DSCAPS_SECONDARY8BIT 0x00000400
|
|
#define MA_DSCAPS_SECONDARY16BIT 0x00000800
|
|
|
|
#define MA_DSBCAPS_PRIMARYBUFFER 0x00000001
|
|
#define MA_DSBCAPS_STATIC 0x00000002
|
|
#define MA_DSBCAPS_LOCHARDWARE 0x00000004
|
|
#define MA_DSBCAPS_LOCSOFTWARE 0x00000008
|
|
#define MA_DSBCAPS_CTRL3D 0x00000010
|
|
#define MA_DSBCAPS_CTRLFREQUENCY 0x00000020
|
|
#define MA_DSBCAPS_CTRLPAN 0x00000040
|
|
#define MA_DSBCAPS_CTRLVOLUME 0x00000080
|
|
#define MA_DSBCAPS_CTRLPOSITIONNOTIFY 0x00000100
|
|
#define MA_DSBCAPS_CTRLFX 0x00000200
|
|
#define MA_DSBCAPS_STICKYFOCUS 0x00004000
|
|
#define MA_DSBCAPS_GLOBALFOCUS 0x00008000
|
|
#define MA_DSBCAPS_GETCURRENTPOSITION2 0x00010000
|
|
#define MA_DSBCAPS_MUTE3DATMAXDISTANCE 0x00020000
|
|
#define MA_DSBCAPS_LOCDEFER 0x00040000
|
|
#define MA_DSBCAPS_TRUEPLAYPOSITION 0x00080000
|
|
|
|
#define MA_DSBPLAY_LOOPING 0x00000001
|
|
#define MA_DSBPLAY_LOCHARDWARE 0x00000002
|
|
#define MA_DSBPLAY_LOCSOFTWARE 0x00000004
|
|
#define MA_DSBPLAY_TERMINATEBY_TIME 0x00000008
|
|
#define MA_DSBPLAY_TERMINATEBY_DISTANCE 0x00000010
|
|
#define MA_DSBPLAY_TERMINATEBY_PRIORITY 0x00000020
|
|
|
|
#define MA_DSBSTATUS_PLAYING 0x00000001
|
|
#define MA_DSBSTATUS_BUFFERLOST 0x00000002
|
|
#define MA_DSBSTATUS_LOOPING 0x00000004
|
|
#define MA_DSBSTATUS_LOCHARDWARE 0x00000008
|
|
#define MA_DSBSTATUS_LOCSOFTWARE 0x00000010
|
|
#define MA_DSBSTATUS_TERMINATED 0x00000020
|
|
|
|
#define MA_DSCBSTART_LOOPING 0x00000001
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwBufferBytes;
|
|
DWORD dwReserved;
|
|
MA_WAVEFORMATEX* lpwfxFormat;
|
|
GUID guid3DAlgorithm;
|
|
} MA_DSBUFFERDESC;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwBufferBytes;
|
|
DWORD dwReserved;
|
|
MA_WAVEFORMATEX* lpwfxFormat;
|
|
DWORD dwFXCount;
|
|
void* lpDSCFXDesc;
|
|
} MA_DSCBUFFERDESC;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwMinSecondarySampleRate;
|
|
DWORD dwMaxSecondarySampleRate;
|
|
DWORD dwPrimaryBuffers;
|
|
DWORD dwMaxHwMixingAllBuffers;
|
|
DWORD dwMaxHwMixingStaticBuffers;
|
|
DWORD dwMaxHwMixingStreamingBuffers;
|
|
DWORD dwFreeHwMixingAllBuffers;
|
|
DWORD dwFreeHwMixingStaticBuffers;
|
|
DWORD dwFreeHwMixingStreamingBuffers;
|
|
DWORD dwMaxHw3DAllBuffers;
|
|
DWORD dwMaxHw3DStaticBuffers;
|
|
DWORD dwMaxHw3DStreamingBuffers;
|
|
DWORD dwFreeHw3DAllBuffers;
|
|
DWORD dwFreeHw3DStaticBuffers;
|
|
DWORD dwFreeHw3DStreamingBuffers;
|
|
DWORD dwTotalHwMemBytes;
|
|
DWORD dwFreeHwMemBytes;
|
|
DWORD dwMaxContigFreeHwMemBytes;
|
|
DWORD dwUnlockTransferRateHwBuffers;
|
|
DWORD dwPlayCpuOverheadSwBuffers;
|
|
DWORD dwReserved1;
|
|
DWORD dwReserved2;
|
|
} MA_DSCAPS;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwBufferBytes;
|
|
DWORD dwUnlockTransferRate;
|
|
DWORD dwPlayCpuOverhead;
|
|
} MA_DSBCAPS;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwFormats;
|
|
DWORD dwChannels;
|
|
} MA_DSCCAPS;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwSize;
|
|
DWORD dwFlags;
|
|
DWORD dwBufferBytes;
|
|
DWORD dwReserved;
|
|
} MA_DSCBCAPS;
|
|
|
|
typedef struct
|
|
{
|
|
DWORD dwOffset;
|
|
HANDLE hEventNotify;
|
|
} MA_DSBPOSITIONNOTIFY;
|
|
|
|
typedef struct ma_IDirectSound ma_IDirectSound;
|
|
typedef struct ma_IDirectSoundBuffer ma_IDirectSoundBuffer;
|
|
typedef struct ma_IDirectSoundCapture ma_IDirectSoundCapture;
|
|
typedef struct ma_IDirectSoundCaptureBuffer ma_IDirectSoundCaptureBuffer;
|
|
typedef struct ma_IDirectSoundNotify ma_IDirectSoundNotify;
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSound* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSound* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSound* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * CreateSoundBuffer) (ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter);
|
|
HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps);
|
|
HRESULT (STDMETHODCALLTYPE * DuplicateSoundBuffer)(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate);
|
|
HRESULT (STDMETHODCALLTYPE * SetCooperativeLevel) (ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel);
|
|
HRESULT (STDMETHODCALLTYPE * Compact) (ma_IDirectSound* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * GetSpeakerConfig) (ma_IDirectSound* pThis, DWORD* pSpeakerConfig);
|
|
HRESULT (STDMETHODCALLTYPE * SetSpeakerConfig) (ma_IDirectSound* pThis, DWORD dwSpeakerConfig);
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSound* pThis, const GUID* pGuidDevice);
|
|
} ma_IDirectSoundVtbl;
|
|
struct ma_IDirectSound
|
|
{
|
|
ma_IDirectSoundVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IDirectSound_QueryInterface(ma_IDirectSound* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IDirectSound_AddRef(ma_IDirectSound* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IDirectSound_Release(ma_IDirectSound* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_CreateSoundBuffer(ma_IDirectSound* pThis, const MA_DSBUFFERDESC* pDSBufferDesc, ma_IDirectSoundBuffer** ppDSBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateSoundBuffer(pThis, pDSBufferDesc, ppDSBuffer, pUnkOuter); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_GetCaps(ma_IDirectSound* pThis, MA_DSCAPS* pDSCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCaps); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_DuplicateSoundBuffer(ma_IDirectSound* pThis, ma_IDirectSoundBuffer* pDSBufferOriginal, ma_IDirectSoundBuffer** ppDSBufferDuplicate) { return pThis->lpVtbl->DuplicateSoundBuffer(pThis, pDSBufferOriginal, ppDSBufferDuplicate); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_SetCooperativeLevel(ma_IDirectSound* pThis, HWND hwnd, DWORD dwLevel) { return pThis->lpVtbl->SetCooperativeLevel(pThis, hwnd, dwLevel); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_Compact(ma_IDirectSound* pThis) { return pThis->lpVtbl->Compact(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_GetSpeakerConfig(ma_IDirectSound* pThis, DWORD* pSpeakerConfig) { return pThis->lpVtbl->GetSpeakerConfig(pThis, pSpeakerConfig); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_SetSpeakerConfig(ma_IDirectSound* pThis, DWORD dwSpeakerConfig) { return pThis->lpVtbl->SetSpeakerConfig(pThis, dwSpeakerConfig); }
|
|
static MA_INLINE HRESULT ma_IDirectSound_Initialize(ma_IDirectSound* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundBuffer* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundBuffer* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps);
|
|
HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor);
|
|
HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
|
|
HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume);
|
|
HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan);
|
|
HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency);
|
|
HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundBuffer* pThis, DWORD* pStatus);
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc);
|
|
HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
|
|
HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags);
|
|
HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition);
|
|
HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat);
|
|
HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume);
|
|
HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan);
|
|
HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency);
|
|
HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundBuffer* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
|
|
HRESULT (STDMETHODCALLTYPE * Restore) (ma_IDirectSoundBuffer* pThis);
|
|
} ma_IDirectSoundBufferVtbl;
|
|
struct ma_IDirectSoundBuffer
|
|
{
|
|
ma_IDirectSoundBufferVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_QueryInterface(ma_IDirectSoundBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetStatus(ma_IDirectSoundBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* pThis, ma_IDirectSound* pDirectSound, const MA_DSBUFFERDESC* pDSBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSound, pDSBufferDesc); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_Stop(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_Unlock(ma_IDirectSoundBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundBuffer_Restore(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Restore(pThis); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCapture* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCapture* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * CreateCaptureBuffer)(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter);
|
|
HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps);
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice);
|
|
} ma_IDirectSoundCaptureVtbl;
|
|
struct ma_IDirectSoundCapture
|
|
{
|
|
ma_IDirectSoundCaptureVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IDirectSoundCapture_QueryInterface (ma_IDirectSoundCapture* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IDirectSoundCapture_AddRef (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IDirectSoundCapture_Release (ma_IDirectSoundCapture* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCapture_CreateCaptureBuffer(ma_IDirectSoundCapture* pThis, const MA_DSCBUFFERDESC* pDSCBufferDesc, ma_IDirectSoundCaptureBuffer** ppDSCBuffer, void* pUnkOuter) { return pThis->lpVtbl->CreateCaptureBuffer(pThis, pDSCBufferDesc, ppDSCBuffer, pUnkOuter); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCapture_GetCaps (ma_IDirectSoundCapture* pThis, MA_DSCCAPS* pDSCCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCCaps); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCapture_Initialize (ma_IDirectSoundCapture* pThis, const GUID* pGuidDevice) { return pThis->lpVtbl->Initialize(pThis, pGuidDevice); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundCaptureBuffer* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundCaptureBuffer* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps);
|
|
HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition);
|
|
HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten);
|
|
HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus);
|
|
HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc);
|
|
HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags);
|
|
HRESULT (STDMETHODCALLTYPE * Start) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags);
|
|
HRESULT (STDMETHODCALLTYPE * Stop) (ma_IDirectSoundCaptureBuffer* pThis);
|
|
HRESULT (STDMETHODCALLTYPE * Unlock) (ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2);
|
|
} ma_IDirectSoundCaptureBufferVtbl;
|
|
struct ma_IDirectSoundCaptureBuffer
|
|
{
|
|
ma_IDirectSoundCaptureBufferVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_QueryInterface(ma_IDirectSoundCaptureBuffer* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Start(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwFlags) { return pThis->lpVtbl->Start(pThis, dwFlags); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Stop(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Stop(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Unlock(ma_IDirectSoundCaptureBuffer* pThis, void* pAudioPtr1, DWORD dwAudioBytes1, void* pAudioPtr2, DWORD dwAudioBytes2) { return pThis->lpVtbl->Unlock(pThis, pAudioPtr1, dwAudioBytes1, pAudioPtr2, dwAudioBytes2); }
|
|
|
|
typedef struct
|
|
{
|
|
HRESULT (STDMETHODCALLTYPE * QueryInterface)(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject);
|
|
ULONG (STDMETHODCALLTYPE * AddRef) (ma_IDirectSoundNotify* pThis);
|
|
ULONG (STDMETHODCALLTYPE * Release) (ma_IDirectSoundNotify* pThis);
|
|
|
|
HRESULT (STDMETHODCALLTYPE * SetNotificationPositions)(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies);
|
|
} ma_IDirectSoundNotifyVtbl;
|
|
struct ma_IDirectSoundNotify
|
|
{
|
|
ma_IDirectSoundNotifyVtbl* lpVtbl;
|
|
};
|
|
static MA_INLINE HRESULT ma_IDirectSoundNotify_QueryInterface(ma_IDirectSoundNotify* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); }
|
|
static MA_INLINE ULONG ma_IDirectSoundNotify_AddRef(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->AddRef(pThis); }
|
|
static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pThis) { return pThis->lpVtbl->Release(pThis); }
|
|
static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); }
|
|
|
|
typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext);
|
|
typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter);
|
|
typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
|
|
typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter);
|
|
typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext);
|
|
|
|
static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax)
|
|
{
|
|
if (sampleRateMin < (ma_uint32)ma_standard_sample_rate_min) {
|
|
sampleRateMin = (ma_uint32)ma_standard_sample_rate_min;
|
|
}
|
|
if (sampleRateMax > (ma_uint32)ma_standard_sample_rate_max) {
|
|
sampleRateMax = (ma_uint32)ma_standard_sample_rate_max;
|
|
}
|
|
if (sampleRateMin > sampleRateMax) {
|
|
sampleRateMin = sampleRateMax;
|
|
}
|
|
|
|
if (sampleRateMin == sampleRateMax) {
|
|
return sampleRateMax;
|
|
} else {
|
|
size_t iStandardRate;
|
|
for (iStandardRate = 0; iStandardRate < ma_countof(g_maStandardSampleRatePriorities); ++iStandardRate) {
|
|
ma_uint32 standardRate = g_maStandardSampleRatePriorities[iStandardRate];
|
|
if (standardRate >= sampleRateMin && standardRate <= sampleRateMax) {
|
|
return standardRate;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(MA_FALSE);
|
|
return 0;
|
|
}
|
|
|
|
static void ma_get_channels_from_speaker_config__dsound(DWORD speakerConfig, WORD* pChannelsOut, DWORD* pChannelMapOut)
|
|
{
|
|
WORD channels;
|
|
DWORD channelMap;
|
|
|
|
channels = 0;
|
|
if (pChannelsOut != NULL) {
|
|
channels = *pChannelsOut;
|
|
}
|
|
|
|
channelMap = 0;
|
|
if (pChannelMapOut != NULL) {
|
|
channelMap = *pChannelMapOut;
|
|
}
|
|
|
|
switch ((BYTE)(speakerConfig)) {
|
|
case 1 : channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
|
|
case 2 : channels = 1; channelMap = SPEAKER_FRONT_CENTER; break;
|
|
case 3 : channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
|
case 4 : channels = 2; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; break;
|
|
case 5 : channels = 4; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER; break;
|
|
case 6 : channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT; break;
|
|
case 7 : channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER; break;
|
|
case 8 : channels = 8; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break;
|
|
case 9 : channels = 6; channelMap = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT; break;
|
|
default: break;
|
|
}
|
|
|
|
if (pChannelsOut != NULL) {
|
|
*pChannelsOut = channels;
|
|
}
|
|
|
|
if (pChannelMapOut != NULL) {
|
|
*pChannelMapOut = channelMap;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSound** ppDirectSound)
|
|
{
|
|
ma_IDirectSound* pDirectSound;
|
|
HWND hWnd;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppDirectSound != NULL);
|
|
|
|
*ppDirectSound = NULL;
|
|
pDirectSound = NULL;
|
|
|
|
if (FAILED(((ma_DirectSoundCreateProc)pContext->dsound.DirectSoundCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSound, NULL))) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCreate() failed for playback device.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
hWnd = (HWND)pContext->dsound.hWnd;
|
|
if (hWnd == 0) {
|
|
hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)();
|
|
if (hWnd == 0) {
|
|
hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)();
|
|
}
|
|
}
|
|
|
|
hr = ma_IDirectSound_SetCooperativeLevel(pDirectSound, hWnd, (shareMode == ma_share_mode_exclusive) ? MA_DSSCL_EXCLUSIVE : MA_DSSCL_PRIORITY);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_SetCooperateiveLevel() failed for playback device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
*ppDirectSound = pDirectSound;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_create_IDirectSoundCapture__dsound(ma_context* pContext, ma_share_mode shareMode, const ma_device_id* pDeviceID, ma_IDirectSoundCapture** ppDirectSoundCapture)
|
|
{
|
|
ma_IDirectSoundCapture* pDirectSoundCapture;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppDirectSoundCapture != NULL);
|
|
|
|
if (shareMode == ma_share_mode_exclusive) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
*ppDirectSoundCapture = NULL;
|
|
pDirectSoundCapture = NULL;
|
|
|
|
hr = ((ma_DirectSoundCaptureCreateProc)pContext->dsound.DirectSoundCaptureCreate)((pDeviceID == NULL) ? NULL : (const GUID*)pDeviceID->dsound, &pDirectSoundCapture, NULL);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] DirectSoundCaptureCreate() failed for capture device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
*ppDirectSoundCapture = pDirectSoundCapture;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_format_info_for_IDirectSoundCapture__dsound(ma_context* pContext, ma_IDirectSoundCapture* pDirectSoundCapture, WORD* pChannels, WORD* pBitsPerSample, DWORD* pSampleRate)
|
|
{
|
|
HRESULT hr;
|
|
MA_DSCCAPS caps;
|
|
WORD bitsPerSample;
|
|
DWORD sampleRate;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDirectSoundCapture != NULL);
|
|
|
|
if (pChannels) {
|
|
*pChannels = 0;
|
|
}
|
|
if (pBitsPerSample) {
|
|
*pBitsPerSample = 0;
|
|
}
|
|
if (pSampleRate) {
|
|
*pSampleRate = 0;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
caps.dwSize = sizeof(caps);
|
|
hr = ma_IDirectSoundCapture_GetCaps(pDirectSoundCapture, &caps);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_GetCaps() failed for capture device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if (pChannels) {
|
|
*pChannels = (WORD)caps.dwChannels;
|
|
}
|
|
|
|
bitsPerSample = 16;
|
|
sampleRate = 48000;
|
|
|
|
if (caps.dwChannels == 1) {
|
|
if ((caps.dwFormats & WAVE_FORMAT_48M16) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_44M16) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_2M16) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_1M16) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_96M16) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
bitsPerSample = 8;
|
|
if ((caps.dwFormats & WAVE_FORMAT_48M08) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_44M08) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_2M08) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_1M08) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_96M08) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
bitsPerSample = 16;
|
|
}
|
|
}
|
|
} else if (caps.dwChannels == 2) {
|
|
if ((caps.dwFormats & WAVE_FORMAT_48S16) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_44S16) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_2S16) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_1S16) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_96S16) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
bitsPerSample = 8;
|
|
if ((caps.dwFormats & WAVE_FORMAT_48S08) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_44S08) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_2S08) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_1S08) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((caps.dwFormats & WAVE_FORMAT_96S08) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
bitsPerSample = 16;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pBitsPerSample) {
|
|
*pBitsPerSample = bitsPerSample;
|
|
}
|
|
if (pSampleRate) {
|
|
*pSampleRate = sampleRate;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_context* pContext;
|
|
ma_device_type deviceType;
|
|
ma_enum_devices_callback_proc callback;
|
|
void* pUserData;
|
|
ma_bool32 terminated;
|
|
} ma_context_enumerate_devices_callback_data__dsound;
|
|
|
|
static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
|
|
{
|
|
ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext;
|
|
ma_device_info deviceInfo;
|
|
|
|
(void)lpcstrModule;
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
if (lpGuid != NULL) {
|
|
MA_COPY_MEMORY(deviceInfo.id.dsound, lpGuid, 16);
|
|
} else {
|
|
MA_ZERO_MEMORY(deviceInfo.id.dsound, 16);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
}
|
|
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), lpcstrDescription, (size_t)-1);
|
|
|
|
MA_ASSERT(pData != NULL);
|
|
pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE);
|
|
if (pData->terminated) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__dsound(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_context_enumerate_devices_callback_data__dsound data;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
data.pContext = pContext;
|
|
data.callback = callback;
|
|
data.pUserData = pUserData;
|
|
data.terminated = MA_FALSE;
|
|
|
|
if (!data.terminated) {
|
|
data.deviceType = ma_device_type_playback;
|
|
((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
|
|
}
|
|
|
|
if (!data.terminated) {
|
|
data.deviceType = ma_device_type_capture;
|
|
((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_enumerate_devices_callback__dsound, &data);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
const ma_device_id* pDeviceID;
|
|
ma_device_info* pDeviceInfo;
|
|
ma_bool32 found;
|
|
} ma_context_get_device_info_callback_data__dsound;
|
|
|
|
static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext)
|
|
{
|
|
ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext;
|
|
MA_ASSERT(pData != NULL);
|
|
|
|
if ((pData->pDeviceID == NULL || ma_is_guid_null(pData->pDeviceID->dsound)) && (lpGuid == NULL || ma_is_guid_null(lpGuid))) {
|
|
ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
|
|
pData->pDeviceInfo->isDefault = MA_TRUE;
|
|
pData->found = MA_TRUE;
|
|
return FALSE;
|
|
} else {
|
|
if (lpGuid != NULL && pData->pDeviceID != NULL) {
|
|
if (memcmp(pData->pDeviceID->dsound, lpGuid, sizeof(pData->pDeviceID->dsound)) == 0) {
|
|
ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), lpcstrDescription, (size_t)-1);
|
|
pData->found = MA_TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
(void)lpcstrModule;
|
|
return TRUE;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__dsound(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_result result;
|
|
HRESULT hr;
|
|
|
|
if (pDeviceID != NULL) {
|
|
ma_context_get_device_info_callback_data__dsound data;
|
|
|
|
MA_COPY_MEMORY(pDeviceInfo->id.dsound, pDeviceID->dsound, 16);
|
|
|
|
data.pDeviceID = pDeviceID;
|
|
data.pDeviceInfo = pDeviceInfo;
|
|
data.found = MA_FALSE;
|
|
if (deviceType == ma_device_type_playback) {
|
|
((ma_DirectSoundEnumerateAProc)pContext->dsound.DirectSoundEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
|
|
} else {
|
|
((ma_DirectSoundCaptureEnumerateAProc)pContext->dsound.DirectSoundCaptureEnumerateA)(ma_context_get_device_info_callback__dsound, &data);
|
|
}
|
|
|
|
if (!data.found) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
} else {
|
|
|
|
MA_ZERO_MEMORY(pDeviceInfo->id.dsound, 16);
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_IDirectSound* pDirectSound;
|
|
MA_DSCAPS caps;
|
|
WORD channels;
|
|
|
|
result = ma_context_create_IDirectSound__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSound);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
caps.dwSize = sizeof(caps);
|
|
hr = ma_IDirectSound_GetCaps(pDirectSound, &caps);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
|
|
DWORD speakerConfig;
|
|
|
|
channels = 2;
|
|
|
|
hr = ma_IDirectSound_GetSpeakerConfig(pDirectSound, &speakerConfig);
|
|
if (SUCCEEDED(hr)) {
|
|
ma_get_channels_from_speaker_config__dsound(speakerConfig, &channels, NULL);
|
|
}
|
|
} else {
|
|
channels = 1;
|
|
}
|
|
|
|
if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
|
|
size_t iStandardSampleRate;
|
|
for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
|
|
ma_uint32 sampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
|
|
if (sampleRate >= caps.dwMinSecondarySampleRate && sampleRate <= caps.dwMaxSecondarySampleRate) {
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
}
|
|
}
|
|
} else {
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = ma_format_unknown;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = caps.dwMaxSecondarySampleRate;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
}
|
|
|
|
ma_IDirectSound_Release(pDirectSound);
|
|
} else {
|
|
ma_IDirectSoundCapture* pDirectSoundCapture;
|
|
WORD channels;
|
|
WORD bitsPerSample;
|
|
DWORD sampleRate;
|
|
|
|
result = ma_context_create_IDirectSoundCapture__dsound(pContext, ma_share_mode_shared, pDeviceID, &pDirectSoundCapture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pContext, pDirectSoundCapture, &channels, &bitsPerSample, &sampleRate);
|
|
if (result != MA_SUCCESS) {
|
|
ma_IDirectSoundCapture_Release(pDirectSoundCapture);
|
|
return result;
|
|
}
|
|
|
|
ma_IDirectSoundCapture_Release(pDirectSoundCapture);
|
|
|
|
if (bitsPerSample == 8) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
|
|
} else if (bitsPerSample == 16) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
|
|
} else if (bitsPerSample == 24) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
|
|
} else if (bitsPerSample == 32) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
|
|
} else {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormats[0].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
|
|
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount = 1;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__dsound(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->dsound.pCaptureBuffer != NULL) {
|
|
ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
|
|
}
|
|
if (pDevice->dsound.pCapture != NULL) {
|
|
ma_IDirectSoundCapture_Release((ma_IDirectSoundCapture*)pDevice->dsound.pCapture);
|
|
}
|
|
|
|
if (pDevice->dsound.pPlaybackBuffer != NULL) {
|
|
ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
|
|
}
|
|
if (pDevice->dsound.pPlaybackPrimaryBuffer != NULL) {
|
|
ma_IDirectSoundBuffer_Release((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer);
|
|
}
|
|
if (pDevice->dsound.pPlayback != NULL) {
|
|
ma_IDirectSound_Release((ma_IDirectSound*)pDevice->dsound.pPlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF)
|
|
{
|
|
GUID subformat;
|
|
|
|
if (format == ma_format_unknown) {
|
|
format = MA_DEFAULT_FORMAT;
|
|
}
|
|
|
|
if (channels == 0) {
|
|
channels = MA_DEFAULT_CHANNELS;
|
|
}
|
|
|
|
if (sampleRate == 0) {
|
|
sampleRate = MA_DEFAULT_SAMPLE_RATE;
|
|
}
|
|
|
|
switch (format)
|
|
{
|
|
case ma_format_u8:
|
|
case ma_format_s16:
|
|
case ma_format_s24:
|
|
case ma_format_s32:
|
|
{
|
|
subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
subformat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
|
} break;
|
|
|
|
default:
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pWF);
|
|
pWF->cbSize = sizeof(*pWF);
|
|
pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
|
pWF->nChannels = (WORD)channels;
|
|
pWF->nSamplesPerSec = (DWORD)sampleRate;
|
|
pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8);
|
|
pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
|
|
pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
|
|
pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample;
|
|
pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels);
|
|
pWF->SubFormat = subformat;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__dsound(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
|
|
{
|
|
ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(30, nativeSampleRate);
|
|
ma_uint32 periodSizeInFrames;
|
|
|
|
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
|
|
if (periodSizeInFrames < minPeriodSizeInFrames) {
|
|
periodSizeInFrames = minPeriodSizeInFrames;
|
|
}
|
|
|
|
return periodSizeInFrames;
|
|
}
|
|
|
|
static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
HRESULT hr;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->dsound);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
MA_WAVEFORMATEXTENSIBLE wf;
|
|
MA_DSCBUFFERDESC descDS;
|
|
ma_uint32 periodSizeInFrames;
|
|
ma_uint32 periodCount;
|
|
char rawdata[1024];
|
|
MA_WAVEFORMATEXTENSIBLE* pActualFormat;
|
|
|
|
result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_context_create_IDirectSoundCapture__dsound(pDevice->pContext, pDescriptorCapture->shareMode, pDescriptorCapture->pDeviceID, (ma_IDirectSoundCapture**)&pDevice->dsound.pCapture);
|
|
if (result != MA_SUCCESS) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
return result;
|
|
}
|
|
|
|
result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec);
|
|
if (result != MA_SUCCESS) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
return result;
|
|
}
|
|
|
|
wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
|
|
wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
|
|
wf.Samples.wValidBitsPerSample = wf.wBitsPerSample;
|
|
wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM;
|
|
|
|
periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile);
|
|
periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS;
|
|
|
|
MA_ZERO_OBJECT(&descDS);
|
|
descDS.dwSize = sizeof(descDS);
|
|
descDS.dwFlags = 0;
|
|
descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign;
|
|
descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf;
|
|
hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
|
|
hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
|
|
pDescriptorCapture->channels = pActualFormat->nChannels;
|
|
pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec;
|
|
|
|
if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
|
|
} else {
|
|
ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap);
|
|
}
|
|
|
|
if (periodSizeInFrames != (descDS.dwBufferBytes / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / periodCount)) {
|
|
descDS.dwBufferBytes = periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * periodCount;
|
|
ma_IDirectSoundCaptureBuffer_Release((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
|
|
|
|
hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Second attempt at IDirectSoundCapture_CreateCaptureBuffer() failed for capture device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
|
|
pDescriptorCapture->periodCount = periodCount;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
MA_WAVEFORMATEXTENSIBLE wf;
|
|
MA_DSBUFFERDESC descDSPrimary;
|
|
MA_DSCAPS caps;
|
|
char rawdata[1024];
|
|
MA_WAVEFORMATEXTENSIBLE* pActualFormat;
|
|
ma_uint32 periodSizeInFrames;
|
|
ma_uint32 periodCount;
|
|
MA_DSBUFFERDESC descDS;
|
|
WORD nativeChannelCount;
|
|
DWORD nativeChannelMask = 0;
|
|
|
|
result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &wf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_context_create_IDirectSound__dsound(pDevice->pContext, pDescriptorPlayback->shareMode, pDescriptorPlayback->pDeviceID, (ma_IDirectSound**)&pDevice->dsound.pPlayback);
|
|
if (result != MA_SUCCESS) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
return result;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&descDSPrimary);
|
|
descDSPrimary.dwSize = sizeof(MA_DSBUFFERDESC);
|
|
descDSPrimary.dwFlags = MA_DSBCAPS_PRIMARYBUFFER | MA_DSBCAPS_CTRLVOLUME;
|
|
hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDSPrimary, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackPrimaryBuffer, NULL);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's primary buffer.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
caps.dwSize = sizeof(caps);
|
|
hr = ma_IDirectSound_GetCaps((ma_IDirectSound*)pDevice->dsound.pPlayback, &caps);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_GetCaps() failed for playback device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if ((caps.dwFlags & MA_DSCAPS_PRIMARYSTEREO) != 0) {
|
|
DWORD speakerConfig;
|
|
|
|
nativeChannelCount = 2;
|
|
|
|
if (SUCCEEDED(ma_IDirectSound_GetSpeakerConfig((ma_IDirectSound*)pDevice->dsound.pPlayback, &speakerConfig))) {
|
|
ma_get_channels_from_speaker_config__dsound(speakerConfig, &nativeChannelCount, &nativeChannelMask);
|
|
}
|
|
} else {
|
|
nativeChannelCount = 1;
|
|
nativeChannelMask = 0x00000001;
|
|
}
|
|
|
|
if (pDescriptorPlayback->channels == 0) {
|
|
wf.nChannels = nativeChannelCount;
|
|
wf.dwChannelMask = nativeChannelMask;
|
|
}
|
|
|
|
if (pDescriptorPlayback->sampleRate == 0) {
|
|
if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) {
|
|
wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate);
|
|
} else {
|
|
wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate;
|
|
}
|
|
}
|
|
|
|
wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8);
|
|
wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec;
|
|
|
|
hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
|
|
if (FAILED(hr)) {
|
|
wf.cbSize = 18;
|
|
wf.wFormatTag = WAVE_FORMAT_PCM;
|
|
wf.wBitsPerSample = 16;
|
|
wf.nChannels = nativeChannelCount;
|
|
wf.nSamplesPerSec = 44100;
|
|
wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8);
|
|
wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign;
|
|
|
|
hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata;
|
|
hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat);
|
|
pDescriptorPlayback->channels = pActualFormat->nChannels;
|
|
pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec;
|
|
|
|
if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
|
|
ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
|
|
} else {
|
|
ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap);
|
|
}
|
|
|
|
periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
|
|
periodCount = (pDescriptorPlayback->periodCount > 0) ? pDescriptorPlayback->periodCount : MA_DEFAULT_PERIODS;
|
|
|
|
MA_ZERO_OBJECT(&descDS);
|
|
descDS.dwSize = sizeof(descDS);
|
|
descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2;
|
|
descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels);
|
|
descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat;
|
|
hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL);
|
|
if (FAILED(hr)) {
|
|
ma_device_uninit__dsound(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSound_CreateSoundBuffer() failed for playback device's secondary buffer.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
|
|
pDescriptorPlayback->periodCount = periodCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_data_loop__dsound(ma_device* pDevice)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 bpfDeviceCapture = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
ma_uint32 bpfDevicePlayback = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
HRESULT hr;
|
|
DWORD lockOffsetInBytesCapture;
|
|
DWORD lockSizeInBytesCapture;
|
|
DWORD mappedSizeInBytesCapture;
|
|
DWORD mappedDeviceFramesProcessedCapture;
|
|
void* pMappedDeviceBufferCapture;
|
|
DWORD lockOffsetInBytesPlayback;
|
|
DWORD lockSizeInBytesPlayback;
|
|
DWORD mappedSizeInBytesPlayback;
|
|
void* pMappedDeviceBufferPlayback;
|
|
DWORD prevReadCursorInBytesCapture = 0;
|
|
DWORD prevPlayCursorInBytesPlayback = 0;
|
|
ma_bool32 physicalPlayCursorLoopFlagPlayback = 0;
|
|
DWORD virtualWriteCursorInBytesPlayback = 0;
|
|
ma_bool32 virtualWriteCursorLoopFlagPlayback = 0;
|
|
ma_bool32 isPlaybackDeviceStarted = MA_FALSE;
|
|
ma_uint32 framesWrittenToPlaybackDevice = 0;
|
|
ma_uint32 waitTimeInMilliseconds = 1;
|
|
DWORD playbackBufferStatus = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
hr = ma_IDirectSoundCaptureBuffer_Start((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, MA_DSCBSTART_LOOPING);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Start() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
switch (pDevice->type)
|
|
{
|
|
case ma_device_type_duplex:
|
|
{
|
|
DWORD physicalCaptureCursorInBytes;
|
|
DWORD physicalReadCursorInBytes;
|
|
hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
|
|
if (FAILED(hr)) {
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if (physicalReadCursorInBytes == prevReadCursorInBytesCapture) {
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
|
|
if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
|
|
lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
|
|
lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
|
|
} else {
|
|
if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
|
|
lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
|
|
lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
|
|
} else {
|
|
lockOffsetInBytesCapture = 0;
|
|
lockSizeInBytesCapture = physicalReadCursorInBytes;
|
|
}
|
|
}
|
|
|
|
if (lockSizeInBytesCapture == 0) {
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
|
|
hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
mappedDeviceFramesProcessedCapture = 0;
|
|
|
|
for (;;) {
|
|
ma_uint8 inputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 inputFramesInClientFormatCap = sizeof(inputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
|
|
ma_uint8 outputFramesInClientFormat[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 outputFramesInClientFormatCap = sizeof(outputFramesInClientFormat) / ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
|
|
ma_uint32 outputFramesInClientFormatCount;
|
|
ma_uint32 outputFramesInClientFormatConsumed = 0;
|
|
ma_uint64 clientCapturedFramesToProcess = ma_min(inputFramesInClientFormatCap, outputFramesInClientFormatCap);
|
|
ma_uint64 deviceCapturedFramesToProcess = (mappedSizeInBytesCapture / bpfDeviceCapture) - mappedDeviceFramesProcessedCapture;
|
|
void* pRunningMappedDeviceBufferCapture = ma_offset_ptr(pMappedDeviceBufferCapture, mappedDeviceFramesProcessedCapture * bpfDeviceCapture);
|
|
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->capture.converter, pRunningMappedDeviceBufferCapture, &deviceCapturedFramesToProcess, inputFramesInClientFormat, &clientCapturedFramesToProcess);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
outputFramesInClientFormatCount = (ma_uint32)clientCapturedFramesToProcess;
|
|
mappedDeviceFramesProcessedCapture += (ma_uint32)deviceCapturedFramesToProcess;
|
|
|
|
ma_device__handle_data_callback(pDevice, outputFramesInClientFormat, inputFramesInClientFormat, (ma_uint32)clientCapturedFramesToProcess);
|
|
|
|
for (;;) {
|
|
ma_uint32 framesWrittenThisIteration;
|
|
DWORD physicalPlayCursorInBytes;
|
|
DWORD physicalWriteCursorInBytes;
|
|
DWORD availableBytesPlayback;
|
|
DWORD silentPaddingInBytes = 0;
|
|
|
|
if (FAILED(ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes))) {
|
|
break;
|
|
}
|
|
|
|
if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
|
|
physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
|
|
}
|
|
prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
|
|
|
|
if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
|
|
if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
|
|
availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
|
|
availableBytesPlayback += physicalPlayCursorInBytes;
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Play cursor has moved in front of the write cursor (same loop iteration). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
|
|
availableBytesPlayback = 0;
|
|
}
|
|
} else {
|
|
if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
|
|
availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
|
|
availableBytesPlayback = 0;
|
|
}
|
|
}
|
|
|
|
if (availableBytesPlayback == 0) {
|
|
if (!isPlaybackDeviceStarted) {
|
|
hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
|
|
if (FAILED(hr)) {
|
|
ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
isPlaybackDeviceStarted = MA_TRUE;
|
|
} else {
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
|
|
if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
|
|
lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
|
|
} else {
|
|
lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
|
|
}
|
|
|
|
hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.");
|
|
result = ma_result_from_HRESULT(hr);
|
|
break;
|
|
}
|
|
|
|
if (isPlaybackDeviceStarted) {
|
|
DWORD bytesQueuedForPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - availableBytesPlayback;
|
|
if (bytesQueuedForPlayback < (pDevice->playback.internalPeriodSizeInFrames*bpfDevicePlayback)) {
|
|
silentPaddingInBytes = (pDevice->playback.internalPeriodSizeInFrames*2*bpfDevicePlayback) - bytesQueuedForPlayback;
|
|
if (silentPaddingInBytes > lockSizeInBytesPlayback) {
|
|
silentPaddingInBytes = lockSizeInBytesPlayback;
|
|
}
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Duplex/Playback) Playback buffer starved. availableBytesPlayback=%ld, silentPaddingInBytes=%ld\n", availableBytesPlayback, silentPaddingInBytes);
|
|
}
|
|
}
|
|
|
|
if (silentPaddingInBytes > 0) {
|
|
MA_ZERO_MEMORY(pMappedDeviceBufferPlayback, silentPaddingInBytes);
|
|
framesWrittenThisIteration = silentPaddingInBytes/bpfDevicePlayback;
|
|
} else {
|
|
ma_uint64 convertedFrameCountIn = (outputFramesInClientFormatCount - outputFramesInClientFormatConsumed);
|
|
ma_uint64 convertedFrameCountOut = mappedSizeInBytesPlayback/bpfDevicePlayback;
|
|
void* pConvertedFramesIn = ma_offset_ptr(outputFramesInClientFormat, outputFramesInClientFormatConsumed * bpfDevicePlayback);
|
|
void* pConvertedFramesOut = pMappedDeviceBufferPlayback;
|
|
|
|
result = ma_data_converter_process_pcm_frames(&pDevice->playback.converter, pConvertedFramesIn, &convertedFrameCountIn, pConvertedFramesOut, &convertedFrameCountOut);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
outputFramesInClientFormatConsumed += (ma_uint32)convertedFrameCountOut;
|
|
framesWrittenThisIteration = (ma_uint32)convertedFrameCountOut;
|
|
}
|
|
|
|
hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, framesWrittenThisIteration*bpfDevicePlayback, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.");
|
|
result = ma_result_from_HRESULT(hr);
|
|
break;
|
|
}
|
|
|
|
virtualWriteCursorInBytesPlayback += framesWrittenThisIteration*bpfDevicePlayback;
|
|
if ((virtualWriteCursorInBytesPlayback/bpfDevicePlayback) == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods) {
|
|
virtualWriteCursorInBytesPlayback = 0;
|
|
virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
|
|
}
|
|
|
|
framesWrittenToPlaybackDevice += framesWrittenThisIteration;
|
|
if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= (pDevice->playback.internalPeriodSizeInFrames*2)) {
|
|
hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
|
|
if (FAILED(hr)) {
|
|
ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
isPlaybackDeviceStarted = MA_TRUE;
|
|
}
|
|
|
|
if (framesWrittenThisIteration < mappedSizeInBytesPlayback/bpfDevicePlayback) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (clientCapturedFramesToProcess == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
prevReadCursorInBytesCapture = (lockOffsetInBytesCapture + mappedSizeInBytesCapture);
|
|
} break;
|
|
|
|
case ma_device_type_capture:
|
|
{
|
|
DWORD physicalCaptureCursorInBytes;
|
|
DWORD physicalReadCursorInBytes;
|
|
hr = ma_IDirectSoundCaptureBuffer_GetCurrentPosition((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, &physicalCaptureCursorInBytes, &physicalReadCursorInBytes);
|
|
if (FAILED(hr)) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (prevReadCursorInBytesCapture == physicalReadCursorInBytes) {
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
|
|
if (prevReadCursorInBytesCapture < physicalReadCursorInBytes) {
|
|
lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
|
|
lockSizeInBytesCapture = (physicalReadCursorInBytes - prevReadCursorInBytesCapture);
|
|
} else {
|
|
if (prevReadCursorInBytesCapture < pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) {
|
|
lockOffsetInBytesCapture = prevReadCursorInBytesCapture;
|
|
lockSizeInBytesCapture = (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture) - prevReadCursorInBytesCapture;
|
|
} else {
|
|
lockOffsetInBytesCapture = 0;
|
|
lockSizeInBytesCapture = physicalReadCursorInBytes;
|
|
}
|
|
}
|
|
|
|
if (lockSizeInBytesCapture < pDevice->capture.internalPeriodSizeInFrames) {
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
|
|
hr = ma_IDirectSoundCaptureBuffer_Lock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, lockOffsetInBytesCapture, lockSizeInBytesCapture, &pMappedDeviceBufferCapture, &mappedSizeInBytesCapture, NULL, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from capture device in preparation for writing to the device.");
|
|
result = ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
if (lockSizeInBytesCapture != mappedSizeInBytesCapture) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[DirectSound] (Capture) lockSizeInBytesCapture=%ld != mappedSizeInBytesCapture=%ld\n", lockSizeInBytesCapture, mappedSizeInBytesCapture);
|
|
}
|
|
|
|
ma_device__send_frames_to_client(pDevice, mappedSizeInBytesCapture/bpfDeviceCapture, pMappedDeviceBufferCapture);
|
|
|
|
hr = ma_IDirectSoundCaptureBuffer_Unlock((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, pMappedDeviceBufferCapture, mappedSizeInBytesCapture, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from capture device after reading from the device.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
prevReadCursorInBytesCapture = lockOffsetInBytesCapture + mappedSizeInBytesCapture;
|
|
|
|
if (prevReadCursorInBytesCapture == (pDevice->capture.internalPeriodSizeInFrames*pDevice->capture.internalPeriods*bpfDeviceCapture)) {
|
|
prevReadCursorInBytesCapture = 0;
|
|
}
|
|
} break;
|
|
|
|
case ma_device_type_playback:
|
|
{
|
|
DWORD availableBytesPlayback;
|
|
DWORD physicalPlayCursorInBytes;
|
|
DWORD physicalWriteCursorInBytes;
|
|
hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
|
|
if (FAILED(hr)) {
|
|
break;
|
|
}
|
|
|
|
hr = ma_IDirectSoundBuffer_GetStatus((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &playbackBufferStatus);
|
|
if (SUCCEEDED(hr) && (playbackBufferStatus & MA_DSBSTATUS_PLAYING) == 0 && isPlaybackDeviceStarted) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[DirectSound] Attempting to resume audio due to state: %d.", (int)playbackBufferStatus);
|
|
hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
|
|
if (FAILED(hr)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed after attempting to resume from state %d.", (int)playbackBufferStatus);
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
isPlaybackDeviceStarted = MA_TRUE;
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
|
|
if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
|
|
physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
|
|
}
|
|
prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
|
|
|
|
if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
|
|
if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
|
|
availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
|
|
availableBytesPlayback += physicalPlayCursorInBytes;
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Play cursor has moved in front of the write cursor (same loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
|
|
availableBytesPlayback = 0;
|
|
}
|
|
} else {
|
|
if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
|
|
availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[DirectSound] (Playback): Write cursor has moved behind the play cursor (different loop iterations). physicalPlayCursorInBytes=%ld, virtualWriteCursorInBytes=%ld.\n", physicalPlayCursorInBytes, virtualWriteCursorInBytesPlayback);
|
|
availableBytesPlayback = 0;
|
|
}
|
|
}
|
|
|
|
if (availableBytesPlayback < pDevice->playback.internalPeriodSizeInFrames) {
|
|
if (availableBytesPlayback == 0 && !isPlaybackDeviceStarted) {
|
|
hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
isPlaybackDeviceStarted = MA_TRUE;
|
|
} else {
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
lockOffsetInBytesPlayback = virtualWriteCursorInBytesPlayback;
|
|
if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
|
|
lockSizeInBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
|
|
} else {
|
|
lockSizeInBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
|
|
}
|
|
|
|
hr = ma_IDirectSoundBuffer_Lock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, lockOffsetInBytesPlayback, lockSizeInBytesPlayback, &pMappedDeviceBufferPlayback, &mappedSizeInBytesPlayback, NULL, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to map buffer from playback device in preparation for writing to the device.");
|
|
result = ma_result_from_HRESULT(hr);
|
|
break;
|
|
}
|
|
|
|
ma_device__read_frames_from_client(pDevice, (mappedSizeInBytesPlayback/bpfDevicePlayback), pMappedDeviceBufferPlayback);
|
|
|
|
hr = ma_IDirectSoundBuffer_Unlock((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, pMappedDeviceBufferPlayback, mappedSizeInBytesPlayback, NULL, 0);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to unlock internal buffer from playback device after writing to the device.");
|
|
result = ma_result_from_HRESULT(hr);
|
|
break;
|
|
}
|
|
|
|
virtualWriteCursorInBytesPlayback += mappedSizeInBytesPlayback;
|
|
if (virtualWriteCursorInBytesPlayback == pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) {
|
|
virtualWriteCursorInBytesPlayback = 0;
|
|
virtualWriteCursorLoopFlagPlayback = !virtualWriteCursorLoopFlagPlayback;
|
|
}
|
|
|
|
framesWrittenToPlaybackDevice += mappedSizeInBytesPlayback/bpfDevicePlayback;
|
|
if (!isPlaybackDeviceStarted && framesWrittenToPlaybackDevice >= pDevice->playback.internalPeriodSizeInFrames) {
|
|
hr = ma_IDirectSoundBuffer_Play((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0, 0, MA_DSBPLAY_LOOPING);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Play() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
isPlaybackDeviceStarted = MA_TRUE;
|
|
}
|
|
} break;
|
|
|
|
default: return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
hr = ma_IDirectSoundCaptureBuffer_Stop((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundCaptureBuffer_Stop() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
if (isPlaybackDeviceStarted) {
|
|
for (;;) {
|
|
DWORD availableBytesPlayback = 0;
|
|
DWORD physicalPlayCursorInBytes;
|
|
DWORD physicalWriteCursorInBytes;
|
|
hr = ma_IDirectSoundBuffer_GetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, &physicalPlayCursorInBytes, &physicalWriteCursorInBytes);
|
|
if (FAILED(hr)) {
|
|
break;
|
|
}
|
|
|
|
if (physicalPlayCursorInBytes < prevPlayCursorInBytesPlayback) {
|
|
physicalPlayCursorLoopFlagPlayback = !physicalPlayCursorLoopFlagPlayback;
|
|
}
|
|
prevPlayCursorInBytesPlayback = physicalPlayCursorInBytes;
|
|
|
|
if (physicalPlayCursorLoopFlagPlayback == virtualWriteCursorLoopFlagPlayback) {
|
|
if (physicalPlayCursorInBytes <= virtualWriteCursorInBytesPlayback) {
|
|
availableBytesPlayback = (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback) - virtualWriteCursorInBytesPlayback;
|
|
availableBytesPlayback += physicalPlayCursorInBytes;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
if (physicalPlayCursorInBytes >= virtualWriteCursorInBytesPlayback) {
|
|
availableBytesPlayback = physicalPlayCursorInBytes - virtualWriteCursorInBytesPlayback;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (availableBytesPlayback >= (pDevice->playback.internalPeriodSizeInFrames*pDevice->playback.internalPeriods*bpfDevicePlayback)) {
|
|
break;
|
|
}
|
|
|
|
ma_sleep(waitTimeInMilliseconds);
|
|
}
|
|
}
|
|
|
|
hr = ma_IDirectSoundBuffer_Stop((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer);
|
|
if (FAILED(hr)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] IDirectSoundBuffer_Stop() failed.");
|
|
return ma_result_from_HRESULT(hr);
|
|
}
|
|
|
|
ma_IDirectSoundBuffer_SetCurrentPosition((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackBuffer, 0);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__dsound(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_dsound);
|
|
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll");
|
|
if (pContext->dsound.hDSoundDLL == NULL) {
|
|
return MA_API_NOT_FOUND;
|
|
}
|
|
|
|
pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate");
|
|
pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA");
|
|
pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate");
|
|
pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA");
|
|
|
|
if (pContext->dsound.DirectSoundCreate == NULL ||
|
|
pContext->dsound.DirectSoundEnumerateA == NULL ||
|
|
pContext->dsound.DirectSoundCaptureCreate == NULL ||
|
|
pContext->dsound.DirectSoundCaptureEnumerateA == NULL) {
|
|
return MA_API_NOT_FOUND;
|
|
}
|
|
|
|
pContext->dsound.hWnd = pConfig->dsound.hWnd;
|
|
|
|
pCallbacks->onContextInit = ma_context_init__dsound;
|
|
pCallbacks->onContextUninit = ma_context_uninit__dsound;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__dsound;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__dsound;
|
|
pCallbacks->onDeviceInit = ma_device_init__dsound;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__dsound;
|
|
pCallbacks->onDeviceStart = NULL;
|
|
pCallbacks->onDeviceStop = NULL;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = ma_device_data_loop__dsound;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_WINMM
|
|
|
|
#define MA_MMSYSERR_NOERROR 0
|
|
#define MA_MMSYSERR_ERROR 1
|
|
#define MA_MMSYSERR_BADDEVICEID 2
|
|
#define MA_MMSYSERR_INVALHANDLE 5
|
|
#define MA_MMSYSERR_NOMEM 7
|
|
#define MA_MMSYSERR_INVALFLAG 10
|
|
#define MA_MMSYSERR_INVALPARAM 11
|
|
#define MA_MMSYSERR_HANDLEBUSY 12
|
|
|
|
#define MA_CALLBACK_EVENT 0x00050000
|
|
#define MA_WAVE_ALLOWSYNC 0x0002
|
|
|
|
#define MA_WHDR_DONE 0x00000001
|
|
#define MA_WHDR_PREPARED 0x00000002
|
|
#define MA_WHDR_BEGINLOOP 0x00000004
|
|
#define MA_WHDR_ENDLOOP 0x00000008
|
|
#define MA_WHDR_INQUEUE 0x00000010
|
|
|
|
#define MA_MAXPNAMELEN 32
|
|
|
|
typedef void* MA_HWAVEIN;
|
|
typedef void* MA_HWAVEOUT;
|
|
typedef UINT MA_MMRESULT;
|
|
typedef UINT MA_MMVERSION;
|
|
|
|
typedef struct
|
|
{
|
|
WORD wMid;
|
|
WORD wPid;
|
|
MA_MMVERSION vDriverVersion;
|
|
CHAR szPname[MA_MAXPNAMELEN];
|
|
DWORD dwFormats;
|
|
WORD wChannels;
|
|
WORD wReserved1;
|
|
} MA_WAVEINCAPSA;
|
|
|
|
typedef struct
|
|
{
|
|
WORD wMid;
|
|
WORD wPid;
|
|
MA_MMVERSION vDriverVersion;
|
|
CHAR szPname[MA_MAXPNAMELEN];
|
|
DWORD dwFormats;
|
|
WORD wChannels;
|
|
WORD wReserved1;
|
|
DWORD dwSupport;
|
|
} MA_WAVEOUTCAPSA;
|
|
|
|
typedef struct tagWAVEHDR
|
|
{
|
|
char* lpData;
|
|
DWORD dwBufferLength;
|
|
DWORD dwBytesRecorded;
|
|
DWORD_PTR dwUser;
|
|
DWORD dwFlags;
|
|
DWORD dwLoops;
|
|
struct tagWAVEHDR* lpNext;
|
|
DWORD_PTR reserved;
|
|
} MA_WAVEHDR;
|
|
|
|
typedef struct
|
|
{
|
|
WORD wMid;
|
|
WORD wPid;
|
|
MA_MMVERSION vDriverVersion;
|
|
CHAR szPname[MA_MAXPNAMELEN];
|
|
DWORD dwFormats;
|
|
WORD wChannels;
|
|
WORD wReserved1;
|
|
DWORD dwSupport;
|
|
GUID ManufacturerGuid;
|
|
GUID ProductGuid;
|
|
GUID NameGuid;
|
|
} MA_WAVEOUTCAPS2A;
|
|
|
|
typedef struct
|
|
{
|
|
WORD wMid;
|
|
WORD wPid;
|
|
MA_MMVERSION vDriverVersion;
|
|
CHAR szPname[MA_MAXPNAMELEN];
|
|
DWORD dwFormats;
|
|
WORD wChannels;
|
|
WORD wReserved1;
|
|
GUID ManufacturerGuid;
|
|
GUID ProductGuid;
|
|
GUID NameGuid;
|
|
} MA_WAVEINCAPS2A;
|
|
|
|
typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo);
|
|
typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi);
|
|
typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi);
|
|
|
|
static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM)
|
|
{
|
|
switch (resultMM)
|
|
{
|
|
case MA_MMSYSERR_NOERROR: return MA_SUCCESS;
|
|
case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS;
|
|
case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS;
|
|
case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY;
|
|
case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS;
|
|
case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS;
|
|
case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY;
|
|
case MA_MMSYSERR_ERROR: return MA_ERROR;
|
|
default: return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
static char* ma_find_last_character(char* str, char ch)
|
|
{
|
|
char* last;
|
|
|
|
if (str == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
last = NULL;
|
|
while (*str != '\0') {
|
|
if (*str == ch) {
|
|
last = str;
|
|
}
|
|
|
|
str += 1;
|
|
}
|
|
|
|
return last;
|
|
}
|
|
|
|
static ma_uint32 ma_get_period_size_in_bytes(ma_uint32 periodSizeInFrames, ma_format format, ma_uint32 channels)
|
|
{
|
|
return periodSizeInFrames * ma_get_bytes_per_frame(format, channels);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
CHAR szPname[MA_MAXPNAMELEN];
|
|
DWORD dwFormats;
|
|
WORD wChannels;
|
|
GUID NameGuid;
|
|
} MA_WAVECAPSA;
|
|
|
|
static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WORD channels, WORD* pBitsPerSample, DWORD* pSampleRate)
|
|
{
|
|
WORD bitsPerSample = 0;
|
|
DWORD sampleRate = 0;
|
|
|
|
if (pBitsPerSample) {
|
|
*pBitsPerSample = 0;
|
|
}
|
|
if (pSampleRate) {
|
|
*pSampleRate = 0;
|
|
}
|
|
|
|
if (channels == 1) {
|
|
bitsPerSample = 16;
|
|
if ((dwFormats & WAVE_FORMAT_48M16) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((dwFormats & WAVE_FORMAT_44M16) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((dwFormats & WAVE_FORMAT_2M16) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((dwFormats & WAVE_FORMAT_1M16) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((dwFormats & WAVE_FORMAT_96M16) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
bitsPerSample = 8;
|
|
if ((dwFormats & WAVE_FORMAT_48M08) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((dwFormats & WAVE_FORMAT_44M08) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((dwFormats & WAVE_FORMAT_2M08) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((dwFormats & WAVE_FORMAT_1M08) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((dwFormats & WAVE_FORMAT_96M08) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
} else {
|
|
bitsPerSample = 16;
|
|
if ((dwFormats & WAVE_FORMAT_48S16) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((dwFormats & WAVE_FORMAT_44S16) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((dwFormats & WAVE_FORMAT_2S16) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((dwFormats & WAVE_FORMAT_1S16) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((dwFormats & WAVE_FORMAT_96S16) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
bitsPerSample = 8;
|
|
if ((dwFormats & WAVE_FORMAT_48S08) != 0) {
|
|
sampleRate = 48000;
|
|
} else if ((dwFormats & WAVE_FORMAT_44S08) != 0) {
|
|
sampleRate = 44100;
|
|
} else if ((dwFormats & WAVE_FORMAT_2S08) != 0) {
|
|
sampleRate = 22050;
|
|
} else if ((dwFormats & WAVE_FORMAT_1S08) != 0) {
|
|
sampleRate = 11025;
|
|
} else if ((dwFormats & WAVE_FORMAT_96S08) != 0) {
|
|
sampleRate = 96000;
|
|
} else {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pBitsPerSample) {
|
|
*pBitsPerSample = bitsPerSample;
|
|
}
|
|
if (pSampleRate) {
|
|
*pSampleRate = sampleRate;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pWF != NULL);
|
|
|
|
MA_ZERO_OBJECT(pWF);
|
|
pWF->cbSize = sizeof(*pWF);
|
|
pWF->wFormatTag = WAVE_FORMAT_PCM;
|
|
pWF->nChannels = (WORD)channels;
|
|
if (pWF->nChannels > 2) {
|
|
pWF->nChannels = 2;
|
|
}
|
|
|
|
result = ma_get_best_info_from_formats_flags__winmm(dwFormats, channels, &pWF->wBitsPerSample, &pWF->nSamplesPerSec);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8);
|
|
pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, MA_WAVECAPSA* pCaps, ma_device_info* pDeviceInfo)
|
|
{
|
|
WORD bitsPerSample;
|
|
DWORD sampleRate;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pCaps != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), pCaps->szPname, (size_t)-1);
|
|
|
|
if (!ma_is_guid_null(&pCaps->NameGuid)) {
|
|
WCHAR guidStrW[256];
|
|
if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) {
|
|
char guidStr[256];
|
|
char keyStr[1024];
|
|
HKEY hKey;
|
|
|
|
WideCharToMultiByte(CP_UTF8, 0, guidStrW, -1, guidStr, sizeof(guidStr), 0, FALSE);
|
|
|
|
ma_strcpy_s(keyStr, sizeof(keyStr), "SYSTEM\\CurrentControlSet\\Control\\MediaCategories\\");
|
|
ma_strcat_s(keyStr, sizeof(keyStr), guidStr);
|
|
|
|
if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
|
|
BYTE nameFromReg[512];
|
|
DWORD nameFromRegSize = sizeof(nameFromReg);
|
|
LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize);
|
|
((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey);
|
|
|
|
if (resultWin32 == ERROR_SUCCESS) {
|
|
char name[1024];
|
|
if (ma_strcpy_s(name, sizeof(name), pDeviceInfo->name) == 0) {
|
|
char* nameBeg = ma_find_last_character(name, '(');
|
|
if (nameBeg != NULL) {
|
|
size_t leadingLen = (nameBeg - name);
|
|
ma_strncpy_s(nameBeg + 1, sizeof(name) - leadingLen, (const char*)nameFromReg, (size_t)-1);
|
|
|
|
if (leadingLen + nameFromRegSize < sizeof(name)-1) {
|
|
ma_strcat_s(name, sizeof(name), ")");
|
|
}
|
|
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), name, (size_t)-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
result = ma_get_best_info_from_formats_flags__winmm(pCaps->dwFormats, pCaps->wChannels, &bitsPerSample, &sampleRate);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (bitsPerSample == 8) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_u8;
|
|
} else if (bitsPerSample == 16) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_s16;
|
|
} else if (bitsPerSample == 24) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_s24;
|
|
} else if (bitsPerSample == 32) {
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_s32;
|
|
} else {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
pDeviceInfo->nativeDataFormats[0].channels = pCaps->wChannels;
|
|
pDeviceInfo->nativeDataFormats[0].sampleRate = sampleRate;
|
|
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info_from_WAVEOUTCAPS2(ma_context* pContext, MA_WAVEOUTCAPS2A* pCaps, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_WAVECAPSA caps;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pCaps != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
|
|
caps.dwFormats = pCaps->dwFormats;
|
|
caps.wChannels = pCaps->wChannels;
|
|
caps.NameGuid = pCaps->NameGuid;
|
|
return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info_from_WAVEINCAPS2(ma_context* pContext, MA_WAVEINCAPS2A* pCaps, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_WAVECAPSA caps;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pCaps != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
MA_COPY_MEMORY(caps.szPname, pCaps->szPname, sizeof(caps.szPname));
|
|
caps.dwFormats = pCaps->dwFormats;
|
|
caps.wChannels = pCaps->wChannels;
|
|
caps.NameGuid = pCaps->NameGuid;
|
|
return ma_context_get_device_info_from_WAVECAPS(pContext, &caps, pDeviceInfo);
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
UINT playbackDeviceCount;
|
|
UINT captureDeviceCount;
|
|
UINT iPlaybackDevice;
|
|
UINT iCaptureDevice;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)();
|
|
for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) {
|
|
MA_MMRESULT result;
|
|
MA_WAVEOUTCAPS2A caps;
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
|
|
result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
|
|
if (result == MA_MMSYSERR_NOERROR) {
|
|
ma_device_info deviceInfo;
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.winmm = iPlaybackDevice;
|
|
|
|
if (iPlaybackDevice == 0) {
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
|
|
ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
if (cbResult == MA_FALSE) {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)();
|
|
for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) {
|
|
MA_MMRESULT result;
|
|
MA_WAVEINCAPS2A caps;
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
|
|
result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
|
|
if (result == MA_MMSYSERR_NOERROR) {
|
|
ma_device_info deviceInfo;
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.winmm = iCaptureDevice;
|
|
|
|
if (iCaptureDevice == 0) {
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, &deviceInfo) == MA_SUCCESS) {
|
|
ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
if (cbResult == MA_FALSE) {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
UINT winMMDeviceID;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
winMMDeviceID = 0;
|
|
if (pDeviceID != NULL) {
|
|
winMMDeviceID = (UINT)pDeviceID->winmm;
|
|
}
|
|
|
|
pDeviceInfo->id.winmm = winMMDeviceID;
|
|
|
|
if (winMMDeviceID == 0) {
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
MA_MMRESULT result;
|
|
MA_WAVEOUTCAPS2A caps;
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
|
|
result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps));
|
|
if (result == MA_MMSYSERR_NOERROR) {
|
|
return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo);
|
|
}
|
|
} else {
|
|
MA_MMRESULT result;
|
|
MA_WAVEINCAPS2A caps;
|
|
|
|
MA_ZERO_OBJECT(&caps);
|
|
|
|
result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps));
|
|
if (result == MA_MMSYSERR_NOERROR) {
|
|
return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo);
|
|
}
|
|
}
|
|
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__winmm(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
|
|
CloseHandle((HANDLE)pDevice->winmm.hEventCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
|
|
((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
|
|
CloseHandle((HANDLE)pDevice->winmm.hEventPlayback);
|
|
}
|
|
|
|
ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->winmm);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__winmm(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
|
|
{
|
|
ma_uint32 minPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(40, nativeSampleRate);
|
|
ma_uint32 periodSizeInFrames;
|
|
|
|
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, nativeSampleRate, performanceProfile);
|
|
if (periodSizeInFrames < minPeriodSizeInFrames) {
|
|
periodSizeInFrames = minPeriodSizeInFrames;
|
|
}
|
|
|
|
return periodSizeInFrames;
|
|
}
|
|
|
|
static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
const char* errorMsg = "";
|
|
ma_result errorCode = MA_ERROR;
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 heapSize;
|
|
UINT winMMDeviceIDPlayback = 0;
|
|
UINT winMMDeviceIDCapture = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->winmm);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pDescriptorPlayback->pDeviceID != NULL) {
|
|
winMMDeviceIDPlayback = (UINT)pDescriptorPlayback->pDeviceID->winmm;
|
|
}
|
|
if (pDescriptorCapture->pDeviceID != NULL) {
|
|
winMMDeviceIDCapture = (UINT)pDescriptorCapture->pDeviceID->winmm;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
MA_WAVEINCAPSA caps;
|
|
MA_WAVEFORMATEX wf;
|
|
MA_MMRESULT resultMM;
|
|
|
|
pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
|
|
if (pDevice->winmm.hEventCapture == NULL) {
|
|
errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the capture device.", errorCode = ma_result_from_GetLastError(GetLastError());
|
|
goto on_error;
|
|
}
|
|
|
|
if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
|
|
errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
|
|
goto on_error;
|
|
}
|
|
|
|
result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
|
|
if (result != MA_SUCCESS) {
|
|
errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
|
|
goto on_error;
|
|
}
|
|
|
|
resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC);
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
goto on_error;
|
|
}
|
|
|
|
pDescriptorCapture->format = ma_format_from_WAVEFORMATEX(&wf);
|
|
pDescriptorCapture->channels = wf.nChannels;
|
|
pDescriptorCapture->sampleRate = wf.nSamplesPerSec;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
|
|
pDescriptorCapture->periodCount = pDescriptorCapture->periodCount;
|
|
pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
MA_WAVEOUTCAPSA caps;
|
|
MA_WAVEFORMATEX wf;
|
|
MA_MMRESULT resultMM;
|
|
|
|
pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL);
|
|
if (pDevice->winmm.hEventPlayback == NULL) {
|
|
errorMsg = "[WinMM] Failed to create event for fragment enqueuing for the playback device.", errorCode = ma_result_from_GetLastError(GetLastError());
|
|
goto on_error;
|
|
}
|
|
|
|
if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) {
|
|
errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED;
|
|
goto on_error;
|
|
}
|
|
|
|
result = ma_formats_flags_to_WAVEFORMATEX__winmm(caps.dwFormats, caps.wChannels, &wf);
|
|
if (result != MA_SUCCESS) {
|
|
errorMsg = "[WinMM] Could not find appropriate format for internal device.", errorCode = result;
|
|
goto on_error;
|
|
}
|
|
|
|
resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC);
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
goto on_error;
|
|
}
|
|
|
|
pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX(&wf);
|
|
pDescriptorPlayback->channels = wf.nChannels;
|
|
pDescriptorPlayback->sampleRate = wf.nSamplesPerSec;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
|
|
pDescriptorPlayback->periodCount = pDescriptorPlayback->periodCount;
|
|
pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__winmm(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
|
|
}
|
|
|
|
heapSize = 0;
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
|
|
}
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels));
|
|
}
|
|
|
|
pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->winmm._pHeapData == NULL) {
|
|
errorMsg = "[WinMM] Failed to allocate memory for the intermediary buffer.", errorCode = MA_OUT_OF_MEMORY;
|
|
goto on_error;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(pDevice->winmm._pHeapData, heapSize);
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_uint32 iPeriod;
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture) {
|
|
pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
|
|
pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
|
|
} else {
|
|
pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData;
|
|
pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount));
|
|
}
|
|
|
|
for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
|
|
ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels);
|
|
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod));
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes;
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L;
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L;
|
|
((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
|
|
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_uint32 iPeriod;
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback) {
|
|
pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData;
|
|
pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount);
|
|
} else {
|
|
pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount));
|
|
pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels));
|
|
}
|
|
|
|
for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
|
|
ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels);
|
|
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod));
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes;
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L;
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L;
|
|
((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
|
|
|
|
((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
|
|
on_error:
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->winmm.pWAVEHDRCapture != NULL) {
|
|
ma_uint32 iPeriod;
|
|
for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) {
|
|
((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
|
|
}
|
|
}
|
|
|
|
((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->winmm.pWAVEHDRCapture != NULL) {
|
|
ma_uint32 iPeriod;
|
|
for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) {
|
|
((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR));
|
|
}
|
|
}
|
|
|
|
((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
|
|
}
|
|
|
|
ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks);
|
|
|
|
if (errorMsg != NULL && errorMsg[0] != '\0') {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "%s", errorMsg);
|
|
}
|
|
|
|
return errorCode;
|
|
}
|
|
|
|
static ma_result ma_device_start__winmm(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
MA_MMRESULT resultMM;
|
|
MA_WAVEHDR* pWAVEHDR;
|
|
ma_uint32 iPeriod;
|
|
|
|
pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
|
|
|
|
ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
|
|
|
|
for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
|
|
resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR));
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture.");
|
|
return ma_result_from_MMRESULT(resultMM);
|
|
}
|
|
|
|
pWAVEHDR[iPeriod].dwUser = 1;
|
|
}
|
|
|
|
resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device.");
|
|
return ma_result_from_MMRESULT(resultMM);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__winmm(ma_device* pDevice)
|
|
{
|
|
MA_MMRESULT resultMM;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->winmm.hDeviceCapture == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture);
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device.");
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_uint32 iPeriod;
|
|
MA_WAVEHDR* pWAVEHDR;
|
|
|
|
if (pDevice->winmm.hDevicePlayback == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
|
|
for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) {
|
|
if (pWAVEHDR[iPeriod].dwUser == 1) {
|
|
if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
|
|
break;
|
|
}
|
|
|
|
pWAVEHDR[iPeriod].dwUser = 0;
|
|
}
|
|
}
|
|
|
|
resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback);
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device.");
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
MA_MMRESULT resultMM;
|
|
ma_uint32 totalFramesWritten;
|
|
MA_WAVEHDR* pWAVEHDR;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pPCMFrames != NULL);
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback;
|
|
|
|
totalFramesWritten = 0;
|
|
while (totalFramesWritten < frameCount) {
|
|
if (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser == 0) {
|
|
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedPlayback;
|
|
|
|
ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesWritten));
|
|
const void* pSrc = ma_offset_ptr(pPCMFrames, totalFramesWritten*bpf);
|
|
void* pDst = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].lpData, pDevice->winmm.headerFramesConsumedPlayback*bpf);
|
|
MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
|
|
|
|
pDevice->winmm.headerFramesConsumedPlayback += framesToCopy;
|
|
totalFramesWritten += framesToCopy;
|
|
|
|
if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) {
|
|
pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1;
|
|
pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE;
|
|
|
|
ResetEvent((HANDLE)pDevice->winmm.hEventPlayback);
|
|
|
|
resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR));
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
result = ma_result_from_MMRESULT(resultMM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed.");
|
|
break;
|
|
}
|
|
|
|
pDevice->winmm.iNextHeaderPlayback = (pDevice->winmm.iNextHeaderPlayback + 1) % pDevice->playback.internalPeriods;
|
|
pDevice->winmm.headerFramesConsumedPlayback = 0;
|
|
}
|
|
|
|
MA_ASSERT(totalFramesWritten <= frameCount);
|
|
if (totalFramesWritten == frameCount) {
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) {
|
|
result = MA_ERROR;
|
|
break;
|
|
}
|
|
|
|
if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) {
|
|
pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0;
|
|
pDevice->winmm.headerFramesConsumedPlayback = 0;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_started) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = totalFramesWritten;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
MA_MMRESULT resultMM;
|
|
ma_uint32 totalFramesRead;
|
|
MA_WAVEHDR* pWAVEHDR;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pPCMFrames != NULL);
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture;
|
|
|
|
totalFramesRead = 0;
|
|
while (totalFramesRead < frameCount) {
|
|
if (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser == 0) {
|
|
ma_uint32 bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
ma_uint32 framesRemainingInHeader = (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf) - pDevice->winmm.headerFramesConsumedCapture;
|
|
|
|
ma_uint32 framesToCopy = ma_min(framesRemainingInHeader, (frameCount - totalFramesRead));
|
|
const void* pSrc = ma_offset_ptr(pWAVEHDR[pDevice->winmm.iNextHeaderCapture].lpData, pDevice->winmm.headerFramesConsumedCapture*bpf);
|
|
void* pDst = ma_offset_ptr(pPCMFrames, totalFramesRead*bpf);
|
|
MA_COPY_MEMORY(pDst, pSrc, framesToCopy*bpf);
|
|
|
|
pDevice->winmm.headerFramesConsumedCapture += framesToCopy;
|
|
totalFramesRead += framesToCopy;
|
|
|
|
if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) {
|
|
pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1;
|
|
pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE;
|
|
|
|
ResetEvent((HANDLE)pDevice->winmm.hEventCapture);
|
|
|
|
resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR));
|
|
if (resultMM != MA_MMSYSERR_NOERROR) {
|
|
result = ma_result_from_MMRESULT(resultMM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed.");
|
|
break;
|
|
}
|
|
|
|
pDevice->winmm.iNextHeaderCapture = (pDevice->winmm.iNextHeaderCapture + 1) % pDevice->capture.internalPeriods;
|
|
pDevice->winmm.headerFramesConsumedCapture = 0;
|
|
}
|
|
|
|
MA_ASSERT(totalFramesRead <= frameCount);
|
|
if (totalFramesRead == frameCount) {
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventCapture, INFINITE) != WAIT_OBJECT_0) {
|
|
result = MA_ERROR;
|
|
break;
|
|
}
|
|
|
|
if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) {
|
|
pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0;
|
|
pDevice->winmm.headerFramesConsumedCapture = 0;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_started) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__winmm(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_winmm);
|
|
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll");
|
|
if (pContext->winmm.hWinMM == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs");
|
|
pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA");
|
|
pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen");
|
|
pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose");
|
|
pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader");
|
|
pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader");
|
|
pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite");
|
|
pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset");
|
|
pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs");
|
|
pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA");
|
|
pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen");
|
|
pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose");
|
|
pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader");
|
|
pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader");
|
|
pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer");
|
|
pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart");
|
|
pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset");
|
|
|
|
pCallbacks->onContextInit = ma_context_init__winmm;
|
|
pCallbacks->onContextUninit = ma_context_uninit__winmm;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__winmm;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__winmm;
|
|
pCallbacks->onDeviceInit = ma_device_init__winmm;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__winmm;
|
|
pCallbacks->onDeviceStart = ma_device_start__winmm;
|
|
pCallbacks->onDeviceStop = ma_device_stop__winmm;
|
|
pCallbacks->onDeviceRead = ma_device_read__winmm;
|
|
pCallbacks->onDeviceWrite = ma_device_write__winmm;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_ALSA
|
|
|
|
#include <poll.h>
|
|
#include <sys/eventfd.h>
|
|
|
|
#ifdef MA_NO_RUNTIME_LINKING
|
|
|
|
#if !defined(__cplusplus)
|
|
#if defined(__STRICT_ANSI__)
|
|
#if !defined(inline)
|
|
#define inline __inline__ __attribute__((always_inline))
|
|
#define MA_INLINE_DEFINED
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#include <alsa/asoundlib.h>
|
|
#if defined(MA_INLINE_DEFINED)
|
|
#undef inline
|
|
#undef MA_INLINE_DEFINED
|
|
#endif
|
|
|
|
typedef snd_pcm_uframes_t ma_snd_pcm_uframes_t;
|
|
typedef snd_pcm_sframes_t ma_snd_pcm_sframes_t;
|
|
typedef snd_pcm_stream_t ma_snd_pcm_stream_t;
|
|
typedef snd_pcm_format_t ma_snd_pcm_format_t;
|
|
typedef snd_pcm_access_t ma_snd_pcm_access_t;
|
|
typedef snd_pcm_t ma_snd_pcm_t;
|
|
typedef snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
|
|
typedef snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
|
|
typedef snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
|
|
typedef snd_pcm_info_t ma_snd_pcm_info_t;
|
|
typedef snd_pcm_channel_area_t ma_snd_pcm_channel_area_t;
|
|
typedef snd_pcm_chmap_t ma_snd_pcm_chmap_t;
|
|
typedef snd_pcm_state_t ma_snd_pcm_state_t;
|
|
|
|
#define MA_SND_PCM_STATE_XRUN SND_PCM_STATE_XRUN
|
|
|
|
#define MA_SND_PCM_STREAM_PLAYBACK SND_PCM_STREAM_PLAYBACK
|
|
#define MA_SND_PCM_STREAM_CAPTURE SND_PCM_STREAM_CAPTURE
|
|
|
|
#define MA_SND_PCM_FORMAT_UNKNOWN SND_PCM_FORMAT_UNKNOWN
|
|
#define MA_SND_PCM_FORMAT_U8 SND_PCM_FORMAT_U8
|
|
#define MA_SND_PCM_FORMAT_S16_LE SND_PCM_FORMAT_S16_LE
|
|
#define MA_SND_PCM_FORMAT_S16_BE SND_PCM_FORMAT_S16_BE
|
|
#define MA_SND_PCM_FORMAT_S24_LE SND_PCM_FORMAT_S24_LE
|
|
#define MA_SND_PCM_FORMAT_S24_BE SND_PCM_FORMAT_S24_BE
|
|
#define MA_SND_PCM_FORMAT_S32_LE SND_PCM_FORMAT_S32_LE
|
|
#define MA_SND_PCM_FORMAT_S32_BE SND_PCM_FORMAT_S32_BE
|
|
#define MA_SND_PCM_FORMAT_FLOAT_LE SND_PCM_FORMAT_FLOAT_LE
|
|
#define MA_SND_PCM_FORMAT_FLOAT_BE SND_PCM_FORMAT_FLOAT_BE
|
|
#define MA_SND_PCM_FORMAT_FLOAT64_LE SND_PCM_FORMAT_FLOAT64_LE
|
|
#define MA_SND_PCM_FORMAT_FLOAT64_BE SND_PCM_FORMAT_FLOAT64_BE
|
|
#define MA_SND_PCM_FORMAT_MU_LAW SND_PCM_FORMAT_MU_LAW
|
|
#define MA_SND_PCM_FORMAT_A_LAW SND_PCM_FORMAT_A_LAW
|
|
#define MA_SND_PCM_FORMAT_S24_3LE SND_PCM_FORMAT_S24_3LE
|
|
#define MA_SND_PCM_FORMAT_S24_3BE SND_PCM_FORMAT_S24_3BE
|
|
|
|
#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED SND_PCM_ACCESS_MMAP_INTERLEAVED
|
|
#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED SND_PCM_ACCESS_MMAP_NONINTERLEAVED
|
|
#define MA_SND_PCM_ACCESS_MMAP_COMPLEX SND_PCM_ACCESS_MMAP_COMPLEX
|
|
#define MA_SND_PCM_ACCESS_RW_INTERLEAVED SND_PCM_ACCESS_RW_INTERLEAVED
|
|
#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED SND_PCM_ACCESS_RW_NONINTERLEAVED
|
|
|
|
#define MA_SND_CHMAP_UNKNOWN SND_CHMAP_UNKNOWN
|
|
#define MA_SND_CHMAP_NA SND_CHMAP_NA
|
|
#define MA_SND_CHMAP_MONO SND_CHMAP_MONO
|
|
#define MA_SND_CHMAP_FL SND_CHMAP_FL
|
|
#define MA_SND_CHMAP_FR SND_CHMAP_FR
|
|
#define MA_SND_CHMAP_RL SND_CHMAP_RL
|
|
#define MA_SND_CHMAP_RR SND_CHMAP_RR
|
|
#define MA_SND_CHMAP_FC SND_CHMAP_FC
|
|
#define MA_SND_CHMAP_LFE SND_CHMAP_LFE
|
|
#define MA_SND_CHMAP_SL SND_CHMAP_SL
|
|
#define MA_SND_CHMAP_SR SND_CHMAP_SR
|
|
#define MA_SND_CHMAP_RC SND_CHMAP_RC
|
|
#define MA_SND_CHMAP_FLC SND_CHMAP_FLC
|
|
#define MA_SND_CHMAP_FRC SND_CHMAP_FRC
|
|
#define MA_SND_CHMAP_RLC SND_CHMAP_RLC
|
|
#define MA_SND_CHMAP_RRC SND_CHMAP_RRC
|
|
#define MA_SND_CHMAP_FLW SND_CHMAP_FLW
|
|
#define MA_SND_CHMAP_FRW SND_CHMAP_FRW
|
|
#define MA_SND_CHMAP_FLH SND_CHMAP_FLH
|
|
#define MA_SND_CHMAP_FCH SND_CHMAP_FCH
|
|
#define MA_SND_CHMAP_FRH SND_CHMAP_FRH
|
|
#define MA_SND_CHMAP_TC SND_CHMAP_TC
|
|
#define MA_SND_CHMAP_TFL SND_CHMAP_TFL
|
|
#define MA_SND_CHMAP_TFR SND_CHMAP_TFR
|
|
#define MA_SND_CHMAP_TFC SND_CHMAP_TFC
|
|
#define MA_SND_CHMAP_TRL SND_CHMAP_TRL
|
|
#define MA_SND_CHMAP_TRR SND_CHMAP_TRR
|
|
#define MA_SND_CHMAP_TRC SND_CHMAP_TRC
|
|
#define MA_SND_CHMAP_TFLC SND_CHMAP_TFLC
|
|
#define MA_SND_CHMAP_TFRC SND_CHMAP_TFRC
|
|
#define MA_SND_CHMAP_TSL SND_CHMAP_TSL
|
|
#define MA_SND_CHMAP_TSR SND_CHMAP_TSR
|
|
#define MA_SND_CHMAP_LLFE SND_CHMAP_LLFE
|
|
#define MA_SND_CHMAP_RLFE SND_CHMAP_RLFE
|
|
#define MA_SND_CHMAP_BC SND_CHMAP_BC
|
|
#define MA_SND_CHMAP_BLC SND_CHMAP_BLC
|
|
#define MA_SND_CHMAP_BRC SND_CHMAP_BRC
|
|
|
|
#define MA_SND_PCM_NO_AUTO_RESAMPLE SND_PCM_NO_AUTO_RESAMPLE
|
|
#define MA_SND_PCM_NO_AUTO_CHANNELS SND_PCM_NO_AUTO_CHANNELS
|
|
#define MA_SND_PCM_NO_AUTO_FORMAT SND_PCM_NO_AUTO_FORMAT
|
|
#else
|
|
#include <errno.h>
|
|
typedef unsigned long ma_snd_pcm_uframes_t;
|
|
typedef long ma_snd_pcm_sframes_t;
|
|
typedef int ma_snd_pcm_stream_t;
|
|
typedef int ma_snd_pcm_format_t;
|
|
typedef int ma_snd_pcm_access_t;
|
|
typedef int ma_snd_pcm_state_t;
|
|
typedef struct ma_snd_pcm_t ma_snd_pcm_t;
|
|
typedef struct ma_snd_pcm_hw_params_t ma_snd_pcm_hw_params_t;
|
|
typedef struct ma_snd_pcm_sw_params_t ma_snd_pcm_sw_params_t;
|
|
typedef struct ma_snd_pcm_format_mask_t ma_snd_pcm_format_mask_t;
|
|
typedef struct ma_snd_pcm_info_t ma_snd_pcm_info_t;
|
|
typedef struct
|
|
{
|
|
void* addr;
|
|
unsigned int first;
|
|
unsigned int step;
|
|
} ma_snd_pcm_channel_area_t;
|
|
typedef struct
|
|
{
|
|
unsigned int channels;
|
|
unsigned int pos[1];
|
|
} ma_snd_pcm_chmap_t;
|
|
|
|
#define MA_SND_PCM_STATE_OPEN 0
|
|
#define MA_SND_PCM_STATE_SETUP 1
|
|
#define MA_SND_PCM_STATE_PREPARED 2
|
|
#define MA_SND_PCM_STATE_RUNNING 3
|
|
#define MA_SND_PCM_STATE_XRUN 4
|
|
#define MA_SND_PCM_STATE_DRAINING 5
|
|
#define MA_SND_PCM_STATE_PAUSED 6
|
|
#define MA_SND_PCM_STATE_SUSPENDED 7
|
|
#define MA_SND_PCM_STATE_DISCONNECTED 8
|
|
|
|
#define MA_SND_PCM_STREAM_PLAYBACK 0
|
|
#define MA_SND_PCM_STREAM_CAPTURE 1
|
|
|
|
#define MA_SND_PCM_FORMAT_UNKNOWN -1
|
|
#define MA_SND_PCM_FORMAT_U8 1
|
|
#define MA_SND_PCM_FORMAT_S16_LE 2
|
|
#define MA_SND_PCM_FORMAT_S16_BE 3
|
|
#define MA_SND_PCM_FORMAT_S24_LE 6
|
|
#define MA_SND_PCM_FORMAT_S24_BE 7
|
|
#define MA_SND_PCM_FORMAT_S32_LE 10
|
|
#define MA_SND_PCM_FORMAT_S32_BE 11
|
|
#define MA_SND_PCM_FORMAT_FLOAT_LE 14
|
|
#define MA_SND_PCM_FORMAT_FLOAT_BE 15
|
|
#define MA_SND_PCM_FORMAT_FLOAT64_LE 16
|
|
#define MA_SND_PCM_FORMAT_FLOAT64_BE 17
|
|
#define MA_SND_PCM_FORMAT_MU_LAW 20
|
|
#define MA_SND_PCM_FORMAT_A_LAW 21
|
|
#define MA_SND_PCM_FORMAT_S24_3LE 32
|
|
#define MA_SND_PCM_FORMAT_S24_3BE 33
|
|
|
|
#define MA_SND_PCM_ACCESS_MMAP_INTERLEAVED 0
|
|
#define MA_SND_PCM_ACCESS_MMAP_NONINTERLEAVED 1
|
|
#define MA_SND_PCM_ACCESS_MMAP_COMPLEX 2
|
|
#define MA_SND_PCM_ACCESS_RW_INTERLEAVED 3
|
|
#define MA_SND_PCM_ACCESS_RW_NONINTERLEAVED 4
|
|
|
|
#define MA_SND_CHMAP_UNKNOWN 0
|
|
#define MA_SND_CHMAP_NA 1
|
|
#define MA_SND_CHMAP_MONO 2
|
|
#define MA_SND_CHMAP_FL 3
|
|
#define MA_SND_CHMAP_FR 4
|
|
#define MA_SND_CHMAP_RL 5
|
|
#define MA_SND_CHMAP_RR 6
|
|
#define MA_SND_CHMAP_FC 7
|
|
#define MA_SND_CHMAP_LFE 8
|
|
#define MA_SND_CHMAP_SL 9
|
|
#define MA_SND_CHMAP_SR 10
|
|
#define MA_SND_CHMAP_RC 11
|
|
#define MA_SND_CHMAP_FLC 12
|
|
#define MA_SND_CHMAP_FRC 13
|
|
#define MA_SND_CHMAP_RLC 14
|
|
#define MA_SND_CHMAP_RRC 15
|
|
#define MA_SND_CHMAP_FLW 16
|
|
#define MA_SND_CHMAP_FRW 17
|
|
#define MA_SND_CHMAP_FLH 18
|
|
#define MA_SND_CHMAP_FCH 19
|
|
#define MA_SND_CHMAP_FRH 20
|
|
#define MA_SND_CHMAP_TC 21
|
|
#define MA_SND_CHMAP_TFL 22
|
|
#define MA_SND_CHMAP_TFR 23
|
|
#define MA_SND_CHMAP_TFC 24
|
|
#define MA_SND_CHMAP_TRL 25
|
|
#define MA_SND_CHMAP_TRR 26
|
|
#define MA_SND_CHMAP_TRC 27
|
|
#define MA_SND_CHMAP_TFLC 28
|
|
#define MA_SND_CHMAP_TFRC 29
|
|
#define MA_SND_CHMAP_TSL 30
|
|
#define MA_SND_CHMAP_TSR 31
|
|
#define MA_SND_CHMAP_LLFE 32
|
|
#define MA_SND_CHMAP_RLFE 33
|
|
#define MA_SND_CHMAP_BC 34
|
|
#define MA_SND_CHMAP_BLC 35
|
|
#define MA_SND_CHMAP_BRC 36
|
|
|
|
#define MA_SND_PCM_NO_AUTO_RESAMPLE 0x00010000
|
|
#define MA_SND_PCM_NO_AUTO_CHANNELS 0x00020000
|
|
#define MA_SND_PCM_NO_AUTO_FORMAT 0x00040000
|
|
#endif
|
|
|
|
typedef int (* ma_snd_pcm_open_proc) (ma_snd_pcm_t **pcm, const char *name, ma_snd_pcm_stream_t stream, int mode);
|
|
typedef int (* ma_snd_pcm_close_proc) (ma_snd_pcm_t *pcm);
|
|
typedef size_t (* ma_snd_pcm_hw_params_sizeof_proc) (void);
|
|
typedef int (* ma_snd_pcm_hw_params_any_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
|
|
typedef int (* ma_snd_pcm_hw_params_set_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);
|
|
typedef int (* ma_snd_pcm_hw_params_set_format_first_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
|
|
typedef void (* ma_snd_pcm_hw_params_get_format_mask_proc) (ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_mask_t *mask);
|
|
typedef int (* ma_snd_pcm_hw_params_set_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
|
|
typedef int (* ma_snd_pcm_hw_params_set_channels_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val);
|
|
typedef int (* ma_snd_pcm_hw_params_set_channels_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *minimum, unsigned int *maximum);
|
|
typedef int (* ma_snd_pcm_hw_params_set_rate_resample_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
|
|
typedef int (* ma_snd_pcm_hw_params_set_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir);
|
|
typedef int (* ma_snd_pcm_hw_params_set_rate_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
|
typedef int (* ma_snd_pcm_hw_params_set_rate_minmax_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *min, int *mindir, unsigned int *max, int *maxdir);
|
|
typedef int (* ma_snd_pcm_hw_params_set_buffer_size_near_proc)(ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
|
|
typedef int (* ma_snd_pcm_hw_params_set_periods_near_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
|
typedef int (* ma_snd_pcm_hw_params_set_access_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t _access);
|
|
typedef int (* ma_snd_pcm_hw_params_get_format_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t *format);
|
|
typedef int (* ma_snd_pcm_hw_params_get_channels_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
|
|
typedef int (* ma_snd_pcm_hw_params_get_channels_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
|
|
typedef int (* ma_snd_pcm_hw_params_get_channels_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val);
|
|
typedef int (* ma_snd_pcm_hw_params_get_rate_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
|
|
typedef int (* ma_snd_pcm_hw_params_get_rate_min_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
|
|
typedef int (* ma_snd_pcm_hw_params_get_rate_max_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *rate, int *dir);
|
|
typedef int (* ma_snd_pcm_hw_params_get_buffer_size_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_uframes_t *val);
|
|
typedef int (* ma_snd_pcm_hw_params_get_periods_proc) (const ma_snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
|
|
typedef int (* ma_snd_pcm_hw_params_get_access_proc) (const ma_snd_pcm_hw_params_t *params, ma_snd_pcm_access_t *_access);
|
|
typedef int (* ma_snd_pcm_hw_params_test_format_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, ma_snd_pcm_format_t val);
|
|
typedef int (* ma_snd_pcm_hw_params_test_channels_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val);
|
|
typedef int (* ma_snd_pcm_hw_params_test_rate_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params, unsigned int val, int dir);
|
|
typedef int (* ma_snd_pcm_hw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_hw_params_t *params);
|
|
typedef size_t (* ma_snd_pcm_sw_params_sizeof_proc) (void);
|
|
typedef int (* ma_snd_pcm_sw_params_current_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
|
|
typedef int (* ma_snd_pcm_sw_params_get_boundary_proc) (const ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t* val);
|
|
typedef int (* ma_snd_pcm_sw_params_set_avail_min_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
|
|
typedef int (* ma_snd_pcm_sw_params_set_start_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
|
|
typedef int (* ma_snd_pcm_sw_params_set_stop_threshold_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params, ma_snd_pcm_uframes_t val);
|
|
typedef int (* ma_snd_pcm_sw_params_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_sw_params_t *params);
|
|
typedef size_t (* ma_snd_pcm_format_mask_sizeof_proc) (void);
|
|
typedef int (* ma_snd_pcm_format_mask_test_proc) (const ma_snd_pcm_format_mask_t *mask, ma_snd_pcm_format_t val);
|
|
typedef ma_snd_pcm_chmap_t * (* ma_snd_pcm_get_chmap_proc) (ma_snd_pcm_t *pcm);
|
|
typedef ma_snd_pcm_state_t (* ma_snd_pcm_state_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_prepare_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_start_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_drop_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_drain_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_reset_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_device_name_hint_proc) (int card, const char *iface, void ***hints);
|
|
typedef char * (* ma_snd_device_name_get_hint_proc) (const void *hint, const char *id);
|
|
typedef int (* ma_snd_card_get_index_proc) (const char *name);
|
|
typedef int (* ma_snd_device_name_free_hint_proc) (void **hints);
|
|
typedef int (* ma_snd_pcm_mmap_begin_proc) (ma_snd_pcm_t *pcm, const ma_snd_pcm_channel_area_t **areas, ma_snd_pcm_uframes_t *offset, ma_snd_pcm_uframes_t *frames);
|
|
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_mmap_commit_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_uframes_t offset, ma_snd_pcm_uframes_t frames);
|
|
typedef int (* ma_snd_pcm_recover_proc) (ma_snd_pcm_t *pcm, int err, int silent);
|
|
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_readi_proc) (ma_snd_pcm_t *pcm, void *buffer, ma_snd_pcm_uframes_t size);
|
|
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_writei_proc) (ma_snd_pcm_t *pcm, const void *buffer, ma_snd_pcm_uframes_t size);
|
|
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_proc) (ma_snd_pcm_t *pcm);
|
|
typedef ma_snd_pcm_sframes_t (* ma_snd_pcm_avail_update_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_wait_proc) (ma_snd_pcm_t *pcm, int timeout);
|
|
typedef int (* ma_snd_pcm_nonblock_proc) (ma_snd_pcm_t *pcm, int nonblock);
|
|
typedef int (* ma_snd_pcm_info_proc) (ma_snd_pcm_t *pcm, ma_snd_pcm_info_t* info);
|
|
typedef size_t (* ma_snd_pcm_info_sizeof_proc) (void);
|
|
typedef const char* (* ma_snd_pcm_info_get_name_proc) (const ma_snd_pcm_info_t* info);
|
|
typedef int (* ma_snd_pcm_poll_descriptors_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
|
|
typedef int (* ma_snd_pcm_poll_descriptors_count_proc) (ma_snd_pcm_t *pcm);
|
|
typedef int (* ma_snd_pcm_poll_descriptors_revents_proc) (ma_snd_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
|
|
typedef int (* ma_snd_config_update_free_global_proc) (void);
|
|
|
|
static const char* g_maCommonDeviceNamesALSA[] = {
|
|
"default",
|
|
"null",
|
|
"pulse",
|
|
"jack"
|
|
};
|
|
|
|
static const char* g_maBlacklistedPlaybackDeviceNamesALSA[] = {
|
|
""
|
|
};
|
|
|
|
static const char* g_maBlacklistedCaptureDeviceNamesALSA[] = {
|
|
""
|
|
};
|
|
|
|
static ma_snd_pcm_format_t ma_convert_ma_format_to_alsa_format(ma_format format)
|
|
{
|
|
ma_snd_pcm_format_t ALSAFormats[] = {
|
|
MA_SND_PCM_FORMAT_UNKNOWN,
|
|
MA_SND_PCM_FORMAT_U8,
|
|
MA_SND_PCM_FORMAT_S16_LE,
|
|
MA_SND_PCM_FORMAT_S24_3LE,
|
|
MA_SND_PCM_FORMAT_S32_LE,
|
|
MA_SND_PCM_FORMAT_FLOAT_LE
|
|
};
|
|
|
|
if (ma_is_big_endian()) {
|
|
ALSAFormats[0] = MA_SND_PCM_FORMAT_UNKNOWN;
|
|
ALSAFormats[1] = MA_SND_PCM_FORMAT_U8;
|
|
ALSAFormats[2] = MA_SND_PCM_FORMAT_S16_BE;
|
|
ALSAFormats[3] = MA_SND_PCM_FORMAT_S24_3BE;
|
|
ALSAFormats[4] = MA_SND_PCM_FORMAT_S32_BE;
|
|
ALSAFormats[5] = MA_SND_PCM_FORMAT_FLOAT_BE;
|
|
}
|
|
|
|
return ALSAFormats[format];
|
|
}
|
|
|
|
static ma_format ma_format_from_alsa(ma_snd_pcm_format_t formatALSA)
|
|
{
|
|
if (ma_is_little_endian()) {
|
|
switch (formatALSA) {
|
|
case MA_SND_PCM_FORMAT_S16_LE: return ma_format_s16;
|
|
case MA_SND_PCM_FORMAT_S24_3LE: return ma_format_s24;
|
|
case MA_SND_PCM_FORMAT_S32_LE: return ma_format_s32;
|
|
case MA_SND_PCM_FORMAT_FLOAT_LE: return ma_format_f32;
|
|
default: break;
|
|
}
|
|
} else {
|
|
switch (formatALSA) {
|
|
case MA_SND_PCM_FORMAT_S16_BE: return ma_format_s16;
|
|
case MA_SND_PCM_FORMAT_S24_3BE: return ma_format_s24;
|
|
case MA_SND_PCM_FORMAT_S32_BE: return ma_format_s32;
|
|
case MA_SND_PCM_FORMAT_FLOAT_BE: return ma_format_f32;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
switch (formatALSA) {
|
|
case MA_SND_PCM_FORMAT_U8: return ma_format_u8;
|
|
default: return ma_format_unknown;
|
|
}
|
|
}
|
|
|
|
static ma_channel ma_convert_alsa_channel_position_to_ma_channel(unsigned int alsaChannelPos)
|
|
{
|
|
switch (alsaChannelPos)
|
|
{
|
|
case MA_SND_CHMAP_MONO: return MA_CHANNEL_MONO;
|
|
case MA_SND_CHMAP_FL: return MA_CHANNEL_FRONT_LEFT;
|
|
case MA_SND_CHMAP_FR: return MA_CHANNEL_FRONT_RIGHT;
|
|
case MA_SND_CHMAP_RL: return MA_CHANNEL_BACK_LEFT;
|
|
case MA_SND_CHMAP_RR: return MA_CHANNEL_BACK_RIGHT;
|
|
case MA_SND_CHMAP_FC: return MA_CHANNEL_FRONT_CENTER;
|
|
case MA_SND_CHMAP_LFE: return MA_CHANNEL_LFE;
|
|
case MA_SND_CHMAP_SL: return MA_CHANNEL_SIDE_LEFT;
|
|
case MA_SND_CHMAP_SR: return MA_CHANNEL_SIDE_RIGHT;
|
|
case MA_SND_CHMAP_RC: return MA_CHANNEL_BACK_CENTER;
|
|
case MA_SND_CHMAP_FLC: return MA_CHANNEL_FRONT_LEFT_CENTER;
|
|
case MA_SND_CHMAP_FRC: return MA_CHANNEL_FRONT_RIGHT_CENTER;
|
|
case MA_SND_CHMAP_RLC: return 0;
|
|
case MA_SND_CHMAP_RRC: return 0;
|
|
case MA_SND_CHMAP_FLW: return 0;
|
|
case MA_SND_CHMAP_FRW: return 0;
|
|
case MA_SND_CHMAP_FLH: return 0;
|
|
case MA_SND_CHMAP_FCH: return 0;
|
|
case MA_SND_CHMAP_FRH: return 0;
|
|
case MA_SND_CHMAP_TC: return MA_CHANNEL_TOP_CENTER;
|
|
case MA_SND_CHMAP_TFL: return MA_CHANNEL_TOP_FRONT_LEFT;
|
|
case MA_SND_CHMAP_TFR: return MA_CHANNEL_TOP_FRONT_RIGHT;
|
|
case MA_SND_CHMAP_TFC: return MA_CHANNEL_TOP_FRONT_CENTER;
|
|
case MA_SND_CHMAP_TRL: return MA_CHANNEL_TOP_BACK_LEFT;
|
|
case MA_SND_CHMAP_TRR: return MA_CHANNEL_TOP_BACK_RIGHT;
|
|
case MA_SND_CHMAP_TRC: return MA_CHANNEL_TOP_BACK_CENTER;
|
|
default: break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ma_bool32 ma_is_common_device_name__alsa(const char* name)
|
|
{
|
|
size_t iName;
|
|
for (iName = 0; iName < ma_countof(g_maCommonDeviceNamesALSA); ++iName) {
|
|
if (ma_strcmp(name, g_maCommonDeviceNamesALSA[iName]) == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
static ma_bool32 ma_is_playback_device_blacklisted__alsa(const char* name)
|
|
{
|
|
size_t iName;
|
|
for (iName = 0; iName < ma_countof(g_maBlacklistedPlaybackDeviceNamesALSA); ++iName) {
|
|
if (ma_strcmp(name, g_maBlacklistedPlaybackDeviceNamesALSA[iName]) == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
static ma_bool32 ma_is_capture_device_blacklisted__alsa(const char* name)
|
|
{
|
|
size_t iName;
|
|
for (iName = 0; iName < ma_countof(g_maBlacklistedCaptureDeviceNamesALSA); ++iName) {
|
|
if (ma_strcmp(name, g_maBlacklistedCaptureDeviceNamesALSA[iName]) == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
static ma_bool32 ma_is_device_blacklisted__alsa(ma_device_type deviceType, const char* name)
|
|
{
|
|
if (deviceType == ma_device_type_playback) {
|
|
return ma_is_playback_device_blacklisted__alsa(name);
|
|
} else {
|
|
return ma_is_capture_device_blacklisted__alsa(name);
|
|
}
|
|
}
|
|
|
|
static const char* ma_find_char(const char* str, char c, int* index)
|
|
{
|
|
int i = 0;
|
|
for (;;) {
|
|
if (str[i] == '\0') {
|
|
if (index) *index = -1;
|
|
return NULL;
|
|
}
|
|
|
|
if (str[i] == c) {
|
|
if (index) *index = i;
|
|
return str + i;
|
|
}
|
|
|
|
i += 1;
|
|
}
|
|
|
|
if (index) *index = -1;
|
|
return NULL;
|
|
}
|
|
|
|
static ma_bool32 ma_is_device_name_in_hw_format__alsa(const char* hwid)
|
|
{
|
|
|
|
int commaPos;
|
|
const char* dev;
|
|
int i;
|
|
|
|
if (hwid == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (hwid[0] != 'h' || hwid[1] != 'w' || hwid[2] != ':') {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
hwid += 3;
|
|
|
|
dev = ma_find_char(hwid, ',', &commaPos);
|
|
if (dev == NULL) {
|
|
return MA_FALSE;
|
|
} else {
|
|
dev += 1;
|
|
}
|
|
|
|
for (i = 0; i < commaPos; ++i) {
|
|
if (hwid[i] < '0' || hwid[i] > '9') {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
i = 0;
|
|
while (dev[i] != '\0') {
|
|
if (dev[i] < '0' || dev[i] > '9') {
|
|
return MA_FALSE;
|
|
}
|
|
i += 1;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static int ma_convert_device_name_to_hw_format__alsa(ma_context* pContext, char* dst, size_t dstSize, const char* src)
|
|
{
|
|
|
|
int colonPos;
|
|
int commaPos;
|
|
char card[256];
|
|
const char* dev;
|
|
int cardIndex;
|
|
|
|
if (dst == NULL) {
|
|
return -1;
|
|
}
|
|
if (dstSize < 7) {
|
|
return -1;
|
|
}
|
|
|
|
*dst = '\0';
|
|
if (src == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (ma_is_device_name_in_hw_format__alsa(src)) {
|
|
return ma_strcpy_s(dst, dstSize, src);
|
|
}
|
|
|
|
src = ma_find_char(src, ':', &colonPos);
|
|
if (src == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
dev = ma_find_char(src, ',', &commaPos);
|
|
if (dev == NULL) {
|
|
dev = "0";
|
|
ma_strncpy_s(card, sizeof(card), src+6, (size_t)-1);
|
|
} else {
|
|
dev = dev + 5;
|
|
ma_strncpy_s(card, sizeof(card), src+6, commaPos-6);
|
|
}
|
|
|
|
cardIndex = ((ma_snd_card_get_index_proc)pContext->alsa.snd_card_get_index)(card);
|
|
if (cardIndex < 0) {
|
|
return -2;
|
|
}
|
|
|
|
dst[0] = 'h'; dst[1] = 'w'; dst[2] = ':';
|
|
if (ma_itoa_s(cardIndex, dst+3, dstSize-3, 10) != 0) {
|
|
return -3;
|
|
}
|
|
if (ma_strcat_s(dst, dstSize, ",") != 0) {
|
|
return -3;
|
|
}
|
|
if (ma_strcat_s(dst, dstSize, dev) != 0) {
|
|
return -3;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ma_bool32 ma_does_id_exist_in_list__alsa(ma_device_id* pUniqueIDs, ma_uint32 count, const char* pHWID)
|
|
{
|
|
ma_uint32 i;
|
|
|
|
MA_ASSERT(pHWID != NULL);
|
|
|
|
for (i = 0; i < count; ++i) {
|
|
if (ma_strcmp(pUniqueIDs[i].alsa, pHWID) == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
static ma_result ma_context_open_pcm__alsa(ma_context* pContext, ma_share_mode shareMode, ma_device_type deviceType, const ma_device_id* pDeviceID, int openMode, ma_snd_pcm_t** ppPCM)
|
|
{
|
|
ma_snd_pcm_t* pPCM;
|
|
ma_snd_pcm_stream_t stream;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppPCM != NULL);
|
|
|
|
*ppPCM = NULL;
|
|
pPCM = NULL;
|
|
|
|
stream = (deviceType == ma_device_type_playback) ? MA_SND_PCM_STREAM_PLAYBACK : MA_SND_PCM_STREAM_CAPTURE;
|
|
|
|
if (pDeviceID == NULL) {
|
|
ma_bool32 isDeviceOpen;
|
|
size_t i;
|
|
|
|
const char* defaultDeviceNames[] = {
|
|
"default",
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
if (shareMode == ma_share_mode_exclusive) {
|
|
defaultDeviceNames[1] = "hw";
|
|
defaultDeviceNames[2] = "hw:0";
|
|
defaultDeviceNames[3] = "hw:0,0";
|
|
} else {
|
|
if (deviceType == ma_device_type_playback) {
|
|
defaultDeviceNames[1] = "dmix";
|
|
defaultDeviceNames[2] = "dmix:0";
|
|
defaultDeviceNames[3] = "dmix:0,0";
|
|
} else {
|
|
defaultDeviceNames[1] = "dsnoop";
|
|
defaultDeviceNames[2] = "dsnoop:0";
|
|
defaultDeviceNames[3] = "dsnoop:0,0";
|
|
}
|
|
defaultDeviceNames[4] = "hw";
|
|
defaultDeviceNames[5] = "hw:0";
|
|
defaultDeviceNames[6] = "hw:0,0";
|
|
}
|
|
|
|
isDeviceOpen = MA_FALSE;
|
|
for (i = 0; i < ma_countof(defaultDeviceNames); ++i) {
|
|
if (defaultDeviceNames[i] != NULL && defaultDeviceNames[i][0] != '\0') {
|
|
if (((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, defaultDeviceNames[i], stream, openMode) == 0) {
|
|
isDeviceOpen = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isDeviceOpen) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed when trying to open an appropriate default device.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
} else {
|
|
|
|
ma_device_id deviceID = *pDeviceID;
|
|
int resultALSA = -ENODEV;
|
|
|
|
if (deviceID.alsa[0] != ':') {
|
|
resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, deviceID.alsa, stream, openMode);
|
|
} else {
|
|
char hwid[256];
|
|
|
|
if (deviceID.alsa[1] == '\0') {
|
|
deviceID.alsa[0] = '\0';
|
|
}
|
|
|
|
if (shareMode == ma_share_mode_shared) {
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strcpy_s(hwid, sizeof(hwid), "dmix");
|
|
} else {
|
|
ma_strcpy_s(hwid, sizeof(hwid), "dsnoop");
|
|
}
|
|
|
|
if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
|
|
resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
|
|
}
|
|
}
|
|
|
|
if (resultALSA != 0) {
|
|
ma_strcpy_s(hwid, sizeof(hwid), "hw");
|
|
if (ma_strcat_s(hwid, sizeof(hwid), deviceID.alsa) == 0) {
|
|
resultALSA = ((ma_snd_pcm_open_proc)pContext->alsa.snd_pcm_open)(&pPCM, hwid, stream, openMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resultALSA < 0) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_open() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
}
|
|
|
|
*ppPCM = pPCM;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__alsa(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
int resultALSA;
|
|
ma_bool32 cbResult = MA_TRUE;
|
|
char** ppDeviceHints;
|
|
ma_device_id* pUniqueIDs = NULL;
|
|
ma_uint32 uniqueIDCount = 0;
|
|
char** ppNextDeviceHint;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
ma_mutex_lock(&pContext->alsa.internalDeviceEnumLock);
|
|
|
|
resultALSA = ((ma_snd_device_name_hint_proc)pContext->alsa.snd_device_name_hint)(-1, "pcm", (void***)&ppDeviceHints);
|
|
if (resultALSA < 0) {
|
|
ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
ppNextDeviceHint = ppDeviceHints;
|
|
while (*ppNextDeviceHint != NULL) {
|
|
char* NAME = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "NAME");
|
|
char* DESC = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "DESC");
|
|
char* IOID = ((ma_snd_device_name_get_hint_proc)pContext->alsa.snd_device_name_get_hint)(*ppNextDeviceHint, "IOID");
|
|
ma_device_type deviceType = ma_device_type_playback;
|
|
ma_bool32 stopEnumeration = MA_FALSE;
|
|
char hwid[sizeof(pUniqueIDs->alsa)];
|
|
ma_device_info deviceInfo;
|
|
|
|
if ((IOID == NULL || ma_strcmp(IOID, "Output") == 0)) {
|
|
deviceType = ma_device_type_playback;
|
|
}
|
|
if ((IOID != NULL && ma_strcmp(IOID, "Input" ) == 0)) {
|
|
deviceType = ma_device_type_capture;
|
|
}
|
|
|
|
if (NAME != NULL) {
|
|
if (pContext->alsa.useVerboseDeviceEnumeration) {
|
|
ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
|
|
} else {
|
|
if (ma_convert_device_name_to_hw_format__alsa(pContext, hwid, sizeof(hwid), NAME) == 0) {
|
|
char* dst = hwid;
|
|
char* src = hwid+2;
|
|
while ((*dst++ = *src++));
|
|
} else {
|
|
ma_strncpy_s(hwid, sizeof(hwid), NAME, (size_t)-1);
|
|
}
|
|
|
|
if (ma_does_id_exist_in_list__alsa(pUniqueIDs, uniqueIDCount, hwid)) {
|
|
goto next_device;
|
|
} else {
|
|
size_t newCapacity = sizeof(*pUniqueIDs) * (uniqueIDCount + 1);
|
|
ma_device_id* pNewUniqueIDs = (ma_device_id*)ma_realloc(pUniqueIDs, newCapacity, &pContext->allocationCallbacks);
|
|
if (pNewUniqueIDs == NULL) {
|
|
goto next_device;
|
|
}
|
|
|
|
pUniqueIDs = pNewUniqueIDs;
|
|
MA_COPY_MEMORY(pUniqueIDs[uniqueIDCount].alsa, hwid, sizeof(hwid));
|
|
uniqueIDCount += 1;
|
|
}
|
|
}
|
|
} else {
|
|
MA_ZERO_MEMORY(hwid, sizeof(hwid));
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.id.alsa, sizeof(deviceInfo.id.alsa), hwid, (size_t)-1);
|
|
|
|
if (ma_strcmp(deviceInfo.id.alsa, "default") == 0) {
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (DESC != NULL) {
|
|
int lfPos;
|
|
const char* line2 = ma_find_char(DESC, '\n', &lfPos);
|
|
if (line2 != NULL) {
|
|
line2 += 1;
|
|
|
|
if (pContext->alsa.useVerboseDeviceEnumeration) {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
|
|
ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), " (");
|
|
ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), line2);
|
|
ma_strcat_s (deviceInfo.name, sizeof(deviceInfo.name), ")");
|
|
} else {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, lfPos);
|
|
}
|
|
} else {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), DESC, (size_t)-1);
|
|
}
|
|
}
|
|
|
|
if (!ma_is_device_blacklisted__alsa(deviceType, NAME)) {
|
|
cbResult = callback(pContext, deviceType, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (cbResult) {
|
|
if (ma_is_common_device_name__alsa(NAME) || IOID == NULL) {
|
|
if (deviceType == ma_device_type_playback) {
|
|
if (!ma_is_capture_device_blacklisted__alsa(NAME)) {
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
} else {
|
|
if (!ma_is_playback_device_blacklisted__alsa(NAME)) {
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cbResult == MA_FALSE) {
|
|
stopEnumeration = MA_TRUE;
|
|
}
|
|
|
|
next_device:
|
|
free(NAME);
|
|
free(DESC);
|
|
free(IOID);
|
|
ppNextDeviceHint += 1;
|
|
|
|
if (stopEnumeration) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ma_free(pUniqueIDs, &pContext->allocationCallbacks);
|
|
((ma_snd_device_name_free_hint_proc)pContext->alsa.snd_device_name_free_hint)((void**)ppDeviceHints);
|
|
|
|
ma_mutex_unlock(&pContext->alsa.internalDeviceEnumLock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_device_type deviceType;
|
|
const ma_device_id* pDeviceID;
|
|
ma_share_mode shareMode;
|
|
ma_device_info* pDeviceInfo;
|
|
ma_bool32 foundDevice;
|
|
} ma_context_get_device_info_enum_callback_data__alsa;
|
|
|
|
static ma_bool32 ma_context_get_device_info_enum_callback__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pDeviceInfo, void* pUserData)
|
|
{
|
|
ma_context_get_device_info_enum_callback_data__alsa* pData = (ma_context_get_device_info_enum_callback_data__alsa*)pUserData;
|
|
MA_ASSERT(pData != NULL);
|
|
|
|
(void)pContext;
|
|
|
|
if (pData->pDeviceID == NULL && ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
|
|
ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
|
|
pData->foundDevice = MA_TRUE;
|
|
} else {
|
|
if (pData->deviceType == deviceType && (pData->pDeviceID != NULL && ma_strcmp(pData->pDeviceID->alsa, pDeviceInfo->id.alsa) == 0)) {
|
|
ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pDeviceInfo->name, (size_t)-1);
|
|
pData->foundDevice = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return !pData->foundDevice;
|
|
}
|
|
|
|
static void ma_context_test_rate_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 flags, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_ASSERT(pPCM != NULL);
|
|
MA_ASSERT(pHWParams != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
if (pDeviceInfo->nativeDataFormatCount < ma_countof(pDeviceInfo->nativeDataFormats) && ((ma_snd_pcm_hw_params_test_rate_proc)pContext->alsa.snd_pcm_hw_params_test_rate)(pPCM, pHWParams, sampleRate, 0) == 0) {
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
}
|
|
}
|
|
|
|
static void ma_context_iterate_rates_and_add_native_data_format__alsa(ma_context* pContext, ma_snd_pcm_t* pPCM, ma_snd_pcm_hw_params_t* pHWParams, ma_format format, ma_uint32 channels, ma_uint32 flags, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_uint32 iSampleRate;
|
|
unsigned int minSampleRate;
|
|
unsigned int maxSampleRate;
|
|
int sampleRateDir;
|
|
|
|
((ma_snd_pcm_hw_params_get_rate_min_proc)pContext->alsa.snd_pcm_hw_params_get_rate_min)(pHWParams, &minSampleRate, &sampleRateDir);
|
|
((ma_snd_pcm_hw_params_get_rate_max_proc)pContext->alsa.snd_pcm_hw_params_get_rate_max)(pHWParams, &maxSampleRate, &sampleRateDir);
|
|
|
|
minSampleRate = ma_clamp(minSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
|
|
maxSampleRate = ma_clamp(maxSampleRate, (unsigned int)ma_standard_sample_rate_min, (unsigned int)ma_standard_sample_rate_max);
|
|
|
|
for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
|
|
ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
|
|
|
|
if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
|
|
ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, standardSampleRate, flags, pDeviceInfo);
|
|
}
|
|
}
|
|
|
|
if (!ma_is_standard_sample_rate(minSampleRate)) {
|
|
ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, minSampleRate, flags, pDeviceInfo);
|
|
}
|
|
|
|
if (!ma_is_standard_sample_rate(maxSampleRate) && maxSampleRate != minSampleRate) {
|
|
ma_context_test_rate_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, maxSampleRate, flags, pDeviceInfo);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__alsa(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_context_get_device_info_enum_callback_data__alsa data;
|
|
ma_result result;
|
|
int resultALSA;
|
|
ma_snd_pcm_t* pPCM;
|
|
ma_snd_pcm_hw_params_t* pHWParams;
|
|
ma_uint32 iFormat;
|
|
ma_uint32 iChannel;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
data.deviceType = deviceType;
|
|
data.pDeviceID = pDeviceID;
|
|
data.pDeviceInfo = pDeviceInfo;
|
|
data.foundDevice = MA_FALSE;
|
|
result = ma_context_enumerate_devices__alsa(pContext, ma_context_get_device_info_enum_callback__alsa, &data);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (!data.foundDevice) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
if (ma_strcmp(pDeviceInfo->id.alsa, "default") == 0) {
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
}
|
|
|
|
result = ma_context_open_pcm__alsa(pContext, ma_share_mode_shared, deviceType, pDeviceID, 0, &pPCM);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pContext->alsa.snd_pcm_hw_params_sizeof)(), &pContext->allocationCallbacks);
|
|
if (pHWParams == NULL) {
|
|
((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
|
|
ma_format format = g_maFormatPriorities[iFormat];
|
|
|
|
((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
|
|
|
|
if (((ma_snd_pcm_hw_params_test_format_proc)pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format)) == 0) {
|
|
unsigned int minChannels;
|
|
unsigned int maxChannels;
|
|
|
|
((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));
|
|
|
|
((ma_snd_pcm_hw_params_get_channels_min_proc)pContext->alsa.snd_pcm_hw_params_get_channels_min)(pHWParams, &minChannels);
|
|
((ma_snd_pcm_hw_params_get_channels_max_proc)pContext->alsa.snd_pcm_hw_params_get_channels_max)(pHWParams, &maxChannels);
|
|
|
|
if (minChannels > MA_MAX_CHANNELS) {
|
|
continue;
|
|
}
|
|
if (maxChannels < MA_MIN_CHANNELS) {
|
|
continue;
|
|
}
|
|
|
|
minChannels = ma_clamp(minChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
|
maxChannels = ma_clamp(maxChannels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
|
|
|
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
|
|
ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, 0, 0, pDeviceInfo);
|
|
} else {
|
|
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
|
|
unsigned int channels = iChannel;
|
|
|
|
((ma_snd_pcm_hw_params_any_proc)pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
|
|
((ma_snd_pcm_hw_params_set_format_proc)pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(format));
|
|
|
|
if (((ma_snd_pcm_hw_params_test_channels_proc)pContext->alsa.snd_pcm_hw_params_test_channels)(pPCM, pHWParams, channels) == 0) {
|
|
|
|
((ma_snd_pcm_hw_params_set_channels_proc)pContext->alsa.snd_pcm_hw_params_set_channels)(pPCM, pHWParams, channels);
|
|
|
|
ma_context_iterate_rates_and_add_native_data_format__alsa(pContext, pPCM, pHWParams, format, channels, 0, pDeviceInfo);
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
|
|
ma_free(pHWParams, &pContext->allocationCallbacks);
|
|
|
|
((ma_snd_pcm_close_proc)pContext->alsa.snd_pcm_close)(pPCM);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__alsa(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if ((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) {
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
|
|
close(pDevice->alsa.wakeupfdCapture);
|
|
ma_free(pDevice->alsa.pPollDescriptorsCapture, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
if ((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) {
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
|
|
close(pDevice->alsa.wakeupfdPlayback);
|
|
ma_free(pDevice->alsa.pPollDescriptorsPlayback, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init_by_type__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
|
|
{
|
|
ma_result result;
|
|
int resultALSA;
|
|
ma_snd_pcm_t* pPCM;
|
|
ma_bool32 isUsingMMap;
|
|
ma_snd_pcm_format_t formatALSA;
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
ma_uint32 internalSampleRate;
|
|
ma_channel internalChannelMap[MA_MAX_CHANNELS];
|
|
ma_uint32 internalPeriodSizeInFrames;
|
|
ma_uint32 internalPeriods;
|
|
int openMode;
|
|
ma_snd_pcm_hw_params_t* pHWParams;
|
|
ma_snd_pcm_sw_params_t* pSWParams;
|
|
ma_snd_pcm_uframes_t bufferBoundary;
|
|
int pollDescriptorCount;
|
|
struct pollfd* pPollDescriptors;
|
|
int wakeupfd;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(deviceType != ma_device_type_duplex);
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
formatALSA = ma_convert_ma_format_to_alsa_format(pDescriptor->format);
|
|
|
|
openMode = 0;
|
|
if (pConfig->alsa.noAutoResample) {
|
|
openMode |= MA_SND_PCM_NO_AUTO_RESAMPLE;
|
|
}
|
|
if (pConfig->alsa.noAutoChannels) {
|
|
openMode |= MA_SND_PCM_NO_AUTO_CHANNELS;
|
|
}
|
|
if (pConfig->alsa.noAutoFormat) {
|
|
openMode |= MA_SND_PCM_NO_AUTO_FORMAT;
|
|
}
|
|
|
|
result = ma_context_open_pcm__alsa(pDevice->pContext, pDescriptor->shareMode, deviceType, pDescriptor->pDeviceID, openMode, &pPCM);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHWParams = (ma_snd_pcm_hw_params_t*)ma_calloc(((ma_snd_pcm_hw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_hw_params_sizeof)(), &pDevice->pContext->allocationCallbacks);
|
|
if (pHWParams == NULL) {
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for hardware parameters.");
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_any_proc)pDevice->pContext->alsa.snd_pcm_hw_params_any)(pPCM, pHWParams);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize hardware parameters. snd_pcm_hw_params_any() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
isUsingMMap = MA_FALSE;
|
|
#if 0
|
|
if (deviceType != ma_device_type_capture) {
|
|
if (!pConfig->alsa.noMMap) {
|
|
if (((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_MMAP_INTERLEAVED) == 0) {
|
|
pDevice->alsa.isUsingMMap = MA_TRUE;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!isUsingMMap) {
|
|
resultALSA = ((ma_snd_pcm_hw_params_set_access_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_access)(pPCM, pHWParams, MA_SND_PCM_ACCESS_RW_INTERLEAVED);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set access mode to neither SND_PCM_ACCESS_MMAP_INTERLEAVED nor SND_PCM_ACCESS_RW_INTERLEAVED. snd_pcm_hw_params_set_access() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
}
|
|
|
|
{
|
|
if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN || ((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, formatALSA) != 0) {
|
|
size_t iFormat;
|
|
|
|
formatALSA = MA_SND_PCM_FORMAT_UNKNOWN;
|
|
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); ++iFormat) {
|
|
if (((ma_snd_pcm_hw_params_test_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_test_format)(pPCM, pHWParams, ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat])) == 0) {
|
|
formatALSA = ma_convert_ma_format_to_alsa_format(g_maFormatPriorities[iFormat]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (formatALSA == MA_SND_PCM_FORMAT_UNKNOWN) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. The device does not support any miniaudio formats.");
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_set_format_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_format)(pPCM, pHWParams, formatALSA);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Format not supported. snd_pcm_hw_params_set_format() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
internalFormat = ma_format_from_alsa(formatALSA);
|
|
if (internalFormat == ma_format_unknown) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] The chosen format is not supported by miniaudio.");
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
|
|
{
|
|
unsigned int channels = pDescriptor->channels;
|
|
if (channels == 0) {
|
|
channels = MA_DEFAULT_CHANNELS;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_set_channels_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_channels_near)(pPCM, pHWParams, &channels);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set channel count. snd_pcm_hw_params_set_channels_near() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
internalChannels = (ma_uint32)channels;
|
|
}
|
|
|
|
{
|
|
unsigned int sampleRate;
|
|
|
|
((ma_snd_pcm_hw_params_set_rate_resample_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_resample)(pPCM, pHWParams, 0);
|
|
|
|
sampleRate = pDescriptor->sampleRate;
|
|
if (sampleRate == 0) {
|
|
sampleRate = MA_DEFAULT_SAMPLE_RATE;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_set_rate_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_rate_near)(pPCM, pHWParams, &sampleRate, 0);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Sample rate not supported. snd_pcm_hw_params_set_rate_near() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
internalSampleRate = (ma_uint32)sampleRate;
|
|
}
|
|
|
|
{
|
|
ma_uint32 periods = pDescriptor->periodCount;
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_set_periods_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_periods_near)(pPCM, pHWParams, &periods, NULL);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set period count. snd_pcm_hw_params_set_periods_near() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
internalPeriods = periods;
|
|
}
|
|
|
|
{
|
|
ma_snd_pcm_uframes_t actualBufferSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile) * internalPeriods;
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_set_buffer_size_near_proc)pDevice->pContext->alsa.snd_pcm_hw_params_set_buffer_size_near)(pPCM, pHWParams, &actualBufferSizeInFrames);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set buffer size for device. snd_pcm_hw_params_set_buffer_size() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
internalPeriodSizeInFrames = actualBufferSizeInFrames / internalPeriods;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_hw_params_proc)pDevice->pContext->alsa.snd_pcm_hw_params)(pPCM, pHWParams);
|
|
if (resultALSA < 0) {
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set hardware parameters. snd_pcm_hw_params() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
ma_free(pHWParams, &pDevice->pContext->allocationCallbacks);
|
|
pHWParams = NULL;
|
|
|
|
pSWParams = (ma_snd_pcm_sw_params_t*)ma_calloc(((ma_snd_pcm_sw_params_sizeof_proc)pDevice->pContext->alsa.snd_pcm_sw_params_sizeof)(), &pDevice->pContext->allocationCallbacks);
|
|
if (pSWParams == NULL) {
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for software parameters.");
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_sw_params_current_proc)pDevice->pContext->alsa.snd_pcm_sw_params_current)(pPCM, pSWParams);
|
|
if (resultALSA < 0) {
|
|
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to initialize software parameters. snd_pcm_sw_params_current() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_sw_params_set_avail_min_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_avail_min)(pPCM, pSWParams, ma_prev_power_of_2(internalPeriodSizeInFrames));
|
|
if (resultALSA < 0) {
|
|
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_sw_params_set_avail_min() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_sw_params_get_boundary_proc)pDevice->pContext->alsa.snd_pcm_sw_params_get_boundary)(pSWParams, &bufferBoundary);
|
|
if (resultALSA < 0) {
|
|
bufferBoundary = internalPeriodSizeInFrames * internalPeriods;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback && !isUsingMMap) {
|
|
resultALSA = ((ma_snd_pcm_sw_params_set_start_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_start_threshold)(pPCM, pSWParams, internalPeriodSizeInFrames*2);
|
|
if (resultALSA < 0) {
|
|
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set start threshold for playback device. snd_pcm_sw_params_set_start_threshold() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_sw_params_set_stop_threshold_proc)pDevice->pContext->alsa.snd_pcm_sw_params_set_stop_threshold)(pPCM, pSWParams, bufferBoundary);
|
|
if (resultALSA < 0) {
|
|
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set stop threshold for playback device. snd_pcm_sw_params_set_stop_threshold() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_sw_params_proc)pDevice->pContext->alsa.snd_pcm_sw_params)(pPCM, pSWParams);
|
|
if (resultALSA < 0) {
|
|
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to set software parameters. snd_pcm_sw_params() failed.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
ma_free(pSWParams, &pDevice->pContext->allocationCallbacks);
|
|
pSWParams = NULL;
|
|
|
|
{
|
|
ma_snd_pcm_chmap_t* pChmap = NULL;
|
|
if (pDevice->pContext->alsa.snd_pcm_get_chmap != NULL) {
|
|
pChmap = ((ma_snd_pcm_get_chmap_proc)pDevice->pContext->alsa.snd_pcm_get_chmap)(pPCM);
|
|
}
|
|
|
|
if (pChmap != NULL) {
|
|
ma_uint32 iChannel;
|
|
|
|
if (pChmap->channels >= internalChannels) {
|
|
for (iChannel = 0; iChannel < internalChannels; ++iChannel) {
|
|
internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
|
|
}
|
|
} else {
|
|
ma_uint32 i;
|
|
|
|
ma_bool32 isValid = MA_TRUE;
|
|
|
|
ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
|
|
|
|
for (iChannel = 0; iChannel < pChmap->channels; ++iChannel) {
|
|
internalChannelMap[iChannel] = ma_convert_alsa_channel_position_to_ma_channel(pChmap->pos[iChannel]);
|
|
}
|
|
|
|
for (i = 0; i < internalChannels && isValid; ++i) {
|
|
ma_uint32 j;
|
|
for (j = i+1; j < internalChannels; ++j) {
|
|
if (internalChannelMap[i] == internalChannelMap[j]) {
|
|
isValid = MA_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isValid) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
|
|
}
|
|
}
|
|
|
|
free(pChmap);
|
|
pChmap = NULL;
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_alsa, internalChannelMap, ma_countof(internalChannelMap), internalChannels);
|
|
}
|
|
}
|
|
|
|
pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_count_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_count)(pPCM);
|
|
if (pollDescriptorCount <= 0) {
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors count.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
pPollDescriptors = (struct pollfd*)ma_malloc(sizeof(*pPollDescriptors) * (pollDescriptorCount + 1), &pDevice->pContext->allocationCallbacks);
|
|
if (pPollDescriptors == NULL) {
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to allocate memory for poll descriptors.");
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
wakeupfd = eventfd(0, 0);
|
|
if (wakeupfd < 0) {
|
|
ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to create eventfd for poll wakeup.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
pPollDescriptors[0].fd = wakeupfd;
|
|
pPollDescriptors[0].events = POLLIN;
|
|
pPollDescriptors[0].revents = 0;
|
|
|
|
pollDescriptorCount = ((ma_snd_pcm_poll_descriptors_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors)(pPCM, pPollDescriptors + 1, pollDescriptorCount);
|
|
if (pollDescriptorCount <= 0) {
|
|
close(wakeupfd);
|
|
ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to retrieve poll descriptors.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
pDevice->alsa.pollDescriptorCountCapture = pollDescriptorCount;
|
|
pDevice->alsa.pPollDescriptorsCapture = pPollDescriptors;
|
|
pDevice->alsa.wakeupfdCapture = wakeupfd;
|
|
} else {
|
|
pDevice->alsa.pollDescriptorCountPlayback = pollDescriptorCount;
|
|
pDevice->alsa.pPollDescriptorsPlayback = pPollDescriptors;
|
|
pDevice->alsa.wakeupfdPlayback = wakeupfd;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)(pPCM);
|
|
if (resultALSA < 0) {
|
|
close(wakeupfd);
|
|
ma_free(pPollDescriptors, &pDevice->pContext->allocationCallbacks);
|
|
((ma_snd_pcm_close_proc)pDevice->pContext->alsa.snd_pcm_close)(pPCM);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to prepare device.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
pDevice->alsa.pPCMCapture = (ma_ptr)pPCM;
|
|
pDevice->alsa.isUsingMMapCapture = isUsingMMap;
|
|
} else {
|
|
pDevice->alsa.pPCMPlayback = (ma_ptr)pPCM;
|
|
pDevice->alsa.isUsingMMapPlayback = isUsingMMap;
|
|
}
|
|
|
|
pDescriptor->format = internalFormat;
|
|
pDescriptor->channels = internalChannels;
|
|
pDescriptor->sampleRate = internalSampleRate;
|
|
ma_channel_map_copy(pDescriptor->channelMap, internalChannelMap, ma_min(internalChannels, MA_MAX_CHANNELS));
|
|
pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
|
|
pDescriptor->periodCount = internalPeriods;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__alsa(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->alsa);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_by_type__alsa(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__alsa(ma_device* pDevice)
|
|
{
|
|
int resultALSA;
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start capture device.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
|
|
resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start playback device.");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__alsa(ma_device* pDevice)
|
|
{
|
|
int resultPoll;
|
|
int resultRead;
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n");
|
|
((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device successful.\n");
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device...\n");
|
|
if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture) < 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device failed.\n");
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n");
|
|
}
|
|
|
|
resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0);
|
|
if (resultPoll > 0) {
|
|
ma_uint64 t;
|
|
resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t));
|
|
if (resultRead != sizeof(t)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from capture wakeupfd. read() = %d\n", resultRead);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device...\n");
|
|
((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping playback device successful.\n");
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device...\n");
|
|
if (((ma_snd_pcm_prepare_proc)pDevice->pContext->alsa.snd_pcm_prepare)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback) < 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device failed.\n");
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n");
|
|
}
|
|
|
|
resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0);
|
|
if (resultPoll > 0) {
|
|
ma_uint64 t;
|
|
resultRead = read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t));
|
|
if (resultRead != sizeof(t)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to read from playback wakeupfd. read() = %d\n", resultRead);
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, struct pollfd* pPollDescriptors, int pollDescriptorCount, short requiredEvent)
|
|
{
|
|
for (;;) {
|
|
unsigned short revents;
|
|
int resultALSA;
|
|
int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1);
|
|
if (resultPoll < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] poll() failed.\n");
|
|
|
|
continue;
|
|
}
|
|
|
|
if ((pPollDescriptors[0].revents & POLLIN) != 0) {
|
|
ma_uint64 t;
|
|
int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t));
|
|
if (resultRead < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] POLLIN set for wakeupfd\n");
|
|
return MA_DEVICE_NOT_STARTED;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n");
|
|
return ma_result_from_errno(-resultALSA);
|
|
}
|
|
|
|
if ((revents & POLLERR) != 0) {
|
|
ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM);
|
|
if (state == MA_SND_PCM_STATE_XRUN) {
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM));
|
|
}
|
|
}
|
|
|
|
if ((revents & requiredEvent) == requiredEvent) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_wait_read__alsa(ma_device* pDevice)
|
|
{
|
|
return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, (struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, pDevice->alsa.pollDescriptorCountCapture + 1, POLLIN);
|
|
}
|
|
|
|
static ma_result ma_device_wait_write__alsa(ma_device* pDevice)
|
|
{
|
|
return ma_device_wait__alsa(pDevice, (ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, (struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, pDevice->alsa.pollDescriptorCountPlayback + 1, POLLOUT);
|
|
}
|
|
|
|
static ma_result ma_device_read__alsa(ma_device* pDevice, void* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
ma_snd_pcm_sframes_t resultALSA = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
ma_result result;
|
|
|
|
result = ma_device_wait_read__alsa(pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_readi_proc)pDevice->pContext->alsa.snd_pcm_readi)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, pFramesOut, frameCount);
|
|
if (resultALSA >= 0) {
|
|
break;
|
|
} else {
|
|
if (resultALSA == -EAGAIN) {
|
|
continue;
|
|
} else if (resultALSA == -EPIPE) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (read)\n");
|
|
|
|
resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture, resultALSA, MA_TRUE);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after overrun.");
|
|
return ma_result_from_errno((int)-resultALSA);
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
|
|
return ma_result_from_errno((int)-resultALSA);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = resultALSA;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_write__alsa(ma_device* pDevice, const void* pFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
ma_snd_pcm_sframes_t resultALSA = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pFrames != NULL);
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
ma_result result;
|
|
|
|
result = ma_device_wait_write__alsa(pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_writei_proc)pDevice->pContext->alsa.snd_pcm_writei)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, pFrames, frameCount);
|
|
if (resultALSA >= 0) {
|
|
break;
|
|
} else {
|
|
if (resultALSA == -EAGAIN) {
|
|
continue;
|
|
} else if (resultALSA == -EPIPE) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "EPIPE (write)\n");
|
|
|
|
resultALSA = ((ma_snd_pcm_recover_proc)pDevice->pContext->alsa.snd_pcm_recover)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback, resultALSA, MA_TRUE);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to recover device after underrun.");
|
|
return ma_result_from_errno((int)-resultALSA);
|
|
}
|
|
|
|
resultALSA = ((ma_snd_pcm_start_proc)pDevice->pContext->alsa.snd_pcm_start)((ma_snd_pcm_t*)pDevice->alsa.pPCMPlayback);
|
|
if (resultALSA < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] Failed to start device after underrun.");
|
|
return ma_result_from_errno((int)-resultALSA);
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = resultALSA;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_data_loop_wakeup__alsa(ma_device* pDevice)
|
|
{
|
|
ma_uint64 t = 1;
|
|
int resultWrite = 0;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up...\n");
|
|
|
|
if (pDevice->alsa.pPollDescriptorsCapture != NULL) {
|
|
resultWrite = write(pDevice->alsa.wakeupfdCapture, &t, sizeof(t));
|
|
}
|
|
if (pDevice->alsa.pPollDescriptorsPlayback != NULL) {
|
|
resultWrite = write(pDevice->alsa.wakeupfdPlayback, &t, sizeof(t));
|
|
}
|
|
|
|
if (resultWrite < 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] write() failed.\n");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Waking up completed successfully.\n");
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__alsa(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_alsa);
|
|
|
|
((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)();
|
|
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO);
|
|
#endif
|
|
|
|
ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
ma_result result;
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
const char* libasoundNames[] = {
|
|
"libasound.so.2",
|
|
"libasound.so"
|
|
};
|
|
size_t i;
|
|
|
|
for (i = 0; i < ma_countof(libasoundNames); ++i) {
|
|
pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]);
|
|
if (pContext->alsa.asoundSO != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pContext->alsa.asoundSO == NULL) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "[ALSA] Failed to open shared object.\n");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open");
|
|
pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close");
|
|
pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof");
|
|
pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any");
|
|
pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format");
|
|
pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first");
|
|
pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask");
|
|
pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels");
|
|
pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near");
|
|
pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax");
|
|
pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample");
|
|
pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate");
|
|
pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near");
|
|
pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near");
|
|
pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near");
|
|
pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access");
|
|
pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format");
|
|
pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels");
|
|
pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min");
|
|
pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max");
|
|
pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate");
|
|
pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min");
|
|
pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max");
|
|
pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size");
|
|
pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods");
|
|
pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access");
|
|
pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format");
|
|
pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels");
|
|
pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate");
|
|
pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params");
|
|
pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof");
|
|
pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current");
|
|
pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary");
|
|
pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min");
|
|
pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold");
|
|
pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold");
|
|
pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params");
|
|
pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof");
|
|
pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test");
|
|
pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap");
|
|
pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state");
|
|
pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare");
|
|
pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start");
|
|
pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop");
|
|
pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain");
|
|
pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset");
|
|
pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint");
|
|
pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint");
|
|
pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index");
|
|
pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint");
|
|
pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin");
|
|
pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit");
|
|
pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover");
|
|
pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi");
|
|
pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei");
|
|
pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail");
|
|
pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update");
|
|
pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait");
|
|
pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock");
|
|
pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info");
|
|
pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof");
|
|
pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name");
|
|
pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors");
|
|
pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count");
|
|
pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents");
|
|
pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global");
|
|
#else
|
|
ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open;
|
|
ma_snd_pcm_close_proc _snd_pcm_close = snd_pcm_close;
|
|
ma_snd_pcm_hw_params_sizeof_proc _snd_pcm_hw_params_sizeof = snd_pcm_hw_params_sizeof;
|
|
ma_snd_pcm_hw_params_any_proc _snd_pcm_hw_params_any = snd_pcm_hw_params_any;
|
|
ma_snd_pcm_hw_params_set_format_proc _snd_pcm_hw_params_set_format = snd_pcm_hw_params_set_format;
|
|
ma_snd_pcm_hw_params_set_format_first_proc _snd_pcm_hw_params_set_format_first = snd_pcm_hw_params_set_format_first;
|
|
ma_snd_pcm_hw_params_get_format_mask_proc _snd_pcm_hw_params_get_format_mask = snd_pcm_hw_params_get_format_mask;
|
|
ma_snd_pcm_hw_params_set_channels_proc _snd_pcm_hw_params_set_channels = snd_pcm_hw_params_set_channels;
|
|
ma_snd_pcm_hw_params_set_channels_near_proc _snd_pcm_hw_params_set_channels_near = snd_pcm_hw_params_set_channels_near;
|
|
ma_snd_pcm_hw_params_set_channels_minmax_proc _snd_pcm_hw_params_set_channels_minmax = snd_pcm_hw_params_set_channels_minmax;
|
|
ma_snd_pcm_hw_params_set_rate_resample_proc _snd_pcm_hw_params_set_rate_resample = snd_pcm_hw_params_set_rate_resample;
|
|
ma_snd_pcm_hw_params_set_rate_proc _snd_pcm_hw_params_set_rate = snd_pcm_hw_params_set_rate;
|
|
ma_snd_pcm_hw_params_set_rate_near_proc _snd_pcm_hw_params_set_rate_near = snd_pcm_hw_params_set_rate_near;
|
|
ma_snd_pcm_hw_params_set_rate_minmax_proc _snd_pcm_hw_params_set_rate_minmax = snd_pcm_hw_params_set_rate_minmax;
|
|
ma_snd_pcm_hw_params_set_buffer_size_near_proc _snd_pcm_hw_params_set_buffer_size_near = snd_pcm_hw_params_set_buffer_size_near;
|
|
ma_snd_pcm_hw_params_set_periods_near_proc _snd_pcm_hw_params_set_periods_near = snd_pcm_hw_params_set_periods_near;
|
|
ma_snd_pcm_hw_params_set_access_proc _snd_pcm_hw_params_set_access = snd_pcm_hw_params_set_access;
|
|
ma_snd_pcm_hw_params_get_format_proc _snd_pcm_hw_params_get_format = snd_pcm_hw_params_get_format;
|
|
ma_snd_pcm_hw_params_get_channels_proc _snd_pcm_hw_params_get_channels = snd_pcm_hw_params_get_channels;
|
|
ma_snd_pcm_hw_params_get_channels_min_proc _snd_pcm_hw_params_get_channels_min = snd_pcm_hw_params_get_channels_min;
|
|
ma_snd_pcm_hw_params_get_channels_max_proc _snd_pcm_hw_params_get_channels_max = snd_pcm_hw_params_get_channels_max;
|
|
ma_snd_pcm_hw_params_get_rate_proc _snd_pcm_hw_params_get_rate = snd_pcm_hw_params_get_rate;
|
|
ma_snd_pcm_hw_params_get_rate_min_proc _snd_pcm_hw_params_get_rate_min = snd_pcm_hw_params_get_rate_min;
|
|
ma_snd_pcm_hw_params_get_rate_max_proc _snd_pcm_hw_params_get_rate_max = snd_pcm_hw_params_get_rate_max;
|
|
ma_snd_pcm_hw_params_get_buffer_size_proc _snd_pcm_hw_params_get_buffer_size = snd_pcm_hw_params_get_buffer_size;
|
|
ma_snd_pcm_hw_params_get_periods_proc _snd_pcm_hw_params_get_periods = snd_pcm_hw_params_get_periods;
|
|
ma_snd_pcm_hw_params_get_access_proc _snd_pcm_hw_params_get_access = snd_pcm_hw_params_get_access;
|
|
ma_snd_pcm_hw_params_test_format_proc _snd_pcm_hw_params_test_format = snd_pcm_hw_params_test_format;
|
|
ma_snd_pcm_hw_params_test_channels_proc _snd_pcm_hw_params_test_channels = snd_pcm_hw_params_test_channels;
|
|
ma_snd_pcm_hw_params_test_rate_proc _snd_pcm_hw_params_test_rate = snd_pcm_hw_params_test_rate;
|
|
ma_snd_pcm_hw_params_proc _snd_pcm_hw_params = snd_pcm_hw_params;
|
|
ma_snd_pcm_sw_params_sizeof_proc _snd_pcm_sw_params_sizeof = snd_pcm_sw_params_sizeof;
|
|
ma_snd_pcm_sw_params_current_proc _snd_pcm_sw_params_current = snd_pcm_sw_params_current;
|
|
ma_snd_pcm_sw_params_get_boundary_proc _snd_pcm_sw_params_get_boundary = snd_pcm_sw_params_get_boundary;
|
|
ma_snd_pcm_sw_params_set_avail_min_proc _snd_pcm_sw_params_set_avail_min = snd_pcm_sw_params_set_avail_min;
|
|
ma_snd_pcm_sw_params_set_start_threshold_proc _snd_pcm_sw_params_set_start_threshold = snd_pcm_sw_params_set_start_threshold;
|
|
ma_snd_pcm_sw_params_set_stop_threshold_proc _snd_pcm_sw_params_set_stop_threshold = snd_pcm_sw_params_set_stop_threshold;
|
|
ma_snd_pcm_sw_params_proc _snd_pcm_sw_params = snd_pcm_sw_params;
|
|
ma_snd_pcm_format_mask_sizeof_proc _snd_pcm_format_mask_sizeof = snd_pcm_format_mask_sizeof;
|
|
ma_snd_pcm_format_mask_test_proc _snd_pcm_format_mask_test = snd_pcm_format_mask_test;
|
|
ma_snd_pcm_get_chmap_proc _snd_pcm_get_chmap = snd_pcm_get_chmap;
|
|
ma_snd_pcm_state_proc _snd_pcm_state = snd_pcm_state;
|
|
ma_snd_pcm_prepare_proc _snd_pcm_prepare = snd_pcm_prepare;
|
|
ma_snd_pcm_start_proc _snd_pcm_start = snd_pcm_start;
|
|
ma_snd_pcm_drop_proc _snd_pcm_drop = snd_pcm_drop;
|
|
ma_snd_pcm_drain_proc _snd_pcm_drain = snd_pcm_drain;
|
|
ma_snd_pcm_reset_proc _snd_pcm_reset = snd_pcm_reset;
|
|
ma_snd_device_name_hint_proc _snd_device_name_hint = snd_device_name_hint;
|
|
ma_snd_device_name_get_hint_proc _snd_device_name_get_hint = snd_device_name_get_hint;
|
|
ma_snd_card_get_index_proc _snd_card_get_index = snd_card_get_index;
|
|
ma_snd_device_name_free_hint_proc _snd_device_name_free_hint = snd_device_name_free_hint;
|
|
ma_snd_pcm_mmap_begin_proc _snd_pcm_mmap_begin = snd_pcm_mmap_begin;
|
|
ma_snd_pcm_mmap_commit_proc _snd_pcm_mmap_commit = snd_pcm_mmap_commit;
|
|
ma_snd_pcm_recover_proc _snd_pcm_recover = snd_pcm_recover;
|
|
ma_snd_pcm_readi_proc _snd_pcm_readi = snd_pcm_readi;
|
|
ma_snd_pcm_writei_proc _snd_pcm_writei = snd_pcm_writei;
|
|
ma_snd_pcm_avail_proc _snd_pcm_avail = snd_pcm_avail;
|
|
ma_snd_pcm_avail_update_proc _snd_pcm_avail_update = snd_pcm_avail_update;
|
|
ma_snd_pcm_wait_proc _snd_pcm_wait = snd_pcm_wait;
|
|
ma_snd_pcm_nonblock_proc _snd_pcm_nonblock = snd_pcm_nonblock;
|
|
ma_snd_pcm_info_proc _snd_pcm_info = snd_pcm_info;
|
|
ma_snd_pcm_info_sizeof_proc _snd_pcm_info_sizeof = snd_pcm_info_sizeof;
|
|
ma_snd_pcm_info_get_name_proc _snd_pcm_info_get_name = snd_pcm_info_get_name;
|
|
ma_snd_pcm_poll_descriptors_proc _snd_pcm_poll_descriptors = snd_pcm_poll_descriptors;
|
|
ma_snd_pcm_poll_descriptors_count_proc _snd_pcm_poll_descriptors_count = snd_pcm_poll_descriptors_count;
|
|
ma_snd_pcm_poll_descriptors_revents_proc _snd_pcm_poll_descriptors_revents = snd_pcm_poll_descriptors_revents;
|
|
ma_snd_config_update_free_global_proc _snd_config_update_free_global = snd_config_update_free_global;
|
|
|
|
pContext->alsa.snd_pcm_open = (ma_proc)_snd_pcm_open;
|
|
pContext->alsa.snd_pcm_close = (ma_proc)_snd_pcm_close;
|
|
pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)_snd_pcm_hw_params_sizeof;
|
|
pContext->alsa.snd_pcm_hw_params_any = (ma_proc)_snd_pcm_hw_params_any;
|
|
pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)_snd_pcm_hw_params_set_format;
|
|
pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)_snd_pcm_hw_params_set_format_first;
|
|
pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)_snd_pcm_hw_params_get_format_mask;
|
|
pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)_snd_pcm_hw_params_set_channels;
|
|
pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)_snd_pcm_hw_params_set_channels_near;
|
|
pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)_snd_pcm_hw_params_set_channels_minmax;
|
|
pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)_snd_pcm_hw_params_set_rate_resample;
|
|
pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)_snd_pcm_hw_params_set_rate;
|
|
pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)_snd_pcm_hw_params_set_rate_near;
|
|
pContext->alsa.snd_pcm_hw_params_set_rate_minmax = (ma_proc)_snd_pcm_hw_params_set_rate_minmax;
|
|
pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)_snd_pcm_hw_params_set_buffer_size_near;
|
|
pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)_snd_pcm_hw_params_set_periods_near;
|
|
pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)_snd_pcm_hw_params_set_access;
|
|
pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)_snd_pcm_hw_params_get_format;
|
|
pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)_snd_pcm_hw_params_get_channels;
|
|
pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)_snd_pcm_hw_params_get_channels_min;
|
|
pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)_snd_pcm_hw_params_get_channels_max;
|
|
pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)_snd_pcm_hw_params_get_rate;
|
|
pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)_snd_pcm_hw_params_get_rate_min;
|
|
pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)_snd_pcm_hw_params_get_rate_max;
|
|
pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)_snd_pcm_hw_params_get_buffer_size;
|
|
pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)_snd_pcm_hw_params_get_periods;
|
|
pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)_snd_pcm_hw_params_get_access;
|
|
pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)_snd_pcm_hw_params_test_format;
|
|
pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)_snd_pcm_hw_params_test_channels;
|
|
pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)_snd_pcm_hw_params_test_rate;
|
|
pContext->alsa.snd_pcm_hw_params = (ma_proc)_snd_pcm_hw_params;
|
|
pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)_snd_pcm_sw_params_sizeof;
|
|
pContext->alsa.snd_pcm_sw_params_current = (ma_proc)_snd_pcm_sw_params_current;
|
|
pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)_snd_pcm_sw_params_get_boundary;
|
|
pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)_snd_pcm_sw_params_set_avail_min;
|
|
pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)_snd_pcm_sw_params_set_start_threshold;
|
|
pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)_snd_pcm_sw_params_set_stop_threshold;
|
|
pContext->alsa.snd_pcm_sw_params = (ma_proc)_snd_pcm_sw_params;
|
|
pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)_snd_pcm_format_mask_sizeof;
|
|
pContext->alsa.snd_pcm_format_mask_test = (ma_proc)_snd_pcm_format_mask_test;
|
|
pContext->alsa.snd_pcm_get_chmap = (ma_proc)_snd_pcm_get_chmap;
|
|
pContext->alsa.snd_pcm_state = (ma_proc)_snd_pcm_state;
|
|
pContext->alsa.snd_pcm_prepare = (ma_proc)_snd_pcm_prepare;
|
|
pContext->alsa.snd_pcm_start = (ma_proc)_snd_pcm_start;
|
|
pContext->alsa.snd_pcm_drop = (ma_proc)_snd_pcm_drop;
|
|
pContext->alsa.snd_pcm_drain = (ma_proc)_snd_pcm_drain;
|
|
pContext->alsa.snd_pcm_reset = (ma_proc)_snd_pcm_reset;
|
|
pContext->alsa.snd_device_name_hint = (ma_proc)_snd_device_name_hint;
|
|
pContext->alsa.snd_device_name_get_hint = (ma_proc)_snd_device_name_get_hint;
|
|
pContext->alsa.snd_card_get_index = (ma_proc)_snd_card_get_index;
|
|
pContext->alsa.snd_device_name_free_hint = (ma_proc)_snd_device_name_free_hint;
|
|
pContext->alsa.snd_pcm_mmap_begin = (ma_proc)_snd_pcm_mmap_begin;
|
|
pContext->alsa.snd_pcm_mmap_commit = (ma_proc)_snd_pcm_mmap_commit;
|
|
pContext->alsa.snd_pcm_recover = (ma_proc)_snd_pcm_recover;
|
|
pContext->alsa.snd_pcm_readi = (ma_proc)_snd_pcm_readi;
|
|
pContext->alsa.snd_pcm_writei = (ma_proc)_snd_pcm_writei;
|
|
pContext->alsa.snd_pcm_avail = (ma_proc)_snd_pcm_avail;
|
|
pContext->alsa.snd_pcm_avail_update = (ma_proc)_snd_pcm_avail_update;
|
|
pContext->alsa.snd_pcm_wait = (ma_proc)_snd_pcm_wait;
|
|
pContext->alsa.snd_pcm_nonblock = (ma_proc)_snd_pcm_nonblock;
|
|
pContext->alsa.snd_pcm_info = (ma_proc)_snd_pcm_info;
|
|
pContext->alsa.snd_pcm_info_sizeof = (ma_proc)_snd_pcm_info_sizeof;
|
|
pContext->alsa.snd_pcm_info_get_name = (ma_proc)_snd_pcm_info_get_name;
|
|
pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)_snd_pcm_poll_descriptors;
|
|
pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)_snd_pcm_poll_descriptors_count;
|
|
pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)_snd_pcm_poll_descriptors_revents;
|
|
pContext->alsa.snd_config_update_free_global = (ma_proc)_snd_config_update_free_global;
|
|
#endif
|
|
|
|
pContext->alsa.useVerboseDeviceEnumeration = pConfig->alsa.useVerboseDeviceEnumeration;
|
|
|
|
result = ma_mutex_init(&pContext->alsa.internalDeviceEnumLock);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[ALSA] WARNING: Failed to initialize mutex for internal device enumeration.");
|
|
return result;
|
|
}
|
|
|
|
pCallbacks->onContextInit = ma_context_init__alsa;
|
|
pCallbacks->onContextUninit = ma_context_uninit__alsa;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__alsa;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__alsa;
|
|
pCallbacks->onDeviceInit = ma_device_init__alsa;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__alsa;
|
|
pCallbacks->onDeviceStart = ma_device_start__alsa;
|
|
pCallbacks->onDeviceStop = ma_device_stop__alsa;
|
|
pCallbacks->onDeviceRead = ma_device_read__alsa;
|
|
pCallbacks->onDeviceWrite = ma_device_write__alsa;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__alsa;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_PULSEAUDIO
|
|
|
|
#ifdef MA_NO_RUNTIME_LINKING
|
|
|
|
#if !defined(__cplusplus)
|
|
#if defined(__STRICT_ANSI__)
|
|
#if !defined(inline)
|
|
#define inline __inline__ __attribute__((always_inline))
|
|
#define MA_INLINE_DEFINED
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#include <pulse/pulseaudio.h>
|
|
#if defined(MA_INLINE_DEFINED)
|
|
#undef inline
|
|
#undef MA_INLINE_DEFINED
|
|
#endif
|
|
|
|
#define MA_PA_OK PA_OK
|
|
#define MA_PA_ERR_ACCESS PA_ERR_ACCESS
|
|
#define MA_PA_ERR_INVALID PA_ERR_INVALID
|
|
#define MA_PA_ERR_NOENTITY PA_ERR_NOENTITY
|
|
#define MA_PA_ERR_NOTSUPPORTED PA_ERR_NOTSUPPORTED
|
|
|
|
#define MA_PA_CHANNELS_MAX PA_CHANNELS_MAX
|
|
#define MA_PA_RATE_MAX PA_RATE_MAX
|
|
|
|
typedef pa_context_flags_t ma_pa_context_flags_t;
|
|
#define MA_PA_CONTEXT_NOFLAGS PA_CONTEXT_NOFLAGS
|
|
#define MA_PA_CONTEXT_NOAUTOSPAWN PA_CONTEXT_NOAUTOSPAWN
|
|
#define MA_PA_CONTEXT_NOFAIL PA_CONTEXT_NOFAIL
|
|
|
|
typedef pa_stream_flags_t ma_pa_stream_flags_t;
|
|
#define MA_PA_STREAM_NOFLAGS PA_STREAM_NOFLAGS
|
|
#define MA_PA_STREAM_START_CORKED PA_STREAM_START_CORKED
|
|
#define MA_PA_STREAM_INTERPOLATE_TIMING PA_STREAM_INTERPOLATE_TIMING
|
|
#define MA_PA_STREAM_NOT_MONOTONIC PA_STREAM_NOT_MONOTONIC
|
|
#define MA_PA_STREAM_AUTO_TIMING_UPDATE PA_STREAM_AUTO_TIMING_UPDATE
|
|
#define MA_PA_STREAM_NO_REMAP_CHANNELS PA_STREAM_NO_REMAP_CHANNELS
|
|
#define MA_PA_STREAM_NO_REMIX_CHANNELS PA_STREAM_NO_REMIX_CHANNELS
|
|
#define MA_PA_STREAM_FIX_FORMAT PA_STREAM_FIX_FORMAT
|
|
#define MA_PA_STREAM_FIX_RATE PA_STREAM_FIX_RATE
|
|
#define MA_PA_STREAM_FIX_CHANNELS PA_STREAM_FIX_CHANNELS
|
|
#define MA_PA_STREAM_DONT_MOVE PA_STREAM_DONT_MOVE
|
|
#define MA_PA_STREAM_VARIABLE_RATE PA_STREAM_VARIABLE_RATE
|
|
#define MA_PA_STREAM_PEAK_DETECT PA_STREAM_PEAK_DETECT
|
|
#define MA_PA_STREAM_START_MUTED PA_STREAM_START_MUTED
|
|
#define MA_PA_STREAM_ADJUST_LATENCY PA_STREAM_ADJUST_LATENCY
|
|
#define MA_PA_STREAM_EARLY_REQUESTS PA_STREAM_EARLY_REQUESTS
|
|
#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND
|
|
#define MA_PA_STREAM_START_UNMUTED PA_STREAM_START_UNMUTED
|
|
#define MA_PA_STREAM_FAIL_ON_SUSPEND PA_STREAM_FAIL_ON_SUSPEND
|
|
#define MA_PA_STREAM_RELATIVE_VOLUME PA_STREAM_RELATIVE_VOLUME
|
|
#define MA_PA_STREAM_PASSTHROUGH PA_STREAM_PASSTHROUGH
|
|
|
|
typedef pa_sink_flags_t ma_pa_sink_flags_t;
|
|
#define MA_PA_SINK_NOFLAGS PA_SINK_NOFLAGS
|
|
#define MA_PA_SINK_HW_VOLUME_CTRL PA_SINK_HW_VOLUME_CTRL
|
|
#define MA_PA_SINK_LATENCY PA_SINK_LATENCY
|
|
#define MA_PA_SINK_HARDWARE PA_SINK_HARDWARE
|
|
#define MA_PA_SINK_NETWORK PA_SINK_NETWORK
|
|
#define MA_PA_SINK_HW_MUTE_CTRL PA_SINK_HW_MUTE_CTRL
|
|
#define MA_PA_SINK_DECIBEL_VOLUME PA_SINK_DECIBEL_VOLUME
|
|
#define MA_PA_SINK_FLAT_VOLUME PA_SINK_FLAT_VOLUME
|
|
#define MA_PA_SINK_DYNAMIC_LATENCY PA_SINK_DYNAMIC_LATENCY
|
|
#define MA_PA_SINK_SET_FORMATS PA_SINK_SET_FORMATS
|
|
|
|
typedef pa_source_flags_t ma_pa_source_flags_t;
|
|
#define MA_PA_SOURCE_NOFLAGS PA_SOURCE_NOFLAGS
|
|
#define MA_PA_SOURCE_HW_VOLUME_CTRL PA_SOURCE_HW_VOLUME_CTRL
|
|
#define MA_PA_SOURCE_LATENCY PA_SOURCE_LATENCY
|
|
#define MA_PA_SOURCE_HARDWARE PA_SOURCE_HARDWARE
|
|
#define MA_PA_SOURCE_NETWORK PA_SOURCE_NETWORK
|
|
#define MA_PA_SOURCE_HW_MUTE_CTRL PA_SOURCE_HW_MUTE_CTRL
|
|
#define MA_PA_SOURCE_DECIBEL_VOLUME PA_SOURCE_DECIBEL_VOLUME
|
|
#define MA_PA_SOURCE_DYNAMIC_LATENCY PA_SOURCE_DYNAMIC_LATENCY
|
|
#define MA_PA_SOURCE_FLAT_VOLUME PA_SOURCE_FLAT_VOLUME
|
|
|
|
typedef pa_context_state_t ma_pa_context_state_t;
|
|
#define MA_PA_CONTEXT_UNCONNECTED PA_CONTEXT_UNCONNECTED
|
|
#define MA_PA_CONTEXT_CONNECTING PA_CONTEXT_CONNECTING
|
|
#define MA_PA_CONTEXT_AUTHORIZING PA_CONTEXT_AUTHORIZING
|
|
#define MA_PA_CONTEXT_SETTING_NAME PA_CONTEXT_SETTING_NAME
|
|
#define MA_PA_CONTEXT_READY PA_CONTEXT_READY
|
|
#define MA_PA_CONTEXT_FAILED PA_CONTEXT_FAILED
|
|
#define MA_PA_CONTEXT_TERMINATED PA_CONTEXT_TERMINATED
|
|
|
|
typedef pa_stream_state_t ma_pa_stream_state_t;
|
|
#define MA_PA_STREAM_UNCONNECTED PA_STREAM_UNCONNECTED
|
|
#define MA_PA_STREAM_CREATING PA_STREAM_CREATING
|
|
#define MA_PA_STREAM_READY PA_STREAM_READY
|
|
#define MA_PA_STREAM_FAILED PA_STREAM_FAILED
|
|
#define MA_PA_STREAM_TERMINATED PA_STREAM_TERMINATED
|
|
|
|
typedef pa_operation_state_t ma_pa_operation_state_t;
|
|
#define MA_PA_OPERATION_RUNNING PA_OPERATION_RUNNING
|
|
#define MA_PA_OPERATION_DONE PA_OPERATION_DONE
|
|
#define MA_PA_OPERATION_CANCELLED PA_OPERATION_CANCELLED
|
|
|
|
typedef pa_sink_state_t ma_pa_sink_state_t;
|
|
#define MA_PA_SINK_INVALID_STATE PA_SINK_INVALID_STATE
|
|
#define MA_PA_SINK_RUNNING PA_SINK_RUNNING
|
|
#define MA_PA_SINK_IDLE PA_SINK_IDLE
|
|
#define MA_PA_SINK_SUSPENDED PA_SINK_SUSPENDED
|
|
|
|
typedef pa_source_state_t ma_pa_source_state_t;
|
|
#define MA_PA_SOURCE_INVALID_STATE PA_SOURCE_INVALID_STATE
|
|
#define MA_PA_SOURCE_RUNNING PA_SOURCE_RUNNING
|
|
#define MA_PA_SOURCE_IDLE PA_SOURCE_IDLE
|
|
#define MA_PA_SOURCE_SUSPENDED PA_SOURCE_SUSPENDED
|
|
|
|
typedef pa_seek_mode_t ma_pa_seek_mode_t;
|
|
#define MA_PA_SEEK_RELATIVE PA_SEEK_RELATIVE
|
|
#define MA_PA_SEEK_ABSOLUTE PA_SEEK_ABSOLUTE
|
|
#define MA_PA_SEEK_RELATIVE_ON_READ PA_SEEK_RELATIVE_ON_READ
|
|
#define MA_PA_SEEK_RELATIVE_END PA_SEEK_RELATIVE_END
|
|
|
|
typedef pa_channel_position_t ma_pa_channel_position_t;
|
|
#define MA_PA_CHANNEL_POSITION_INVALID PA_CHANNEL_POSITION_INVALID
|
|
#define MA_PA_CHANNEL_POSITION_MONO PA_CHANNEL_POSITION_MONO
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_LEFT PA_CHANNEL_POSITION_FRONT_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT PA_CHANNEL_POSITION_FRONT_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_CENTER PA_CHANNEL_POSITION_FRONT_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_REAR_CENTER PA_CHANNEL_POSITION_REAR_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_REAR_LEFT PA_CHANNEL_POSITION_REAR_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_REAR_RIGHT PA_CHANNEL_POSITION_REAR_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_LFE PA_CHANNEL_POSITION_LFE
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_SIDE_LEFT PA_CHANNEL_POSITION_SIDE_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT PA_CHANNEL_POSITION_SIDE_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_AUX0 PA_CHANNEL_POSITION_AUX0
|
|
#define MA_PA_CHANNEL_POSITION_AUX1 PA_CHANNEL_POSITION_AUX1
|
|
#define MA_PA_CHANNEL_POSITION_AUX2 PA_CHANNEL_POSITION_AUX2
|
|
#define MA_PA_CHANNEL_POSITION_AUX3 PA_CHANNEL_POSITION_AUX3
|
|
#define MA_PA_CHANNEL_POSITION_AUX4 PA_CHANNEL_POSITION_AUX4
|
|
#define MA_PA_CHANNEL_POSITION_AUX5 PA_CHANNEL_POSITION_AUX5
|
|
#define MA_PA_CHANNEL_POSITION_AUX6 PA_CHANNEL_POSITION_AUX6
|
|
#define MA_PA_CHANNEL_POSITION_AUX7 PA_CHANNEL_POSITION_AUX7
|
|
#define MA_PA_CHANNEL_POSITION_AUX8 PA_CHANNEL_POSITION_AUX8
|
|
#define MA_PA_CHANNEL_POSITION_AUX9 PA_CHANNEL_POSITION_AUX9
|
|
#define MA_PA_CHANNEL_POSITION_AUX10 PA_CHANNEL_POSITION_AUX10
|
|
#define MA_PA_CHANNEL_POSITION_AUX11 PA_CHANNEL_POSITION_AUX11
|
|
#define MA_PA_CHANNEL_POSITION_AUX12 PA_CHANNEL_POSITION_AUX12
|
|
#define MA_PA_CHANNEL_POSITION_AUX13 PA_CHANNEL_POSITION_AUX13
|
|
#define MA_PA_CHANNEL_POSITION_AUX14 PA_CHANNEL_POSITION_AUX14
|
|
#define MA_PA_CHANNEL_POSITION_AUX15 PA_CHANNEL_POSITION_AUX15
|
|
#define MA_PA_CHANNEL_POSITION_AUX16 PA_CHANNEL_POSITION_AUX16
|
|
#define MA_PA_CHANNEL_POSITION_AUX17 PA_CHANNEL_POSITION_AUX17
|
|
#define MA_PA_CHANNEL_POSITION_AUX18 PA_CHANNEL_POSITION_AUX18
|
|
#define MA_PA_CHANNEL_POSITION_AUX19 PA_CHANNEL_POSITION_AUX19
|
|
#define MA_PA_CHANNEL_POSITION_AUX20 PA_CHANNEL_POSITION_AUX20
|
|
#define MA_PA_CHANNEL_POSITION_AUX21 PA_CHANNEL_POSITION_AUX21
|
|
#define MA_PA_CHANNEL_POSITION_AUX22 PA_CHANNEL_POSITION_AUX22
|
|
#define MA_PA_CHANNEL_POSITION_AUX23 PA_CHANNEL_POSITION_AUX23
|
|
#define MA_PA_CHANNEL_POSITION_AUX24 PA_CHANNEL_POSITION_AUX24
|
|
#define MA_PA_CHANNEL_POSITION_AUX25 PA_CHANNEL_POSITION_AUX25
|
|
#define MA_PA_CHANNEL_POSITION_AUX26 PA_CHANNEL_POSITION_AUX26
|
|
#define MA_PA_CHANNEL_POSITION_AUX27 PA_CHANNEL_POSITION_AUX27
|
|
#define MA_PA_CHANNEL_POSITION_AUX28 PA_CHANNEL_POSITION_AUX28
|
|
#define MA_PA_CHANNEL_POSITION_AUX29 PA_CHANNEL_POSITION_AUX29
|
|
#define MA_PA_CHANNEL_POSITION_AUX30 PA_CHANNEL_POSITION_AUX30
|
|
#define MA_PA_CHANNEL_POSITION_AUX31 PA_CHANNEL_POSITION_AUX31
|
|
#define MA_PA_CHANNEL_POSITION_TOP_CENTER PA_CHANNEL_POSITION_TOP_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT PA_CHANNEL_POSITION_TOP_FRONT_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT PA_CHANNEL_POSITION_TOP_FRONT_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER PA_CHANNEL_POSITION_TOP_FRONT_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT PA_CHANNEL_POSITION_TOP_REAR_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT PA_CHANNEL_POSITION_TOP_REAR_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER PA_CHANNEL_POSITION_TOP_REAR_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_LEFT PA_CHANNEL_POSITION_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_RIGHT PA_CHANNEL_POSITION_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_CENTER PA_CHANNEL_POSITION_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_SUBWOOFER PA_CHANNEL_POSITION_SUBWOOFER
|
|
|
|
typedef pa_channel_map_def_t ma_pa_channel_map_def_t;
|
|
#define MA_PA_CHANNEL_MAP_AIFF PA_CHANNEL_MAP_AIFF
|
|
#define MA_PA_CHANNEL_MAP_ALSA PA_CHANNEL_MAP_ALSA
|
|
#define MA_PA_CHANNEL_MAP_AUX PA_CHANNEL_MAP_AUX
|
|
#define MA_PA_CHANNEL_MAP_WAVEEX PA_CHANNEL_MAP_WAVEEX
|
|
#define MA_PA_CHANNEL_MAP_OSS PA_CHANNEL_MAP_OSS
|
|
#define MA_PA_CHANNEL_MAP_DEFAULT PA_CHANNEL_MAP_DEFAULT
|
|
|
|
typedef pa_sample_format_t ma_pa_sample_format_t;
|
|
#define MA_PA_SAMPLE_INVALID PA_SAMPLE_INVALID
|
|
#define MA_PA_SAMPLE_U8 PA_SAMPLE_U8
|
|
#define MA_PA_SAMPLE_ALAW PA_SAMPLE_ALAW
|
|
#define MA_PA_SAMPLE_ULAW PA_SAMPLE_ULAW
|
|
#define MA_PA_SAMPLE_S16LE PA_SAMPLE_S16LE
|
|
#define MA_PA_SAMPLE_S16BE PA_SAMPLE_S16BE
|
|
#define MA_PA_SAMPLE_FLOAT32LE PA_SAMPLE_FLOAT32LE
|
|
#define MA_PA_SAMPLE_FLOAT32BE PA_SAMPLE_FLOAT32BE
|
|
#define MA_PA_SAMPLE_S32LE PA_SAMPLE_S32LE
|
|
#define MA_PA_SAMPLE_S32BE PA_SAMPLE_S32BE
|
|
#define MA_PA_SAMPLE_S24LE PA_SAMPLE_S24LE
|
|
#define MA_PA_SAMPLE_S24BE PA_SAMPLE_S24BE
|
|
#define MA_PA_SAMPLE_S24_32LE PA_SAMPLE_S24_32LE
|
|
#define MA_PA_SAMPLE_S24_32BE PA_SAMPLE_S24_32BE
|
|
|
|
typedef pa_mainloop ma_pa_mainloop;
|
|
typedef pa_threaded_mainloop ma_pa_threaded_mainloop;
|
|
typedef pa_mainloop_api ma_pa_mainloop_api;
|
|
typedef pa_context ma_pa_context;
|
|
typedef pa_operation ma_pa_operation;
|
|
typedef pa_stream ma_pa_stream;
|
|
typedef pa_spawn_api ma_pa_spawn_api;
|
|
typedef pa_buffer_attr ma_pa_buffer_attr;
|
|
typedef pa_channel_map ma_pa_channel_map;
|
|
typedef pa_cvolume ma_pa_cvolume;
|
|
typedef pa_sample_spec ma_pa_sample_spec;
|
|
typedef pa_sink_info ma_pa_sink_info;
|
|
typedef pa_source_info ma_pa_source_info;
|
|
|
|
typedef pa_context_notify_cb_t ma_pa_context_notify_cb_t;
|
|
typedef pa_sink_info_cb_t ma_pa_sink_info_cb_t;
|
|
typedef pa_source_info_cb_t ma_pa_source_info_cb_t;
|
|
typedef pa_stream_success_cb_t ma_pa_stream_success_cb_t;
|
|
typedef pa_stream_request_cb_t ma_pa_stream_request_cb_t;
|
|
typedef pa_stream_notify_cb_t ma_pa_stream_notify_cb_t;
|
|
typedef pa_free_cb_t ma_pa_free_cb_t;
|
|
#else
|
|
#define MA_PA_OK 0
|
|
#define MA_PA_ERR_ACCESS 1
|
|
#define MA_PA_ERR_INVALID 2
|
|
#define MA_PA_ERR_NOENTITY 5
|
|
#define MA_PA_ERR_NOTSUPPORTED 19
|
|
|
|
#define MA_PA_CHANNELS_MAX 32
|
|
#define MA_PA_RATE_MAX 384000
|
|
|
|
typedef int ma_pa_context_flags_t;
|
|
#define MA_PA_CONTEXT_NOFLAGS 0x00000000
|
|
#define MA_PA_CONTEXT_NOAUTOSPAWN 0x00000001
|
|
#define MA_PA_CONTEXT_NOFAIL 0x00000002
|
|
|
|
typedef int ma_pa_stream_flags_t;
|
|
#define MA_PA_STREAM_NOFLAGS 0x00000000
|
|
#define MA_PA_STREAM_START_CORKED 0x00000001
|
|
#define MA_PA_STREAM_INTERPOLATE_TIMING 0x00000002
|
|
#define MA_PA_STREAM_NOT_MONOTONIC 0x00000004
|
|
#define MA_PA_STREAM_AUTO_TIMING_UPDATE 0x00000008
|
|
#define MA_PA_STREAM_NO_REMAP_CHANNELS 0x00000010
|
|
#define MA_PA_STREAM_NO_REMIX_CHANNELS 0x00000020
|
|
#define MA_PA_STREAM_FIX_FORMAT 0x00000040
|
|
#define MA_PA_STREAM_FIX_RATE 0x00000080
|
|
#define MA_PA_STREAM_FIX_CHANNELS 0x00000100
|
|
#define MA_PA_STREAM_DONT_MOVE 0x00000200
|
|
#define MA_PA_STREAM_VARIABLE_RATE 0x00000400
|
|
#define MA_PA_STREAM_PEAK_DETECT 0x00000800
|
|
#define MA_PA_STREAM_START_MUTED 0x00001000
|
|
#define MA_PA_STREAM_ADJUST_LATENCY 0x00002000
|
|
#define MA_PA_STREAM_EARLY_REQUESTS 0x00004000
|
|
#define MA_PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND 0x00008000
|
|
#define MA_PA_STREAM_START_UNMUTED 0x00010000
|
|
#define MA_PA_STREAM_FAIL_ON_SUSPEND 0x00020000
|
|
#define MA_PA_STREAM_RELATIVE_VOLUME 0x00040000
|
|
#define MA_PA_STREAM_PASSTHROUGH 0x00080000
|
|
|
|
typedef int ma_pa_sink_flags_t;
|
|
#define MA_PA_SINK_NOFLAGS 0x00000000
|
|
#define MA_PA_SINK_HW_VOLUME_CTRL 0x00000001
|
|
#define MA_PA_SINK_LATENCY 0x00000002
|
|
#define MA_PA_SINK_HARDWARE 0x00000004
|
|
#define MA_PA_SINK_NETWORK 0x00000008
|
|
#define MA_PA_SINK_HW_MUTE_CTRL 0x00000010
|
|
#define MA_PA_SINK_DECIBEL_VOLUME 0x00000020
|
|
#define MA_PA_SINK_FLAT_VOLUME 0x00000040
|
|
#define MA_PA_SINK_DYNAMIC_LATENCY 0x00000080
|
|
#define MA_PA_SINK_SET_FORMATS 0x00000100
|
|
|
|
typedef int ma_pa_source_flags_t;
|
|
#define MA_PA_SOURCE_NOFLAGS 0x00000000
|
|
#define MA_PA_SOURCE_HW_VOLUME_CTRL 0x00000001
|
|
#define MA_PA_SOURCE_LATENCY 0x00000002
|
|
#define MA_PA_SOURCE_HARDWARE 0x00000004
|
|
#define MA_PA_SOURCE_NETWORK 0x00000008
|
|
#define MA_PA_SOURCE_HW_MUTE_CTRL 0x00000010
|
|
#define MA_PA_SOURCE_DECIBEL_VOLUME 0x00000020
|
|
#define MA_PA_SOURCE_DYNAMIC_LATENCY 0x00000040
|
|
#define MA_PA_SOURCE_FLAT_VOLUME 0x00000080
|
|
|
|
typedef int ma_pa_context_state_t;
|
|
#define MA_PA_CONTEXT_UNCONNECTED 0
|
|
#define MA_PA_CONTEXT_CONNECTING 1
|
|
#define MA_PA_CONTEXT_AUTHORIZING 2
|
|
#define MA_PA_CONTEXT_SETTING_NAME 3
|
|
#define MA_PA_CONTEXT_READY 4
|
|
#define MA_PA_CONTEXT_FAILED 5
|
|
#define MA_PA_CONTEXT_TERMINATED 6
|
|
|
|
typedef int ma_pa_stream_state_t;
|
|
#define MA_PA_STREAM_UNCONNECTED 0
|
|
#define MA_PA_STREAM_CREATING 1
|
|
#define MA_PA_STREAM_READY 2
|
|
#define MA_PA_STREAM_FAILED 3
|
|
#define MA_PA_STREAM_TERMINATED 4
|
|
|
|
typedef int ma_pa_operation_state_t;
|
|
#define MA_PA_OPERATION_RUNNING 0
|
|
#define MA_PA_OPERATION_DONE 1
|
|
#define MA_PA_OPERATION_CANCELLED 2
|
|
|
|
typedef int ma_pa_sink_state_t;
|
|
#define MA_PA_SINK_INVALID_STATE -1
|
|
#define MA_PA_SINK_RUNNING 0
|
|
#define MA_PA_SINK_IDLE 1
|
|
#define MA_PA_SINK_SUSPENDED 2
|
|
|
|
typedef int ma_pa_source_state_t;
|
|
#define MA_PA_SOURCE_INVALID_STATE -1
|
|
#define MA_PA_SOURCE_RUNNING 0
|
|
#define MA_PA_SOURCE_IDLE 1
|
|
#define MA_PA_SOURCE_SUSPENDED 2
|
|
|
|
typedef int ma_pa_seek_mode_t;
|
|
#define MA_PA_SEEK_RELATIVE 0
|
|
#define MA_PA_SEEK_ABSOLUTE 1
|
|
#define MA_PA_SEEK_RELATIVE_ON_READ 2
|
|
#define MA_PA_SEEK_RELATIVE_END 3
|
|
|
|
typedef int ma_pa_channel_position_t;
|
|
#define MA_PA_CHANNEL_POSITION_INVALID -1
|
|
#define MA_PA_CHANNEL_POSITION_MONO 0
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_LEFT 1
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT 2
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_CENTER 3
|
|
#define MA_PA_CHANNEL_POSITION_REAR_CENTER 4
|
|
#define MA_PA_CHANNEL_POSITION_REAR_LEFT 5
|
|
#define MA_PA_CHANNEL_POSITION_REAR_RIGHT 6
|
|
#define MA_PA_CHANNEL_POSITION_LFE 7
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER 8
|
|
#define MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER 9
|
|
#define MA_PA_CHANNEL_POSITION_SIDE_LEFT 10
|
|
#define MA_PA_CHANNEL_POSITION_SIDE_RIGHT 11
|
|
#define MA_PA_CHANNEL_POSITION_AUX0 12
|
|
#define MA_PA_CHANNEL_POSITION_AUX1 13
|
|
#define MA_PA_CHANNEL_POSITION_AUX2 14
|
|
#define MA_PA_CHANNEL_POSITION_AUX3 15
|
|
#define MA_PA_CHANNEL_POSITION_AUX4 16
|
|
#define MA_PA_CHANNEL_POSITION_AUX5 17
|
|
#define MA_PA_CHANNEL_POSITION_AUX6 18
|
|
#define MA_PA_CHANNEL_POSITION_AUX7 19
|
|
#define MA_PA_CHANNEL_POSITION_AUX8 20
|
|
#define MA_PA_CHANNEL_POSITION_AUX9 21
|
|
#define MA_PA_CHANNEL_POSITION_AUX10 22
|
|
#define MA_PA_CHANNEL_POSITION_AUX11 23
|
|
#define MA_PA_CHANNEL_POSITION_AUX12 24
|
|
#define MA_PA_CHANNEL_POSITION_AUX13 25
|
|
#define MA_PA_CHANNEL_POSITION_AUX14 26
|
|
#define MA_PA_CHANNEL_POSITION_AUX15 27
|
|
#define MA_PA_CHANNEL_POSITION_AUX16 28
|
|
#define MA_PA_CHANNEL_POSITION_AUX17 29
|
|
#define MA_PA_CHANNEL_POSITION_AUX18 30
|
|
#define MA_PA_CHANNEL_POSITION_AUX19 31
|
|
#define MA_PA_CHANNEL_POSITION_AUX20 32
|
|
#define MA_PA_CHANNEL_POSITION_AUX21 33
|
|
#define MA_PA_CHANNEL_POSITION_AUX22 34
|
|
#define MA_PA_CHANNEL_POSITION_AUX23 35
|
|
#define MA_PA_CHANNEL_POSITION_AUX24 36
|
|
#define MA_PA_CHANNEL_POSITION_AUX25 37
|
|
#define MA_PA_CHANNEL_POSITION_AUX26 38
|
|
#define MA_PA_CHANNEL_POSITION_AUX27 39
|
|
#define MA_PA_CHANNEL_POSITION_AUX28 40
|
|
#define MA_PA_CHANNEL_POSITION_AUX29 41
|
|
#define MA_PA_CHANNEL_POSITION_AUX30 42
|
|
#define MA_PA_CHANNEL_POSITION_AUX31 43
|
|
#define MA_PA_CHANNEL_POSITION_TOP_CENTER 44
|
|
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT 45
|
|
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT 46
|
|
#define MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER 47
|
|
#define MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT 48
|
|
#define MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT 49
|
|
#define MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER 50
|
|
#define MA_PA_CHANNEL_POSITION_LEFT MA_PA_CHANNEL_POSITION_FRONT_LEFT
|
|
#define MA_PA_CHANNEL_POSITION_RIGHT MA_PA_CHANNEL_POSITION_FRONT_RIGHT
|
|
#define MA_PA_CHANNEL_POSITION_CENTER MA_PA_CHANNEL_POSITION_FRONT_CENTER
|
|
#define MA_PA_CHANNEL_POSITION_SUBWOOFER MA_PA_CHANNEL_POSITION_LFE
|
|
|
|
typedef int ma_pa_channel_map_def_t;
|
|
#define MA_PA_CHANNEL_MAP_AIFF 0
|
|
#define MA_PA_CHANNEL_MAP_ALSA 1
|
|
#define MA_PA_CHANNEL_MAP_AUX 2
|
|
#define MA_PA_CHANNEL_MAP_WAVEEX 3
|
|
#define MA_PA_CHANNEL_MAP_OSS 4
|
|
#define MA_PA_CHANNEL_MAP_DEFAULT MA_PA_CHANNEL_MAP_AIFF
|
|
|
|
typedef int ma_pa_sample_format_t;
|
|
#define MA_PA_SAMPLE_INVALID -1
|
|
#define MA_PA_SAMPLE_U8 0
|
|
#define MA_PA_SAMPLE_ALAW 1
|
|
#define MA_PA_SAMPLE_ULAW 2
|
|
#define MA_PA_SAMPLE_S16LE 3
|
|
#define MA_PA_SAMPLE_S16BE 4
|
|
#define MA_PA_SAMPLE_FLOAT32LE 5
|
|
#define MA_PA_SAMPLE_FLOAT32BE 6
|
|
#define MA_PA_SAMPLE_S32LE 7
|
|
#define MA_PA_SAMPLE_S32BE 8
|
|
#define MA_PA_SAMPLE_S24LE 9
|
|
#define MA_PA_SAMPLE_S24BE 10
|
|
#define MA_PA_SAMPLE_S24_32LE 11
|
|
#define MA_PA_SAMPLE_S24_32BE 12
|
|
|
|
typedef struct ma_pa_mainloop ma_pa_mainloop;
|
|
typedef struct ma_pa_threaded_mainloop ma_pa_threaded_mainloop;
|
|
typedef struct ma_pa_mainloop_api ma_pa_mainloop_api;
|
|
typedef struct ma_pa_context ma_pa_context;
|
|
typedef struct ma_pa_operation ma_pa_operation;
|
|
typedef struct ma_pa_stream ma_pa_stream;
|
|
typedef struct ma_pa_spawn_api ma_pa_spawn_api;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 maxlength;
|
|
ma_uint32 tlength;
|
|
ma_uint32 prebuf;
|
|
ma_uint32 minreq;
|
|
ma_uint32 fragsize;
|
|
} ma_pa_buffer_attr;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint8 channels;
|
|
ma_pa_channel_position_t map[MA_PA_CHANNELS_MAX];
|
|
} ma_pa_channel_map;
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint8 channels;
|
|
ma_uint32 values[MA_PA_CHANNELS_MAX];
|
|
} ma_pa_cvolume;
|
|
|
|
typedef struct
|
|
{
|
|
ma_pa_sample_format_t format;
|
|
ma_uint32 rate;
|
|
ma_uint8 channels;
|
|
} ma_pa_sample_spec;
|
|
|
|
typedef struct
|
|
{
|
|
const char* name;
|
|
ma_uint32 index;
|
|
const char* description;
|
|
ma_pa_sample_spec sample_spec;
|
|
ma_pa_channel_map channel_map;
|
|
ma_uint32 owner_module;
|
|
ma_pa_cvolume volume;
|
|
int mute;
|
|
ma_uint32 monitor_source;
|
|
const char* monitor_source_name;
|
|
ma_uint64 latency;
|
|
const char* driver;
|
|
ma_pa_sink_flags_t flags;
|
|
void* proplist;
|
|
ma_uint64 configured_latency;
|
|
ma_uint32 base_volume;
|
|
ma_pa_sink_state_t state;
|
|
ma_uint32 n_volume_steps;
|
|
ma_uint32 card;
|
|
ma_uint32 n_ports;
|
|
void** ports;
|
|
void* active_port;
|
|
ma_uint8 n_formats;
|
|
void** formats;
|
|
} ma_pa_sink_info;
|
|
|
|
typedef struct
|
|
{
|
|
const char *name;
|
|
ma_uint32 index;
|
|
const char *description;
|
|
ma_pa_sample_spec sample_spec;
|
|
ma_pa_channel_map channel_map;
|
|
ma_uint32 owner_module;
|
|
ma_pa_cvolume volume;
|
|
int mute;
|
|
ma_uint32 monitor_of_sink;
|
|
const char *monitor_of_sink_name;
|
|
ma_uint64 latency;
|
|
const char *driver;
|
|
ma_pa_source_flags_t flags;
|
|
void* proplist;
|
|
ma_uint64 configured_latency;
|
|
ma_uint32 base_volume;
|
|
ma_pa_source_state_t state;
|
|
ma_uint32 n_volume_steps;
|
|
ma_uint32 card;
|
|
ma_uint32 n_ports;
|
|
void** ports;
|
|
void* active_port;
|
|
ma_uint8 n_formats;
|
|
void** formats;
|
|
} ma_pa_source_info;
|
|
|
|
typedef void (* ma_pa_context_notify_cb_t)(ma_pa_context* c, void* userdata);
|
|
typedef void (* ma_pa_sink_info_cb_t) (ma_pa_context* c, const ma_pa_sink_info* i, int eol, void* userdata);
|
|
typedef void (* ma_pa_source_info_cb_t) (ma_pa_context* c, const ma_pa_source_info* i, int eol, void* userdata);
|
|
typedef void (* ma_pa_stream_success_cb_t)(ma_pa_stream* s, int success, void* userdata);
|
|
typedef void (* ma_pa_stream_request_cb_t)(ma_pa_stream* s, size_t nbytes, void* userdata);
|
|
typedef void (* ma_pa_stream_notify_cb_t) (ma_pa_stream* s, void* userdata);
|
|
typedef void (* ma_pa_free_cb_t) (void* p);
|
|
#endif
|
|
|
|
typedef ma_pa_mainloop* (* ma_pa_mainloop_new_proc) (void);
|
|
typedef void (* ma_pa_mainloop_free_proc) (ma_pa_mainloop* m);
|
|
typedef void (* ma_pa_mainloop_quit_proc) (ma_pa_mainloop* m, int retval);
|
|
typedef ma_pa_mainloop_api* (* ma_pa_mainloop_get_api_proc) (ma_pa_mainloop* m);
|
|
typedef int (* ma_pa_mainloop_iterate_proc) (ma_pa_mainloop* m, int block, int* retval);
|
|
typedef void (* ma_pa_mainloop_wakeup_proc) (ma_pa_mainloop* m);
|
|
typedef ma_pa_threaded_mainloop* (* ma_pa_threaded_mainloop_new_proc) (void);
|
|
typedef void (* ma_pa_threaded_mainloop_free_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef int (* ma_pa_threaded_mainloop_start_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef void (* ma_pa_threaded_mainloop_stop_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef void (* ma_pa_threaded_mainloop_lock_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef void (* ma_pa_threaded_mainloop_unlock_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef void (* ma_pa_threaded_mainloop_wait_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef void (* ma_pa_threaded_mainloop_signal_proc) (ma_pa_threaded_mainloop* m, int wait_for_accept);
|
|
typedef void (* ma_pa_threaded_mainloop_accept_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef int (* ma_pa_threaded_mainloop_get_retval_proc) (const ma_pa_threaded_mainloop* m);
|
|
typedef ma_pa_mainloop_api* (* ma_pa_threaded_mainloop_get_api_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef int (* ma_pa_threaded_mainloop_in_thread_proc) (ma_pa_threaded_mainloop* m);
|
|
typedef void (* ma_pa_threaded_mainloop_set_name_proc) (ma_pa_threaded_mainloop* m, const char* name);
|
|
typedef ma_pa_context* (* ma_pa_context_new_proc) (ma_pa_mainloop_api* mainloop, const char* name);
|
|
typedef void (* ma_pa_context_unref_proc) (ma_pa_context* c);
|
|
typedef int (* ma_pa_context_connect_proc) (ma_pa_context* c, const char* server, ma_pa_context_flags_t flags, const ma_pa_spawn_api* api);
|
|
typedef void (* ma_pa_context_disconnect_proc) (ma_pa_context* c);
|
|
typedef void (* ma_pa_context_set_state_callback_proc) (ma_pa_context* c, ma_pa_context_notify_cb_t cb, void* userdata);
|
|
typedef ma_pa_context_state_t (* ma_pa_context_get_state_proc) (const ma_pa_context* c);
|
|
typedef ma_pa_operation* (* ma_pa_context_get_sink_info_list_proc) (ma_pa_context* c, ma_pa_sink_info_cb_t cb, void* userdata);
|
|
typedef ma_pa_operation* (* ma_pa_context_get_source_info_list_proc) (ma_pa_context* c, ma_pa_source_info_cb_t cb, void* userdata);
|
|
typedef ma_pa_operation* (* ma_pa_context_get_sink_info_by_name_proc) (ma_pa_context* c, const char* name, ma_pa_sink_info_cb_t cb, void* userdata);
|
|
typedef ma_pa_operation* (* ma_pa_context_get_source_info_by_name_proc)(ma_pa_context* c, const char* name, ma_pa_source_info_cb_t cb, void* userdata);
|
|
typedef void (* ma_pa_operation_unref_proc) (ma_pa_operation* o);
|
|
typedef ma_pa_operation_state_t (* ma_pa_operation_get_state_proc) (const ma_pa_operation* o);
|
|
typedef ma_pa_channel_map* (* ma_pa_channel_map_init_extend_proc) (ma_pa_channel_map* m, unsigned channels, ma_pa_channel_map_def_t def);
|
|
typedef int (* ma_pa_channel_map_valid_proc) (const ma_pa_channel_map* m);
|
|
typedef int (* ma_pa_channel_map_compatible_proc) (const ma_pa_channel_map* m, const ma_pa_sample_spec* ss);
|
|
typedef ma_pa_stream* (* ma_pa_stream_new_proc) (ma_pa_context* c, const char* name, const ma_pa_sample_spec* ss, const ma_pa_channel_map* map);
|
|
typedef void (* ma_pa_stream_unref_proc) (ma_pa_stream* s);
|
|
typedef int (* ma_pa_stream_connect_playback_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags, const ma_pa_cvolume* volume, ma_pa_stream* sync_stream);
|
|
typedef int (* ma_pa_stream_connect_record_proc) (ma_pa_stream* s, const char* dev, const ma_pa_buffer_attr* attr, ma_pa_stream_flags_t flags);
|
|
typedef int (* ma_pa_stream_disconnect_proc) (ma_pa_stream* s);
|
|
typedef ma_pa_stream_state_t (* ma_pa_stream_get_state_proc) (const ma_pa_stream* s);
|
|
typedef const ma_pa_sample_spec* (* ma_pa_stream_get_sample_spec_proc) (ma_pa_stream* s);
|
|
typedef const ma_pa_channel_map* (* ma_pa_stream_get_channel_map_proc) (ma_pa_stream* s);
|
|
typedef const ma_pa_buffer_attr* (* ma_pa_stream_get_buffer_attr_proc) (ma_pa_stream* s);
|
|
typedef ma_pa_operation* (* ma_pa_stream_set_buffer_attr_proc) (ma_pa_stream* s, const ma_pa_buffer_attr* attr, ma_pa_stream_success_cb_t cb, void* userdata);
|
|
typedef const char* (* ma_pa_stream_get_device_name_proc) (const ma_pa_stream* s);
|
|
typedef void (* ma_pa_stream_set_write_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
|
|
typedef void (* ma_pa_stream_set_read_callback_proc) (ma_pa_stream* s, ma_pa_stream_request_cb_t cb, void* userdata);
|
|
typedef void (* ma_pa_stream_set_suspended_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
|
|
typedef void (* ma_pa_stream_set_moved_callback_proc) (ma_pa_stream* s, ma_pa_stream_notify_cb_t cb, void* userdata);
|
|
typedef int (* ma_pa_stream_is_suspended_proc) (const ma_pa_stream* s);
|
|
typedef ma_pa_operation* (* ma_pa_stream_flush_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
|
|
typedef ma_pa_operation* (* ma_pa_stream_drain_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
|
|
typedef int (* ma_pa_stream_is_corked_proc) (const ma_pa_stream* s);
|
|
typedef ma_pa_operation* (* ma_pa_stream_cork_proc) (ma_pa_stream* s, int b, ma_pa_stream_success_cb_t cb, void* userdata);
|
|
typedef ma_pa_operation* (* ma_pa_stream_trigger_proc) (ma_pa_stream* s, ma_pa_stream_success_cb_t cb, void* userdata);
|
|
typedef int (* ma_pa_stream_begin_write_proc) (ma_pa_stream* s, void** data, size_t* nbytes);
|
|
typedef int (* ma_pa_stream_write_proc) (ma_pa_stream* s, const void* data, size_t nbytes, ma_pa_free_cb_t free_cb, int64_t offset, ma_pa_seek_mode_t seek);
|
|
typedef int (* ma_pa_stream_peek_proc) (ma_pa_stream* s, const void** data, size_t* nbytes);
|
|
typedef int (* ma_pa_stream_drop_proc) (ma_pa_stream* s);
|
|
typedef size_t (* ma_pa_stream_writable_size_proc) (const ma_pa_stream* s);
|
|
typedef size_t (* ma_pa_stream_readable_size_proc) (const ma_pa_stream* s);
|
|
|
|
typedef struct
|
|
{
|
|
ma_uint32 count;
|
|
ma_uint32 capacity;
|
|
ma_device_info* pInfo;
|
|
} ma_pulse_device_enum_data;
|
|
|
|
static ma_result ma_result_from_pulse(int result)
|
|
{
|
|
if (result < 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
switch (result) {
|
|
case MA_PA_OK: return MA_SUCCESS;
|
|
case MA_PA_ERR_ACCESS: return MA_ACCESS_DENIED;
|
|
case MA_PA_ERR_INVALID: return MA_INVALID_ARGS;
|
|
case MA_PA_ERR_NOENTITY: return MA_NO_DEVICE;
|
|
default: return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static ma_pa_sample_format_t ma_format_to_pulse(ma_format format)
|
|
{
|
|
if (ma_is_little_endian()) {
|
|
switch (format) {
|
|
case ma_format_s16: return MA_PA_SAMPLE_S16LE;
|
|
case ma_format_s24: return MA_PA_SAMPLE_S24LE;
|
|
case ma_format_s32: return MA_PA_SAMPLE_S32LE;
|
|
case ma_format_f32: return MA_PA_SAMPLE_FLOAT32LE;
|
|
default: break;
|
|
}
|
|
} else {
|
|
switch (format) {
|
|
case ma_format_s16: return MA_PA_SAMPLE_S16BE;
|
|
case ma_format_s24: return MA_PA_SAMPLE_S24BE;
|
|
case ma_format_s32: return MA_PA_SAMPLE_S32BE;
|
|
case ma_format_f32: return MA_PA_SAMPLE_FLOAT32BE;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
switch (format) {
|
|
case ma_format_u8: return MA_PA_SAMPLE_U8;
|
|
default: return MA_PA_SAMPLE_INVALID;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static ma_format ma_format_from_pulse(ma_pa_sample_format_t format)
|
|
{
|
|
if (ma_is_little_endian()) {
|
|
switch (format) {
|
|
case MA_PA_SAMPLE_S16LE: return ma_format_s16;
|
|
case MA_PA_SAMPLE_S24LE: return ma_format_s24;
|
|
case MA_PA_SAMPLE_S32LE: return ma_format_s32;
|
|
case MA_PA_SAMPLE_FLOAT32LE: return ma_format_f32;
|
|
default: break;
|
|
}
|
|
} else {
|
|
switch (format) {
|
|
case MA_PA_SAMPLE_S16BE: return ma_format_s16;
|
|
case MA_PA_SAMPLE_S24BE: return ma_format_s24;
|
|
case MA_PA_SAMPLE_S32BE: return ma_format_s32;
|
|
case MA_PA_SAMPLE_FLOAT32BE: return ma_format_f32;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
switch (format) {
|
|
case MA_PA_SAMPLE_U8: return ma_format_u8;
|
|
default: return ma_format_unknown;
|
|
}
|
|
}
|
|
|
|
static ma_channel ma_channel_position_from_pulse(ma_pa_channel_position_t position)
|
|
{
|
|
switch (position)
|
|
{
|
|
case MA_PA_CHANNEL_POSITION_INVALID: return MA_CHANNEL_NONE;
|
|
case MA_PA_CHANNEL_POSITION_MONO: return MA_CHANNEL_MONO;
|
|
case MA_PA_CHANNEL_POSITION_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
|
|
case MA_PA_CHANNEL_POSITION_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
|
|
case MA_PA_CHANNEL_POSITION_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
|
|
case MA_PA_CHANNEL_POSITION_REAR_CENTER: return MA_CHANNEL_BACK_CENTER;
|
|
case MA_PA_CHANNEL_POSITION_REAR_LEFT: return MA_CHANNEL_BACK_LEFT;
|
|
case MA_PA_CHANNEL_POSITION_REAR_RIGHT: return MA_CHANNEL_BACK_RIGHT;
|
|
case MA_PA_CHANNEL_POSITION_LFE: return MA_CHANNEL_LFE;
|
|
case MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
|
|
case MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
|
|
case MA_PA_CHANNEL_POSITION_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
|
|
case MA_PA_CHANNEL_POSITION_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
|
|
case MA_PA_CHANNEL_POSITION_AUX0: return MA_CHANNEL_AUX_0;
|
|
case MA_PA_CHANNEL_POSITION_AUX1: return MA_CHANNEL_AUX_1;
|
|
case MA_PA_CHANNEL_POSITION_AUX2: return MA_CHANNEL_AUX_2;
|
|
case MA_PA_CHANNEL_POSITION_AUX3: return MA_CHANNEL_AUX_3;
|
|
case MA_PA_CHANNEL_POSITION_AUX4: return MA_CHANNEL_AUX_4;
|
|
case MA_PA_CHANNEL_POSITION_AUX5: return MA_CHANNEL_AUX_5;
|
|
case MA_PA_CHANNEL_POSITION_AUX6: return MA_CHANNEL_AUX_6;
|
|
case MA_PA_CHANNEL_POSITION_AUX7: return MA_CHANNEL_AUX_7;
|
|
case MA_PA_CHANNEL_POSITION_AUX8: return MA_CHANNEL_AUX_8;
|
|
case MA_PA_CHANNEL_POSITION_AUX9: return MA_CHANNEL_AUX_9;
|
|
case MA_PA_CHANNEL_POSITION_AUX10: return MA_CHANNEL_AUX_10;
|
|
case MA_PA_CHANNEL_POSITION_AUX11: return MA_CHANNEL_AUX_11;
|
|
case MA_PA_CHANNEL_POSITION_AUX12: return MA_CHANNEL_AUX_12;
|
|
case MA_PA_CHANNEL_POSITION_AUX13: return MA_CHANNEL_AUX_13;
|
|
case MA_PA_CHANNEL_POSITION_AUX14: return MA_CHANNEL_AUX_14;
|
|
case MA_PA_CHANNEL_POSITION_AUX15: return MA_CHANNEL_AUX_15;
|
|
case MA_PA_CHANNEL_POSITION_AUX16: return MA_CHANNEL_AUX_16;
|
|
case MA_PA_CHANNEL_POSITION_AUX17: return MA_CHANNEL_AUX_17;
|
|
case MA_PA_CHANNEL_POSITION_AUX18: return MA_CHANNEL_AUX_18;
|
|
case MA_PA_CHANNEL_POSITION_AUX19: return MA_CHANNEL_AUX_19;
|
|
case MA_PA_CHANNEL_POSITION_AUX20: return MA_CHANNEL_AUX_20;
|
|
case MA_PA_CHANNEL_POSITION_AUX21: return MA_CHANNEL_AUX_21;
|
|
case MA_PA_CHANNEL_POSITION_AUX22: return MA_CHANNEL_AUX_22;
|
|
case MA_PA_CHANNEL_POSITION_AUX23: return MA_CHANNEL_AUX_23;
|
|
case MA_PA_CHANNEL_POSITION_AUX24: return MA_CHANNEL_AUX_24;
|
|
case MA_PA_CHANNEL_POSITION_AUX25: return MA_CHANNEL_AUX_25;
|
|
case MA_PA_CHANNEL_POSITION_AUX26: return MA_CHANNEL_AUX_26;
|
|
case MA_PA_CHANNEL_POSITION_AUX27: return MA_CHANNEL_AUX_27;
|
|
case MA_PA_CHANNEL_POSITION_AUX28: return MA_CHANNEL_AUX_28;
|
|
case MA_PA_CHANNEL_POSITION_AUX29: return MA_CHANNEL_AUX_29;
|
|
case MA_PA_CHANNEL_POSITION_AUX30: return MA_CHANNEL_AUX_30;
|
|
case MA_PA_CHANNEL_POSITION_AUX31: return MA_CHANNEL_AUX_31;
|
|
case MA_PA_CHANNEL_POSITION_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
|
|
case MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
|
|
case MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
|
|
case MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
|
|
case MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
|
|
case MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
|
|
case MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
|
|
default: return MA_CHANNEL_NONE;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static ma_pa_channel_position_t ma_channel_position_to_pulse(ma_channel position)
|
|
{
|
|
switch (position)
|
|
{
|
|
case MA_CHANNEL_NONE: return MA_PA_CHANNEL_POSITION_INVALID;
|
|
case MA_CHANNEL_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_FRONT_LEFT;
|
|
case MA_CHANNEL_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT;
|
|
case MA_CHANNEL_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_CENTER;
|
|
case MA_CHANNEL_LFE: return MA_PA_CHANNEL_POSITION_LFE;
|
|
case MA_CHANNEL_BACK_LEFT: return MA_PA_CHANNEL_POSITION_REAR_LEFT;
|
|
case MA_CHANNEL_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_REAR_RIGHT;
|
|
case MA_CHANNEL_FRONT_LEFT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
|
|
case MA_CHANNEL_FRONT_RIGHT_CENTER: return MA_PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
|
|
case MA_CHANNEL_BACK_CENTER: return MA_PA_CHANNEL_POSITION_REAR_CENTER;
|
|
case MA_CHANNEL_SIDE_LEFT: return MA_PA_CHANNEL_POSITION_SIDE_LEFT;
|
|
case MA_CHANNEL_SIDE_RIGHT: return MA_PA_CHANNEL_POSITION_SIDE_RIGHT;
|
|
case MA_CHANNEL_TOP_CENTER: return MA_PA_CHANNEL_POSITION_TOP_CENTER;
|
|
case MA_CHANNEL_TOP_FRONT_LEFT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_LEFT;
|
|
case MA_CHANNEL_TOP_FRONT_CENTER: return MA_PA_CHANNEL_POSITION_TOP_FRONT_CENTER;
|
|
case MA_CHANNEL_TOP_FRONT_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_FRONT_RIGHT;
|
|
case MA_CHANNEL_TOP_BACK_LEFT: return MA_PA_CHANNEL_POSITION_TOP_REAR_LEFT;
|
|
case MA_CHANNEL_TOP_BACK_CENTER: return MA_PA_CHANNEL_POSITION_TOP_REAR_CENTER;
|
|
case MA_CHANNEL_TOP_BACK_RIGHT: return MA_PA_CHANNEL_POSITION_TOP_REAR_RIGHT;
|
|
case MA_CHANNEL_19: return MA_PA_CHANNEL_POSITION_AUX18;
|
|
case MA_CHANNEL_20: return MA_PA_CHANNEL_POSITION_AUX19;
|
|
case MA_CHANNEL_21: return MA_PA_CHANNEL_POSITION_AUX20;
|
|
case MA_CHANNEL_22: return MA_PA_CHANNEL_POSITION_AUX21;
|
|
case MA_CHANNEL_23: return MA_PA_CHANNEL_POSITION_AUX22;
|
|
case MA_CHANNEL_24: return MA_PA_CHANNEL_POSITION_AUX23;
|
|
case MA_CHANNEL_25: return MA_PA_CHANNEL_POSITION_AUX24;
|
|
case MA_CHANNEL_26: return MA_PA_CHANNEL_POSITION_AUX25;
|
|
case MA_CHANNEL_27: return MA_PA_CHANNEL_POSITION_AUX26;
|
|
case MA_CHANNEL_28: return MA_PA_CHANNEL_POSITION_AUX27;
|
|
case MA_CHANNEL_29: return MA_PA_CHANNEL_POSITION_AUX28;
|
|
case MA_CHANNEL_30: return MA_PA_CHANNEL_POSITION_AUX29;
|
|
case MA_CHANNEL_31: return MA_PA_CHANNEL_POSITION_AUX30;
|
|
case MA_CHANNEL_32: return MA_PA_CHANNEL_POSITION_AUX31;
|
|
default: return (ma_pa_channel_position_t)position;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_wait_for_operation__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
|
|
{
|
|
int resultPA;
|
|
ma_pa_operation_state_t state;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pOP != NULL);
|
|
|
|
for (;;) {
|
|
state = ((ma_pa_operation_get_state_proc)pContext->pulse.pa_operation_get_state)(pOP);
|
|
if (state != MA_PA_OPERATION_RUNNING) {
|
|
break;
|
|
}
|
|
|
|
resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
|
|
if (resultPA < 0) {
|
|
return ma_result_from_pulse(resultPA);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_wait_for_operation_and_unref__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_pa_operation* pOP)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pOP == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_wait_for_operation__pulse(pContext, pMainLoop, pOP);
|
|
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_wait_for_pa_context_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pPulseContext)
|
|
{
|
|
int resultPA;
|
|
ma_pa_context_state_t state;
|
|
|
|
for (;;) {
|
|
state = ((ma_pa_context_get_state_proc)pContext->pulse.pa_context_get_state)((ma_pa_context*)pPulseContext);
|
|
if (state == MA_PA_CONTEXT_READY) {
|
|
break;
|
|
}
|
|
|
|
if (state == MA_PA_CONTEXT_FAILED || state == MA_PA_CONTEXT_TERMINATED) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio context.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
|
|
if (resultPA < 0) {
|
|
return ma_result_from_pulse(resultPA);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_wait_for_pa_stream_to_connect__pulse(ma_context* pContext, ma_ptr pMainLoop, ma_ptr pStream)
|
|
{
|
|
int resultPA;
|
|
ma_pa_stream_state_t state;
|
|
|
|
for (;;) {
|
|
state = ((ma_pa_stream_get_state_proc)pContext->pulse.pa_stream_get_state)((ma_pa_stream*)pStream);
|
|
if (state == MA_PA_STREAM_READY) {
|
|
break;
|
|
}
|
|
|
|
if (state == MA_PA_STREAM_FAILED || state == MA_PA_STREAM_TERMINATED) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while connecting the PulseAudio stream.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
resultPA = ((ma_pa_mainloop_iterate_proc)pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pMainLoop, 1, NULL);
|
|
if (resultPA < 0) {
|
|
return ma_result_from_pulse(resultPA);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_init_pa_mainloop_and_pa_context__pulse(ma_context* pContext, const char* pApplicationName, const char* pServerName, ma_bool32 tryAutoSpawn, ma_ptr* ppMainLoop, ma_ptr* ppPulseContext)
|
|
{
|
|
ma_result result;
|
|
ma_ptr pMainLoop;
|
|
ma_ptr pPulseContext;
|
|
|
|
MA_ASSERT(ppMainLoop != NULL);
|
|
MA_ASSERT(ppPulseContext != NULL);
|
|
|
|
pMainLoop = ((ma_pa_mainloop_new_proc)pContext->pulse.pa_mainloop_new)();
|
|
if (pMainLoop == NULL) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create mainloop.");
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
pPulseContext = ((ma_pa_context_new_proc)pContext->pulse.pa_context_new)(((ma_pa_mainloop_get_api_proc)pContext->pulse.pa_mainloop_get_api)((ma_pa_mainloop*)pMainLoop), pApplicationName);
|
|
if (pPulseContext == NULL) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio context.");
|
|
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
result = ma_result_from_pulse(((ma_pa_context_connect_proc)pContext->pulse.pa_context_connect)((ma_pa_context*)pPulseContext, pServerName, (tryAutoSpawn) ? MA_PA_CONTEXT_NOFLAGS : MA_PA_CONTEXT_NOAUTOSPAWN, NULL));
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio context.");
|
|
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)(pPulseContext));
|
|
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
|
|
return result;
|
|
}
|
|
|
|
result = ma_wait_for_pa_context_to_connect__pulse(pContext, pMainLoop, pPulseContext);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[PulseAudio] Waiting for connection failed.");
|
|
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)(pPulseContext));
|
|
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)(pMainLoop));
|
|
return result;
|
|
}
|
|
|
|
*ppMainLoop = pMainLoop;
|
|
*ppPulseContext = pPulseContext;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_device_sink_info_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_pa_sink_info* pInfoOut;
|
|
|
|
if (endOfList > 0) {
|
|
return;
|
|
}
|
|
|
|
if (pInfo == NULL) {
|
|
return;
|
|
}
|
|
|
|
pInfoOut = (ma_pa_sink_info*)pUserData;
|
|
MA_ASSERT(pInfoOut != NULL);
|
|
|
|
*pInfoOut = *pInfo;
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
static void ma_device_source_info_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_pa_source_info* pInfoOut;
|
|
|
|
if (endOfList > 0) {
|
|
return;
|
|
}
|
|
|
|
if (pInfo == NULL) {
|
|
return;
|
|
}
|
|
|
|
pInfoOut = (ma_pa_source_info*)pUserData;
|
|
MA_ASSERT(pInfoOut != NULL);
|
|
|
|
*pInfoOut = *pInfo;
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
#if 0
|
|
static void ma_device_sink_name_callback(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_device* pDevice;
|
|
|
|
if (endOfList > 0) {
|
|
return;
|
|
}
|
|
|
|
pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), pInfo->description, (size_t)-1);
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
static void ma_device_source_name_callback(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_device* pDevice;
|
|
|
|
if (endOfList > 0) {
|
|
return;
|
|
}
|
|
|
|
pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), pInfo->description, (size_t)-1);
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_context_get_sink_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_sink_info* pSinkInfo)
|
|
{
|
|
ma_pa_operation* pOP;
|
|
|
|
pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_sink_info_callback, pSinkInfo);
|
|
if (pOP == NULL) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
|
|
}
|
|
|
|
static ma_result ma_context_get_source_info__pulse(ma_context* pContext, const char* pDeviceName, ma_pa_source_info* pSourceInfo)
|
|
{
|
|
ma_pa_operation* pOP;
|
|
|
|
pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)pContext->pulse.pPulseContext, pDeviceName, ma_device_source_info_callback, pSourceInfo);
|
|
if (pOP == NULL) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
|
|
}
|
|
|
|
static ma_result ma_context_get_default_device_index__pulse(ma_context* pContext, ma_device_type deviceType, ma_uint32* pIndex)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pIndex != NULL);
|
|
|
|
if (pIndex != NULL) {
|
|
*pIndex = (ma_uint32)-1;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_pa_sink_info sinkInfo;
|
|
result = ma_context_get_sink_info__pulse(pContext, NULL, &sinkInfo);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pIndex != NULL) {
|
|
*pIndex = sinkInfo.index;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
ma_pa_source_info sourceInfo;
|
|
result = ma_context_get_source_info__pulse(pContext, NULL, &sourceInfo);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pIndex != NULL) {
|
|
*pIndex = sourceInfo.index;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_context* pContext;
|
|
ma_enum_devices_callback_proc callback;
|
|
void* pUserData;
|
|
ma_bool32 isTerminated;
|
|
ma_uint32 defaultDeviceIndexPlayback;
|
|
ma_uint32 defaultDeviceIndexCapture;
|
|
} ma_context_enumerate_devices_callback_data__pulse;
|
|
|
|
static void ma_context_enumerate_devices_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pSinkInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
|
|
ma_device_info deviceInfo;
|
|
|
|
MA_ASSERT(pData != NULL);
|
|
|
|
if (endOfList || pData->isTerminated) {
|
|
return;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
if (pSinkInfo->name != NULL) {
|
|
ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSinkInfo->name, (size_t)-1);
|
|
}
|
|
|
|
if (pSinkInfo->description != NULL) {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSinkInfo->description, (size_t)-1);
|
|
}
|
|
|
|
if (pSinkInfo->index == pData->defaultDeviceIndexPlayback) {
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
}
|
|
|
|
pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_playback, &deviceInfo, pData->pUserData);
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
static void ma_context_enumerate_devices_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pSourceInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_context_enumerate_devices_callback_data__pulse* pData = (ma_context_enumerate_devices_callback_data__pulse*)pUserData;
|
|
ma_device_info deviceInfo;
|
|
|
|
MA_ASSERT(pData != NULL);
|
|
|
|
if (endOfList || pData->isTerminated) {
|
|
return;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
if (pSourceInfo->name != NULL) {
|
|
ma_strncpy_s(deviceInfo.id.pulse, sizeof(deviceInfo.id.pulse), pSourceInfo->name, (size_t)-1);
|
|
}
|
|
|
|
if (pSourceInfo->description != NULL) {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), pSourceInfo->description, (size_t)-1);
|
|
}
|
|
|
|
if (pSourceInfo->index == pData->defaultDeviceIndexCapture) {
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
}
|
|
|
|
pData->isTerminated = !pData->callback(pData->pContext, ma_device_type_capture, &deviceInfo, pData->pUserData);
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__pulse(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_context_enumerate_devices_callback_data__pulse callbackData;
|
|
ma_pa_operation* pOP = NULL;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
callbackData.pContext = pContext;
|
|
callbackData.callback = callback;
|
|
callbackData.pUserData = pUserData;
|
|
callbackData.isTerminated = MA_FALSE;
|
|
callbackData.defaultDeviceIndexPlayback = (ma_uint32)-1;
|
|
callbackData.defaultDeviceIndexCapture = (ma_uint32)-1;
|
|
|
|
ma_context_get_default_device_index__pulse(pContext, ma_device_type_playback, &callbackData.defaultDeviceIndexPlayback);
|
|
ma_context_get_default_device_index__pulse(pContext, ma_device_type_capture, &callbackData.defaultDeviceIndexCapture);
|
|
|
|
if (!callbackData.isTerminated) {
|
|
pOP = ((ma_pa_context_get_sink_info_list_proc)pContext->pulse.pa_context_get_sink_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_sink_callback__pulse, &callbackData);
|
|
if (pOP == NULL) {
|
|
result = MA_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
|
|
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
if (!callbackData.isTerminated) {
|
|
pOP = ((ma_pa_context_get_source_info_list_proc)pContext->pulse.pa_context_get_source_info_list)((ma_pa_context*)(pContext->pulse.pPulseContext), ma_context_enumerate_devices_source_callback__pulse, &callbackData);
|
|
if (pOP == NULL) {
|
|
result = MA_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
result = ma_wait_for_operation__pulse(pContext, pContext->pulse.pMainLoop, pOP);
|
|
((ma_pa_operation_unref_proc)pContext->pulse.pa_operation_unref)(pOP);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_device_info* pDeviceInfo;
|
|
ma_uint32 defaultDeviceIndex;
|
|
ma_bool32 foundDevice;
|
|
} ma_context_get_device_info_callback_data__pulse;
|
|
|
|
static void ma_context_get_device_info_sink_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_sink_info* pInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
|
|
|
|
if (endOfList > 0) {
|
|
return;
|
|
}
|
|
|
|
MA_ASSERT(pData != NULL);
|
|
pData->foundDevice = MA_TRUE;
|
|
|
|
if (pInfo->name != NULL) {
|
|
ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
|
|
}
|
|
|
|
if (pInfo->description != NULL) {
|
|
ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
|
|
}
|
|
|
|
pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
|
|
pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
|
|
pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
|
|
pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pData->pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
if (pData->defaultDeviceIndex == pInfo->index) {
|
|
pData->pDeviceInfo->isDefault = MA_TRUE;
|
|
}
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
static void ma_context_get_device_info_source_callback__pulse(ma_pa_context* pPulseContext, const ma_pa_source_info* pInfo, int endOfList, void* pUserData)
|
|
{
|
|
ma_context_get_device_info_callback_data__pulse* pData = (ma_context_get_device_info_callback_data__pulse*)pUserData;
|
|
|
|
if (endOfList > 0) {
|
|
return;
|
|
}
|
|
|
|
MA_ASSERT(pData != NULL);
|
|
pData->foundDevice = MA_TRUE;
|
|
|
|
if (pInfo->name != NULL) {
|
|
ma_strncpy_s(pData->pDeviceInfo->id.pulse, sizeof(pData->pDeviceInfo->id.pulse), pInfo->name, (size_t)-1);
|
|
}
|
|
|
|
if (pInfo->description != NULL) {
|
|
ma_strncpy_s(pData->pDeviceInfo->name, sizeof(pData->pDeviceInfo->name), pInfo->description, (size_t)-1);
|
|
}
|
|
|
|
pData->pDeviceInfo->nativeDataFormats[0].format = ma_format_from_pulse(pInfo->sample_spec.format);
|
|
pData->pDeviceInfo->nativeDataFormats[0].channels = pInfo->sample_spec.channels;
|
|
pData->pDeviceInfo->nativeDataFormats[0].sampleRate = pInfo->sample_spec.rate;
|
|
pData->pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pData->pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
if (pData->defaultDeviceIndex == pInfo->index) {
|
|
pData->pDeviceInfo->isDefault = MA_TRUE;
|
|
}
|
|
|
|
(void)pPulseContext;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__pulse(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_context_get_device_info_callback_data__pulse callbackData;
|
|
ma_pa_operation* pOP = NULL;
|
|
const char* pDeviceName = NULL;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
callbackData.pDeviceInfo = pDeviceInfo;
|
|
callbackData.foundDevice = MA_FALSE;
|
|
|
|
if (pDeviceID != NULL) {
|
|
pDeviceName = pDeviceID->pulse;
|
|
} else {
|
|
pDeviceName = NULL;
|
|
}
|
|
|
|
result = ma_context_get_default_device_index__pulse(pContext, deviceType, &callbackData.defaultDeviceIndex);
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
pOP = ((ma_pa_context_get_sink_info_by_name_proc)pContext->pulse.pa_context_get_sink_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_sink_callback__pulse, &callbackData);
|
|
} else {
|
|
pOP = ((ma_pa_context_get_source_info_by_name_proc)pContext->pulse.pa_context_get_source_info_by_name)((ma_pa_context*)(pContext->pulse.pPulseContext), pDeviceName, ma_context_get_device_info_source_callback__pulse, &callbackData);
|
|
}
|
|
|
|
if (pOP != NULL) {
|
|
ma_wait_for_operation_and_unref__pulse(pContext, pContext->pulse.pMainLoop, pOP);
|
|
} else {
|
|
result = MA_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
if (!callbackData.foundDevice) {
|
|
result = MA_NO_DEVICE;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__pulse(ma_device* pDevice)
|
|
{
|
|
ma_context* pContext;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
pContext = pDevice->pContext;
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
((ma_pa_stream_disconnect_proc)pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
((ma_pa_stream_unref_proc)pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
ma_duplex_rb_uninit(&pDevice->duplexRB);
|
|
}
|
|
|
|
((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pDevice->pulse.pPulseContext);
|
|
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pDevice->pulse.pPulseContext);
|
|
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_pa_buffer_attr ma_device__pa_buffer_attr_new(ma_uint32 periodSizeInFrames, ma_uint32 periods, const ma_pa_sample_spec* ss)
|
|
{
|
|
ma_pa_buffer_attr attr;
|
|
attr.maxlength = periodSizeInFrames * periods * ma_get_bytes_per_frame(ma_format_from_pulse(ss->format), ss->channels);
|
|
attr.tlength = attr.maxlength / periods;
|
|
attr.prebuf = (ma_uint32)-1;
|
|
attr.minreq = (ma_uint32)-1;
|
|
attr.fragsize = attr.maxlength / periods;
|
|
|
|
return attr;
|
|
}
|
|
|
|
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 ma_atomic_uint32 g_StreamCounter = { 0 };
|
|
char actualStreamName[256];
|
|
|
|
if (pStreamName != NULL) {
|
|
ma_strncpy_s(actualStreamName, sizeof(actualStreamName), pStreamName, (size_t)-1);
|
|
} else {
|
|
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);
|
|
}
|
|
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);
|
|
}
|
|
|
|
static void ma_device_on_read__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
ma_uint32 bpf;
|
|
ma_uint32 deviceState;
|
|
ma_uint64 frameCount;
|
|
ma_uint64 framesProcessed;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
deviceState = ma_device_get_state(pDevice);
|
|
if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
|
|
return;
|
|
}
|
|
|
|
bpf = ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
MA_ASSERT(bpf > 0);
|
|
|
|
frameCount = byteCount / bpf;
|
|
framesProcessed = 0;
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started && framesProcessed < frameCount) {
|
|
const void* pMappedPCMFrames;
|
|
size_t bytesMapped;
|
|
ma_uint64 framesMapped;
|
|
|
|
int pulseResult = ((ma_pa_stream_peek_proc)pDevice->pContext->pulse.pa_stream_peek)(pStream, &pMappedPCMFrames, &bytesMapped);
|
|
if (pulseResult < 0) {
|
|
break;
|
|
}
|
|
|
|
framesMapped = bytesMapped / bpf;
|
|
if (framesMapped > 0) {
|
|
if (pMappedPCMFrames != NULL) {
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, pMappedPCMFrames, framesMapped);
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[PulseAudio] ma_device_on_read__pulse: Hole.\n");
|
|
}
|
|
|
|
pulseResult = ((ma_pa_stream_drop_proc)pDevice->pContext->pulse.pa_stream_drop)(pStream);
|
|
if (pulseResult < 0) {
|
|
break;
|
|
}
|
|
|
|
framesProcessed += framesMapped;
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static ma_result ma_device_write_to_stream__pulse(ma_device* pDevice, ma_pa_stream* pStream, ma_uint64* pFramesProcessed)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 framesProcessed = 0;
|
|
size_t bytesMapped;
|
|
ma_uint32 bpf;
|
|
ma_uint32 deviceState;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pStream != NULL);
|
|
|
|
bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
MA_ASSERT(bpf > 0);
|
|
|
|
deviceState = ma_device_get_state(pDevice);
|
|
|
|
bytesMapped = ((ma_pa_stream_writable_size_proc)pDevice->pContext->pulse.pa_stream_writable_size)(pStream);
|
|
if (bytesMapped != (size_t)-1) {
|
|
if (bytesMapped > 0) {
|
|
ma_uint64 framesMapped;
|
|
void* pMappedPCMFrames;
|
|
int pulseResult = ((ma_pa_stream_begin_write_proc)pDevice->pContext->pulse.pa_stream_begin_write)(pStream, &pMappedPCMFrames, &bytesMapped);
|
|
if (pulseResult < 0) {
|
|
result = ma_result_from_pulse(pulseResult);
|
|
goto done;
|
|
}
|
|
|
|
framesMapped = bytesMapped / bpf;
|
|
|
|
if (deviceState == ma_device_state_started || deviceState == ma_device_state_starting) {
|
|
ma_device_handle_backend_data_callback(pDevice, pMappedPCMFrames, NULL, framesMapped);
|
|
} else {
|
|
ma_silence_pcm_frames(pMappedPCMFrames, framesMapped, pDevice->playback.format, pDevice->playback.channels);
|
|
}
|
|
|
|
pulseResult = ((ma_pa_stream_write_proc)pDevice->pContext->pulse.pa_stream_write)(pStream, pMappedPCMFrames, bytesMapped, NULL, 0, MA_PA_SEEK_RELATIVE);
|
|
if (pulseResult < 0) {
|
|
result = ma_result_from_pulse(pulseResult);
|
|
goto done;
|
|
}
|
|
|
|
framesProcessed += framesMapped;
|
|
} else {
|
|
result = MA_SUCCESS;
|
|
goto done;
|
|
}
|
|
} else {
|
|
result = MA_ERROR;
|
|
goto done;
|
|
}
|
|
|
|
done:
|
|
if (pFramesProcessed != NULL) {
|
|
*pFramesProcessed = framesProcessed;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void ma_device_on_write__pulse(ma_pa_stream* pStream, size_t byteCount, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
ma_uint32 bpf;
|
|
ma_uint64 frameCount;
|
|
ma_uint64 framesProcessed;
|
|
ma_uint32 deviceState;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
deviceState = ma_device_get_state(pDevice);
|
|
if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
|
|
return;
|
|
}
|
|
|
|
bpf = ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
MA_ASSERT(bpf > 0);
|
|
|
|
frameCount = byteCount / bpf;
|
|
framesProcessed = 0;
|
|
|
|
while (framesProcessed < frameCount) {
|
|
ma_uint64 framesProcessedThisIteration;
|
|
|
|
deviceState = ma_device_get_state(pDevice);
|
|
if (deviceState != ma_device_state_starting && deviceState != ma_device_state_started) {
|
|
break;
|
|
}
|
|
|
|
result = ma_device_write_to_stream__pulse(pDevice, pStream, &framesProcessedThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
framesProcessed += framesProcessedThisIteration;
|
|
}
|
|
}
|
|
|
|
static void ma_device_on_suspended__pulse(ma_pa_stream* pStream, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
int suspended;
|
|
|
|
(void)pStream;
|
|
|
|
suspended = ((ma_pa_stream_is_suspended_proc)pDevice->pContext->pulse.pa_stream_is_suspended)(pStream);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. pa_stream_is_suspended() returned %d.\n", suspended);
|
|
|
|
if (suspended < 0) {
|
|
return;
|
|
}
|
|
|
|
if (suspended == 1) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Suspended.\n");
|
|
ma_device__on_notification_stopped(pDevice);
|
|
} else {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[Pulse] Device suspended state changed. Resumed.\n");
|
|
ma_device__on_notification_started(pDevice);
|
|
}
|
|
}
|
|
|
|
static void ma_device_on_rerouted__pulse(ma_pa_stream* pStream, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
|
|
(void)pStream;
|
|
(void)pUserData;
|
|
|
|
ma_device__on_notification_rerouted(pDevice);
|
|
}
|
|
|
|
static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__pulse(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
|
|
{
|
|
const ma_uint32 defaultPeriodSizeInMilliseconds_LowLatency = 25;
|
|
const ma_uint32 defaultPeriodSizeInMilliseconds_Conservative = MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE;
|
|
|
|
MA_ASSERT(nativeSampleRate != 0);
|
|
|
|
if (pDescriptor->periodSizeInFrames == 0) {
|
|
if (pDescriptor->periodSizeInMilliseconds == 0) {
|
|
if (performanceProfile == ma_performance_profile_low_latency) {
|
|
return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_LowLatency, nativeSampleRate);
|
|
} else {
|
|
return ma_calculate_buffer_size_in_frames_from_milliseconds(defaultPeriodSizeInMilliseconds_Conservative, nativeSampleRate);
|
|
}
|
|
} else {
|
|
return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
|
|
}
|
|
} else {
|
|
return pDescriptor->periodSizeInFrames;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
|
|
ma_result result = MA_SUCCESS;
|
|
int error = 0;
|
|
const char* devPlayback = NULL;
|
|
const char* devCapture = NULL;
|
|
ma_format format = ma_format_unknown;
|
|
ma_uint32 channels = 0;
|
|
ma_uint32 sampleRate = 0;
|
|
ma_pa_sink_info sinkInfo;
|
|
ma_pa_source_info sourceInfo;
|
|
ma_pa_sample_spec ss;
|
|
ma_pa_channel_map cmap;
|
|
ma_pa_buffer_attr attr;
|
|
const ma_pa_sample_spec* pActualSS = NULL;
|
|
const ma_pa_buffer_attr* pActualAttr = NULL;
|
|
const ma_pa_channel_map* pActualChannelMap = NULL;
|
|
ma_uint32 iChannel;
|
|
int streamFlags;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ZERO_OBJECT(&pDevice->pulse);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pConfig->playback.shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pConfig->capture.shareMode == ma_share_mode_exclusive)) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
if (pDescriptorPlayback->pDeviceID != NULL) {
|
|
devPlayback = pDescriptorPlayback->pDeviceID->pulse;
|
|
}
|
|
|
|
format = pDescriptorPlayback->format;
|
|
channels = pDescriptorPlayback->channels;
|
|
sampleRate = pDescriptorPlayback->sampleRate;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
if (pDescriptorCapture->pDeviceID != NULL) {
|
|
devCapture = pDescriptorCapture->pDeviceID->pulse;
|
|
}
|
|
|
|
format = pDescriptorCapture->format;
|
|
channels = pDescriptorCapture->channels;
|
|
sampleRate = pDescriptorCapture->sampleRate;
|
|
}
|
|
|
|
result = ma_init_pa_mainloop_and_pa_context__pulse(pDevice->pContext, pDevice->pContext->pulse.pApplicationName, pDevice->pContext->pulse.pServerName, MA_FALSE, &pDevice->pulse.pMainLoop, &pDevice->pulse.pPulseContext);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize PA mainloop and context for device.\n");
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
result = ma_context_get_source_info__pulse(pDevice->pContext, devCapture, &sourceInfo);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve source info for capture device.");
|
|
goto on_error0;
|
|
}
|
|
|
|
ss = sourceInfo.sample_spec;
|
|
cmap = sourceInfo.channel_map;
|
|
|
|
if (pDescriptorCapture->channels != 0) {
|
|
ss.channels = pDescriptorCapture->channels;
|
|
}
|
|
|
|
if (ss.channels > 32) {
|
|
ss.channels = 32;
|
|
}
|
|
|
|
((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap);
|
|
|
|
if (pDescriptorCapture->sampleRate != 0) {
|
|
ss.rate = pDescriptorCapture->sampleRate;
|
|
}
|
|
streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;
|
|
|
|
if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
|
|
if (ma_is_little_endian()) {
|
|
ss.format = MA_PA_SAMPLE_FLOAT32LE;
|
|
} else {
|
|
ss.format = MA_PA_SAMPLE_FLOAT32BE;
|
|
}
|
|
streamFlags |= MA_PA_STREAM_FIX_FORMAT;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n");
|
|
}
|
|
if (ss.rate == 0) {
|
|
ss.rate = MA_DEFAULT_SAMPLE_RATE;
|
|
streamFlags |= MA_PA_STREAM_FIX_RATE;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
|
|
}
|
|
if (ss.channels == 0) {
|
|
ss.channels = MA_DEFAULT_CHANNELS;
|
|
streamFlags |= MA_PA_STREAM_FIX_CHANNELS;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
|
|
}
|
|
|
|
pDescriptorCapture->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorCapture, ss.rate, pConfig->performanceProfile);
|
|
|
|
attr = ma_device__pa_buffer_attr_new(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->periodCount, &ss);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames);
|
|
|
|
pDevice->pulse.pStreamCapture = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNameCapture, &ss, &cmap);
|
|
if (pDevice->pulse.pStreamCapture == NULL) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio capture stream.\n");
|
|
result = MA_ERROR;
|
|
goto on_error0;
|
|
}
|
|
|
|
((ma_pa_stream_set_read_callback_proc)pDevice->pContext->pulse.pa_stream_set_read_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_read__pulse, pDevice);
|
|
|
|
((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_suspended__pulse, pDevice);
|
|
|
|
((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamCapture, ma_device_on_rerouted__pulse, pDevice);
|
|
|
|
if (devCapture != NULL) {
|
|
streamFlags |= MA_PA_STREAM_DONT_MOVE;
|
|
}
|
|
|
|
error = ((ma_pa_stream_connect_record_proc)pDevice->pContext->pulse.pa_stream_connect_record)((ma_pa_stream*)pDevice->pulse.pStreamCapture, devCapture, &attr, (ma_pa_stream_flags_t)streamFlags);
|
|
if (error != MA_PA_OK) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio capture stream.");
|
|
result = ma_result_from_pulse(error);
|
|
goto on_error1;
|
|
}
|
|
|
|
result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
if (result != MA_SUCCESS) {
|
|
goto on_error2;
|
|
}
|
|
|
|
pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
if (pActualSS != NULL) {
|
|
ss = *pActualSS;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate);
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve capture sample spec.\n");
|
|
}
|
|
|
|
pDescriptorCapture->format = ma_format_from_pulse(ss.format);
|
|
pDescriptorCapture->channels = ss.channels;
|
|
pDescriptorCapture->sampleRate = ss.rate;
|
|
|
|
if (pDescriptorCapture->format == ma_format_unknown || pDescriptorCapture->channels == 0 || pDescriptorCapture->sampleRate == 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Capture sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorCapture->format), pDescriptorCapture->channels, pDescriptorCapture->sampleRate);
|
|
result = MA_ERROR;
|
|
goto on_error4;
|
|
}
|
|
|
|
pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
if (pActualChannelMap == NULL) {
|
|
pActualChannelMap = &cmap;
|
|
}
|
|
|
|
if (pDescriptorCapture->channels > 2) {
|
|
for (iChannel = 0; iChannel < pDescriptorCapture->channels; iChannel += 1) {
|
|
pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]);
|
|
}
|
|
} else {
|
|
if (pDescriptorCapture->channels == 1) {
|
|
pDescriptorCapture->channelMap[0] = MA_CHANNEL_MONO;
|
|
} else if (pDescriptorCapture->channels == 2) {
|
|
pDescriptorCapture->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
|
|
pDescriptorCapture->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
}
|
|
|
|
pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
if (pActualAttr != NULL) {
|
|
attr = *pActualAttr;
|
|
}
|
|
|
|
if (attr.fragsize > 0) {
|
|
pDescriptorCapture->periodCount = ma_max(attr.maxlength / attr.fragsize, 1);
|
|
} else {
|
|
pDescriptorCapture->periodCount = 1;
|
|
}
|
|
|
|
pDescriptorCapture->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) / pDescriptorCapture->periodCount;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Capture actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorCapture->periodSizeInFrames);
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
result = ma_context_get_sink_info__pulse(pDevice->pContext, devPlayback, &sinkInfo);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to retrieve sink info for playback device.\n");
|
|
goto on_error2;
|
|
}
|
|
|
|
ss = sinkInfo.sample_spec;
|
|
cmap = sinkInfo.channel_map;
|
|
|
|
if (pDescriptorPlayback->channels != 0) {
|
|
ss.channels = pDescriptorPlayback->channels;
|
|
}
|
|
|
|
if (ss.channels > 32) {
|
|
ss.channels = 32;
|
|
}
|
|
|
|
((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, (ma_pa_channel_map_def_t)pConfig->pulse.channelMap);
|
|
|
|
if (pDescriptorPlayback->sampleRate != 0) {
|
|
ss.rate = pDescriptorPlayback->sampleRate;
|
|
}
|
|
|
|
streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY;
|
|
if (ma_format_from_pulse(ss.format) == ma_format_unknown) {
|
|
if (ma_is_little_endian()) {
|
|
ss.format = MA_PA_SAMPLE_FLOAT32LE;
|
|
} else {
|
|
ss.format = MA_PA_SAMPLE_FLOAT32BE;
|
|
}
|
|
streamFlags |= MA_PA_STREAM_FIX_FORMAT;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n");
|
|
}
|
|
if (ss.rate == 0) {
|
|
ss.rate = MA_DEFAULT_SAMPLE_RATE;
|
|
streamFlags |= MA_PA_STREAM_FIX_RATE;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate);
|
|
}
|
|
if (ss.channels == 0) {
|
|
ss.channels = MA_DEFAULT_CHANNELS;
|
|
streamFlags |= MA_PA_STREAM_FIX_CHANNELS;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels);
|
|
}
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__pulse(pDescriptorPlayback, ss.rate, pConfig->performanceProfile);
|
|
|
|
attr = ma_device__pa_buffer_attr_new(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->periodCount, &ss);
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; periodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames);
|
|
|
|
pDevice->pulse.pStreamPlayback = ma_device__pa_stream_new__pulse(pDevice, pConfig->pulse.pStreamNamePlayback, &ss, &cmap);
|
|
if (pDevice->pulse.pStreamPlayback == NULL) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to create PulseAudio playback stream.\n");
|
|
result = MA_ERROR;
|
|
goto on_error2;
|
|
}
|
|
|
|
((ma_pa_stream_set_write_callback_proc)pDevice->pContext->pulse.pa_stream_set_write_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_write__pulse, pDevice);
|
|
|
|
((ma_pa_stream_set_suspended_callback_proc)pDevice->pContext->pulse.pa_stream_set_suspended_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_suspended__pulse, pDevice);
|
|
|
|
((ma_pa_stream_set_moved_callback_proc)pDevice->pContext->pulse.pa_stream_set_moved_callback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_device_on_rerouted__pulse, pDevice);
|
|
|
|
if (devPlayback != NULL) {
|
|
streamFlags |= MA_PA_STREAM_DONT_MOVE;
|
|
}
|
|
|
|
error = ((ma_pa_stream_connect_playback_proc)pDevice->pContext->pulse.pa_stream_connect_playback)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, devPlayback, &attr, (ma_pa_stream_flags_t)streamFlags, NULL, NULL);
|
|
if (error != MA_PA_OK) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to connect PulseAudio playback stream.");
|
|
result = ma_result_from_pulse(error);
|
|
goto on_error3;
|
|
}
|
|
|
|
result = ma_wait_for_pa_stream_to_connect__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, (ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
if (result != MA_SUCCESS) {
|
|
goto on_error3;
|
|
}
|
|
|
|
pActualSS = ((ma_pa_stream_get_sample_spec_proc)pDevice->pContext->pulse.pa_stream_get_sample_spec)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
if (pActualSS != NULL) {
|
|
ss = *pActualSS;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback sample spec: format=%s, channels=%d, rate=%d\n", ma_get_format_name(ma_format_from_pulse(ss.format)), ss.channels, ss.rate);
|
|
} else {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Failed to retrieve playback sample spec.\n");
|
|
}
|
|
|
|
pDescriptorPlayback->format = ma_format_from_pulse(ss.format);
|
|
pDescriptorPlayback->channels = ss.channels;
|
|
pDescriptorPlayback->sampleRate = ss.rate;
|
|
|
|
if (pDescriptorPlayback->format == ma_format_unknown || pDescriptorPlayback->channels == 0 || pDescriptorPlayback->sampleRate == 0) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Playback sample spec is invalid. Device unusable by miniaudio. format=%s, channels=%d, sampleRate=%d.\n", ma_get_format_name(pDescriptorPlayback->format), pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate);
|
|
result = MA_ERROR;
|
|
goto on_error4;
|
|
}
|
|
|
|
pActualChannelMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
if (pActualChannelMap == NULL) {
|
|
pActualChannelMap = &cmap;
|
|
}
|
|
|
|
if (pDescriptorPlayback->channels > 2) {
|
|
for (iChannel = 0; iChannel < pDescriptorPlayback->channels; iChannel += 1) {
|
|
pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(pActualChannelMap->map[iChannel]);
|
|
}
|
|
} else {
|
|
if (pDescriptorPlayback->channels == 1) {
|
|
pDescriptorPlayback->channelMap[0] = MA_CHANNEL_MONO;
|
|
} else if (pDescriptorPlayback->channels == 2) {
|
|
pDescriptorPlayback->channelMap[0] = MA_CHANNEL_FRONT_LEFT;
|
|
pDescriptorPlayback->channelMap[1] = MA_CHANNEL_FRONT_RIGHT;
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
}
|
|
|
|
pActualAttr = ((ma_pa_stream_get_buffer_attr_proc)pDevice->pContext->pulse.pa_stream_get_buffer_attr)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
if (pActualAttr != NULL) {
|
|
attr = *pActualAttr;
|
|
}
|
|
|
|
if (attr.tlength > 0) {
|
|
pDescriptorPlayback->periodCount = ma_max(attr.maxlength / attr.tlength, 1);
|
|
} else {
|
|
pDescriptorPlayback->periodCount = 1;
|
|
}
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = attr.maxlength / ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) / pDescriptorPlayback->periodCount;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] Playback actual attr: maxlength=%d, tlength=%d, prebuf=%d, minreq=%d, fragsize=%d; internalPeriodSizeInFrames=%d\n", attr.maxlength, attr.tlength, attr.prebuf, attr.minreq, attr.fragsize, pDescriptorPlayback->periodSizeInFrames);
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_format rbFormat = (format != ma_format_unknown) ? format : pDescriptorCapture->format;
|
|
ma_uint32 rbChannels = (channels > 0) ? channels : pDescriptorCapture->channels;
|
|
ma_uint32 rbSampleRate = (sampleRate > 0) ? sampleRate : pDescriptorCapture->sampleRate;
|
|
|
|
result = ma_duplex_rb_init(rbFormat, rbChannels, rbSampleRate, pDescriptorCapture->sampleRate, pDescriptorCapture->periodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to initialize ring buffer. %s.\n", ma_result_description(result));
|
|
goto on_error4;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
|
|
on_error4:
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
}
|
|
on_error3:
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamPlayback);
|
|
}
|
|
on_error2:
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
((ma_pa_stream_disconnect_proc)pDevice->pContext->pulse.pa_stream_disconnect)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
}
|
|
on_error1:
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
((ma_pa_stream_unref_proc)pDevice->pContext->pulse.pa_stream_unref)((ma_pa_stream*)pDevice->pulse.pStreamCapture);
|
|
}
|
|
on_error0:
|
|
return result;
|
|
}
|
|
|
|
static void ma_pulse_operation_complete_callback(ma_pa_stream* pStream, int success, void* pUserData)
|
|
{
|
|
ma_bool32* pIsSuccessful = (ma_bool32*)pUserData;
|
|
MA_ASSERT(pIsSuccessful != NULL);
|
|
|
|
*pIsSuccessful = (ma_bool32)success;
|
|
|
|
(void)pStream;
|
|
}
|
|
|
|
static ma_result ma_device__cork_stream__pulse(ma_device* pDevice, ma_device_type deviceType, int cork)
|
|
{
|
|
ma_context* pContext = pDevice->pContext;
|
|
ma_bool32 wasSuccessful;
|
|
ma_pa_stream* pStream;
|
|
ma_pa_operation* pOP;
|
|
ma_result result;
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
wasSuccessful = MA_FALSE;
|
|
|
|
pStream = (ma_pa_stream*)((deviceType == ma_device_type_capture) ? pDevice->pulse.pStreamCapture : pDevice->pulse.pStreamPlayback);
|
|
MA_ASSERT(pStream != NULL);
|
|
|
|
pOP = ((ma_pa_stream_cork_proc)pContext->pulse.pa_stream_cork)(pStream, cork, ma_pulse_operation_complete_callback, &wasSuccessful);
|
|
if (pOP == NULL) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to cork PulseAudio stream.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
result = ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] An error occurred while waiting for the PulseAudio stream to cork.");
|
|
return result;
|
|
}
|
|
|
|
if (!wasSuccessful) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[PulseAudio] Failed to %s PulseAudio stream.", (cork) ? "stop" : "start");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__pulse(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_device_write_to_stream__pulse(pDevice, (ma_pa_stream*)(pDevice->pulse.pStreamPlayback), NULL);
|
|
|
|
result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__pulse(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
result = ma_device__cork_stream__pulse(pDevice, ma_device_type_capture, 1);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
#if 0
|
|
ma_pa_operation* pOP = ((ma_pa_stream_drain_proc)pDevice->pContext->pulse.pa_stream_drain)((ma_pa_stream*)pDevice->pulse.pStreamPlayback, ma_pulse_operation_complete_callback, &wasSuccessful);
|
|
ma_wait_for_operation_and_unref__pulse(pDevice->pContext, pDevice->pulse.pMainLoop, pOP);
|
|
#endif
|
|
|
|
result = ma_device__cork_stream__pulse(pDevice, ma_device_type_playback, 1);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_data_loop__pulse(ma_device* pDevice)
|
|
{
|
|
int resultPA;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
while (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
resultPA = ((ma_pa_mainloop_iterate_proc)pDevice->pContext->pulse.pa_mainloop_iterate)((ma_pa_mainloop*)pDevice->pulse.pMainLoop, 1, NULL);
|
|
if (resultPA < 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_data_loop_wakeup__pulse(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
((ma_pa_mainloop_wakeup_proc)pDevice->pContext->pulse.pa_mainloop_wakeup)((ma_pa_mainloop*)pDevice->pulse.pMainLoop);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__pulse(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_pulseaudio);
|
|
|
|
((ma_pa_context_disconnect_proc)pContext->pulse.pa_context_disconnect)((ma_pa_context*)pContext->pulse.pPulseContext);
|
|
((ma_pa_context_unref_proc)pContext->pulse.pa_context_unref)((ma_pa_context*)pContext->pulse.pPulseContext);
|
|
((ma_pa_mainloop_free_proc)pContext->pulse.pa_mainloop_free)((ma_pa_mainloop*)pContext->pulse.pMainLoop);
|
|
|
|
ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
|
|
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
|
|
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
ma_result result;
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
const char* libpulseNames[] = {
|
|
"libpulse.so",
|
|
"libpulse.so.0"
|
|
};
|
|
size_t i;
|
|
|
|
for (i = 0; i < ma_countof(libpulseNames); ++i) {
|
|
pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]);
|
|
if (pContext->pulse.pulseSO != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pContext->pulse.pulseSO == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new");
|
|
pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free");
|
|
pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit");
|
|
pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api");
|
|
pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate");
|
|
pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup");
|
|
pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new");
|
|
pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free");
|
|
pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start");
|
|
pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop");
|
|
pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock");
|
|
pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock");
|
|
pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait");
|
|
pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal");
|
|
pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept");
|
|
pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval");
|
|
pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api");
|
|
pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread");
|
|
pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name");
|
|
pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new");
|
|
pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref");
|
|
pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect");
|
|
pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect");
|
|
pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback");
|
|
pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state");
|
|
pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list");
|
|
pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list");
|
|
pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name");
|
|
pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name");
|
|
pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref");
|
|
pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state");
|
|
pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend");
|
|
pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid");
|
|
pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible");
|
|
pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new");
|
|
pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref");
|
|
pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback");
|
|
pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record");
|
|
pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect");
|
|
pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state");
|
|
pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec");
|
|
pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map");
|
|
pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr");
|
|
pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr");
|
|
pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name");
|
|
pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback");
|
|
pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback");
|
|
pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback");
|
|
pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback");
|
|
pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended");
|
|
pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush");
|
|
pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain");
|
|
pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked");
|
|
pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork");
|
|
pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger");
|
|
pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write");
|
|
pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write");
|
|
pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek");
|
|
pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop");
|
|
pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size");
|
|
pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size");
|
|
#else
|
|
ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new;
|
|
ma_pa_mainloop_free_proc _pa_mainloop_free = pa_mainloop_free;
|
|
ma_pa_mainloop_quit_proc _pa_mainloop_quit = pa_mainloop_quit;
|
|
ma_pa_mainloop_get_api_proc _pa_mainloop_get_api = pa_mainloop_get_api;
|
|
ma_pa_mainloop_iterate_proc _pa_mainloop_iterate = pa_mainloop_iterate;
|
|
ma_pa_mainloop_wakeup_proc _pa_mainloop_wakeup = pa_mainloop_wakeup;
|
|
ma_pa_threaded_mainloop_new_proc _pa_threaded_mainloop_new = pa_threaded_mainloop_new;
|
|
ma_pa_threaded_mainloop_free_proc _pa_threaded_mainloop_free = pa_threaded_mainloop_free;
|
|
ma_pa_threaded_mainloop_start_proc _pa_threaded_mainloop_start = pa_threaded_mainloop_start;
|
|
ma_pa_threaded_mainloop_stop_proc _pa_threaded_mainloop_stop = pa_threaded_mainloop_stop;
|
|
ma_pa_threaded_mainloop_lock_proc _pa_threaded_mainloop_lock = pa_threaded_mainloop_lock;
|
|
ma_pa_threaded_mainloop_unlock_proc _pa_threaded_mainloop_unlock = pa_threaded_mainloop_unlock;
|
|
ma_pa_threaded_mainloop_wait_proc _pa_threaded_mainloop_wait = pa_threaded_mainloop_wait;
|
|
ma_pa_threaded_mainloop_signal_proc _pa_threaded_mainloop_signal = pa_threaded_mainloop_signal;
|
|
ma_pa_threaded_mainloop_accept_proc _pa_threaded_mainloop_accept = pa_threaded_mainloop_accept;
|
|
ma_pa_threaded_mainloop_get_retval_proc _pa_threaded_mainloop_get_retval = pa_threaded_mainloop_get_retval;
|
|
ma_pa_threaded_mainloop_get_api_proc _pa_threaded_mainloop_get_api = pa_threaded_mainloop_get_api;
|
|
ma_pa_threaded_mainloop_in_thread_proc _pa_threaded_mainloop_in_thread = pa_threaded_mainloop_in_thread;
|
|
ma_pa_threaded_mainloop_set_name_proc _pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
|
|
ma_pa_context_new_proc _pa_context_new = pa_context_new;
|
|
ma_pa_context_unref_proc _pa_context_unref = pa_context_unref;
|
|
ma_pa_context_connect_proc _pa_context_connect = pa_context_connect;
|
|
ma_pa_context_disconnect_proc _pa_context_disconnect = pa_context_disconnect;
|
|
ma_pa_context_set_state_callback_proc _pa_context_set_state_callback = pa_context_set_state_callback;
|
|
ma_pa_context_get_state_proc _pa_context_get_state = pa_context_get_state;
|
|
ma_pa_context_get_sink_info_list_proc _pa_context_get_sink_info_list = pa_context_get_sink_info_list;
|
|
ma_pa_context_get_source_info_list_proc _pa_context_get_source_info_list = pa_context_get_source_info_list;
|
|
ma_pa_context_get_sink_info_by_name_proc _pa_context_get_sink_info_by_name = pa_context_get_sink_info_by_name;
|
|
ma_pa_context_get_source_info_by_name_proc _pa_context_get_source_info_by_name= pa_context_get_source_info_by_name;
|
|
ma_pa_operation_unref_proc _pa_operation_unref = pa_operation_unref;
|
|
ma_pa_operation_get_state_proc _pa_operation_get_state = pa_operation_get_state;
|
|
ma_pa_channel_map_init_extend_proc _pa_channel_map_init_extend = pa_channel_map_init_extend;
|
|
ma_pa_channel_map_valid_proc _pa_channel_map_valid = pa_channel_map_valid;
|
|
ma_pa_channel_map_compatible_proc _pa_channel_map_compatible = pa_channel_map_compatible;
|
|
ma_pa_stream_new_proc _pa_stream_new = pa_stream_new;
|
|
ma_pa_stream_unref_proc _pa_stream_unref = pa_stream_unref;
|
|
ma_pa_stream_connect_playback_proc _pa_stream_connect_playback = pa_stream_connect_playback;
|
|
ma_pa_stream_connect_record_proc _pa_stream_connect_record = pa_stream_connect_record;
|
|
ma_pa_stream_disconnect_proc _pa_stream_disconnect = pa_stream_disconnect;
|
|
ma_pa_stream_get_state_proc _pa_stream_get_state = pa_stream_get_state;
|
|
ma_pa_stream_get_sample_spec_proc _pa_stream_get_sample_spec = pa_stream_get_sample_spec;
|
|
ma_pa_stream_get_channel_map_proc _pa_stream_get_channel_map = pa_stream_get_channel_map;
|
|
ma_pa_stream_get_buffer_attr_proc _pa_stream_get_buffer_attr = pa_stream_get_buffer_attr;
|
|
ma_pa_stream_set_buffer_attr_proc _pa_stream_set_buffer_attr = pa_stream_set_buffer_attr;
|
|
ma_pa_stream_get_device_name_proc _pa_stream_get_device_name = pa_stream_get_device_name;
|
|
ma_pa_stream_set_write_callback_proc _pa_stream_set_write_callback = pa_stream_set_write_callback;
|
|
ma_pa_stream_set_read_callback_proc _pa_stream_set_read_callback = pa_stream_set_read_callback;
|
|
ma_pa_stream_set_suspended_callback_proc _pa_stream_set_suspended_callback = pa_stream_set_suspended_callback;
|
|
ma_pa_stream_set_moved_callback_proc _pa_stream_set_moved_callback = pa_stream_set_moved_callback;
|
|
ma_pa_stream_is_suspended_proc _pa_stream_is_suspended = pa_stream_is_suspended;
|
|
ma_pa_stream_flush_proc _pa_stream_flush = pa_stream_flush;
|
|
ma_pa_stream_drain_proc _pa_stream_drain = pa_stream_drain;
|
|
ma_pa_stream_is_corked_proc _pa_stream_is_corked = pa_stream_is_corked;
|
|
ma_pa_stream_cork_proc _pa_stream_cork = pa_stream_cork;
|
|
ma_pa_stream_trigger_proc _pa_stream_trigger = pa_stream_trigger;
|
|
ma_pa_stream_begin_write_proc _pa_stream_begin_write = pa_stream_begin_write;
|
|
ma_pa_stream_write_proc _pa_stream_write = pa_stream_write;
|
|
ma_pa_stream_peek_proc _pa_stream_peek = pa_stream_peek;
|
|
ma_pa_stream_drop_proc _pa_stream_drop = pa_stream_drop;
|
|
ma_pa_stream_writable_size_proc _pa_stream_writable_size = pa_stream_writable_size;
|
|
ma_pa_stream_readable_size_proc _pa_stream_readable_size = pa_stream_readable_size;
|
|
|
|
pContext->pulse.pa_mainloop_new = (ma_proc)_pa_mainloop_new;
|
|
pContext->pulse.pa_mainloop_free = (ma_proc)_pa_mainloop_free;
|
|
pContext->pulse.pa_mainloop_quit = (ma_proc)_pa_mainloop_quit;
|
|
pContext->pulse.pa_mainloop_get_api = (ma_proc)_pa_mainloop_get_api;
|
|
pContext->pulse.pa_mainloop_iterate = (ma_proc)_pa_mainloop_iterate;
|
|
pContext->pulse.pa_mainloop_wakeup = (ma_proc)_pa_mainloop_wakeup;
|
|
pContext->pulse.pa_threaded_mainloop_new = (ma_proc)_pa_threaded_mainloop_new;
|
|
pContext->pulse.pa_threaded_mainloop_free = (ma_proc)_pa_threaded_mainloop_free;
|
|
pContext->pulse.pa_threaded_mainloop_start = (ma_proc)_pa_threaded_mainloop_start;
|
|
pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)_pa_threaded_mainloop_stop;
|
|
pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)_pa_threaded_mainloop_lock;
|
|
pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)_pa_threaded_mainloop_unlock;
|
|
pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)_pa_threaded_mainloop_wait;
|
|
pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)_pa_threaded_mainloop_signal;
|
|
pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)_pa_threaded_mainloop_accept;
|
|
pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)_pa_threaded_mainloop_get_retval;
|
|
pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)_pa_threaded_mainloop_get_api;
|
|
pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)_pa_threaded_mainloop_in_thread;
|
|
pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)_pa_threaded_mainloop_set_name;
|
|
pContext->pulse.pa_context_new = (ma_proc)_pa_context_new;
|
|
pContext->pulse.pa_context_unref = (ma_proc)_pa_context_unref;
|
|
pContext->pulse.pa_context_connect = (ma_proc)_pa_context_connect;
|
|
pContext->pulse.pa_context_disconnect = (ma_proc)_pa_context_disconnect;
|
|
pContext->pulse.pa_context_set_state_callback = (ma_proc)_pa_context_set_state_callback;
|
|
pContext->pulse.pa_context_get_state = (ma_proc)_pa_context_get_state;
|
|
pContext->pulse.pa_context_get_sink_info_list = (ma_proc)_pa_context_get_sink_info_list;
|
|
pContext->pulse.pa_context_get_source_info_list = (ma_proc)_pa_context_get_source_info_list;
|
|
pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)_pa_context_get_sink_info_by_name;
|
|
pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)_pa_context_get_source_info_by_name;
|
|
pContext->pulse.pa_operation_unref = (ma_proc)_pa_operation_unref;
|
|
pContext->pulse.pa_operation_get_state = (ma_proc)_pa_operation_get_state;
|
|
pContext->pulse.pa_channel_map_init_extend = (ma_proc)_pa_channel_map_init_extend;
|
|
pContext->pulse.pa_channel_map_valid = (ma_proc)_pa_channel_map_valid;
|
|
pContext->pulse.pa_channel_map_compatible = (ma_proc)_pa_channel_map_compatible;
|
|
pContext->pulse.pa_stream_new = (ma_proc)_pa_stream_new;
|
|
pContext->pulse.pa_stream_unref = (ma_proc)_pa_stream_unref;
|
|
pContext->pulse.pa_stream_connect_playback = (ma_proc)_pa_stream_connect_playback;
|
|
pContext->pulse.pa_stream_connect_record = (ma_proc)_pa_stream_connect_record;
|
|
pContext->pulse.pa_stream_disconnect = (ma_proc)_pa_stream_disconnect;
|
|
pContext->pulse.pa_stream_get_state = (ma_proc)_pa_stream_get_state;
|
|
pContext->pulse.pa_stream_get_sample_spec = (ma_proc)_pa_stream_get_sample_spec;
|
|
pContext->pulse.pa_stream_get_channel_map = (ma_proc)_pa_stream_get_channel_map;
|
|
pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)_pa_stream_get_buffer_attr;
|
|
pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)_pa_stream_set_buffer_attr;
|
|
pContext->pulse.pa_stream_get_device_name = (ma_proc)_pa_stream_get_device_name;
|
|
pContext->pulse.pa_stream_set_write_callback = (ma_proc)_pa_stream_set_write_callback;
|
|
pContext->pulse.pa_stream_set_read_callback = (ma_proc)_pa_stream_set_read_callback;
|
|
pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)_pa_stream_set_suspended_callback;
|
|
pContext->pulse.pa_stream_set_moved_callback = (ma_proc)_pa_stream_set_moved_callback;
|
|
pContext->pulse.pa_stream_is_suspended = (ma_proc)_pa_stream_is_suspended;
|
|
pContext->pulse.pa_stream_flush = (ma_proc)_pa_stream_flush;
|
|
pContext->pulse.pa_stream_drain = (ma_proc)_pa_stream_drain;
|
|
pContext->pulse.pa_stream_is_corked = (ma_proc)_pa_stream_is_corked;
|
|
pContext->pulse.pa_stream_cork = (ma_proc)_pa_stream_cork;
|
|
pContext->pulse.pa_stream_trigger = (ma_proc)_pa_stream_trigger;
|
|
pContext->pulse.pa_stream_begin_write = (ma_proc)_pa_stream_begin_write;
|
|
pContext->pulse.pa_stream_write = (ma_proc)_pa_stream_write;
|
|
pContext->pulse.pa_stream_peek = (ma_proc)_pa_stream_peek;
|
|
pContext->pulse.pa_stream_drop = (ma_proc)_pa_stream_drop;
|
|
pContext->pulse.pa_stream_writable_size = (ma_proc)_pa_stream_writable_size;
|
|
pContext->pulse.pa_stream_readable_size = (ma_proc)_pa_stream_readable_size;
|
|
#endif
|
|
|
|
pContext->pulse.pApplicationName = ma_copy_string(pConfig->pulse.pApplicationName, &pContext->allocationCallbacks);
|
|
if (pContext->pulse.pApplicationName == NULL && pConfig->pulse.pApplicationName != NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pContext->pulse.pServerName = ma_copy_string(pConfig->pulse.pServerName, &pContext->allocationCallbacks);
|
|
if (pContext->pulse.pServerName == NULL && pConfig->pulse.pServerName != NULL) {
|
|
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_init_pa_mainloop_and_pa_context__pulse(pContext, pConfig->pulse.pApplicationName, pConfig->pulse.pServerName, pConfig->pulse.tryAutoSpawn, &pContext->pulse.pMainLoop, &pContext->pulse.pPulseContext);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks);
|
|
ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks);
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
pCallbacks->onContextInit = ma_context_init__pulse;
|
|
pCallbacks->onContextUninit = ma_context_uninit__pulse;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__pulse;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__pulse;
|
|
pCallbacks->onDeviceInit = ma_device_init__pulse;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__pulse;
|
|
pCallbacks->onDeviceStart = ma_device_start__pulse;
|
|
pCallbacks->onDeviceStop = ma_device_stop__pulse;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = ma_device_data_loop__pulse;
|
|
pCallbacks->onDeviceDataLoopWakeup = ma_device_data_loop_wakeup__pulse;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_JACK
|
|
|
|
#ifdef MA_NO_RUNTIME_LINKING
|
|
#include <jack/jack.h>
|
|
|
|
typedef jack_nframes_t ma_jack_nframes_t;
|
|
typedef jack_options_t ma_jack_options_t;
|
|
typedef jack_status_t ma_jack_status_t;
|
|
typedef jack_client_t ma_jack_client_t;
|
|
typedef jack_port_t ma_jack_port_t;
|
|
typedef JackProcessCallback ma_JackProcessCallback;
|
|
typedef JackBufferSizeCallback ma_JackBufferSizeCallback;
|
|
typedef JackShutdownCallback ma_JackShutdownCallback;
|
|
#define MA_JACK_DEFAULT_AUDIO_TYPE JACK_DEFAULT_AUDIO_TYPE
|
|
#define ma_JackNullOption JackNullOption
|
|
#define ma_JackNoStartServer JackNoStartServer
|
|
#define ma_JackPortIsInput JackPortIsInput
|
|
#define ma_JackPortIsOutput JackPortIsOutput
|
|
#define ma_JackPortIsPhysical JackPortIsPhysical
|
|
#else
|
|
typedef ma_uint32 ma_jack_nframes_t;
|
|
typedef int ma_jack_options_t;
|
|
typedef int ma_jack_status_t;
|
|
typedef struct ma_jack_client_t ma_jack_client_t;
|
|
typedef struct ma_jack_port_t ma_jack_port_t;
|
|
typedef int (* ma_JackProcessCallback) (ma_jack_nframes_t nframes, void* arg);
|
|
typedef int (* ma_JackBufferSizeCallback)(ma_jack_nframes_t nframes, void* arg);
|
|
typedef void (* ma_JackShutdownCallback) (void* arg);
|
|
#define MA_JACK_DEFAULT_AUDIO_TYPE "32 bit float mono audio"
|
|
#define ma_JackNullOption 0
|
|
#define ma_JackNoStartServer 1
|
|
#define ma_JackPortIsInput 1
|
|
#define ma_JackPortIsOutput 2
|
|
#define ma_JackPortIsPhysical 4
|
|
#endif
|
|
|
|
typedef ma_jack_client_t* (* ma_jack_client_open_proc) (const char* client_name, ma_jack_options_t options, ma_jack_status_t* status, ...);
|
|
typedef int (* ma_jack_client_close_proc) (ma_jack_client_t* client);
|
|
typedef int (* ma_jack_client_name_size_proc) (void);
|
|
typedef int (* ma_jack_set_process_callback_proc) (ma_jack_client_t* client, ma_JackProcessCallback process_callback, void* arg);
|
|
typedef int (* ma_jack_set_buffer_size_callback_proc)(ma_jack_client_t* client, ma_JackBufferSizeCallback bufsize_callback, void* arg);
|
|
typedef void (* ma_jack_on_shutdown_proc) (ma_jack_client_t* client, ma_JackShutdownCallback function, void* arg);
|
|
typedef ma_jack_nframes_t (* ma_jack_get_sample_rate_proc) (ma_jack_client_t* client);
|
|
typedef ma_jack_nframes_t (* ma_jack_get_buffer_size_proc) (ma_jack_client_t* client);
|
|
typedef const char** (* ma_jack_get_ports_proc) (ma_jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags);
|
|
typedef int (* ma_jack_activate_proc) (ma_jack_client_t* client);
|
|
typedef int (* ma_jack_deactivate_proc) (ma_jack_client_t* client);
|
|
typedef int (* ma_jack_connect_proc) (ma_jack_client_t* client, const char* source_port, const char* destination_port);
|
|
typedef ma_jack_port_t* (* ma_jack_port_register_proc) (ma_jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size);
|
|
typedef const char* (* ma_jack_port_name_proc) (const ma_jack_port_t* port);
|
|
typedef void* (* ma_jack_port_get_buffer_proc) (ma_jack_port_t* port, ma_jack_nframes_t nframes);
|
|
typedef void (* ma_jack_free_proc) (void* ptr);
|
|
|
|
static ma_result ma_context_open_client__jack(ma_context* pContext, ma_jack_client_t** ppClient)
|
|
{
|
|
size_t maxClientNameSize;
|
|
char clientName[256];
|
|
ma_jack_status_t status;
|
|
ma_jack_client_t* pClient;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppClient != NULL);
|
|
|
|
if (ppClient) {
|
|
*ppClient = NULL;
|
|
}
|
|
|
|
maxClientNameSize = ((ma_jack_client_name_size_proc)pContext->jack.jack_client_name_size)();
|
|
ma_strncpy_s(clientName, ma_min(sizeof(clientName), maxClientNameSize), (pContext->jack.pClientName != NULL) ? pContext->jack.pClientName : "miniaudio", (size_t)-1);
|
|
|
|
pClient = ((ma_jack_client_open_proc)pContext->jack.jack_client_open)(clientName, (pContext->jack.tryStartServer) ? ma_JackNullOption : ma_JackNoStartServer, &status, NULL);
|
|
if (pClient == NULL) {
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
if (ppClient) {
|
|
*ppClient = pClient;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__jack(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_bool32 cbResult = MA_TRUE;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
|
|
(void)cbResult;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__jack(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_jack_client_t* pClient;
|
|
ma_result result;
|
|
const char** ppPorts;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDeviceID != NULL && pDeviceID->jack != 0) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_f32;
|
|
|
|
result = ma_context_open_client__jack(pContext, &pClient);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
|
|
return result;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormats[0].sampleRate = ((ma_jack_get_sample_rate_proc)pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pClient);
|
|
pDeviceInfo->nativeDataFormats[0].channels = 0;
|
|
|
|
ppPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ((deviceType == ma_device_type_playback) ? ma_JackPortIsInput : ma_JackPortIsOutput));
|
|
if (ppPorts == NULL) {
|
|
((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
while (ppPorts[pDeviceInfo->nativeDataFormats[0].channels] != NULL) {
|
|
pDeviceInfo->nativeDataFormats[0].channels += 1;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppPorts);
|
|
((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pClient);
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__jack(ma_device* pDevice)
|
|
{
|
|
ma_context* pContext;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
pContext = pDevice->pContext;
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDevice->jack.pClient != NULL) {
|
|
((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
|
|
ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
|
|
ma_free(pDevice->jack.ppPortsPlayback, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_device__jack_shutdown_callback(void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_device_stop(pDevice);
|
|
}
|
|
|
|
static int ma_device__jack_buffer_size_callback(ma_jack_nframes_t frameCount, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
size_t newBufferSize = frameCount * (pDevice->capture.internalChannels * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
|
|
float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
|
|
if (pNewBuffer == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_free(pDevice->jack.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks);
|
|
|
|
pDevice->jack.pIntermediaryBufferCapture = pNewBuffer;
|
|
pDevice->playback.internalPeriodSizeInFrames = frameCount;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
size_t newBufferSize = frameCount * (pDevice->playback.internalChannels * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
|
|
float* pNewBuffer = (float*)ma_calloc(newBufferSize, &pDevice->pContext->allocationCallbacks);
|
|
if (pNewBuffer == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_free(pDevice->jack.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks);
|
|
|
|
pDevice->jack.pIntermediaryBufferPlayback = pNewBuffer;
|
|
pDevice->playback.internalPeriodSizeInFrames = frameCount;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ma_device__jack_process_callback(ma_jack_nframes_t frameCount, void* pUserData)
|
|
{
|
|
ma_device* pDevice;
|
|
ma_context* pContext;
|
|
ma_uint32 iChannel;
|
|
|
|
pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
pContext = pDevice->pContext;
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
|
|
const float* pSrc = (const float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[iChannel], frameCount);
|
|
if (pSrc != NULL) {
|
|
float* pDst = pDevice->jack.pIntermediaryBufferCapture + iChannel;
|
|
ma_jack_nframes_t iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
*pDst = *pSrc;
|
|
|
|
pDst += pDevice->capture.internalChannels;
|
|
pSrc += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, pDevice->jack.pIntermediaryBufferCapture, frameCount);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_device_handle_backend_data_callback(pDevice, pDevice->jack.pIntermediaryBufferPlayback, NULL, frameCount);
|
|
|
|
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
|
|
float* pDst = (float*)((ma_jack_port_get_buffer_proc)pContext->jack.jack_port_get_buffer)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[iChannel], frameCount);
|
|
if (pDst != NULL) {
|
|
const float* pSrc = pDevice->jack.pIntermediaryBufferPlayback + iChannel;
|
|
ma_jack_nframes_t iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
*pDst = *pSrc;
|
|
|
|
pDst += 1;
|
|
pSrc += pDevice->playback.internalChannels;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ma_result ma_device_init__jack(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 periodSizeInFrames;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Loopback mode not supported.");
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->pDeviceID != NULL && pDescriptorPlayback->pDeviceID->jack != 0) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->pDeviceID != NULL && pDescriptorCapture->pDeviceID->jack != 0)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Only default devices are supported.");
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Exclusive mode not supported.");
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
result = ma_context_open_client__jack(pDevice->pContext, (ma_jack_client_t**)&pDevice->jack.pClient);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to open client.");
|
|
return result;
|
|
}
|
|
|
|
if (((ma_jack_set_process_callback_proc)pDevice->pContext->jack.jack_set_process_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_process_callback, pDevice) != 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set process callback.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
if (((ma_jack_set_buffer_size_callback_proc)pDevice->pContext->jack.jack_set_buffer_size_callback)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_buffer_size_callback, pDevice) != 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to set buffer size callback.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
((ma_jack_on_shutdown_proc)pDevice->pContext->jack.jack_on_shutdown)((ma_jack_client_t*)pDevice->jack.pClient, ma_device__jack_shutdown_callback, pDevice);
|
|
|
|
periodSizeInFrames = ((ma_jack_get_buffer_size_proc)pDevice->pContext->jack.jack_get_buffer_size)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_uint32 iPort;
|
|
const char** ppPorts;
|
|
|
|
pDescriptorCapture->format = ma_format_f32;
|
|
pDescriptorCapture->channels = 0;
|
|
pDescriptorCapture->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
|
|
|
|
ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput);
|
|
if (ppPorts == NULL) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
while (ppPorts[pDescriptorCapture->channels] != NULL) {
|
|
pDescriptorCapture->channels += 1;
|
|
}
|
|
|
|
pDevice->jack.ppPortsCapture = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsCapture) * pDescriptorCapture->channels, &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->jack.ppPortsCapture == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (iPort = 0; iPort < pDescriptorCapture->channels; iPort += 1) {
|
|
char name[64];
|
|
ma_strcpy_s(name, sizeof(name), "capture");
|
|
ma_itoa_s((int)iPort, name+7, sizeof(name)-7, 10);
|
|
|
|
pDevice->jack.ppPortsCapture[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsInput, 0);
|
|
if (pDevice->jack.ppPortsCapture[iPort] == NULL) {
|
|
((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
|
|
ma_device_uninit__jack(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
}
|
|
|
|
((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
|
|
|
|
pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
|
|
pDescriptorCapture->periodCount = 1;
|
|
|
|
pDevice->jack.pIntermediaryBufferCapture = (float*)ma_calloc(pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels), &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->jack.pIntermediaryBufferCapture == NULL) {
|
|
ma_device_uninit__jack(pDevice);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_uint32 iPort;
|
|
const char** ppPorts;
|
|
|
|
pDescriptorPlayback->format = ma_format_f32;
|
|
pDescriptorPlayback->channels = 0;
|
|
pDescriptorPlayback->sampleRate = ((ma_jack_get_sample_rate_proc)pDevice->pContext->jack.jack_get_sample_rate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
ma_channel_map_init_standard(ma_standard_channel_map_alsa, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
|
|
|
|
ppPorts = ((ma_jack_get_ports_proc)pDevice->pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput);
|
|
if (ppPorts == NULL) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to query physical ports.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
while (ppPorts[pDescriptorPlayback->channels] != NULL) {
|
|
pDescriptorPlayback->channels += 1;
|
|
}
|
|
|
|
pDevice->jack.ppPortsPlayback = (ma_ptr*)ma_malloc(sizeof(*pDevice->jack.ppPortsPlayback) * pDescriptorPlayback->channels, &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->jack.ppPortsPlayback == NULL) {
|
|
ma_free(pDevice->jack.ppPortsCapture, &pDevice->pContext->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (iPort = 0; iPort < pDescriptorPlayback->channels; iPort += 1) {
|
|
char name[64];
|
|
ma_strcpy_s(name, sizeof(name), "playback");
|
|
ma_itoa_s((int)iPort, name+8, sizeof(name)-8, 10);
|
|
|
|
pDevice->jack.ppPortsPlayback[iPort] = ((ma_jack_port_register_proc)pDevice->pContext->jack.jack_port_register)((ma_jack_client_t*)pDevice->jack.pClient, name, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsOutput, 0);
|
|
if (pDevice->jack.ppPortsPlayback[iPort] == NULL) {
|
|
((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
|
|
ma_device_uninit__jack(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to register ports.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
}
|
|
|
|
((ma_jack_free_proc)pDevice->pContext->jack.jack_free)((void*)ppPorts);
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
|
|
pDescriptorPlayback->periodCount = 1;
|
|
|
|
pDevice->jack.pIntermediaryBufferPlayback = (float*)ma_calloc(pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels), &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->jack.pIntermediaryBufferPlayback == NULL) {
|
|
ma_device_uninit__jack(pDevice);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__jack(ma_device* pDevice)
|
|
{
|
|
ma_context* pContext = pDevice->pContext;
|
|
int resultJACK;
|
|
size_t i;
|
|
|
|
resultJACK = ((ma_jack_activate_proc)pContext->jack.jack_activate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
if (resultJACK != 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to activate the JACK client.");
|
|
return MA_FAILED_TO_START_BACKEND_DEVICE;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsOutput);
|
|
if (ppServerPorts == NULL) {
|
|
((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
for (i = 0; ppServerPorts[i] != NULL; ++i) {
|
|
const char* pServerPort = ppServerPorts[i];
|
|
const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsCapture[i]);
|
|
|
|
resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pServerPort, pClientPort);
|
|
if (resultJACK != 0) {
|
|
((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
|
|
((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
|
|
return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
const char** ppServerPorts = ((ma_jack_get_ports_proc)pContext->jack.jack_get_ports)((ma_jack_client_t*)pDevice->jack.pClient, NULL, MA_JACK_DEFAULT_AUDIO_TYPE, ma_JackPortIsPhysical | ma_JackPortIsInput);
|
|
if (ppServerPorts == NULL) {
|
|
((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to retrieve physical ports.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
for (i = 0; ppServerPorts[i] != NULL; ++i) {
|
|
const char* pServerPort = ppServerPorts[i];
|
|
const char* pClientPort = ((ma_jack_port_name_proc)pContext->jack.jack_port_name)((ma_jack_port_t*)pDevice->jack.ppPortsPlayback[i]);
|
|
|
|
resultJACK = ((ma_jack_connect_proc)pContext->jack.jack_connect)((ma_jack_client_t*)pDevice->jack.pClient, pClientPort, pServerPort);
|
|
if (resultJACK != 0) {
|
|
((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
|
|
((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] Failed to connect ports.");
|
|
return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
((ma_jack_free_proc)pContext->jack.jack_free)((void*)ppServerPorts);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__jack(ma_device* pDevice)
|
|
{
|
|
ma_context* pContext = pDevice->pContext;
|
|
|
|
if (((ma_jack_deactivate_proc)pContext->jack.jack_deactivate)((ma_jack_client_t*)pDevice->jack.pClient) != 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[JACK] An error occurred when deactivating the JACK client.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
ma_device__on_notification_stopped(pDevice);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__jack(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_jack);
|
|
|
|
ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
|
|
pContext->jack.pClientName = NULL;
|
|
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
const char* libjackNames[] = {
|
|
#if defined(MA_WIN32)
|
|
"libjack.dll",
|
|
"libjack64.dll"
|
|
#endif
|
|
#if defined(MA_UNIX)
|
|
"libjack.so",
|
|
"libjack.so.0"
|
|
#endif
|
|
};
|
|
size_t i;
|
|
|
|
for (i = 0; i < ma_countof(libjackNames); ++i) {
|
|
pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]);
|
|
if (pContext->jack.jackSO != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pContext->jack.jackSO == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open");
|
|
pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close");
|
|
pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size");
|
|
pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback");
|
|
pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback");
|
|
pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown");
|
|
pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate");
|
|
pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size");
|
|
pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports");
|
|
pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate");
|
|
pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate");
|
|
pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect");
|
|
pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register");
|
|
pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name");
|
|
pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer");
|
|
pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free");
|
|
#else
|
|
ma_jack_client_open_proc _jack_client_open = jack_client_open;
|
|
ma_jack_client_close_proc _jack_client_close = jack_client_close;
|
|
ma_jack_client_name_size_proc _jack_client_name_size = jack_client_name_size;
|
|
ma_jack_set_process_callback_proc _jack_set_process_callback = jack_set_process_callback;
|
|
ma_jack_set_buffer_size_callback_proc _jack_set_buffer_size_callback = jack_set_buffer_size_callback;
|
|
ma_jack_on_shutdown_proc _jack_on_shutdown = jack_on_shutdown;
|
|
ma_jack_get_sample_rate_proc _jack_get_sample_rate = jack_get_sample_rate;
|
|
ma_jack_get_buffer_size_proc _jack_get_buffer_size = jack_get_buffer_size;
|
|
ma_jack_get_ports_proc _jack_get_ports = jack_get_ports;
|
|
ma_jack_activate_proc _jack_activate = jack_activate;
|
|
ma_jack_deactivate_proc _jack_deactivate = jack_deactivate;
|
|
ma_jack_connect_proc _jack_connect = jack_connect;
|
|
ma_jack_port_register_proc _jack_port_register = jack_port_register;
|
|
ma_jack_port_name_proc _jack_port_name = jack_port_name;
|
|
ma_jack_port_get_buffer_proc _jack_port_get_buffer = jack_port_get_buffer;
|
|
ma_jack_free_proc _jack_free = jack_free;
|
|
|
|
pContext->jack.jack_client_open = (ma_proc)_jack_client_open;
|
|
pContext->jack.jack_client_close = (ma_proc)_jack_client_close;
|
|
pContext->jack.jack_client_name_size = (ma_proc)_jack_client_name_size;
|
|
pContext->jack.jack_set_process_callback = (ma_proc)_jack_set_process_callback;
|
|
pContext->jack.jack_set_buffer_size_callback = (ma_proc)_jack_set_buffer_size_callback;
|
|
pContext->jack.jack_on_shutdown = (ma_proc)_jack_on_shutdown;
|
|
pContext->jack.jack_get_sample_rate = (ma_proc)_jack_get_sample_rate;
|
|
pContext->jack.jack_get_buffer_size = (ma_proc)_jack_get_buffer_size;
|
|
pContext->jack.jack_get_ports = (ma_proc)_jack_get_ports;
|
|
pContext->jack.jack_activate = (ma_proc)_jack_activate;
|
|
pContext->jack.jack_deactivate = (ma_proc)_jack_deactivate;
|
|
pContext->jack.jack_connect = (ma_proc)_jack_connect;
|
|
pContext->jack.jack_port_register = (ma_proc)_jack_port_register;
|
|
pContext->jack.jack_port_name = (ma_proc)_jack_port_name;
|
|
pContext->jack.jack_port_get_buffer = (ma_proc)_jack_port_get_buffer;
|
|
pContext->jack.jack_free = (ma_proc)_jack_free;
|
|
#endif
|
|
|
|
if (pConfig->jack.pClientName != NULL) {
|
|
pContext->jack.pClientName = ma_copy_string(pConfig->jack.pClientName, &pContext->allocationCallbacks);
|
|
}
|
|
pContext->jack.tryStartServer = pConfig->jack.tryStartServer;
|
|
|
|
{
|
|
ma_jack_client_t* pDummyClient;
|
|
ma_result result = ma_context_open_client__jack(pContext, &pDummyClient);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks);
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO);
|
|
#endif
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
((ma_jack_client_close_proc)pContext->jack.jack_client_close)((ma_jack_client_t*)pDummyClient);
|
|
}
|
|
|
|
pCallbacks->onContextInit = ma_context_init__jack;
|
|
pCallbacks->onContextUninit = ma_context_uninit__jack;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__jack;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__jack;
|
|
pCallbacks->onDeviceInit = ma_device_init__jack;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__jack;
|
|
pCallbacks->onDeviceStart = ma_device_start__jack;
|
|
pCallbacks->onDeviceStop = ma_device_stop__jack;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_COREAUDIO
|
|
#include <TargetConditionals.h>
|
|
|
|
#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1
|
|
#define MA_APPLE_MOBILE
|
|
#if defined(TARGET_OS_TV) && TARGET_OS_TV == 1
|
|
#define MA_APPLE_TV
|
|
#endif
|
|
#if defined(TARGET_OS_WATCH) && TARGET_OS_WATCH == 1
|
|
#define MA_APPLE_WATCH
|
|
#endif
|
|
#if __has_feature(objc_arc)
|
|
#define MA_BRIDGE_TRANSFER __bridge_transfer
|
|
#define MA_BRIDGE_RETAINED __bridge_retained
|
|
#else
|
|
#define MA_BRIDGE_TRANSFER
|
|
#define MA_BRIDGE_RETAINED
|
|
#endif
|
|
#else
|
|
#define MA_APPLE_DESKTOP
|
|
#endif
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
#include <CoreAudio/CoreAudio.h>
|
|
#else
|
|
#include <AVFoundation/AVFoundation.h>
|
|
#endif
|
|
|
|
#include <AudioToolbox/AudioToolbox.h>
|
|
|
|
typedef Boolean (* ma_CFStringGetCString_proc)(CFStringRef theString, char* buffer, CFIndex bufferSize, CFStringEncoding encoding);
|
|
typedef void (* ma_CFRelease_proc)(CFTypeRef cf);
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
typedef OSStatus (* ma_AudioObjectGetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* ioDataSize, void* outData);
|
|
typedef OSStatus (* ma_AudioObjectGetPropertyDataSize_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32* outDataSize);
|
|
typedef OSStatus (* ma_AudioObjectSetPropertyData_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, UInt32 inQualifierDataSize, const void* inQualifierData, UInt32 inDataSize, const void* inData);
|
|
typedef OSStatus (* ma_AudioObjectAddPropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
|
|
typedef OSStatus (* ma_AudioObjectRemovePropertyListener_proc)(AudioObjectID inObjectID, const AudioObjectPropertyAddress* inAddress, AudioObjectPropertyListenerProc inListener, void* inClientData);
|
|
#endif
|
|
|
|
typedef AudioComponent (* ma_AudioComponentFindNext_proc)(AudioComponent inComponent, const AudioComponentDescription* inDesc);
|
|
typedef OSStatus (* ma_AudioComponentInstanceDispose_proc)(AudioComponentInstance inInstance);
|
|
typedef OSStatus (* ma_AudioComponentInstanceNew_proc)(AudioComponent inComponent, AudioComponentInstance* outInstance);
|
|
typedef OSStatus (* ma_AudioOutputUnitStart_proc)(AudioUnit inUnit);
|
|
typedef OSStatus (* ma_AudioOutputUnitStop_proc)(AudioUnit inUnit);
|
|
typedef OSStatus (* ma_AudioUnitAddPropertyListener_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitPropertyListenerProc inProc, void* inProcUserData);
|
|
typedef OSStatus (* ma_AudioUnitGetPropertyInfo_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, UInt32* outDataSize, Boolean* outWriteable);
|
|
typedef OSStatus (* ma_AudioUnitGetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, void* outData, UInt32* ioDataSize);
|
|
typedef OSStatus (* ma_AudioUnitSetProperty_proc)(AudioUnit inUnit, AudioUnitPropertyID inID, AudioUnitScope inScope, AudioUnitElement inElement, const void* inData, UInt32 inDataSize);
|
|
typedef OSStatus (* ma_AudioUnitInitialize_proc)(AudioUnit inUnit);
|
|
typedef OSStatus (* ma_AudioUnitRender_proc)(AudioUnit inUnit, AudioUnitRenderActionFlags* ioActionFlags, const AudioTimeStamp* inTimeStamp, UInt32 inOutputBusNumber, UInt32 inNumberFrames, AudioBufferList* ioData);
|
|
|
|
#define MA_COREAUDIO_OUTPUT_BUS 0
|
|
#define MA_COREAUDIO_INPUT_BUS 1
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit);
|
|
#endif
|
|
|
|
#if defined(MA_APPLE_MOBILE)
|
|
static void ma_device__on_notification_interruption_began(ma_device* pDevice)
|
|
{
|
|
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began));
|
|
}
|
|
|
|
static void ma_device__on_notification_interruption_ended(ma_device* pDevice)
|
|
{
|
|
ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended));
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_result_from_OSStatus(OSStatus status)
|
|
{
|
|
switch (status)
|
|
{
|
|
case noErr: return MA_SUCCESS;
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
case kAudioHardwareNotRunningError: return MA_DEVICE_NOT_STARTED;
|
|
case kAudioHardwareUnspecifiedError: return MA_ERROR;
|
|
case kAudioHardwareUnknownPropertyError: return MA_INVALID_ARGS;
|
|
case kAudioHardwareBadPropertySizeError: return MA_INVALID_OPERATION;
|
|
case kAudioHardwareIllegalOperationError: return MA_INVALID_OPERATION;
|
|
case kAudioHardwareBadObjectError: return MA_INVALID_ARGS;
|
|
case kAudioHardwareBadDeviceError: return MA_INVALID_ARGS;
|
|
case kAudioHardwareBadStreamError: return MA_INVALID_ARGS;
|
|
case kAudioHardwareUnsupportedOperationError: return MA_INVALID_OPERATION;
|
|
case kAudioDeviceUnsupportedFormatError: return MA_FORMAT_NOT_SUPPORTED;
|
|
case kAudioDevicePermissionsError: return MA_ACCESS_DENIED;
|
|
#endif
|
|
default: return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static ma_channel ma_channel_from_AudioChannelBitmap(AudioChannelBitmap bit)
|
|
{
|
|
switch (bit)
|
|
{
|
|
case kAudioChannelBit_Left: return MA_CHANNEL_LEFT;
|
|
case kAudioChannelBit_Right: return MA_CHANNEL_RIGHT;
|
|
case kAudioChannelBit_Center: return MA_CHANNEL_FRONT_CENTER;
|
|
case kAudioChannelBit_LFEScreen: return MA_CHANNEL_LFE;
|
|
case kAudioChannelBit_LeftSurround: return MA_CHANNEL_BACK_LEFT;
|
|
case kAudioChannelBit_RightSurround: return MA_CHANNEL_BACK_RIGHT;
|
|
case kAudioChannelBit_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
|
|
case kAudioChannelBit_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
|
|
case kAudioChannelBit_CenterSurround: return MA_CHANNEL_BACK_CENTER;
|
|
case kAudioChannelBit_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
|
|
case kAudioChannelBit_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
|
|
case kAudioChannelBit_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
|
|
case kAudioChannelBit_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
|
|
case kAudioChannelBit_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
|
|
case kAudioChannelBit_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
|
|
case kAudioChannelBit_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
|
|
case kAudioChannelBit_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
|
|
case kAudioChannelBit_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
|
|
default: return MA_CHANNEL_NONE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_format_from_AudioStreamBasicDescription(const AudioStreamBasicDescription* pDescription, ma_format* pFormatOut)
|
|
{
|
|
MA_ASSERT(pDescription != NULL);
|
|
MA_ASSERT(pFormatOut != NULL);
|
|
|
|
*pFormatOut = ma_format_unknown;
|
|
|
|
if (pDescription->mFormatID != kAudioFormatLinearPCM) {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsAlignedHigh) != 0) {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ((ma_is_little_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) != 0) || (ma_is_big_endian() && (pDescription->mFormatFlags & kAudioFormatFlagIsBigEndian) == 0)) {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) {
|
|
if (pDescription->mBitsPerChannel == 32) {
|
|
*pFormatOut = ma_format_f32;
|
|
return MA_SUCCESS;
|
|
}
|
|
} else {
|
|
if ((pDescription->mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) {
|
|
if (pDescription->mBitsPerChannel == 16) {
|
|
*pFormatOut = ma_format_s16;
|
|
return MA_SUCCESS;
|
|
} else if (pDescription->mBitsPerChannel == 24) {
|
|
if (pDescription->mBytesPerFrame == (pDescription->mBitsPerChannel/8 * pDescription->mChannelsPerFrame)) {
|
|
*pFormatOut = ma_format_s24;
|
|
return MA_SUCCESS;
|
|
} else {
|
|
if (pDescription->mBytesPerFrame/pDescription->mChannelsPerFrame == sizeof(ma_int32)) {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
}
|
|
} else if (pDescription->mBitsPerChannel == 32) {
|
|
*pFormatOut = ma_format_s32;
|
|
return MA_SUCCESS;
|
|
}
|
|
} else {
|
|
if (pDescription->mBitsPerChannel == 8) {
|
|
*pFormatOut = ma_format_u8;
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
static ma_channel ma_channel_from_AudioChannelLabel(AudioChannelLabel label)
|
|
{
|
|
switch (label)
|
|
{
|
|
case kAudioChannelLabel_Unknown: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Unused: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_UseCoordinates: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Left: return MA_CHANNEL_LEFT;
|
|
case kAudioChannelLabel_Right: return MA_CHANNEL_RIGHT;
|
|
case kAudioChannelLabel_Center: return MA_CHANNEL_FRONT_CENTER;
|
|
case kAudioChannelLabel_LFEScreen: return MA_CHANNEL_LFE;
|
|
case kAudioChannelLabel_LeftSurround: return MA_CHANNEL_BACK_LEFT;
|
|
case kAudioChannelLabel_RightSurround: return MA_CHANNEL_BACK_RIGHT;
|
|
case kAudioChannelLabel_LeftCenter: return MA_CHANNEL_FRONT_LEFT_CENTER;
|
|
case kAudioChannelLabel_RightCenter: return MA_CHANNEL_FRONT_RIGHT_CENTER;
|
|
case kAudioChannelLabel_CenterSurround: return MA_CHANNEL_BACK_CENTER;
|
|
case kAudioChannelLabel_LeftSurroundDirect: return MA_CHANNEL_SIDE_LEFT;
|
|
case kAudioChannelLabel_RightSurroundDirect: return MA_CHANNEL_SIDE_RIGHT;
|
|
case kAudioChannelLabel_TopCenterSurround: return MA_CHANNEL_TOP_CENTER;
|
|
case kAudioChannelLabel_VerticalHeightLeft: return MA_CHANNEL_TOP_FRONT_LEFT;
|
|
case kAudioChannelLabel_VerticalHeightCenter: return MA_CHANNEL_TOP_FRONT_CENTER;
|
|
case kAudioChannelLabel_VerticalHeightRight: return MA_CHANNEL_TOP_FRONT_RIGHT;
|
|
case kAudioChannelLabel_TopBackLeft: return MA_CHANNEL_TOP_BACK_LEFT;
|
|
case kAudioChannelLabel_TopBackCenter: return MA_CHANNEL_TOP_BACK_CENTER;
|
|
case kAudioChannelLabel_TopBackRight: return MA_CHANNEL_TOP_BACK_RIGHT;
|
|
case kAudioChannelLabel_RearSurroundLeft: return MA_CHANNEL_BACK_LEFT;
|
|
case kAudioChannelLabel_RearSurroundRight: return MA_CHANNEL_BACK_RIGHT;
|
|
case kAudioChannelLabel_LeftWide: return MA_CHANNEL_SIDE_LEFT;
|
|
case kAudioChannelLabel_RightWide: return MA_CHANNEL_SIDE_RIGHT;
|
|
case kAudioChannelLabel_LFE2: return MA_CHANNEL_LFE;
|
|
case kAudioChannelLabel_LeftTotal: return MA_CHANNEL_LEFT;
|
|
case kAudioChannelLabel_RightTotal: return MA_CHANNEL_RIGHT;
|
|
case kAudioChannelLabel_HearingImpaired: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Narration: return MA_CHANNEL_MONO;
|
|
case kAudioChannelLabel_Mono: return MA_CHANNEL_MONO;
|
|
case kAudioChannelLabel_DialogCentricMix: return MA_CHANNEL_MONO;
|
|
case kAudioChannelLabel_CenterSurroundDirect: return MA_CHANNEL_BACK_CENTER;
|
|
case kAudioChannelLabel_Haptic: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Ambisonic_W: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Ambisonic_X: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Ambisonic_Y: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Ambisonic_Z: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_MS_Mid: return MA_CHANNEL_LEFT;
|
|
case kAudioChannelLabel_MS_Side: return MA_CHANNEL_RIGHT;
|
|
case kAudioChannelLabel_XY_X: return MA_CHANNEL_LEFT;
|
|
case kAudioChannelLabel_XY_Y: return MA_CHANNEL_RIGHT;
|
|
case kAudioChannelLabel_HeadphonesLeft: return MA_CHANNEL_LEFT;
|
|
case kAudioChannelLabel_HeadphonesRight: return MA_CHANNEL_RIGHT;
|
|
case kAudioChannelLabel_ClickTrack: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_ForeignLanguage: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Discrete: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_Discrete_0: return MA_CHANNEL_AUX_0;
|
|
case kAudioChannelLabel_Discrete_1: return MA_CHANNEL_AUX_1;
|
|
case kAudioChannelLabel_Discrete_2: return MA_CHANNEL_AUX_2;
|
|
case kAudioChannelLabel_Discrete_3: return MA_CHANNEL_AUX_3;
|
|
case kAudioChannelLabel_Discrete_4: return MA_CHANNEL_AUX_4;
|
|
case kAudioChannelLabel_Discrete_5: return MA_CHANNEL_AUX_5;
|
|
case kAudioChannelLabel_Discrete_6: return MA_CHANNEL_AUX_6;
|
|
case kAudioChannelLabel_Discrete_7: return MA_CHANNEL_AUX_7;
|
|
case kAudioChannelLabel_Discrete_8: return MA_CHANNEL_AUX_8;
|
|
case kAudioChannelLabel_Discrete_9: return MA_CHANNEL_AUX_9;
|
|
case kAudioChannelLabel_Discrete_10: return MA_CHANNEL_AUX_10;
|
|
case kAudioChannelLabel_Discrete_11: return MA_CHANNEL_AUX_11;
|
|
case kAudioChannelLabel_Discrete_12: return MA_CHANNEL_AUX_12;
|
|
case kAudioChannelLabel_Discrete_13: return MA_CHANNEL_AUX_13;
|
|
case kAudioChannelLabel_Discrete_14: return MA_CHANNEL_AUX_14;
|
|
case kAudioChannelLabel_Discrete_15: return MA_CHANNEL_AUX_15;
|
|
case kAudioChannelLabel_Discrete_65535: return MA_CHANNEL_NONE;
|
|
|
|
#if 0
|
|
case kAudioChannelLabel_HOA_ACN: return MA_CHANNEL_NONE;
|
|
case kAudioChannelLabel_HOA_ACN_0: return MA_CHANNEL_AUX_0;
|
|
case kAudioChannelLabel_HOA_ACN_1: return MA_CHANNEL_AUX_1;
|
|
case kAudioChannelLabel_HOA_ACN_2: return MA_CHANNEL_AUX_2;
|
|
case kAudioChannelLabel_HOA_ACN_3: return MA_CHANNEL_AUX_3;
|
|
case kAudioChannelLabel_HOA_ACN_4: return MA_CHANNEL_AUX_4;
|
|
case kAudioChannelLabel_HOA_ACN_5: return MA_CHANNEL_AUX_5;
|
|
case kAudioChannelLabel_HOA_ACN_6: return MA_CHANNEL_AUX_6;
|
|
case kAudioChannelLabel_HOA_ACN_7: return MA_CHANNEL_AUX_7;
|
|
case kAudioChannelLabel_HOA_ACN_8: return MA_CHANNEL_AUX_8;
|
|
case kAudioChannelLabel_HOA_ACN_9: return MA_CHANNEL_AUX_9;
|
|
case kAudioChannelLabel_HOA_ACN_10: return MA_CHANNEL_AUX_10;
|
|
case kAudioChannelLabel_HOA_ACN_11: return MA_CHANNEL_AUX_11;
|
|
case kAudioChannelLabel_HOA_ACN_12: return MA_CHANNEL_AUX_12;
|
|
case kAudioChannelLabel_HOA_ACN_13: return MA_CHANNEL_AUX_13;
|
|
case kAudioChannelLabel_HOA_ACN_14: return MA_CHANNEL_AUX_14;
|
|
case kAudioChannelLabel_HOA_ACN_15: return MA_CHANNEL_AUX_15;
|
|
case kAudioChannelLabel_HOA_ACN_65024: return MA_CHANNEL_NONE;
|
|
#endif
|
|
|
|
default: return MA_CHANNEL_NONE;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_get_channel_map_from_AudioChannelLayout(AudioChannelLayout* pChannelLayout, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
MA_ASSERT(pChannelLayout != NULL);
|
|
|
|
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
|
|
UInt32 iChannel;
|
|
for (iChannel = 0; iChannel < pChannelLayout->mNumberChannelDescriptions && iChannel < channelMapCap; ++iChannel) {
|
|
pChannelMap[iChannel] = ma_channel_from_AudioChannelLabel(pChannelLayout->mChannelDescriptions[iChannel].mChannelLabel);
|
|
}
|
|
} else
|
|
#if 0
|
|
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
|
|
UInt32 iChannel = 0;
|
|
UInt32 iBit;
|
|
AudioChannelBitmap bitmap = pChannelLayout->mChannelBitmap;
|
|
for (iBit = 0; iBit < 32 && iChannel < channelMapCap; ++iBit) {
|
|
AudioChannelBitmap bit = bitmap & (1 << iBit);
|
|
if (bit != 0) {
|
|
pChannelMap[iChannel++] = ma_channel_from_AudioChannelBit(bit);
|
|
}
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
UInt32 channelCount;
|
|
|
|
if (channelMapCap > 0xFFFFFFFF) {
|
|
channelMapCap = 0xFFFFFFFF;
|
|
}
|
|
|
|
channelCount = ma_min(AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag), (UInt32)channelMapCap);
|
|
|
|
switch (pChannelLayout->mChannelLayoutTag)
|
|
{
|
|
case kAudioChannelLayoutTag_Mono:
|
|
case kAudioChannelLayoutTag_Stereo:
|
|
case kAudioChannelLayoutTag_StereoHeadphones:
|
|
case kAudioChannelLayoutTag_MatrixStereo:
|
|
case kAudioChannelLayoutTag_MidSide:
|
|
case kAudioChannelLayoutTag_XY:
|
|
case kAudioChannelLayoutTag_Binaural:
|
|
case kAudioChannelLayoutTag_Ambisonic_B_Format:
|
|
{
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
|
|
} break;
|
|
|
|
case kAudioChannelLayoutTag_Octagonal:
|
|
{
|
|
pChannelMap[7] = MA_CHANNEL_SIDE_RIGHT;
|
|
pChannelMap[6] = MA_CHANNEL_SIDE_LEFT;
|
|
} MA_FALLTHROUGH;
|
|
case kAudioChannelLayoutTag_Hexagonal:
|
|
{
|
|
pChannelMap[5] = MA_CHANNEL_BACK_CENTER;
|
|
} MA_FALLTHROUGH;
|
|
case kAudioChannelLayoutTag_Pentagonal:
|
|
{
|
|
pChannelMap[4] = MA_CHANNEL_FRONT_CENTER;
|
|
} MA_FALLTHROUGH;
|
|
case kAudioChannelLayoutTag_Quadraphonic:
|
|
{
|
|
pChannelMap[3] = MA_CHANNEL_BACK_RIGHT;
|
|
pChannelMap[2] = MA_CHANNEL_BACK_LEFT;
|
|
pChannelMap[1] = MA_CHANNEL_RIGHT;
|
|
pChannelMap[0] = MA_CHANNEL_LEFT;
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if (defined(MAC_OS_VERSION_12_0) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_12_0) || \
|
|
(defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0)
|
|
#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMain
|
|
#else
|
|
#define AUDIO_OBJECT_PROPERTY_ELEMENT kAudioObjectPropertyElementMaster
|
|
#endif
|
|
|
|
#if !defined(MAC_OS_X_VERSION_10_8) || (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_8)
|
|
#define kAudioObjectPropertyScopeInput kAudioDevicePropertyScopeInput
|
|
#define kAudioObjectPropertyScopeOutput kAudioDevicePropertyScopeOutput
|
|
#endif
|
|
|
|
static ma_result ma_get_device_object_ids__coreaudio(ma_context* pContext, UInt32* pDeviceCount, AudioObjectID** ppDeviceObjectIDs)
|
|
{
|
|
AudioObjectPropertyAddress propAddressDevices;
|
|
UInt32 deviceObjectsDataSize;
|
|
OSStatus status;
|
|
AudioObjectID* pDeviceObjectIDs;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDeviceCount != NULL);
|
|
MA_ASSERT(ppDeviceObjectIDs != NULL);
|
|
|
|
*pDeviceCount = 0;
|
|
*ppDeviceObjectIDs = NULL;
|
|
|
|
propAddressDevices.mSelector = kAudioHardwarePropertyDevices;
|
|
propAddressDevices.mScope = kAudioObjectPropertyScopeGlobal;
|
|
propAddressDevices.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
pDeviceObjectIDs = (AudioObjectID*)ma_malloc(deviceObjectsDataSize, &pContext->allocationCallbacks);
|
|
if (pDeviceObjectIDs == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDevices, 0, NULL, &deviceObjectsDataSize, pDeviceObjectIDs);
|
|
if (status != noErr) {
|
|
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
*pDeviceCount = deviceObjectsDataSize / sizeof(AudioObjectID);
|
|
*ppDeviceObjectIDs = pDeviceObjectIDs;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_get_AudioObject_uid_as_CFStringRef(ma_context* pContext, AudioObjectID objectID, CFStringRef* pUID)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyDeviceUID;
|
|
propAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
dataSize = sizeof(*pUID);
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, pUID);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_get_AudioObject_uid(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
|
|
{
|
|
CFStringRef uid;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
result = ma_get_AudioObject_uid_as_CFStringRef(pContext, objectID, &uid);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(uid, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(uid);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_get_AudioObject_name(ma_context* pContext, AudioObjectID objectID, size_t bufferSize, char* bufferOut)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
CFStringRef deviceName = NULL;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyDeviceNameCFString;
|
|
propAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
dataSize = sizeof(deviceName);
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(objectID, &propAddress, 0, NULL, &dataSize, &deviceName);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
if (!((ma_CFStringGetCString_proc)pContext->coreaudio.CFStringGetCString)(deviceName, bufferOut, bufferSize, kCFStringEncodingUTF8)) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
((ma_CFRelease_proc)pContext->coreaudio.CFRelease)(deviceName);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_bool32 ma_does_AudioObject_support_scope(ma_context* pContext, AudioObjectID deviceObjectID, AudioObjectPropertyScope scope)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
AudioBufferList* pBufferList;
|
|
ma_bool32 isSupported;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
|
|
propAddress.mScope = scope;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
|
|
if (status != noErr) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
pBufferList = (AudioBufferList*)ma_malloc(dataSize, &pContext->allocationCallbacks);
|
|
if (pBufferList == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pBufferList);
|
|
if (status != noErr) {
|
|
ma_free(pBufferList, &pContext->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
|
|
isSupported = MA_FALSE;
|
|
if (pBufferList->mNumberBuffers > 0) {
|
|
isSupported = MA_TRUE;
|
|
}
|
|
|
|
ma_free(pBufferList, &pContext->allocationCallbacks);
|
|
return isSupported;
|
|
}
|
|
|
|
static ma_bool32 ma_does_AudioObject_support_playback(ma_context* pContext, AudioObjectID deviceObjectID)
|
|
{
|
|
return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeOutput);
|
|
}
|
|
|
|
static ma_bool32 ma_does_AudioObject_support_capture(ma_context* pContext, AudioObjectID deviceObjectID)
|
|
{
|
|
return ma_does_AudioObject_support_scope(pContext, deviceObjectID, kAudioObjectPropertyScopeInput);
|
|
}
|
|
|
|
static ma_result ma_get_AudioObject_stream_descriptions(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pDescriptionCount, AudioStreamRangedDescription** ppDescriptions)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
AudioStreamRangedDescription* pDescriptions;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDescriptionCount != NULL);
|
|
MA_ASSERT(ppDescriptions != NULL);
|
|
|
|
propAddress.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
|
|
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
pDescriptions = (AudioStreamRangedDescription*)ma_malloc(dataSize, &pContext->allocationCallbacks);
|
|
if (pDescriptions == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pDescriptions);
|
|
if (status != noErr) {
|
|
ma_free(pDescriptions, &pContext->allocationCallbacks);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
*pDescriptionCount = dataSize / sizeof(*pDescriptions);
|
|
*ppDescriptions = pDescriptions;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_get_AudioObject_channel_layout(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, AudioChannelLayout** ppChannelLayout)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
AudioChannelLayout* pChannelLayout;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(ppChannelLayout != NULL);
|
|
|
|
*ppChannelLayout = NULL;
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyPreferredChannelLayout;
|
|
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
pChannelLayout = (AudioChannelLayout*)ma_malloc(dataSize, &pContext->allocationCallbacks);
|
|
if (pChannelLayout == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pChannelLayout);
|
|
if (status != noErr) {
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
*ppChannelLayout = pChannelLayout;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_get_AudioObject_channel_count(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pChannelCount)
|
|
{
|
|
AudioChannelLayout* pChannelLayout;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pChannelCount != NULL);
|
|
|
|
*pChannelCount = 0;
|
|
|
|
result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelDescriptions) {
|
|
*pChannelCount = pChannelLayout->mNumberChannelDescriptions;
|
|
} else if (pChannelLayout->mChannelLayoutTag == kAudioChannelLayoutTag_UseChannelBitmap) {
|
|
*pChannelCount = ma_count_set_bits(pChannelLayout->mChannelBitmap);
|
|
} else {
|
|
*pChannelCount = AudioChannelLayoutTag_GetNumberOfChannels(pChannelLayout->mChannelLayoutTag);
|
|
}
|
|
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if 0
|
|
static ma_result ma_get_AudioObject_channel_map(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
AudioChannelLayout* pChannelLayout;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
result = ma_get_AudioObject_channel_layout(pContext, deviceObjectID, deviceType, &pChannelLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_get_AudioObject_sample_rates(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, UInt32* pSampleRateRangesCount, AudioValueRange** ppSampleRateRanges)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
AudioValueRange* pSampleRateRanges;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pSampleRateRangesCount != NULL);
|
|
MA_ASSERT(ppSampleRateRanges != NULL);
|
|
|
|
*pSampleRateRangesCount = 0;
|
|
*ppSampleRateRanges = NULL;
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
|
|
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
status = ((ma_AudioObjectGetPropertyDataSize_proc)pContext->coreaudio.AudioObjectGetPropertyDataSize)(deviceObjectID, &propAddress, 0, NULL, &dataSize);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
pSampleRateRanges = (AudioValueRange*)ma_malloc(dataSize, &pContext->allocationCallbacks);
|
|
if (pSampleRateRanges == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, pSampleRateRanges);
|
|
if (status != noErr) {
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
*pSampleRateRangesCount = dataSize / sizeof(*pSampleRateRanges);
|
|
*ppSampleRateRanges = pSampleRateRanges;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if 0
|
|
static ma_result ma_get_AudioObject_get_closest_sample_rate(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 sampleRateIn, ma_uint32* pSampleRateOut)
|
|
{
|
|
UInt32 sampleRateRangeCount;
|
|
AudioValueRange* pSampleRateRanges;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pSampleRateOut != NULL);
|
|
|
|
*pSampleRateOut = 0;
|
|
|
|
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (sampleRateRangeCount == 0) {
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (sampleRateIn == 0) {
|
|
UInt32 iMALSampleRate;
|
|
for (iMALSampleRate = 0; iMALSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iMALSampleRate) {
|
|
ma_uint32 malSampleRate = g_maStandardSampleRatePriorities[iMALSampleRate];
|
|
UInt32 iCASampleRate;
|
|
for (iCASampleRate = 0; iCASampleRate < sampleRateRangeCount; ++iCASampleRate) {
|
|
AudioValueRange caSampleRate = pSampleRateRanges[iCASampleRate];
|
|
if (caSampleRate.mMinimum <= malSampleRate && caSampleRate.mMaximum >= malSampleRate) {
|
|
*pSampleRateOut = malSampleRate;
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(sampleRateRangeCount > 0);
|
|
|
|
*pSampleRateOut = pSampleRateRanges[0].mMinimum;
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
} else {
|
|
UInt32 currentAbsoluteDifference = INT32_MAX;
|
|
UInt32 iCurrentClosestRange = (UInt32)-1;
|
|
UInt32 iRange;
|
|
for (iRange = 0; iRange < sampleRateRangeCount; ++iRange) {
|
|
if (pSampleRateRanges[iRange].mMinimum <= sampleRateIn && pSampleRateRanges[iRange].mMaximum >= sampleRateIn) {
|
|
*pSampleRateOut = sampleRateIn;
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
} else {
|
|
UInt32 absoluteDifference;
|
|
if (pSampleRateRanges[iRange].mMinimum > sampleRateIn) {
|
|
absoluteDifference = pSampleRateRanges[iRange].mMinimum - sampleRateIn;
|
|
} else {
|
|
absoluteDifference = sampleRateIn - pSampleRateRanges[iRange].mMaximum;
|
|
}
|
|
|
|
if (currentAbsoluteDifference > absoluteDifference) {
|
|
currentAbsoluteDifference = absoluteDifference;
|
|
iCurrentClosestRange = iRange;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(iCurrentClosestRange != (UInt32)-1);
|
|
|
|
*pSampleRateOut = pSampleRateRanges[iCurrentClosestRange].mMinimum;
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_get_AudioObject_closest_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32 bufferSizeInFramesIn, ma_uint32* pBufferSizeInFramesOut)
|
|
{
|
|
AudioObjectPropertyAddress propAddress;
|
|
AudioValueRange bufferSizeRange;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pBufferSizeInFramesOut != NULL);
|
|
|
|
*pBufferSizeInFramesOut = 0;
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
|
|
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
dataSize = sizeof(bufferSizeRange);
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &bufferSizeRange);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
if (bufferSizeInFramesIn < bufferSizeRange.mMinimum) {
|
|
*pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMinimum;
|
|
} else if (bufferSizeInFramesIn > bufferSizeRange.mMaximum) {
|
|
*pBufferSizeInFramesOut = (ma_uint32)bufferSizeRange.mMaximum;
|
|
} else {
|
|
*pBufferSizeInFramesOut = bufferSizeInFramesIn;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_set_AudioObject_buffer_size_in_frames(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_uint32* pPeriodSizeInOut)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 chosenBufferSizeInFrames;
|
|
AudioObjectPropertyAddress propAddress;
|
|
UInt32 dataSize;
|
|
OSStatus status;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
result = ma_get_AudioObject_closest_buffer_size_in_frames(pContext, deviceObjectID, deviceType, *pPeriodSizeInOut, &chosenBufferSizeInFrames);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyBufferFrameSize;
|
|
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(chosenBufferSizeInFrames), &chosenBufferSizeInFrames);
|
|
|
|
dataSize = sizeof(*pPeriodSizeInOut);
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(deviceObjectID, &propAddress, 0, NULL, &dataSize, &chosenBufferSizeInFrames);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
*pPeriodSizeInOut = chosenBufferSizeInFrames;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_find_default_AudioObjectID(ma_context* pContext, ma_device_type deviceType, AudioObjectID* pDeviceObjectID)
|
|
{
|
|
AudioObjectPropertyAddress propAddressDefaultDevice;
|
|
UInt32 defaultDeviceObjectIDSize = sizeof(AudioObjectID);
|
|
AudioObjectID defaultDeviceObjectID;
|
|
OSStatus status;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDeviceObjectID != NULL);
|
|
|
|
*pDeviceObjectID = 0;
|
|
|
|
propAddressDefaultDevice.mScope = kAudioObjectPropertyScopeGlobal;
|
|
propAddressDefaultDevice.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
if (deviceType == ma_device_type_playback) {
|
|
propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
|
} else {
|
|
propAddressDefaultDevice.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
|
}
|
|
|
|
defaultDeviceObjectIDSize = sizeof(AudioObjectID);
|
|
status = ((ma_AudioObjectGetPropertyData_proc)pContext->coreaudio.AudioObjectGetPropertyData)(kAudioObjectSystemObject, &propAddressDefaultDevice, 0, NULL, &defaultDeviceObjectIDSize, &defaultDeviceObjectID);
|
|
if (status == noErr) {
|
|
*pDeviceObjectID = defaultDeviceObjectID;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
static ma_result ma_find_AudioObjectID(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, AudioObjectID* pDeviceObjectID)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDeviceObjectID != NULL);
|
|
|
|
*pDeviceObjectID = 0;
|
|
|
|
if (pDeviceID == NULL) {
|
|
return ma_find_default_AudioObjectID(pContext, deviceType, pDeviceObjectID);
|
|
} else {
|
|
UInt32 deviceCount;
|
|
AudioObjectID* pDeviceObjectIDs;
|
|
ma_result result;
|
|
UInt32 iDevice;
|
|
|
|
result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
|
AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
|
|
|
|
char uid[256];
|
|
if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(uid), uid) != MA_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
|
|
if (strcmp(uid, pDeviceID->coreaudio) == 0) {
|
|
*pDeviceObjectID = deviceObjectID;
|
|
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
} else {
|
|
if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
|
|
if (strcmp(uid, pDeviceID->coreaudio) == 0) {
|
|
*pDeviceObjectID = deviceObjectID;
|
|
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
|
}
|
|
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
static ma_result ma_find_best_format__coreaudio(ma_context* pContext, AudioObjectID deviceObjectID, ma_device_type deviceType, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const AudioStreamBasicDescription* pOrigFormat, AudioStreamBasicDescription* pFormat)
|
|
{
|
|
UInt32 deviceFormatDescriptionCount;
|
|
AudioStreamRangedDescription* pDeviceFormatDescriptions;
|
|
ma_result result;
|
|
ma_uint32 desiredSampleRate;
|
|
ma_uint32 desiredChannelCount;
|
|
ma_format desiredFormat;
|
|
AudioStreamBasicDescription bestDeviceFormatSoFar;
|
|
ma_bool32 hasSupportedFormat;
|
|
UInt32 iFormat;
|
|
|
|
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &deviceFormatDescriptionCount, &pDeviceFormatDescriptions);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
desiredSampleRate = sampleRate;
|
|
if (desiredSampleRate == 0) {
|
|
desiredSampleRate = (ma_uint32)pOrigFormat->mSampleRate;
|
|
}
|
|
|
|
desiredChannelCount = channels;
|
|
if (desiredChannelCount == 0) {
|
|
desiredChannelCount = pOrigFormat->mChannelsPerFrame;
|
|
}
|
|
|
|
desiredFormat = format;
|
|
if (desiredFormat == ma_format_unknown) {
|
|
result = ma_format_from_AudioStreamBasicDescription(pOrigFormat, &desiredFormat);
|
|
if (result != MA_SUCCESS || desiredFormat == ma_format_unknown) {
|
|
desiredFormat = g_maFormatPriorities[0];
|
|
}
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&bestDeviceFormatSoFar);
|
|
|
|
hasSupportedFormat = MA_FALSE;
|
|
for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
|
|
ma_format formatFromDescription;
|
|
ma_result formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &formatFromDescription);
|
|
if (formatResult == MA_SUCCESS && formatFromDescription != ma_format_unknown) {
|
|
hasSupportedFormat = MA_TRUE;
|
|
bestDeviceFormatSoFar = pDeviceFormatDescriptions[iFormat].mFormat;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasSupportedFormat) {
|
|
ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
for (iFormat = 0; iFormat < deviceFormatDescriptionCount; ++iFormat) {
|
|
AudioStreamBasicDescription thisDeviceFormat = pDeviceFormatDescriptions[iFormat].mFormat;
|
|
ma_format thisSampleFormat;
|
|
ma_result formatResult;
|
|
ma_format bestSampleFormatSoFar;
|
|
|
|
formatResult = ma_format_from_AudioStreamBasicDescription(&pDeviceFormatDescriptions[iFormat].mFormat, &thisSampleFormat);
|
|
if (formatResult != MA_SUCCESS || thisSampleFormat == ma_format_unknown) {
|
|
continue;
|
|
}
|
|
|
|
ma_format_from_AudioStreamBasicDescription(&bestDeviceFormatSoFar, &bestSampleFormatSoFar);
|
|
|
|
if (thisDeviceFormat.mSampleRate != desiredSampleRate) {
|
|
if (bestDeviceFormatSoFar.mSampleRate == desiredSampleRate) {
|
|
continue;
|
|
} else {
|
|
if (thisDeviceFormat.mChannelsPerFrame != desiredChannelCount) {
|
|
if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
|
|
continue;
|
|
} else {
|
|
if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
} else {
|
|
if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
|
|
if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
} else {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (bestDeviceFormatSoFar.mSampleRate != desiredSampleRate) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
} else {
|
|
if (thisDeviceFormat.mChannelsPerFrame == desiredChannelCount) {
|
|
if (bestDeviceFormatSoFar.mChannelsPerFrame != desiredChannelCount) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
} else {
|
|
if (thisSampleFormat == desiredFormat) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
break;
|
|
} else {
|
|
if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (bestDeviceFormatSoFar.mChannelsPerFrame == desiredChannelCount) {
|
|
continue;
|
|
} else {
|
|
if (thisSampleFormat == bestSampleFormatSoFar) {
|
|
if (ma_get_format_priority_index(thisSampleFormat) < ma_get_format_priority_index(bestSampleFormatSoFar)) {
|
|
bestDeviceFormatSoFar = thisDeviceFormat;
|
|
continue;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*pFormat = bestDeviceFormatSoFar;
|
|
|
|
ma_free(pDeviceFormatDescriptions, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_get_AudioUnit_channel_map(ma_context* pContext, AudioUnit audioUnit, ma_device_type deviceType, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
AudioUnitScope deviceScope;
|
|
AudioUnitElement deviceBus;
|
|
UInt32 channelLayoutSize;
|
|
OSStatus status;
|
|
AudioChannelLayout* pChannelLayout;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
deviceScope = kAudioUnitScope_Input;
|
|
deviceBus = MA_COREAUDIO_OUTPUT_BUS;
|
|
} else {
|
|
deviceScope = kAudioUnitScope_Output;
|
|
deviceBus = MA_COREAUDIO_INPUT_BUS;
|
|
}
|
|
|
|
status = ((ma_AudioUnitGetPropertyInfo_proc)pContext->coreaudio.AudioUnitGetPropertyInfo)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, &channelLayoutSize, NULL);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
pChannelLayout = (AudioChannelLayout*)ma_malloc(channelLayoutSize, &pContext->allocationCallbacks);
|
|
if (pChannelLayout == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_AudioChannelLayout, deviceScope, deviceBus, pChannelLayout, &channelLayoutSize);
|
|
if (status != noErr) {
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
result = ma_get_channel_map_from_AudioChannelLayout(pChannelLayout, pChannelMap, channelMapCap);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
ma_free(pChannelLayout, &pContext->allocationCallbacks);
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_APPLE_DESKTOP)
|
|
static void ma_AVAudioSessionPortDescription_to_device_info(AVAudioSessionPortDescription* pPortDesc, ma_device_info* pInfo)
|
|
{
|
|
MA_ZERO_OBJECT(pInfo);
|
|
ma_strncpy_s(pInfo->name, sizeof(pInfo->name), [pPortDesc.portName UTF8String], (size_t)-1);
|
|
ma_strncpy_s(pInfo->id.coreaudio, sizeof(pInfo->id.coreaudio), [pPortDesc.UID UTF8String], (size_t)-1);
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_context_enumerate_devices__coreaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
UInt32 deviceCount;
|
|
AudioObjectID* pDeviceObjectIDs;
|
|
AudioObjectID defaultDeviceObjectIDPlayback;
|
|
AudioObjectID defaultDeviceObjectIDCapture;
|
|
ma_result result;
|
|
UInt32 iDevice;
|
|
|
|
ma_find_default_AudioObjectID(pContext, ma_device_type_playback, &defaultDeviceObjectIDPlayback);
|
|
ma_find_default_AudioObjectID(pContext, ma_device_type_capture, &defaultDeviceObjectIDCapture);
|
|
|
|
result = ma_get_device_object_ids__coreaudio(pContext, &deviceCount, &pDeviceObjectIDs);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
for (iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
|
AudioObjectID deviceObjectID = pDeviceObjectIDs[iDevice];
|
|
ma_device_info info;
|
|
|
|
MA_ZERO_OBJECT(&info);
|
|
if (ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(info.id.coreaudio), info.id.coreaudio) != MA_SUCCESS) {
|
|
continue;
|
|
}
|
|
if (ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(info.name), info.name) != MA_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
if (ma_does_AudioObject_support_playback(pContext, deviceObjectID)) {
|
|
if (deviceObjectID == defaultDeviceObjectIDPlayback) {
|
|
info.isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
|
|
break;
|
|
}
|
|
}
|
|
if (ma_does_AudioObject_support_capture(pContext, deviceObjectID)) {
|
|
if (deviceObjectID == defaultDeviceObjectIDCapture) {
|
|
info.isDefault = MA_TRUE;
|
|
}
|
|
|
|
if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_free(pDeviceObjectIDs, &pContext->allocationCallbacks);
|
|
#else
|
|
ma_device_info info;
|
|
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
|
|
NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
|
|
|
|
for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
|
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
|
|
if (!callback(pContext, ma_device_type_playback, &info, pUserData)) {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
|
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, &info);
|
|
if (!callback(pContext, ma_device_type_capture, &info, pUserData)) {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
{
|
|
AudioObjectID deviceObjectID;
|
|
AudioObjectID defaultDeviceObjectID;
|
|
UInt32 streamDescriptionCount;
|
|
AudioStreamRangedDescription* pStreamDescriptions;
|
|
UInt32 iStreamDescription;
|
|
UInt32 sampleRateRangeCount;
|
|
AudioValueRange* pSampleRateRanges;
|
|
|
|
ma_find_default_AudioObjectID(pContext, deviceType, &defaultDeviceObjectID);
|
|
|
|
result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_get_AudioObject_uid(pContext, deviceObjectID, sizeof(pDeviceInfo->id.coreaudio), pDeviceInfo->id.coreaudio);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pDeviceInfo->name), pDeviceInfo->name);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (deviceObjectID == defaultDeviceObjectID) {
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
|
|
{
|
|
ma_format uniqueFormats[ma_format_count];
|
|
ma_uint32 uniqueFormatCount = 0;
|
|
ma_uint32 channels;
|
|
|
|
result = ma_get_AudioObject_channel_count(pContext, deviceObjectID, deviceType, &channels);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_get_AudioObject_stream_descriptions(pContext, deviceObjectID, deviceType, &streamDescriptionCount, &pStreamDescriptions);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
for (iStreamDescription = 0; iStreamDescription < streamDescriptionCount; ++iStreamDescription) {
|
|
ma_format format;
|
|
ma_bool32 hasFormatBeenHandled = MA_FALSE;
|
|
ma_uint32 iOutputFormat;
|
|
ma_uint32 iSampleRate;
|
|
|
|
result = ma_format_from_AudioStreamBasicDescription(&pStreamDescriptions[iStreamDescription].mFormat, &format);
|
|
if (result != MA_SUCCESS) {
|
|
continue;
|
|
}
|
|
|
|
MA_ASSERT(format != ma_format_unknown);
|
|
|
|
for (iOutputFormat = 0; iOutputFormat < uniqueFormatCount; ++iOutputFormat) {
|
|
if (uniqueFormats[iOutputFormat] == format) {
|
|
hasFormatBeenHandled = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (hasFormatBeenHandled) {
|
|
continue;
|
|
}
|
|
|
|
uniqueFormats[uniqueFormatCount] = format;
|
|
uniqueFormatCount += 1;
|
|
|
|
result = ma_get_AudioObject_sample_rates(pContext, deviceObjectID, deviceType, &sampleRateRangeCount, &pSampleRateRanges);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
for (iSampleRate = 0; iSampleRate < sampleRateRangeCount; ++iSampleRate) {
|
|
ma_uint32 iStandardSampleRate;
|
|
for (iStandardSampleRate = 0; iStandardSampleRate < ma_countof(g_maStandardSampleRatePriorities); iStandardSampleRate += 1) {
|
|
ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iStandardSampleRate];
|
|
if (standardSampleRate >= pSampleRateRanges[iSampleRate].mMinimum && standardSampleRate <= pSampleRateRanges[iSampleRate].mMaximum) {
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = standardSampleRate;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
|
|
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_free(pSampleRateRanges, &pContext->allocationCallbacks);
|
|
|
|
if (pDeviceInfo->nativeDataFormatCount >= ma_countof(pDeviceInfo->nativeDataFormats)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
ma_free(pStreamDescriptions, &pContext->allocationCallbacks);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
AudioComponentDescription desc;
|
|
AudioComponent component;
|
|
AudioUnit audioUnit;
|
|
OSStatus status;
|
|
AudioUnitScope formatScope;
|
|
AudioUnitElement formatElement;
|
|
AudioStreamBasicDescription bestFormat;
|
|
UInt32 propSize;
|
|
|
|
if (pDeviceID != NULL && pDeviceID->coreaudio[0] != '\0') {
|
|
ma_bool32 found = MA_FALSE;
|
|
if (deviceType == ma_device_type_playback) {
|
|
NSArray *pOutputs = [[[AVAudioSession sharedInstance] currentRoute] outputs];
|
|
for (AVAudioSessionPortDescription* pPortDesc in pOutputs) {
|
|
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
|
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
|
|
found = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
|
|
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
|
|
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
|
|
ma_AVAudioSessionPortDescription_to_device_info(pPortDesc, pDeviceInfo);
|
|
found = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
return MA_DOES_NOT_EXIST;
|
|
}
|
|
} else {
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
}
|
|
|
|
desc.componentType = kAudioUnitType_Output;
|
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
desc.componentFlags = 0;
|
|
desc.componentFlagsMask = 0;
|
|
|
|
component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
|
|
if (component == NULL) {
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)(component, &audioUnit);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
|
|
formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
|
|
|
|
propSize = sizeof(bestFormat);
|
|
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, &propSize);
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(audioUnit);
|
|
audioUnit = NULL;
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pDeviceInfo->nativeDataFormats[0].format);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormats[0].channels = bestFormat.mChannelsPerFrame;
|
|
|
|
@autoreleasepool {
|
|
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
|
|
MA_ASSERT(pAudioSession != NULL);
|
|
|
|
pDeviceInfo->nativeDataFormats[0].sampleRate = (ma_uint32)pAudioSession.sampleRate;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
(void)pDeviceInfo;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static AudioBufferList* ma_allocate_AudioBufferList__coreaudio(ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
AudioBufferList* pBufferList;
|
|
UInt32 audioBufferSizeInBytes;
|
|
size_t allocationSize;
|
|
|
|
MA_ASSERT(sizeInFrames > 0);
|
|
MA_ASSERT(format != ma_format_unknown);
|
|
MA_ASSERT(channels > 0);
|
|
|
|
allocationSize = sizeof(AudioBufferList) - sizeof(AudioBuffer);
|
|
if (layout == ma_stream_layout_interleaved) {
|
|
allocationSize += sizeof(AudioBuffer) * 1;
|
|
} else {
|
|
allocationSize += sizeof(AudioBuffer) * channels;
|
|
}
|
|
|
|
allocationSize += sizeInFrames * ma_get_bytes_per_frame(format, channels);
|
|
|
|
pBufferList = (AudioBufferList*)ma_malloc(allocationSize, pAllocationCallbacks);
|
|
if (pBufferList == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
audioBufferSizeInBytes = (UInt32)(sizeInFrames * ma_get_bytes_per_sample(format));
|
|
|
|
if (layout == ma_stream_layout_interleaved) {
|
|
pBufferList->mNumberBuffers = 1;
|
|
pBufferList->mBuffers[0].mNumberChannels = channels;
|
|
pBufferList->mBuffers[0].mDataByteSize = audioBufferSizeInBytes * channels;
|
|
pBufferList->mBuffers[0].mData = (ma_uint8*)pBufferList + sizeof(AudioBufferList);
|
|
} else {
|
|
ma_uint32 iBuffer;
|
|
pBufferList->mNumberBuffers = channels;
|
|
for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
|
|
pBufferList->mBuffers[iBuffer].mNumberChannels = 1;
|
|
pBufferList->mBuffers[iBuffer].mDataByteSize = audioBufferSizeInBytes;
|
|
pBufferList->mBuffers[iBuffer].mData = (ma_uint8*)pBufferList + ((sizeof(AudioBufferList) - sizeof(AudioBuffer)) + (sizeof(AudioBuffer) * channels)) + (audioBufferSizeInBytes * iBuffer);
|
|
}
|
|
}
|
|
|
|
return pBufferList;
|
|
}
|
|
|
|
static ma_result ma_device_realloc_AudioBufferList__coreaudio(ma_device* pDevice, ma_uint32 sizeInFrames, ma_format format, ma_uint32 channels, ma_stream_layout layout)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(format != ma_format_unknown);
|
|
MA_ASSERT(channels > 0);
|
|
|
|
if (pDevice->coreaudio.audioBufferCapInFrames < sizeInFrames) {
|
|
AudioBufferList* pNewAudioBufferList;
|
|
|
|
pNewAudioBufferList = ma_allocate_AudioBufferList__coreaudio(sizeInFrames, format, channels, layout, &pDevice->pContext->allocationCallbacks);
|
|
if (pNewAudioBufferList == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
|
|
pDevice->coreaudio.pAudioBufferList = pNewAudioBufferList;
|
|
pDevice->coreaudio.audioBufferCapInFrames = sizeInFrames;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static OSStatus ma_on_output__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pBufferList)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
ma_stream_layout layout;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
layout = ma_stream_layout_interleaved;
|
|
if (pBufferList->mBuffers[0].mNumberChannels != pDevice->playback.internalChannels) {
|
|
layout = ma_stream_layout_deinterleaved;
|
|
}
|
|
|
|
if (layout == ma_stream_layout_interleaved) {
|
|
UInt32 iBuffer;
|
|
for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; ++iBuffer) {
|
|
if (pBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->playback.internalChannels) {
|
|
ma_uint32 frameCountForThisBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
if (frameCountForThisBuffer > 0) {
|
|
ma_device_handle_backend_data_callback(pDevice, pBufferList->mBuffers[iBuffer].mData, NULL, frameCountForThisBuffer);
|
|
}
|
|
|
|
} else {
|
|
MA_ZERO_MEMORY(pBufferList->mBuffers[iBuffer].mData, pBufferList->mBuffers[iBuffer].mDataByteSize);
|
|
}
|
|
}
|
|
} else {
|
|
MA_ASSERT(pDevice->playback.internalChannels <= MA_MAX_CHANNELS);
|
|
|
|
if ((pBufferList->mNumberBuffers % pDevice->playback.internalChannels) == 0) {
|
|
ma_uint8 tempBuffer[4096];
|
|
UInt32 iBuffer;
|
|
|
|
for (iBuffer = 0; iBuffer < pBufferList->mNumberBuffers; iBuffer += pDevice->playback.internalChannels) {
|
|
ma_uint32 frameCountPerBuffer = pBufferList->mBuffers[iBuffer].mDataByteSize / ma_get_bytes_per_sample(pDevice->playback.internalFormat);
|
|
ma_uint32 framesRemaining = frameCountPerBuffer;
|
|
|
|
while (framesRemaining > 0) {
|
|
void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
|
|
ma_uint32 iChannel;
|
|
ma_uint32 framesToRead = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
if (framesToRead > framesRemaining) {
|
|
framesToRead = framesRemaining;
|
|
}
|
|
|
|
ma_device_handle_backend_data_callback(pDevice, tempBuffer, NULL, framesToRead);
|
|
|
|
for (iChannel = 0; iChannel < pDevice->playback.internalChannels; ++iChannel) {
|
|
ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pBufferList->mBuffers[iBuffer+iChannel].mData, (frameCountPerBuffer - framesRemaining) * ma_get_bytes_per_sample(pDevice->playback.internalFormat));
|
|
}
|
|
|
|
ma_deinterleave_pcm_frames(pDevice->playback.internalFormat, pDevice->playback.internalChannels, framesToRead, tempBuffer, ppDeinterleavedBuffers);
|
|
|
|
framesRemaining -= framesToRead;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(void)pActionFlags;
|
|
(void)pTimeStamp;
|
|
(void)busNumber;
|
|
(void)frameCount;
|
|
|
|
return noErr;
|
|
}
|
|
|
|
static OSStatus ma_on_input__coreaudio(void* pUserData, AudioUnitRenderActionFlags* pActionFlags, const AudioTimeStamp* pTimeStamp, UInt32 busNumber, UInt32 frameCount, AudioBufferList* pUnusedBufferList)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
AudioBufferList* pRenderedBufferList;
|
|
ma_result result;
|
|
ma_stream_layout layout;
|
|
ma_uint32 iBuffer;
|
|
OSStatus status;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
|
|
MA_ASSERT(pRenderedBufferList);
|
|
|
|
layout = ma_stream_layout_interleaved;
|
|
if (pRenderedBufferList->mBuffers[0].mNumberChannels != pDevice->capture.internalChannels) {
|
|
layout = ma_stream_layout_deinterleaved;
|
|
}
|
|
|
|
result = ma_device_realloc_AudioBufferList__coreaudio(pDevice, frameCount, pDevice->capture.internalFormat, pDevice->capture.internalChannels, layout);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "Failed to allocate AudioBufferList for capture.\n");
|
|
return noErr;
|
|
}
|
|
|
|
pRenderedBufferList = (AudioBufferList*)pDevice->coreaudio.pAudioBufferList;
|
|
MA_ASSERT(pRenderedBufferList);
|
|
|
|
for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
|
|
pRenderedBufferList->mBuffers[iBuffer].mDataByteSize = pDevice->coreaudio.audioBufferCapInFrames * ma_get_bytes_per_sample(pDevice->capture.internalFormat) * pRenderedBufferList->mBuffers[iBuffer].mNumberChannels;
|
|
}
|
|
|
|
status = ((ma_AudioUnitRender_proc)pDevice->pContext->coreaudio.AudioUnitRender)((AudioUnit)pDevice->coreaudio.audioUnitCapture, pActionFlags, pTimeStamp, busNumber, frameCount, pRenderedBufferList);
|
|
if (status != noErr) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "ERROR: AudioUnitRender() failed with %d.\n", (int)status);
|
|
return status;
|
|
}
|
|
|
|
if (layout == ma_stream_layout_interleaved) {
|
|
for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; ++iBuffer) {
|
|
if (pRenderedBufferList->mBuffers[iBuffer].mNumberChannels == pDevice->capture.internalChannels) {
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, pRenderedBufferList->mBuffers[iBuffer].mData, frameCount);
|
|
} else {
|
|
ma_uint8 silentBuffer[4096];
|
|
ma_uint32 framesRemaining;
|
|
|
|
MA_ZERO_MEMORY(silentBuffer, sizeof(silentBuffer));
|
|
|
|
framesRemaining = frameCount;
|
|
while (framesRemaining > 0) {
|
|
ma_uint32 framesToSend = sizeof(silentBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
if (framesToSend > framesRemaining) {
|
|
framesToSend = framesRemaining;
|
|
}
|
|
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, silentBuffer, framesToSend);
|
|
|
|
framesRemaining -= framesToSend;
|
|
}
|
|
|
|
}
|
|
}
|
|
} else {
|
|
MA_ASSERT(pDevice->capture.internalChannels <= MA_MAX_CHANNELS);
|
|
|
|
if ((pRenderedBufferList->mNumberBuffers % pDevice->capture.internalChannels) == 0) {
|
|
ma_uint8 tempBuffer[4096];
|
|
for (iBuffer = 0; iBuffer < pRenderedBufferList->mNumberBuffers; iBuffer += pDevice->capture.internalChannels) {
|
|
ma_uint32 framesRemaining = frameCount;
|
|
while (framesRemaining > 0) {
|
|
void* ppDeinterleavedBuffers[MA_MAX_CHANNELS];
|
|
ma_uint32 iChannel;
|
|
ma_uint32 framesToSend = sizeof(tempBuffer) / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
if (framesToSend > framesRemaining) {
|
|
framesToSend = framesRemaining;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < pDevice->capture.internalChannels; ++iChannel) {
|
|
ppDeinterleavedBuffers[iChannel] = (void*)ma_offset_ptr(pRenderedBufferList->mBuffers[iBuffer+iChannel].mData, (frameCount - framesRemaining) * ma_get_bytes_per_sample(pDevice->capture.internalFormat));
|
|
}
|
|
|
|
ma_interleave_pcm_frames(pDevice->capture.internalFormat, pDevice->capture.internalChannels, framesToSend, (const void**)ppDeinterleavedBuffers, tempBuffer);
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, tempBuffer, framesToSend);
|
|
|
|
framesRemaining -= framesToSend;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
(void)pActionFlags;
|
|
(void)pTimeStamp;
|
|
(void)busNumber;
|
|
(void)frameCount;
|
|
(void)pUnusedBufferList;
|
|
|
|
return noErr;
|
|
}
|
|
|
|
static void on_start_stop__coreaudio(void* pUserData, AudioUnit audioUnit, AudioUnitPropertyID propertyID, AudioUnitScope scope, AudioUnitElement element)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
|
|
((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
|
|
return;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized || ma_device_get_state(pDevice) == ma_device_state_stopping || ma_device_get_state(pDevice) == ma_device_state_stopped) {
|
|
ma_device__on_notification_stopped(pDevice);
|
|
} else {
|
|
UInt32 isRunning;
|
|
UInt32 isRunningSize = sizeof(isRunning);
|
|
OSStatus status = ((ma_AudioUnitGetProperty_proc)pDevice->pContext->coreaudio.AudioUnitGetProperty)(audioUnit, kAudioOutputUnitProperty_IsRunning, scope, element, &isRunning, &isRunningSize);
|
|
if (status != noErr) {
|
|
goto done;
|
|
}
|
|
|
|
if (!isRunning) {
|
|
|
|
if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isDefaultPlaybackDevice) ||
|
|
((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isDefaultCaptureDevice)) {
|
|
if (((audioUnit == pDevice->coreaudio.audioUnitPlayback) && pDevice->coreaudio.isSwitchingPlaybackDevice) ||
|
|
((audioUnit == pDevice->coreaudio.audioUnitCapture) && pDevice->coreaudio.isSwitchingCaptureDevice)) {
|
|
goto done;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
ma_device__on_notification_stopped(pDevice);
|
|
}
|
|
}
|
|
|
|
(void)propertyID;
|
|
|
|
done:
|
|
ma_event_signal(&pDevice->coreaudio.stopEvent);
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
static ma_spinlock g_DeviceTrackingInitLock_CoreAudio = 0;
|
|
static ma_uint32 g_DeviceTrackingInitCounter_CoreAudio = 0;
|
|
static ma_mutex g_DeviceTrackingMutex_CoreAudio;
|
|
static ma_device** g_ppTrackedDevices_CoreAudio = NULL;
|
|
static ma_uint32 g_TrackedDeviceCap_CoreAudio = 0;
|
|
static ma_uint32 g_TrackedDeviceCount_CoreAudio = 0;
|
|
|
|
static OSStatus ma_default_device_changed__coreaudio(AudioObjectID objectID, UInt32 addressCount, const AudioObjectPropertyAddress* pAddresses, void* pUserData)
|
|
{
|
|
ma_device_type deviceType;
|
|
|
|
if (addressCount == 0) {
|
|
return noErr;
|
|
}
|
|
|
|
if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultOutputDevice) {
|
|
deviceType = ma_device_type_playback;
|
|
} else if (pAddresses[0].mSelector == kAudioHardwarePropertyDefaultInputDevice) {
|
|
deviceType = ma_device_type_capture;
|
|
} else {
|
|
return noErr;
|
|
}
|
|
|
|
ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
|
|
{
|
|
ma_uint32 iDevice;
|
|
for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
|
|
ma_result reinitResult;
|
|
ma_device* pDevice;
|
|
|
|
pDevice = g_ppTrackedDevices_CoreAudio[iDevice];
|
|
if (pDevice->type == deviceType || pDevice->type == ma_device_type_duplex) {
|
|
if (deviceType == ma_device_type_playback) {
|
|
pDevice->coreaudio.isSwitchingPlaybackDevice = MA_TRUE;
|
|
reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
|
|
pDevice->coreaudio.isSwitchingPlaybackDevice = MA_FALSE;
|
|
} else {
|
|
pDevice->coreaudio.isSwitchingCaptureDevice = MA_TRUE;
|
|
reinitResult = ma_device_reinit_internal__coreaudio(pDevice, deviceType, MA_TRUE);
|
|
pDevice->coreaudio.isSwitchingCaptureDevice = MA_FALSE;
|
|
}
|
|
|
|
if (reinitResult == MA_SUCCESS) {
|
|
ma_device__post_init_setup(pDevice, deviceType);
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
OSStatus status;
|
|
if (deviceType == ma_device_type_playback) {
|
|
status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
if (status != noErr) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
}
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
}
|
|
} else if (deviceType == ma_device_type_capture) {
|
|
status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
if (status != noErr) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
}
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_device__on_notification_rerouted(pDevice);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
|
|
|
|
(void)objectID;
|
|
(void)pUserData;
|
|
|
|
return noErr;
|
|
}
|
|
|
|
static ma_result ma_context__init_device_tracking__coreaudio(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
|
|
{
|
|
if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
|
|
AudioObjectPropertyAddress propAddress;
|
|
propAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
ma_mutex_init(&g_DeviceTrackingMutex_CoreAudio);
|
|
|
|
propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
|
((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
|
|
|
|
propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
|
((ma_AudioObjectAddPropertyListener_proc)pContext->coreaudio.AudioObjectAddPropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
|
|
|
|
}
|
|
g_DeviceTrackingInitCounter_CoreAudio += 1;
|
|
}
|
|
ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context__uninit_device_tracking__coreaudio(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
ma_spinlock_lock(&g_DeviceTrackingInitLock_CoreAudio);
|
|
{
|
|
if (g_DeviceTrackingInitCounter_CoreAudio > 0)
|
|
g_DeviceTrackingInitCounter_CoreAudio -= 1;
|
|
|
|
if (g_DeviceTrackingInitCounter_CoreAudio == 0) {
|
|
AudioObjectPropertyAddress propAddress;
|
|
propAddress.mScope = kAudioObjectPropertyScopeGlobal;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
propAddress.mSelector = kAudioHardwarePropertyDefaultInputDevice;
|
|
((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
|
|
|
|
propAddress.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
|
((ma_AudioObjectRemovePropertyListener_proc)pContext->coreaudio.AudioObjectRemovePropertyListener)(kAudioObjectSystemObject, &propAddress, &ma_default_device_changed__coreaudio, NULL);
|
|
|
|
if (g_ppTrackedDevices_CoreAudio != NULL) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "You have uninitialized all contexts while an associated device is still active.");
|
|
ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_mutex_uninit(&g_DeviceTrackingMutex_CoreAudio);
|
|
}
|
|
}
|
|
ma_spinlock_unlock(&g_DeviceTrackingInitLock_CoreAudio);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device__track__coreaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
|
|
{
|
|
if (g_TrackedDeviceCap_CoreAudio <= g_TrackedDeviceCount_CoreAudio) {
|
|
ma_uint32 newCap;
|
|
ma_device** ppNewDevices;
|
|
|
|
newCap = g_TrackedDeviceCap_CoreAudio * 2;
|
|
if (newCap == 0) {
|
|
newCap = 1;
|
|
}
|
|
|
|
ppNewDevices = (ma_device**)ma_realloc(g_ppTrackedDevices_CoreAudio, sizeof(*g_ppTrackedDevices_CoreAudio)*newCap, &pDevice->pContext->allocationCallbacks);
|
|
if (ppNewDevices == NULL) {
|
|
ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
g_ppTrackedDevices_CoreAudio = ppNewDevices;
|
|
g_TrackedDeviceCap_CoreAudio = newCap;
|
|
}
|
|
|
|
g_ppTrackedDevices_CoreAudio[g_TrackedDeviceCount_CoreAudio] = pDevice;
|
|
g_TrackedDeviceCount_CoreAudio += 1;
|
|
}
|
|
ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device__untrack__coreaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_mutex_lock(&g_DeviceTrackingMutex_CoreAudio);
|
|
{
|
|
ma_uint32 iDevice;
|
|
for (iDevice = 0; iDevice < g_TrackedDeviceCount_CoreAudio; iDevice += 1) {
|
|
if (g_ppTrackedDevices_CoreAudio[iDevice] == pDevice) {
|
|
ma_uint32 jDevice;
|
|
for (jDevice = iDevice; jDevice < g_TrackedDeviceCount_CoreAudio-1; jDevice += 1) {
|
|
g_ppTrackedDevices_CoreAudio[jDevice] = g_ppTrackedDevices_CoreAudio[jDevice+1];
|
|
}
|
|
|
|
g_TrackedDeviceCount_CoreAudio -= 1;
|
|
|
|
if (g_TrackedDeviceCount_CoreAudio == 0) {
|
|
ma_free(g_ppTrackedDevices_CoreAudio, &pDevice->pContext->allocationCallbacks);
|
|
g_ppTrackedDevices_CoreAudio = NULL;
|
|
g_TrackedDeviceCap_CoreAudio = 0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
ma_mutex_unlock(&g_DeviceTrackingMutex_CoreAudio);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_APPLE_MOBILE)
|
|
@interface ma_ios_notification_handler:NSObject {
|
|
ma_device* m_pDevice;
|
|
}
|
|
@end
|
|
|
|
@implementation ma_ios_notification_handler
|
|
-(id)init:(ma_device*)pDevice
|
|
{
|
|
self = [super init];
|
|
m_pDevice = pDevice;
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_route_change:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
|
|
|
|
return self;
|
|
}
|
|
|
|
-(void)dealloc
|
|
{
|
|
[self remove_handler];
|
|
|
|
#if defined(__has_feature)
|
|
#if !__has_feature(objc_arc)
|
|
[super dealloc];
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
-(void)remove_handler
|
|
{
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionRouteChangeNotification object:nil];
|
|
[[NSNotificationCenter defaultCenter] removeObserver:self name:AVAudioSessionInterruptionNotification object:nil];
|
|
}
|
|
|
|
-(void)handle_interruption:(NSNotification*)pNotification
|
|
{
|
|
NSInteger type = [[[pNotification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey] integerValue];
|
|
switch (type)
|
|
{
|
|
case AVAudioSessionInterruptionTypeBegan:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeBegan\n");
|
|
|
|
ma_device_stop(m_pDevice);
|
|
|
|
ma_device__on_notification_interruption_began(m_pDevice);
|
|
} break;
|
|
|
|
case AVAudioSessionInterruptionTypeEnded:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Interruption: AVAudioSessionInterruptionTypeEnded\n");
|
|
ma_device__on_notification_interruption_ended(m_pDevice);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
-(void)handle_route_change:(NSNotification*)pNotification
|
|
{
|
|
AVAudioSession* pSession = [AVAudioSession sharedInstance];
|
|
|
|
NSInteger reason = [[[pNotification userInfo] objectForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
|
|
switch (reason)
|
|
{
|
|
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOldDeviceUnavailable\n");
|
|
} break;
|
|
|
|
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNewDeviceAvailable\n");
|
|
} break;
|
|
|
|
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory\n");
|
|
} break;
|
|
|
|
case AVAudioSessionRouteChangeReasonWakeFromSleep:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonWakeFromSleep\n");
|
|
} break;
|
|
|
|
case AVAudioSessionRouteChangeReasonOverride:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonOverride\n");
|
|
} break;
|
|
|
|
case AVAudioSessionRouteChangeReasonCategoryChange:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonCategoryChange\n");
|
|
} break;
|
|
|
|
case AVAudioSessionRouteChangeReasonUnknown:
|
|
default:
|
|
{
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_INFO, "[Core Audio] Route Changed: AVAudioSessionRouteChangeReasonUnknown\n");
|
|
} break;
|
|
}
|
|
|
|
ma_log_postf(ma_device_get_log(m_pDevice), MA_LOG_LEVEL_DEBUG, "[Core Audio] Changing Route. inputNumberChannels=%d; outputNumberOfChannels=%d\n", (int)pSession.inputNumberOfChannels, (int)pSession.outputNumberOfChannels);
|
|
|
|
ma_device__on_notification_rerouted(m_pDevice);
|
|
}
|
|
@end
|
|
#endif
|
|
|
|
static ma_result ma_device_uninit__coreaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_uninitialized);
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
ma_device__untrack__coreaudio(pDevice);
|
|
#endif
|
|
#if defined(MA_APPLE_MOBILE)
|
|
if (pDevice->coreaudio.pNotificationHandler != NULL) {
|
|
ma_ios_notification_handler* pNotificationHandler = (MA_BRIDGE_TRANSFER ma_ios_notification_handler*)pDevice->coreaudio.pNotificationHandler;
|
|
[pNotificationHandler remove_handler];
|
|
}
|
|
#endif
|
|
|
|
if (pDevice->coreaudio.audioUnitCapture != NULL) {
|
|
((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
}
|
|
if (pDevice->coreaudio.audioUnitPlayback != NULL) {
|
|
((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
}
|
|
|
|
if (pDevice->coreaudio.pAudioBufferList) {
|
|
ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
ma_bool32 allowNominalSampleRateChange;
|
|
|
|
ma_format formatIn;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 sampleRateIn;
|
|
ma_channel channelMapIn[MA_MAX_CHANNELS];
|
|
ma_uint32 periodSizeInFramesIn;
|
|
ma_uint32 periodSizeInMillisecondsIn;
|
|
ma_uint32 periodsIn;
|
|
ma_share_mode shareMode;
|
|
ma_performance_profile performanceProfile;
|
|
ma_bool32 registerStopEvent;
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
AudioObjectID deviceObjectID;
|
|
#endif
|
|
AudioComponent component;
|
|
AudioUnit audioUnit;
|
|
AudioBufferList* pAudioBufferList;
|
|
ma_format formatOut;
|
|
ma_uint32 channelsOut;
|
|
ma_uint32 sampleRateOut;
|
|
ma_channel channelMapOut[MA_MAX_CHANNELS];
|
|
ma_uint32 periodSizeInFramesOut;
|
|
ma_uint32 periodsOut;
|
|
char deviceName[256];
|
|
} ma_device_init_internal_data__coreaudio;
|
|
|
|
static ma_result ma_device_init_internal__coreaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_init_internal_data__coreaudio* pData, void* pDevice_DoNotReference)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
OSStatus status;
|
|
UInt32 enableIOFlag;
|
|
AudioStreamBasicDescription bestFormat;
|
|
ma_uint32 actualPeriodSizeInFrames;
|
|
AURenderCallbackStruct callbackInfo;
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
AudioObjectID deviceObjectID;
|
|
#endif
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(deviceType == ma_device_type_playback || deviceType == ma_device_type_capture);
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
pData->deviceObjectID = 0;
|
|
#endif
|
|
pData->component = NULL;
|
|
pData->audioUnit = NULL;
|
|
pData->pAudioBufferList = NULL;
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
result = ma_find_AudioObjectID(pContext, deviceType, pDeviceID, &deviceObjectID);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pData->deviceObjectID = deviceObjectID;
|
|
#endif
|
|
|
|
pData->periodsOut = pData->periodsIn;
|
|
if (pData->periodsOut == 0) {
|
|
pData->periodsOut = MA_DEFAULT_PERIODS;
|
|
}
|
|
if (pData->periodsOut > 16) {
|
|
pData->periodsOut = 16;
|
|
}
|
|
|
|
status = ((ma_AudioComponentInstanceNew_proc)pContext->coreaudio.AudioComponentInstanceNew)((AudioComponent)pContext->coreaudio.component, (AudioUnit*)&pData->audioUnit);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
enableIOFlag = 1;
|
|
if (deviceType == ma_device_type_capture) {
|
|
enableIOFlag = 0;
|
|
}
|
|
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
enableIOFlag = (enableIOFlag == 0) ? 1 : 0;
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &enableIOFlag, sizeof(enableIOFlag));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_CurrentDevice, kAudioUnitScope_Global, 0, &deviceObjectID, sizeof(deviceObjectID));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(result);
|
|
}
|
|
#else
|
|
if (pDeviceID != NULL) {
|
|
if (deviceType == ma_device_type_capture) {
|
|
ma_bool32 found = MA_FALSE;
|
|
NSArray *pInputs = [[[AVAudioSession sharedInstance] currentRoute] inputs];
|
|
for (AVAudioSessionPortDescription* pPortDesc in pInputs) {
|
|
if (strcmp(pDeviceID->coreaudio, [pPortDesc.UID UTF8String]) == 0) {
|
|
[[AVAudioSession sharedInstance] setPreferredInput:pPortDesc error:nil];
|
|
found = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found == MA_FALSE) {
|
|
return MA_DOES_NOT_EXIST;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
{
|
|
AudioStreamBasicDescription origFormat;
|
|
UInt32 origFormatSize = sizeof(origFormat);
|
|
AudioUnitScope formatScope = (deviceType == ma_device_type_playback) ? kAudioUnitScope_Input : kAudioUnitScope_Output;
|
|
AudioUnitElement formatElement = (deviceType == ma_device_type_playback) ? MA_COREAUDIO_OUTPUT_BUS : MA_COREAUDIO_INPUT_BUS;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, MA_COREAUDIO_OUTPUT_BUS, &origFormat, &origFormatSize);
|
|
} else {
|
|
status = ((ma_AudioUnitGetProperty_proc)pContext->coreaudio.AudioUnitGetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, MA_COREAUDIO_INPUT_BUS, &origFormat, &origFormatSize);
|
|
}
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
result = ma_find_best_format__coreaudio(pContext, deviceObjectID, deviceType, pData->formatIn, pData->channelsIn, pData->sampleRateIn, &origFormat, &bestFormat);
|
|
if (result != MA_SUCCESS) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return result;
|
|
}
|
|
|
|
if (pData->allowNominalSampleRateChange) {
|
|
AudioValueRange sampleRateRange;
|
|
AudioObjectPropertyAddress propAddress;
|
|
|
|
sampleRateRange.mMinimum = bestFormat.mSampleRate;
|
|
sampleRateRange.mMaximum = bestFormat.mSampleRate;
|
|
|
|
propAddress.mSelector = kAudioDevicePropertyNominalSampleRate;
|
|
propAddress.mScope = (deviceType == ma_device_type_playback) ? kAudioObjectPropertyScopeOutput : kAudioObjectPropertyScopeInput;
|
|
propAddress.mElement = AUDIO_OBJECT_PROPERTY_ELEMENT;
|
|
|
|
status = ((ma_AudioObjectSetPropertyData_proc)pContext->coreaudio.AudioObjectSetPropertyData)(deviceObjectID, &propAddress, 0, NULL, sizeof(sampleRateRange), &sampleRateRange);
|
|
if (status != noErr) {
|
|
bestFormat.mSampleRate = origFormat.mSampleRate;
|
|
}
|
|
} else {
|
|
bestFormat.mSampleRate = origFormat.mSampleRate;
|
|
}
|
|
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
|
|
if (status != noErr) {
|
|
bestFormat = origFormat;
|
|
}
|
|
#else
|
|
bestFormat = origFormat;
|
|
|
|
@autoreleasepool {
|
|
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
|
|
MA_ASSERT(pAudioSession != NULL);
|
|
|
|
[pAudioSession setPreferredSampleRate:(double)pData->sampleRateIn error:nil];
|
|
bestFormat.mSampleRate = pAudioSession.sampleRate;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.outputNumberOfChannels;
|
|
}
|
|
|
|
#if 0
|
|
if (deviceType == ma_device_type_capture) {
|
|
bestFormat.mChannelsPerFrame = (UInt32)pAudioSession.inputNumberOfChannels;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_StreamFormat, formatScope, formatElement, &bestFormat, sizeof(bestFormat));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
#endif
|
|
|
|
result = ma_format_from_AudioStreamBasicDescription(&bestFormat, &pData->formatOut);
|
|
if (result != MA_SUCCESS) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return result;
|
|
}
|
|
|
|
if (pData->formatOut == ma_format_unknown) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
pData->channelsOut = bestFormat.mChannelsPerFrame;
|
|
pData->sampleRateOut = (ma_uint32)bestFormat.mSampleRate;
|
|
}
|
|
|
|
if (pData->channelsOut > MA_MAX_CHANNELS) {
|
|
pData->channelsOut = MA_MAX_CHANNELS;
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
result = ma_get_AudioUnit_channel_map(pContext, pData->audioUnit, deviceType, pData->channelMapOut, pData->channelsOut);
|
|
if (result != MA_SUCCESS) {
|
|
#if 0
|
|
result = ma_get_AudioObject_channel_map(pContext, deviceObjectID, deviceType, pData->channelMapOut, pData->channelsOut);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
#else
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
|
|
#endif
|
|
}
|
|
#else
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut);
|
|
#endif
|
|
|
|
if (pData->periodSizeInFramesIn == 0) {
|
|
if (pData->periodSizeInMillisecondsIn == 0) {
|
|
if (pData->performanceProfile == ma_performance_profile_low_latency) {
|
|
actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, pData->sampleRateOut);
|
|
} else {
|
|
actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, pData->sampleRateOut);
|
|
}
|
|
} else {
|
|
actualPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, pData->sampleRateOut);
|
|
}
|
|
} else {
|
|
actualPeriodSizeInFrames = pData->periodSizeInFramesIn;
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
result = ma_set_AudioObject_buffer_size_in_frames(pContext, deviceObjectID, deviceType, &actualPeriodSizeInFrames);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
#else
|
|
@autoreleasepool {
|
|
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
|
|
MA_ASSERT(pAudioSession != NULL);
|
|
|
|
[pAudioSession setPreferredIOBufferDuration:((float)actualPeriodSizeInFrames / pAudioSession.sampleRate) error:nil];
|
|
actualPeriodSizeInFrames = ma_next_power_of_2((ma_uint32)(pAudioSession.IOBufferDuration * pAudioSession.sampleRate));
|
|
}
|
|
#endif
|
|
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Global, 0, &actualPeriodSizeInFrames, sizeof(actualPeriodSizeInFrames));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
pData->periodSizeInFramesOut = (ma_uint32)actualPeriodSizeInFrames;
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
ma_bool32 isInterleaved = (bestFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0;
|
|
AudioBufferList* pBufferList;
|
|
|
|
pBufferList = ma_allocate_AudioBufferList__coreaudio(pData->periodSizeInFramesOut, pData->formatOut, pData->channelsOut, (isInterleaved) ? ma_stream_layout_interleaved : ma_stream_layout_deinterleaved, &pContext->allocationCallbacks);
|
|
if (pBufferList == NULL) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pData->pAudioBufferList = pBufferList;
|
|
}
|
|
|
|
callbackInfo.inputProcRefCon = pDevice_DoNotReference;
|
|
if (deviceType == ma_device_type_playback) {
|
|
callbackInfo.inputProc = ma_on_output__coreaudio;
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
} else {
|
|
callbackInfo.inputProc = ma_on_input__coreaudio;
|
|
status = ((ma_AudioUnitSetProperty_proc)pContext->coreaudio.AudioUnitSetProperty)(pData->audioUnit, kAudioOutputUnitProperty_SetInputCallback, kAudioUnitScope_Global, 0, &callbackInfo, sizeof(callbackInfo));
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
}
|
|
|
|
if (pData->registerStopEvent) {
|
|
status = ((ma_AudioUnitAddPropertyListener_proc)pContext->coreaudio.AudioUnitAddPropertyListener)(pData->audioUnit, kAudioOutputUnitProperty_IsRunning, on_start_stop__coreaudio, pDevice_DoNotReference);
|
|
if (status != noErr) {
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
}
|
|
|
|
status = ((ma_AudioUnitInitialize_proc)pContext->coreaudio.AudioUnitInitialize)(pData->audioUnit);
|
|
if (status != noErr) {
|
|
ma_free(pData->pAudioBufferList, &pContext->allocationCallbacks);
|
|
pData->pAudioBufferList = NULL;
|
|
((ma_AudioComponentInstanceDispose_proc)pContext->coreaudio.AudioComponentInstanceDispose)(pData->audioUnit);
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
ma_get_AudioObject_name(pContext, deviceObjectID, sizeof(pData->deviceName), pData->deviceName);
|
|
#else
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
|
|
} else {
|
|
ma_strcpy_s(pData->deviceName, sizeof(pData->deviceName), MA_DEFAULT_CAPTURE_DEVICE_NAME);
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
static ma_result ma_device_reinit_internal__coreaudio(ma_device* pDevice, ma_device_type deviceType, ma_bool32 disposePreviousAudioUnit)
|
|
{
|
|
ma_device_init_internal_data__coreaudio data;
|
|
ma_result result;
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
data.allowNominalSampleRateChange = MA_FALSE;
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
data.formatIn = pDevice->capture.format;
|
|
data.channelsIn = pDevice->capture.channels;
|
|
data.sampleRateIn = pDevice->sampleRate;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDevice->capture.channelMap, sizeof(pDevice->capture.channelMap));
|
|
data.shareMode = pDevice->capture.shareMode;
|
|
data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
|
|
data.registerStopEvent = MA_TRUE;
|
|
|
|
if (disposePreviousAudioUnit) {
|
|
((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
}
|
|
if (pDevice->coreaudio.pAudioBufferList) {
|
|
ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
} else if (deviceType == ma_device_type_playback) {
|
|
data.formatIn = pDevice->playback.format;
|
|
data.channelsIn = pDevice->playback.channels;
|
|
data.sampleRateIn = pDevice->sampleRate;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDevice->playback.channelMap, sizeof(pDevice->playback.channelMap));
|
|
data.shareMode = pDevice->playback.shareMode;
|
|
data.performanceProfile = pDevice->coreaudio.originalPerformanceProfile;
|
|
data.registerStopEvent = (pDevice->type != ma_device_type_duplex);
|
|
|
|
if (disposePreviousAudioUnit) {
|
|
((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
}
|
|
}
|
|
data.periodSizeInFramesIn = pDevice->coreaudio.originalPeriodSizeInFrames;
|
|
data.periodSizeInMillisecondsIn = pDevice->coreaudio.originalPeriodSizeInMilliseconds;
|
|
data.periodsIn = pDevice->coreaudio.originalPeriods;
|
|
|
|
if (data.periodsIn < 3 && pDevice->type == ma_device_type_duplex) {
|
|
data.periodsIn = 3;
|
|
}
|
|
|
|
result = ma_device_init_internal__coreaudio(pDevice->pContext, deviceType, NULL, &data, (void*)pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
|
|
ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
|
|
#endif
|
|
pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
|
|
pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
|
|
pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
|
|
|
|
pDevice->capture.internalFormat = data.formatOut;
|
|
pDevice->capture.internalChannels = data.channelsOut;
|
|
pDevice->capture.internalSampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDevice->capture.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDevice->capture.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDevice->capture.internalPeriods = data.periodsOut;
|
|
} else if (deviceType == ma_device_type_playback) {
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
|
|
ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
|
|
#endif
|
|
pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
|
|
|
|
pDevice->playback.internalFormat = data.formatOut;
|
|
pDevice->playback.internalChannels = data.channelsOut;
|
|
pDevice->playback.internalSampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDevice->playback.internalChannelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDevice->playback.internalPeriodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDevice->playback.internalPeriods = data.periodsOut;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_device_init__coreaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive)) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_device_init_internal_data__coreaudio data;
|
|
data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
|
|
data.formatIn = pDescriptorCapture->format;
|
|
data.channelsIn = pDescriptorCapture->channels;
|
|
data.sampleRateIn = pDescriptorCapture->sampleRate;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
|
|
data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
|
|
data.periodSizeInMillisecondsIn = pDescriptorCapture->periodSizeInMilliseconds;
|
|
data.periodsIn = pDescriptorCapture->periodCount;
|
|
data.shareMode = pDescriptorCapture->shareMode;
|
|
data.performanceProfile = pConfig->performanceProfile;
|
|
data.registerStopEvent = MA_TRUE;
|
|
|
|
if (data.periodsIn < 3 && pConfig->deviceType == ma_device_type_duplex) {
|
|
data.periodsIn = 3;
|
|
}
|
|
|
|
result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_capture, pDescriptorCapture->pDeviceID, &data, (void*)pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDevice->coreaudio.isDefaultCaptureDevice = (pConfig->capture.pDeviceID == NULL);
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
pDevice->coreaudio.deviceObjectIDCapture = (ma_uint32)data.deviceObjectID;
|
|
#endif
|
|
pDevice->coreaudio.audioUnitCapture = (ma_ptr)data.audioUnit;
|
|
pDevice->coreaudio.pAudioBufferList = (ma_ptr)data.pAudioBufferList;
|
|
pDevice->coreaudio.audioBufferCapInFrames = data.periodSizeInFramesOut;
|
|
pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
|
|
pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorCapture->periodSizeInMilliseconds;
|
|
pDevice->coreaudio.originalPeriods = pDescriptorCapture->periodCount;
|
|
pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
|
|
|
|
pDescriptorCapture->format = data.formatOut;
|
|
pDescriptorCapture->channels = data.channelsOut;
|
|
pDescriptorCapture->sampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDescriptorCapture->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDescriptorCapture->periodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDescriptorCapture->periodCount = data.periodsOut;
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDCapture, sizeof(pDevice->capture.id.coreaudio), pDevice->capture.id.coreaudio);
|
|
|
|
if (pConfig->capture.pDeviceID == NULL) {
|
|
ma_device__track__coreaudio(pDevice);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_device_init_internal_data__coreaudio data;
|
|
data.allowNominalSampleRateChange = pConfig->coreaudio.allowNominalSampleRateChange;
|
|
data.formatIn = pDescriptorPlayback->format;
|
|
data.channelsIn = pDescriptorPlayback->channels;
|
|
data.sampleRateIn = pDescriptorPlayback->sampleRate;
|
|
MA_COPY_MEMORY(data.channelMapIn, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
|
|
data.shareMode = pDescriptorPlayback->shareMode;
|
|
data.performanceProfile = pConfig->performanceProfile;
|
|
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
data.periodSizeInFramesIn = pDescriptorCapture->periodSizeInFrames;
|
|
data.periodsIn = pDescriptorCapture->periodCount;
|
|
data.registerStopEvent = MA_FALSE;
|
|
} else {
|
|
data.periodSizeInFramesIn = pDescriptorPlayback->periodSizeInFrames;
|
|
data.periodSizeInMillisecondsIn = pDescriptorPlayback->periodSizeInMilliseconds;
|
|
data.periodsIn = pDescriptorPlayback->periodCount;
|
|
data.registerStopEvent = MA_TRUE;
|
|
}
|
|
|
|
result = ma_device_init_internal__coreaudio(pDevice->pContext, ma_device_type_playback, pDescriptorPlayback->pDeviceID, &data, (void*)pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
((ma_AudioComponentInstanceDispose_proc)pDevice->pContext->coreaudio.AudioComponentInstanceDispose)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
if (pDevice->coreaudio.pAudioBufferList) {
|
|
ma_free(pDevice->coreaudio.pAudioBufferList, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
pDevice->coreaudio.isDefaultPlaybackDevice = (pConfig->playback.pDeviceID == NULL);
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
pDevice->coreaudio.deviceObjectIDPlayback = (ma_uint32)data.deviceObjectID;
|
|
#endif
|
|
pDevice->coreaudio.audioUnitPlayback = (ma_ptr)data.audioUnit;
|
|
pDevice->coreaudio.originalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
|
|
pDevice->coreaudio.originalPeriodSizeInMilliseconds = pDescriptorPlayback->periodSizeInMilliseconds;
|
|
pDevice->coreaudio.originalPeriods = pDescriptorPlayback->periodCount;
|
|
pDevice->coreaudio.originalPerformanceProfile = pConfig->performanceProfile;
|
|
|
|
pDescriptorPlayback->format = data.formatOut;
|
|
pDescriptorPlayback->channels = data.channelsOut;
|
|
pDescriptorPlayback->sampleRate = data.sampleRateOut;
|
|
MA_COPY_MEMORY(pDescriptorPlayback->channelMap, data.channelMapOut, sizeof(data.channelMapOut));
|
|
pDescriptorPlayback->periodSizeInFrames = data.periodSizeInFramesOut;
|
|
pDescriptorPlayback->periodCount = data.periodsOut;
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
ma_get_AudioObject_uid(pDevice->pContext, pDevice->coreaudio.deviceObjectIDPlayback, sizeof(pDevice->playback.id.coreaudio), pDevice->playback.id.coreaudio);
|
|
|
|
if (pDescriptorPlayback->pDeviceID == NULL && (pConfig->deviceType != ma_device_type_duplex || pDescriptorCapture->pDeviceID != NULL)) {
|
|
ma_device__track__coreaudio(pDevice);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ma_event_init(&pDevice->coreaudio.stopEvent);
|
|
|
|
#if defined(MA_APPLE_MOBILE)
|
|
pDevice->coreaudio.pNotificationHandler = (MA_BRIDGE_RETAINED void*)[[ma_ios_notification_handler alloc] init:pDevice];
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__coreaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
OSStatus status = ((ma_AudioOutputUnitStart_proc)pDevice->pContext->coreaudio.AudioOutputUnitStart)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
if (status != noErr) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
}
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__coreaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitCapture);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
OSStatus status = ((ma_AudioOutputUnitStop_proc)pDevice->pContext->coreaudio.AudioOutputUnitStop)((AudioUnit)pDevice->coreaudio.audioUnitPlayback);
|
|
if (status != noErr) {
|
|
return ma_result_from_OSStatus(status);
|
|
}
|
|
}
|
|
|
|
ma_event_wait(&pDevice->coreaudio.stopEvent);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__coreaudio(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_coreaudio);
|
|
|
|
#if defined(MA_APPLE_MOBILE)
|
|
if (!pContext->coreaudio.noAudioSessionDeactivate) {
|
|
if (![[AVAudioSession sharedInstance] setActive:false error:nil]) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to deactivate audio session.");
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
|
|
#endif
|
|
|
|
#if !defined(MA_APPLE_MOBILE)
|
|
ma_context__uninit_device_tracking__coreaudio(pContext);
|
|
#endif
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if defined(MA_APPLE_MOBILE) && defined(__IPHONE_12_0)
|
|
static AVAudioSessionCategory ma_to_AVAudioSessionCategory(ma_ios_session_category category)
|
|
{
|
|
MA_ASSERT(category != ma_ios_session_category_default);
|
|
MA_ASSERT(category != ma_ios_session_category_none);
|
|
|
|
switch (category) {
|
|
case ma_ios_session_category_ambient: return AVAudioSessionCategoryAmbient;
|
|
case ma_ios_session_category_solo_ambient: return AVAudioSessionCategorySoloAmbient;
|
|
case ma_ios_session_category_playback: return AVAudioSessionCategoryPlayback;
|
|
case ma_ios_session_category_record: return AVAudioSessionCategoryRecord;
|
|
case ma_ios_session_category_play_and_record: return AVAudioSessionCategoryPlayAndRecord;
|
|
case ma_ios_session_category_multi_route: return AVAudioSessionCategoryMultiRoute;
|
|
case ma_ios_session_category_none: return AVAudioSessionCategoryAmbient;
|
|
case ma_ios_session_category_default: return AVAudioSessionCategoryAmbient;
|
|
default: return AVAudioSessionCategoryAmbient;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
#if !defined(MA_APPLE_MOBILE)
|
|
ma_result result;
|
|
#endif
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
#if defined(MA_APPLE_MOBILE)
|
|
@autoreleasepool {
|
|
AVAudioSession* pAudioSession = [AVAudioSession sharedInstance];
|
|
AVAudioSessionCategoryOptions options = pConfig->coreaudio.sessionCategoryOptions;
|
|
|
|
MA_ASSERT(pAudioSession != NULL);
|
|
|
|
if (pConfig->coreaudio.sessionCategory == ma_ios_session_category_default) {
|
|
#if !defined(MA_APPLE_TV) && !defined(MA_APPLE_WATCH)
|
|
options |= AVAudioSessionCategoryOptionDefaultToSpeaker;
|
|
#endif
|
|
|
|
if ([pAudioSession setCategory: AVAudioSessionCategoryPlayAndRecord withOptions:options error:nil]) {
|
|
} else if ([pAudioSession setCategory: AVAudioSessionCategoryPlayback withOptions:options error:nil]) {
|
|
} else if ([pAudioSession setCategory: AVAudioSessionCategoryRecord withOptions:options error:nil]) {
|
|
} else {
|
|
}
|
|
} else {
|
|
if (pConfig->coreaudio.sessionCategory != ma_ios_session_category_none) {
|
|
#if defined(__IPHONE_12_0)
|
|
if (![pAudioSession setCategory: ma_to_AVAudioSessionCategory(pConfig->coreaudio.sessionCategory) withOptions:options error:nil]) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
#else
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Session category only supported in iOS 12 and newer.");
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (!pConfig->coreaudio.noAudioSessionActivate) {
|
|
if (![pAudioSession setActive:true error:nil]) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "Failed to activate audio session.");
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
|
|
pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation");
|
|
if (pContext->coreaudio.hCoreFoundation == NULL) {
|
|
return MA_API_NOT_FOUND;
|
|
}
|
|
|
|
pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString");
|
|
pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease");
|
|
|
|
pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio");
|
|
if (pContext->coreaudio.hCoreAudio == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
|
|
return MA_API_NOT_FOUND;
|
|
}
|
|
|
|
pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData");
|
|
pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize");
|
|
pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData");
|
|
pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener");
|
|
pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener");
|
|
|
|
pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit");
|
|
if (pContext->coreaudio.hAudioUnit == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
|
|
return MA_API_NOT_FOUND;
|
|
}
|
|
|
|
if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
|
|
pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox");
|
|
if (pContext->coreaudio.hAudioUnit == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
|
|
return MA_API_NOT_FOUND;
|
|
}
|
|
}
|
|
|
|
pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext");
|
|
pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose");
|
|
pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew");
|
|
pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart");
|
|
pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop");
|
|
pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener");
|
|
pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo");
|
|
pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty");
|
|
pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty");
|
|
pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize");
|
|
pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender");
|
|
#else
|
|
pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString;
|
|
pContext->coreaudio.CFRelease = (ma_proc)CFRelease;
|
|
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
pContext->coreaudio.AudioObjectGetPropertyData = (ma_proc)AudioObjectGetPropertyData;
|
|
pContext->coreaudio.AudioObjectGetPropertyDataSize = (ma_proc)AudioObjectGetPropertyDataSize;
|
|
pContext->coreaudio.AudioObjectSetPropertyData = (ma_proc)AudioObjectSetPropertyData;
|
|
pContext->coreaudio.AudioObjectAddPropertyListener = (ma_proc)AudioObjectAddPropertyListener;
|
|
pContext->coreaudio.AudioObjectRemovePropertyListener = (ma_proc)AudioObjectRemovePropertyListener;
|
|
#endif
|
|
|
|
pContext->coreaudio.AudioComponentFindNext = (ma_proc)AudioComponentFindNext;
|
|
pContext->coreaudio.AudioComponentInstanceDispose = (ma_proc)AudioComponentInstanceDispose;
|
|
pContext->coreaudio.AudioComponentInstanceNew = (ma_proc)AudioComponentInstanceNew;
|
|
pContext->coreaudio.AudioOutputUnitStart = (ma_proc)AudioOutputUnitStart;
|
|
pContext->coreaudio.AudioOutputUnitStop = (ma_proc)AudioOutputUnitStop;
|
|
pContext->coreaudio.AudioUnitAddPropertyListener = (ma_proc)AudioUnitAddPropertyListener;
|
|
pContext->coreaudio.AudioUnitGetPropertyInfo = (ma_proc)AudioUnitGetPropertyInfo;
|
|
pContext->coreaudio.AudioUnitGetProperty = (ma_proc)AudioUnitGetProperty;
|
|
pContext->coreaudio.AudioUnitSetProperty = (ma_proc)AudioUnitSetProperty;
|
|
pContext->coreaudio.AudioUnitInitialize = (ma_proc)AudioUnitInitialize;
|
|
pContext->coreaudio.AudioUnitRender = (ma_proc)AudioUnitRender;
|
|
#endif
|
|
|
|
{
|
|
AudioComponentDescription desc;
|
|
desc.componentType = kAudioUnitType_Output;
|
|
#if defined(MA_APPLE_DESKTOP)
|
|
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
|
#else
|
|
desc.componentSubType = kAudioUnitSubType_RemoteIO;
|
|
#endif
|
|
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
|
desc.componentFlags = 0;
|
|
desc.componentFlagsMask = 0;
|
|
|
|
pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc);
|
|
if (pContext->coreaudio.component == NULL) {
|
|
#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
|
|
#endif
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
}
|
|
|
|
#if !defined(MA_APPLE_MOBILE)
|
|
result = ma_context__init_device_tracking__coreaudio(pContext);
|
|
if (result != MA_SUCCESS) {
|
|
#if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE)
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation);
|
|
#endif
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
pContext->coreaudio.noAudioSessionDeactivate = pConfig->coreaudio.noAudioSessionDeactivate;
|
|
|
|
pCallbacks->onContextInit = ma_context_init__coreaudio;
|
|
pCallbacks->onContextUninit = ma_context_uninit__coreaudio;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__coreaudio;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__coreaudio;
|
|
pCallbacks->onDeviceInit = ma_device_init__coreaudio;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__coreaudio;
|
|
pCallbacks->onDeviceStart = ma_device_start__coreaudio;
|
|
pCallbacks->onDeviceStop = ma_device_stop__coreaudio;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_SNDIO
|
|
#include <fcntl.h>
|
|
|
|
#if 0
|
|
#if defined(__NetBSD__) || defined(__OpenBSD__)
|
|
#include <sys/audioio.h>
|
|
#endif
|
|
#if defined(__FreeBSD__) || defined(__DragonFly__)
|
|
#include <sys/soundcard.h>
|
|
#endif
|
|
#endif
|
|
|
|
#define MA_SIO_DEVANY "default"
|
|
#define MA_SIO_PLAY 1
|
|
#define MA_SIO_REC 2
|
|
#define MA_SIO_NENC 8
|
|
#define MA_SIO_NCHAN 8
|
|
#define MA_SIO_NRATE 16
|
|
#define MA_SIO_NCONF 4
|
|
|
|
struct ma_sio_hdl;
|
|
|
|
struct ma_sio_par
|
|
{
|
|
unsigned int bits;
|
|
unsigned int bps;
|
|
unsigned int sig;
|
|
unsigned int le;
|
|
unsigned int msb;
|
|
unsigned int rchan;
|
|
unsigned int pchan;
|
|
unsigned int rate;
|
|
unsigned int bufsz;
|
|
unsigned int xrun;
|
|
unsigned int round;
|
|
unsigned int appbufsz;
|
|
int __pad[3];
|
|
unsigned int __magic;
|
|
};
|
|
|
|
struct ma_sio_enc
|
|
{
|
|
unsigned int bits;
|
|
unsigned int bps;
|
|
unsigned int sig;
|
|
unsigned int le;
|
|
unsigned int msb;
|
|
};
|
|
|
|
struct ma_sio_conf
|
|
{
|
|
unsigned int enc;
|
|
unsigned int rchan;
|
|
unsigned int pchan;
|
|
unsigned int rate;
|
|
};
|
|
|
|
struct ma_sio_cap
|
|
{
|
|
struct ma_sio_enc enc[MA_SIO_NENC];
|
|
unsigned int rchan[MA_SIO_NCHAN];
|
|
unsigned int pchan[MA_SIO_NCHAN];
|
|
unsigned int rate[MA_SIO_NRATE];
|
|
int __pad[7];
|
|
unsigned int nconf;
|
|
struct ma_sio_conf confs[MA_SIO_NCONF];
|
|
};
|
|
|
|
typedef struct ma_sio_hdl* (* ma_sio_open_proc) (const char*, unsigned int, int);
|
|
typedef void (* ma_sio_close_proc) (struct ma_sio_hdl*);
|
|
typedef int (* ma_sio_setpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
|
|
typedef int (* ma_sio_getpar_proc) (struct ma_sio_hdl*, struct ma_sio_par*);
|
|
typedef int (* ma_sio_getcap_proc) (struct ma_sio_hdl*, struct ma_sio_cap*);
|
|
typedef size_t (* ma_sio_write_proc) (struct ma_sio_hdl*, const void*, size_t);
|
|
typedef size_t (* ma_sio_read_proc) (struct ma_sio_hdl*, void*, size_t);
|
|
typedef int (* ma_sio_start_proc) (struct ma_sio_hdl*);
|
|
typedef int (* ma_sio_stop_proc) (struct ma_sio_hdl*);
|
|
typedef int (* ma_sio_initpar_proc)(struct ma_sio_par*);
|
|
|
|
static ma_uint32 ma_get_standard_sample_rate_priority_index__sndio(ma_uint32 sampleRate)
|
|
{
|
|
ma_uint32 i;
|
|
for (i = 0; i < ma_countof(g_maStandardSampleRatePriorities); ++i) {
|
|
if (g_maStandardSampleRatePriorities[i] == sampleRate) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return (ma_uint32)-1;
|
|
}
|
|
|
|
static ma_format ma_format_from_sio_enc__sndio(unsigned int bits, unsigned int bps, unsigned int sig, unsigned int le, unsigned int msb)
|
|
{
|
|
if ((ma_is_little_endian() && le == 0) || (ma_is_big_endian() && le == 1)) {
|
|
return ma_format_unknown;
|
|
}
|
|
|
|
if (bits == 8 && bps == 1 && sig == 0) {
|
|
return ma_format_u8;
|
|
}
|
|
if (bits == 16 && bps == 2 && sig == 1) {
|
|
return ma_format_s16;
|
|
}
|
|
if (bits == 24 && bps == 3 && sig == 1) {
|
|
return ma_format_s24;
|
|
}
|
|
if (bits == 24 && bps == 4 && sig == 1 && msb == 0) {
|
|
}
|
|
if (bits == 32 && bps == 4 && sig == 1) {
|
|
return ma_format_s32;
|
|
}
|
|
|
|
return ma_format_unknown;
|
|
}
|
|
|
|
static ma_format ma_find_best_format_from_sio_cap__sndio(struct ma_sio_cap* caps)
|
|
{
|
|
ma_format bestFormat;
|
|
unsigned int iConfig;
|
|
|
|
MA_ASSERT(caps != NULL);
|
|
|
|
bestFormat = ma_format_unknown;
|
|
for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
|
|
unsigned int iEncoding;
|
|
for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
|
|
unsigned int bits;
|
|
unsigned int bps;
|
|
unsigned int sig;
|
|
unsigned int le;
|
|
unsigned int msb;
|
|
ma_format format;
|
|
|
|
if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
bits = caps->enc[iEncoding].bits;
|
|
bps = caps->enc[iEncoding].bps;
|
|
sig = caps->enc[iEncoding].sig;
|
|
le = caps->enc[iEncoding].le;
|
|
msb = caps->enc[iEncoding].msb;
|
|
format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
|
|
if (format == ma_format_unknown) {
|
|
continue;
|
|
}
|
|
|
|
if (bestFormat == ma_format_unknown) {
|
|
bestFormat = format;
|
|
} else {
|
|
if (ma_get_format_priority_index(bestFormat) > ma_get_format_priority_index(format)) {
|
|
bestFormat = format;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return bestFormat;
|
|
}
|
|
|
|
static ma_uint32 ma_find_best_channels_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat)
|
|
{
|
|
ma_uint32 maxChannels;
|
|
unsigned int iConfig;
|
|
|
|
MA_ASSERT(caps != NULL);
|
|
MA_ASSERT(requiredFormat != ma_format_unknown);
|
|
|
|
maxChannels = 0;
|
|
for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
|
|
unsigned int iEncoding;
|
|
for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
|
|
unsigned int iChannel;
|
|
unsigned int bits;
|
|
unsigned int bps;
|
|
unsigned int sig;
|
|
unsigned int le;
|
|
unsigned int msb;
|
|
ma_format format;
|
|
|
|
if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
bits = caps->enc[iEncoding].bits;
|
|
bps = caps->enc[iEncoding].bps;
|
|
sig = caps->enc[iEncoding].sig;
|
|
le = caps->enc[iEncoding].le;
|
|
msb = caps->enc[iEncoding].msb;
|
|
format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
|
|
if (format != requiredFormat) {
|
|
continue;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
|
|
unsigned int chan = 0;
|
|
unsigned int channels;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
chan = caps->confs[iConfig].pchan;
|
|
} else {
|
|
chan = caps->confs[iConfig].rchan;
|
|
}
|
|
|
|
if ((chan & (1UL << iChannel)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
channels = caps->pchan[iChannel];
|
|
} else {
|
|
channels = caps->rchan[iChannel];
|
|
}
|
|
|
|
if (maxChannels < channels) {
|
|
maxChannels = channels;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return maxChannels;
|
|
}
|
|
|
|
static ma_uint32 ma_find_best_sample_rate_from_sio_cap__sndio(struct ma_sio_cap* caps, ma_device_type deviceType, ma_format requiredFormat, ma_uint32 requiredChannels)
|
|
{
|
|
ma_uint32 firstSampleRate;
|
|
ma_uint32 bestSampleRate;
|
|
unsigned int iConfig;
|
|
|
|
MA_ASSERT(caps != NULL);
|
|
MA_ASSERT(requiredFormat != ma_format_unknown);
|
|
MA_ASSERT(requiredChannels > 0);
|
|
MA_ASSERT(requiredChannels <= MA_MAX_CHANNELS);
|
|
|
|
firstSampleRate = 0;
|
|
bestSampleRate = 0;
|
|
|
|
for (iConfig = 0; iConfig < caps->nconf; iConfig += 1) {
|
|
unsigned int iEncoding;
|
|
for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
|
|
unsigned int iChannel;
|
|
unsigned int bits;
|
|
unsigned int bps;
|
|
unsigned int sig;
|
|
unsigned int le;
|
|
unsigned int msb;
|
|
ma_format format;
|
|
|
|
if ((caps->confs[iConfig].enc & (1UL << iEncoding)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
bits = caps->enc[iEncoding].bits;
|
|
bps = caps->enc[iEncoding].bps;
|
|
sig = caps->enc[iEncoding].sig;
|
|
le = caps->enc[iEncoding].le;
|
|
msb = caps->enc[iEncoding].msb;
|
|
format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
|
|
if (format != requiredFormat) {
|
|
continue;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
|
|
unsigned int chan = 0;
|
|
unsigned int channels;
|
|
unsigned int iRate;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
chan = caps->confs[iConfig].pchan;
|
|
} else {
|
|
chan = caps->confs[iConfig].rchan;
|
|
}
|
|
|
|
if ((chan & (1UL << iChannel)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
channels = caps->pchan[iChannel];
|
|
} else {
|
|
channels = caps->rchan[iChannel];
|
|
}
|
|
|
|
if (channels != requiredChannels) {
|
|
continue;
|
|
}
|
|
|
|
for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
|
|
ma_uint32 rate = (ma_uint32)caps->rate[iRate];
|
|
ma_uint32 ratePriority;
|
|
|
|
if (firstSampleRate == 0) {
|
|
firstSampleRate = rate;
|
|
}
|
|
|
|
ratePriority = ma_get_standard_sample_rate_priority_index__sndio(rate);
|
|
if (ratePriority == (ma_uint32)-1) {
|
|
continue;
|
|
}
|
|
|
|
if (ma_get_standard_sample_rate_priority_index__sndio(bestSampleRate) > ratePriority) {
|
|
bestSampleRate = rate;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bestSampleRate == 0) {
|
|
bestSampleRate = firstSampleRate;
|
|
}
|
|
|
|
return bestSampleRate;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__sndio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_bool32 isTerminating = MA_FALSE;
|
|
struct ma_sio_hdl* handle;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
if (!isTerminating) {
|
|
handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_PLAY, 0);
|
|
if (handle != NULL) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), MA_SIO_DEVANY);
|
|
ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME);
|
|
|
|
isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
|
|
((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
|
|
}
|
|
}
|
|
|
|
if (!isTerminating) {
|
|
handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(MA_SIO_DEVANY, MA_SIO_REC, 0);
|
|
if (handle != NULL) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strcpy_s(deviceInfo.id.sndio, sizeof(deviceInfo.id.sndio), "default");
|
|
ma_strcpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME);
|
|
|
|
isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
|
|
((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__sndio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
char devid[256];
|
|
struct ma_sio_hdl* handle;
|
|
struct ma_sio_cap caps;
|
|
unsigned int iConfig;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDeviceID == NULL) {
|
|
ma_strcpy_s(devid, sizeof(devid), MA_SIO_DEVANY);
|
|
ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (deviceType == ma_device_type_playback) ? MA_DEFAULT_PLAYBACK_DEVICE_NAME : MA_DEFAULT_CAPTURE_DEVICE_NAME);
|
|
} else {
|
|
ma_strcpy_s(devid, sizeof(devid), pDeviceID->sndio);
|
|
ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), devid);
|
|
}
|
|
|
|
handle = ((ma_sio_open_proc)pContext->sndio.sio_open)(devid, (deviceType == ma_device_type_playback) ? MA_SIO_PLAY : MA_SIO_REC, 0);
|
|
if (handle == NULL) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
if (((ma_sio_getcap_proc)pContext->sndio.sio_getcap)(handle, &caps) == 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
|
|
for (iConfig = 0; iConfig < caps.nconf; iConfig += 1) {
|
|
unsigned int iEncoding;
|
|
unsigned int iChannel;
|
|
unsigned int iRate;
|
|
|
|
for (iEncoding = 0; iEncoding < MA_SIO_NENC; iEncoding += 1) {
|
|
unsigned int bits;
|
|
unsigned int bps;
|
|
unsigned int sig;
|
|
unsigned int le;
|
|
unsigned int msb;
|
|
ma_format format;
|
|
|
|
if ((caps.confs[iConfig].enc & (1UL << iEncoding)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
bits = caps.enc[iEncoding].bits;
|
|
bps = caps.enc[iEncoding].bps;
|
|
sig = caps.enc[iEncoding].sig;
|
|
le = caps.enc[iEncoding].le;
|
|
msb = caps.enc[iEncoding].msb;
|
|
format = ma_format_from_sio_enc__sndio(bits, bps, sig, le, msb);
|
|
if (format == ma_format_unknown) {
|
|
continue;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < MA_SIO_NCHAN; iChannel += 1) {
|
|
unsigned int chan = 0;
|
|
unsigned int channels;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
chan = caps.confs[iConfig].pchan;
|
|
} else {
|
|
chan = caps.confs[iConfig].rchan;
|
|
}
|
|
|
|
if ((chan & (1UL << iChannel)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
channels = caps.pchan[iChannel];
|
|
} else {
|
|
channels = caps.rchan[iChannel];
|
|
}
|
|
|
|
for (iRate = 0; iRate < MA_SIO_NRATE; iRate += 1) {
|
|
if ((caps.confs[iConfig].rate & (1UL << iRate)) != 0) {
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, channels, caps.rate[iRate], 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
((ma_sio_close_proc)pContext->sndio.sio_close)(handle);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__sndio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init_handle__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
|
|
{
|
|
const char* pDeviceName;
|
|
ma_ptr handle;
|
|
int openFlags = 0;
|
|
struct ma_sio_cap caps;
|
|
struct ma_sio_par par;
|
|
const ma_device_id* pDeviceID;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
ma_uint32 internalSampleRate;
|
|
ma_uint32 internalPeriodSizeInFrames;
|
|
ma_uint32 internalPeriods;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(deviceType != ma_device_type_duplex);
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
openFlags = MA_SIO_REC;
|
|
} else {
|
|
openFlags = MA_SIO_PLAY;
|
|
}
|
|
|
|
pDeviceID = pDescriptor->pDeviceID;
|
|
format = pDescriptor->format;
|
|
channels = pDescriptor->channels;
|
|
sampleRate = pDescriptor->sampleRate;
|
|
|
|
pDeviceName = MA_SIO_DEVANY;
|
|
if (pDeviceID != NULL) {
|
|
pDeviceName = pDeviceID->sndio;
|
|
}
|
|
|
|
handle = (ma_ptr)((ma_sio_open_proc)pDevice->pContext->sndio.sio_open)(pDeviceName, openFlags, 0);
|
|
if (handle == NULL) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to open device.");
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
if (((ma_sio_getcap_proc)pDevice->pContext->sndio.sio_getcap)((struct ma_sio_hdl*)handle, &caps) == 0) {
|
|
((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve device caps.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
if (format == ma_format_unknown) {
|
|
format = ma_find_best_format_from_sio_cap__sndio(&caps);
|
|
}
|
|
|
|
if (channels == 0) {
|
|
if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
|
|
channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
|
|
} else {
|
|
channels = MA_DEFAULT_CHANNELS;
|
|
}
|
|
}
|
|
} else {
|
|
if (format == ma_format_unknown) {
|
|
format = ma_find_best_format_from_sio_cap__sndio(&caps);
|
|
}
|
|
|
|
if (channels == 0) {
|
|
if (strlen(pDeviceName) > strlen("rsnd/") && strncmp(pDeviceName, "rsnd/", strlen("rsnd/")) == 0) {
|
|
channels = ma_find_best_channels_from_sio_cap__sndio(&caps, deviceType, format);
|
|
} else {
|
|
channels = MA_DEFAULT_CHANNELS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sampleRate == 0) {
|
|
sampleRate = ma_find_best_sample_rate_from_sio_cap__sndio(&caps, pConfig->deviceType, format, channels);
|
|
}
|
|
|
|
((ma_sio_initpar_proc)pDevice->pContext->sndio.sio_initpar)(&par);
|
|
par.msb = 0;
|
|
par.le = ma_is_little_endian();
|
|
|
|
switch (format) {
|
|
case ma_format_u8:
|
|
{
|
|
par.bits = 8;
|
|
par.bps = 1;
|
|
par.sig = 0;
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
par.bits = 24;
|
|
par.bps = 3;
|
|
par.sig = 1;
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
par.bits = 32;
|
|
par.bps = 4;
|
|
par.sig = 1;
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
case ma_format_f32:
|
|
case ma_format_unknown:
|
|
default:
|
|
{
|
|
par.bits = 16;
|
|
par.bps = 2;
|
|
par.sig = 1;
|
|
} break;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
par.rchan = channels;
|
|
} else {
|
|
par.pchan = channels;
|
|
}
|
|
|
|
par.rate = sampleRate;
|
|
|
|
internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, par.rate, pConfig->performanceProfile);
|
|
|
|
par.round = internalPeriodSizeInFrames;
|
|
par.appbufsz = par.round * pDescriptor->periodCount;
|
|
|
|
if (((ma_sio_setpar_proc)pDevice->pContext->sndio.sio_setpar)((struct ma_sio_hdl*)handle, &par) == 0) {
|
|
((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to set buffer size.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (((ma_sio_getpar_proc)pDevice->pContext->sndio.sio_getpar)((struct ma_sio_hdl*)handle, &par) == 0) {
|
|
((ma_sio_close_proc)pDevice->pContext->sndio.sio_close)((struct ma_sio_hdl*)handle);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to retrieve buffer size.");
|
|
return MA_ERROR;
|
|
}
|
|
|
|
internalFormat = ma_format_from_sio_enc__sndio(par.bits, par.bps, par.sig, par.le, par.msb);
|
|
internalChannels = (deviceType == ma_device_type_capture) ? par.rchan : par.pchan;
|
|
internalSampleRate = par.rate;
|
|
internalPeriods = par.appbufsz / par.round;
|
|
internalPeriodSizeInFrames = par.round;
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
pDevice->sndio.handleCapture = handle;
|
|
} else {
|
|
pDevice->sndio.handlePlayback = handle;
|
|
}
|
|
|
|
pDescriptor->format = internalFormat;
|
|
pDescriptor->channels = internalChannels;
|
|
pDescriptor->sampleRate = internalSampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_sndio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
|
|
pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
|
|
pDescriptor->periodCount = internalPeriods;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__sndio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->sndio);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_handle__sndio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__sndio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
((ma_sio_start_proc)pDevice->pContext->sndio.sio_start)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__sndio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handleCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
((ma_sio_stop_proc)pDevice->pContext->sndio.sio_stop)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_write__sndio(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
int result;
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
result = ((ma_sio_write_proc)pDevice->pContext->sndio.sio_write)((struct ma_sio_hdl*)pDevice->sndio.handlePlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
if (result == 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to send data from the client to the device.");
|
|
return MA_IO_ERROR;
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_read__sndio(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
int result;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
result = ((ma_sio_read_proc)pDevice->pContext->sndio.sio_read)((struct ma_sio_hdl*)pDevice->sndio.handleCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
|
|
if (result == 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[sndio] Failed to read data from the device to be sent to the device.");
|
|
return MA_IO_ERROR;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__sndio(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_sndio);
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
#ifndef MA_NO_RUNTIME_LINKING
|
|
const char* libsndioNames[] = {
|
|
"libsndio.so"
|
|
};
|
|
size_t i;
|
|
|
|
for (i = 0; i < ma_countof(libsndioNames); ++i) {
|
|
pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]);
|
|
if (pContext->sndio.sndioSO != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pContext->sndio.sndioSO == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open");
|
|
pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close");
|
|
pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar");
|
|
pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar");
|
|
pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap");
|
|
pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write");
|
|
pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read");
|
|
pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start");
|
|
pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop");
|
|
pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar");
|
|
#else
|
|
pContext->sndio.sio_open = sio_open;
|
|
pContext->sndio.sio_close = sio_close;
|
|
pContext->sndio.sio_setpar = sio_setpar;
|
|
pContext->sndio.sio_getpar = sio_getpar;
|
|
pContext->sndio.sio_getcap = sio_getcap;
|
|
pContext->sndio.sio_write = sio_write;
|
|
pContext->sndio.sio_read = sio_read;
|
|
pContext->sndio.sio_start = sio_start;
|
|
pContext->sndio.sio_stop = sio_stop;
|
|
pContext->sndio.sio_initpar = sio_initpar;
|
|
#endif
|
|
|
|
pCallbacks->onContextInit = ma_context_init__sndio;
|
|
pCallbacks->onContextUninit = ma_context_uninit__sndio;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__sndio;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__sndio;
|
|
pCallbacks->onDeviceInit = ma_device_init__sndio;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__sndio;
|
|
pCallbacks->onDeviceStart = ma_device_start__sndio;
|
|
pCallbacks->onDeviceStop = ma_device_stop__sndio;
|
|
pCallbacks->onDeviceRead = ma_device_read__sndio;
|
|
pCallbacks->onDeviceWrite = ma_device_write__sndio;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
(void)pConfig;
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_AUDIO4
|
|
#include <fcntl.h>
|
|
#include <poll.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/audioio.h>
|
|
|
|
#ifdef __NetBSD__
|
|
#include <sys/param.h>
|
|
#endif
|
|
|
|
#if defined(__OpenBSD__)
|
|
#include <sys/param.h>
|
|
#if defined(OpenBSD) && OpenBSD >= 201709
|
|
#define MA_AUDIO4_USE_NEW_API
|
|
#endif
|
|
#endif
|
|
|
|
static void ma_construct_device_id__audio4(char* id, size_t idSize, const char* base, int deviceIndex)
|
|
{
|
|
size_t baseLen;
|
|
|
|
MA_ASSERT(id != NULL);
|
|
MA_ASSERT(idSize > 0);
|
|
MA_ASSERT(deviceIndex >= 0);
|
|
|
|
baseLen = strlen(base);
|
|
MA_ASSERT(idSize > baseLen);
|
|
|
|
ma_strcpy_s(id, idSize, base);
|
|
ma_itoa_s(deviceIndex, id+baseLen, idSize-baseLen, 10);
|
|
}
|
|
|
|
static ma_result ma_extract_device_index_from_id__audio4(const char* id, const char* base, int* pIndexOut)
|
|
{
|
|
size_t idLen;
|
|
size_t baseLen;
|
|
const char* deviceIndexStr;
|
|
|
|
MA_ASSERT(id != NULL);
|
|
MA_ASSERT(base != NULL);
|
|
MA_ASSERT(pIndexOut != NULL);
|
|
|
|
idLen = strlen(id);
|
|
baseLen = strlen(base);
|
|
if (idLen <= baseLen) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (strncmp(id, base, baseLen) != 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
deviceIndexStr = id + baseLen;
|
|
if (deviceIndexStr[0] == '\0') {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
if (pIndexOut) {
|
|
*pIndexOut = atoi(deviceIndexStr);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if !defined(MA_AUDIO4_USE_NEW_API)
|
|
static ma_format ma_format_from_encoding__audio4(unsigned int encoding, unsigned int precision)
|
|
{
|
|
if (precision == 8 && (encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR || encoding == AUDIO_ENCODING_ULINEAR_LE || encoding == AUDIO_ENCODING_ULINEAR_BE)) {
|
|
return ma_format_u8;
|
|
} else {
|
|
if (ma_is_little_endian() && encoding == AUDIO_ENCODING_SLINEAR_LE) {
|
|
if (precision == 16) {
|
|
return ma_format_s16;
|
|
} else if (precision == 24) {
|
|
return ma_format_s24;
|
|
} else if (precision == 32) {
|
|
return ma_format_s32;
|
|
}
|
|
} else if (ma_is_big_endian() && encoding == AUDIO_ENCODING_SLINEAR_BE) {
|
|
if (precision == 16) {
|
|
return ma_format_s16;
|
|
} else if (precision == 24) {
|
|
return ma_format_s24;
|
|
} else if (precision == 32) {
|
|
return ma_format_s32;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ma_format_unknown;
|
|
}
|
|
|
|
static void ma_encoding_from_format__audio4(ma_format format, unsigned int* pEncoding, unsigned int* pPrecision)
|
|
{
|
|
MA_ASSERT(pEncoding != NULL);
|
|
MA_ASSERT(pPrecision != NULL);
|
|
|
|
switch (format)
|
|
{
|
|
case ma_format_u8:
|
|
{
|
|
*pEncoding = AUDIO_ENCODING_ULINEAR;
|
|
*pPrecision = 8;
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
*pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
|
|
*pPrecision = 24;
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
*pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
|
|
*pPrecision = 32;
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
case ma_format_f32:
|
|
case ma_format_unknown:
|
|
default:
|
|
{
|
|
*pEncoding = (ma_is_little_endian()) ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_SLINEAR_BE;
|
|
*pPrecision = 16;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static ma_format ma_format_from_prinfo__audio4(struct audio_prinfo* prinfo)
|
|
{
|
|
return ma_format_from_encoding__audio4(prinfo->encoding, prinfo->precision);
|
|
}
|
|
|
|
static ma_format ma_best_format_from_fd__audio4(int fd, ma_format preferredFormat)
|
|
{
|
|
audio_encoding_t encoding;
|
|
ma_uint32 iFormat;
|
|
int counter = 0;
|
|
|
|
if (preferredFormat != ma_format_unknown) {
|
|
counter = 0;
|
|
for (;;) {
|
|
MA_ZERO_OBJECT(&encoding);
|
|
encoding.index = counter;
|
|
if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
|
|
break;
|
|
}
|
|
|
|
if (preferredFormat == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
|
|
return preferredFormat;
|
|
}
|
|
|
|
counter += 1;
|
|
}
|
|
}
|
|
|
|
for (iFormat = 0; iFormat < ma_countof(g_maFormatPriorities); iFormat += 1) {
|
|
ma_format format = g_maFormatPriorities[iFormat];
|
|
|
|
counter = 0;
|
|
for (;;) {
|
|
MA_ZERO_OBJECT(&encoding);
|
|
encoding.index = counter;
|
|
if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
|
|
break;
|
|
}
|
|
|
|
if (format == ma_format_from_encoding__audio4(encoding.encoding, encoding.precision)) {
|
|
return format;
|
|
}
|
|
|
|
counter += 1;
|
|
}
|
|
}
|
|
|
|
return ma_format_unknown;
|
|
}
|
|
#else
|
|
static ma_format ma_format_from_swpar__audio4(struct audio_swpar* par)
|
|
{
|
|
if (par->bits == 8 && par->bps == 1 && par->sig == 0) {
|
|
return ma_format_u8;
|
|
}
|
|
if (par->bits == 16 && par->bps == 2 && par->sig == 1 && par->le == ma_is_little_endian()) {
|
|
return ma_format_s16;
|
|
}
|
|
if (par->bits == 24 && par->bps == 3 && par->sig == 1 && par->le == ma_is_little_endian()) {
|
|
return ma_format_s24;
|
|
}
|
|
if (par->bits == 32 && par->bps == 4 && par->sig == 1 && par->le == ma_is_little_endian()) {
|
|
return ma_format_f32;
|
|
}
|
|
|
|
return ma_format_unknown;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_context_get_device_info_from_fd__audio4(ma_context* pContext, ma_device_type deviceType, int fd, ma_device_info* pDeviceInfo)
|
|
{
|
|
audio_device_t fdDevice;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(fd >= 0);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
(void)pContext;
|
|
(void)deviceType;
|
|
|
|
if (ioctl(fd, AUDIO_GETDEV, &fdDevice) < 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
ma_strcpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), fdDevice.name);
|
|
|
|
#if !defined(MA_AUDIO4_USE_NEW_API)
|
|
{
|
|
audio_info_t fdInfo;
|
|
int counter = 0;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
|
|
#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000)
|
|
if (ioctl(fd, AUDIO_GETFORMAT, &fdInfo) < 0) {
|
|
return MA_ERROR;
|
|
}
|
|
#else
|
|
if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
|
|
return MA_ERROR;
|
|
}
|
|
#endif
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
channels = fdInfo.play.channels;
|
|
sampleRate = fdInfo.play.sample_rate;
|
|
} else {
|
|
channels = fdInfo.record.channels;
|
|
sampleRate = fdInfo.record.sample_rate;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
for (;;) {
|
|
audio_encoding_t encoding;
|
|
ma_format format;
|
|
|
|
MA_ZERO_OBJECT(&encoding);
|
|
encoding.index = counter;
|
|
if (ioctl(fd, AUDIO_GETENC, &encoding) < 0) {
|
|
break;
|
|
}
|
|
|
|
format = ma_format_from_encoding__audio4(encoding.encoding, encoding.precision);
|
|
if (format != ma_format_unknown) {
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
|
|
}
|
|
|
|
counter += 1;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
struct audio_swpar fdPar;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
|
|
if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
format = ma_format_from_swpar__audio4(&fdPar);
|
|
if (format == ma_format_unknown) {
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
channels = fdPar.pchan;
|
|
} else {
|
|
channels = fdPar.rchan;
|
|
}
|
|
|
|
sampleRate = fdPar.rate;
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, channels, sampleRate, 0);
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__audio4(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
const int maxDevices = 64;
|
|
char devpath[256];
|
|
int iDevice;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
for (iDevice = 0; iDevice < maxDevices; ++iDevice) {
|
|
struct stat st;
|
|
int fd;
|
|
ma_bool32 isTerminating = MA_FALSE;
|
|
|
|
ma_strcpy_s(devpath, sizeof(devpath), "/dev/audioctl");
|
|
ma_itoa_s(iDevice, devpath+strlen(devpath), sizeof(devpath)-strlen(devpath), 10);
|
|
|
|
if (stat(devpath, &st) < 0) {
|
|
break;
|
|
}
|
|
|
|
if (!isTerminating) {
|
|
fd = open(devpath, O_RDONLY, 0);
|
|
if (fd >= 0) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
|
|
if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_playback, fd, &deviceInfo) == MA_SUCCESS) {
|
|
isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
if (!isTerminating) {
|
|
fd = open(devpath, O_WRONLY, 0);
|
|
if (fd >= 0) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_construct_device_id__audio4(deviceInfo.id.audio4, sizeof(deviceInfo.id.audio4), "/dev/audio", iDevice);
|
|
if (ma_context_get_device_info_from_fd__audio4(pContext, ma_device_type_capture, fd, &deviceInfo) == MA_SUCCESS) {
|
|
isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
|
|
close(fd);
|
|
}
|
|
}
|
|
|
|
if (isTerminating) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__audio4(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
int fd = -1;
|
|
int deviceIndex = -1;
|
|
char ctlid[256];
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDeviceID == NULL) {
|
|
ma_strcpy_s(ctlid, sizeof(ctlid), "/dev/audioctl");
|
|
} else {
|
|
result = ma_extract_device_index_from_id__audio4(pDeviceID->audio4, "/dev/audio", &deviceIndex);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
ma_construct_device_id__audio4(ctlid, sizeof(ctlid), "/dev/audioctl", deviceIndex);
|
|
}
|
|
|
|
fd = open(ctlid, (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY, 0);
|
|
if (fd == -1) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
if (deviceIndex == -1) {
|
|
ma_strcpy_s(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio");
|
|
} else {
|
|
ma_construct_device_id__audio4(pDeviceInfo->id.audio4, sizeof(pDeviceInfo->id.audio4), "/dev/audio", deviceIndex);
|
|
}
|
|
|
|
result = ma_context_get_device_info_from_fd__audio4(pContext, deviceType, fd, pDeviceInfo);
|
|
|
|
close(fd);
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__audio4(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
close(pDevice->audio4.fdCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
close(pDevice->audio4.fdPlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
|
|
{
|
|
const char* pDefaultDeviceNames[] = {
|
|
"/dev/audio",
|
|
"/dev/audio0"
|
|
};
|
|
const char* pDefaultDeviceCtlNames[] = {
|
|
"/dev/audioctl",
|
|
"/dev/audioctl0"
|
|
};
|
|
int fd;
|
|
int fdFlags = 0;
|
|
size_t iDefaultDevice = (size_t)-1;
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
ma_uint32 internalSampleRate;
|
|
ma_uint32 internalPeriodSizeInFrames;
|
|
ma_uint32 internalPeriods;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(deviceType != ma_device_type_duplex);
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
fdFlags = O_RDONLY;
|
|
} else {
|
|
fdFlags = O_WRONLY;
|
|
}
|
|
|
|
if (pDescriptor->pDeviceID == NULL) {
|
|
for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) {
|
|
fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0);
|
|
if (fd != -1) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0);
|
|
|
|
for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) {
|
|
if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) {
|
|
iDefaultDevice = (size_t)-1;
|
|
}
|
|
}
|
|
|
|
if (fd == -1) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to open device.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
#if !defined(MA_AUDIO4_USE_NEW_API)
|
|
{
|
|
audio_info_t fdInfo;
|
|
int fdInfoResult = -1;
|
|
|
|
AUDIO_INITINFO(&fdInfo);
|
|
|
|
if (iDefaultDevice != (size_t)-1) {
|
|
int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0);
|
|
if (fdctl != -1) {
|
|
#if defined(__NetBSD__) && (__NetBSD_Version__ >= 900000000)
|
|
fdInfoResult = ioctl(fdctl, AUDIO_GETFORMAT, &fdInfo);
|
|
#else
|
|
fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo);
|
|
#endif
|
|
close(fdctl);
|
|
}
|
|
}
|
|
|
|
if (fdInfoResult == -1) {
|
|
if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
fdInfo.mode = AUMODE_RECORD;
|
|
ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.record.encoding, &fdInfo.record.precision);
|
|
|
|
if (pDescriptor->channels != 0) {
|
|
fdInfo.record.channels = ma_clamp(pDescriptor->channels, 1, 12);
|
|
}
|
|
|
|
if (pDescriptor->sampleRate != 0) {
|
|
fdInfo.record.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000);
|
|
}
|
|
} else {
|
|
fdInfo.mode = AUMODE_PLAY;
|
|
ma_encoding_from_format__audio4(ma_best_format_from_fd__audio4(fd, pDescriptor->format), &fdInfo.play.encoding, &fdInfo.play.precision);
|
|
|
|
if (pDescriptor->channels != 0) {
|
|
fdInfo.play.channels = ma_clamp(pDescriptor->channels, 1, 12);
|
|
}
|
|
|
|
if (pDescriptor->sampleRate != 0) {
|
|
fdInfo.play.sample_rate = ma_clamp(pDescriptor->sampleRate, 1000, 192000);
|
|
}
|
|
}
|
|
|
|
if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device format. AUDIO_SETINFO failed.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
internalFormat = ma_format_from_prinfo__audio4(&fdInfo.record);
|
|
internalChannels = fdInfo.record.channels;
|
|
internalSampleRate = fdInfo.record.sample_rate;
|
|
} else {
|
|
internalFormat = ma_format_from_prinfo__audio4(&fdInfo.play);
|
|
internalChannels = fdInfo.play.channels;
|
|
internalSampleRate = fdInfo.play.sample_rate;
|
|
}
|
|
|
|
if (internalFormat == ma_format_unknown) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.");
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
{
|
|
ma_uint32 internalPeriodSizeInBytes;
|
|
|
|
internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
|
|
|
|
internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
if (internalPeriodSizeInBytes < 16) {
|
|
internalPeriodSizeInBytes = 16;
|
|
}
|
|
|
|
internalPeriods = pDescriptor->periodCount;
|
|
if (internalPeriods < 2) {
|
|
internalPeriods = 2;
|
|
}
|
|
|
|
AUDIO_INITINFO(&fdInfo);
|
|
fdInfo.hiwat = internalPeriods;
|
|
fdInfo.lowat = internalPeriods-1;
|
|
fdInfo.blocksize = internalPeriodSizeInBytes;
|
|
if (ioctl(fd, AUDIO_SETINFO, &fdInfo) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set internal buffer size. AUDIO_SETINFO failed.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
internalPeriods = fdInfo.hiwat;
|
|
internalPeriodSizeInFrames = fdInfo.blocksize / ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
struct audio_swpar fdPar;
|
|
|
|
if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve initial device parameters.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
internalFormat = ma_format_from_swpar__audio4(&fdPar);
|
|
internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
|
|
internalSampleRate = fdPar.rate;
|
|
|
|
if (internalFormat == ma_format_unknown) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.");
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
{
|
|
ma_uint32 internalPeriodSizeInBytes;
|
|
|
|
internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, internalSampleRate, pConfig->performanceProfile);
|
|
|
|
internalPeriodSizeInBytes = internalPeriodSizeInFrames * ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
if (internalPeriodSizeInBytes < 16) {
|
|
internalPeriodSizeInBytes = 16;
|
|
}
|
|
|
|
fdPar.nblks = pDescriptor->periodCount;
|
|
fdPar.round = internalPeriodSizeInBytes;
|
|
|
|
if (ioctl(fd, AUDIO_SETPAR, &fdPar) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to set device parameters.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (ioctl(fd, AUDIO_GETPAR, &fdPar) < 0) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to retrieve actual device parameters.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
}
|
|
|
|
internalFormat = ma_format_from_swpar__audio4(&fdPar);
|
|
internalChannels = (deviceType == ma_device_type_capture) ? fdPar.rchan : fdPar.pchan;
|
|
internalSampleRate = fdPar.rate;
|
|
internalPeriods = fdPar.nblks;
|
|
internalPeriodSizeInFrames = fdPar.round / ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
}
|
|
#endif
|
|
|
|
if (internalFormat == ma_format_unknown) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] The device's internal device format is not supported by miniaudio. The device is unusable.");
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
pDevice->audio4.fdCapture = fd;
|
|
} else {
|
|
pDevice->audio4.fdPlayback = fd;
|
|
}
|
|
|
|
pDescriptor->format = internalFormat;
|
|
pDescriptor->channels = internalChannels;
|
|
pDescriptor->sampleRate = internalSampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), internalChannels);
|
|
pDescriptor->periodSizeInFrames = internalPeriodSizeInFrames;
|
|
pDescriptor->periodCount = internalPeriods;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__audio4(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->audio4);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
pDevice->audio4.fdCapture = -1;
|
|
pDevice->audio4.fdPlayback = -1;
|
|
|
|
#if defined(__NetBSD_Version__) && __NetBSD_Version__ >= 800000000
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
#else
|
|
#endif
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_fd__audio4(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
|
|
if (result != MA_SUCCESS) {
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
close(pDevice->audio4.fdCapture);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__audio4(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->audio4.fdCapture == -1) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->audio4.fdPlayback == -1) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop_fd__audio4(ma_device* pDevice, int fd)
|
|
{
|
|
if (fd == -1) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_AUDIO4_USE_NEW_API)
|
|
if (ioctl(fd, AUDIO_FLUSH, 0) < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_FLUSH failed.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
#else
|
|
if (ioctl(fd, AUDIO_STOP, 0) < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to stop device. AUDIO_STOP failed.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__audio4(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_result result;
|
|
|
|
result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdCapture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_result result;
|
|
|
|
#if !defined(MA_AUDIO4_USE_NEW_API)
|
|
ioctl(pDevice->audio4.fdPlayback, AUDIO_DRAIN, 0);
|
|
#endif
|
|
|
|
result = ma_device_stop_fd__audio4(pDevice, pDevice->audio4.fdPlayback);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_write__audio4(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
int result;
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
result = write(pDevice->audio4.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
if (result < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to write data to the device.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_read__audio4(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
int result;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
result = read(pDevice->audio4.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
|
|
if (result < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] Failed to read data from the device.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = (ma_uint32)result / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__audio4(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_audio4);
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__audio4(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
pCallbacks->onContextInit = ma_context_init__audio4;
|
|
pCallbacks->onContextUninit = ma_context_uninit__audio4;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__audio4;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__audio4;
|
|
pCallbacks->onDeviceInit = ma_device_init__audio4;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__audio4;
|
|
pCallbacks->onDeviceStart = ma_device_start__audio4;
|
|
pCallbacks->onDeviceStop = ma_device_stop__audio4;
|
|
pCallbacks->onDeviceRead = ma_device_read__audio4;
|
|
pCallbacks->onDeviceWrite = ma_device_write__audio4;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_OSS
|
|
#include <sys/ioctl.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/soundcard.h>
|
|
|
|
#ifndef SNDCTL_DSP_HALT
|
|
#define SNDCTL_DSP_HALT SNDCTL_DSP_RESET
|
|
#endif
|
|
|
|
#define MA_OSS_DEFAULT_DEVICE_NAME "/dev/dsp"
|
|
|
|
static int ma_open_temp_device__oss(void)
|
|
{
|
|
int fd = open("/dev/mixer", O_RDONLY, 0);
|
|
if (fd >= 0) {
|
|
return fd;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static ma_result ma_context_open_device__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_share_mode shareMode, int* pfd)
|
|
{
|
|
const char* deviceName;
|
|
int flags;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pfd != NULL);
|
|
(void)pContext;
|
|
|
|
*pfd = -1;
|
|
|
|
if (deviceType == ma_device_type_duplex) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
deviceName = MA_OSS_DEFAULT_DEVICE_NAME;
|
|
if (pDeviceID != NULL) {
|
|
deviceName = pDeviceID->oss;
|
|
}
|
|
|
|
flags = (deviceType == ma_device_type_playback) ? O_WRONLY : O_RDONLY;
|
|
if (shareMode == ma_share_mode_exclusive) {
|
|
flags |= O_EXCL;
|
|
}
|
|
|
|
*pfd = open(deviceName, flags, 0);
|
|
if (*pfd == -1) {
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__oss(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
int fd;
|
|
oss_sysinfo si;
|
|
int result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
fd = ma_open_temp_device__oss();
|
|
if (fd == -1) {
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
result = ioctl(fd, SNDCTL_SYSINFO, &si);
|
|
if (result != -1) {
|
|
int iAudioDevice;
|
|
for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
|
|
oss_audioinfo ai;
|
|
ai.dev = iAudioDevice;
|
|
result = ioctl(fd, SNDCTL_AUDIOINFO, &ai);
|
|
if (result != -1) {
|
|
if (ai.devnode[0] != '\0') {
|
|
ma_device_info deviceInfo;
|
|
ma_bool32 isTerminating = MA_FALSE;
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
ma_strncpy_s(deviceInfo.id.oss, sizeof(deviceInfo.id.oss), ai.devnode, (size_t)-1);
|
|
|
|
if (ai.handle[0] != '\0') {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.handle, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), ai.name, (size_t)-1);
|
|
}
|
|
|
|
if (!isTerminating && (ai.caps & PCM_CAP_OUTPUT) != 0) {
|
|
isTerminating = !callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
if (!isTerminating && (ai.caps & PCM_CAP_INPUT) != 0) {
|
|
isTerminating = !callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (isTerminating) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
close(fd);
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
close(fd);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_context_add_native_data_format__oss(ma_context* pContext, oss_audioinfo* pAudioInfo, ma_format format, ma_device_info* pDeviceInfo)
|
|
{
|
|
unsigned int minChannels;
|
|
unsigned int maxChannels;
|
|
unsigned int iRate;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pAudioInfo != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
minChannels = ma_clamp(pAudioInfo->min_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
|
maxChannels = ma_clamp(pAudioInfo->max_channels, MA_MIN_CHANNELS, MA_MAX_CHANNELS);
|
|
|
|
if (pAudioInfo->nrates > 0) {
|
|
for (iRate = 0; iRate < pAudioInfo->nrates; iRate += 1) {
|
|
unsigned int rate = pAudioInfo->rates[iRate];
|
|
|
|
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, 0, rate, 0);
|
|
} else {
|
|
unsigned int iChannel;
|
|
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, rate, 0);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (iRate = 0; iRate < ma_countof(g_maStandardSampleRatePriorities); iRate += 1) {
|
|
ma_uint32 standardRate = g_maStandardSampleRatePriorities[iRate];
|
|
|
|
if (standardRate >= (ma_uint32)pAudioInfo->min_rate && standardRate <= (ma_uint32)pAudioInfo->max_rate) {
|
|
if (minChannels == MA_MIN_CHANNELS && maxChannels == MA_MAX_CHANNELS) {
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, 0, standardRate, 0);
|
|
} else {
|
|
unsigned int iChannel;
|
|
for (iChannel = minChannels; iChannel <= maxChannels; iChannel += 1) {
|
|
ma_device_info_add_native_data_format(pDeviceInfo, format, iChannel, standardRate, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__oss(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_bool32 foundDevice;
|
|
int fdTemp;
|
|
oss_sysinfo si;
|
|
int result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDeviceID == NULL) {
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
foundDevice = MA_FALSE;
|
|
|
|
fdTemp = ma_open_temp_device__oss();
|
|
if (fdTemp == -1) {
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open a temporary device for retrieving system information used for device enumeration.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
result = ioctl(fdTemp, SNDCTL_SYSINFO, &si);
|
|
if (result != -1) {
|
|
int iAudioDevice;
|
|
for (iAudioDevice = 0; iAudioDevice < si.numaudios; ++iAudioDevice) {
|
|
oss_audioinfo ai;
|
|
ai.dev = iAudioDevice;
|
|
result = ioctl(fdTemp, SNDCTL_AUDIOINFO, &ai);
|
|
if (result != -1) {
|
|
if (ma_strcmp(ai.devnode, pDeviceID->oss) == 0) {
|
|
if ((deviceType == ma_device_type_playback && ((ai.caps & PCM_CAP_OUTPUT) != 0)) ||
|
|
(deviceType == ma_device_type_capture && ((ai.caps & PCM_CAP_INPUT) != 0))) {
|
|
unsigned int formatMask;
|
|
|
|
ma_strncpy_s(pDeviceInfo->id.oss, sizeof(pDeviceInfo->id.oss), ai.devnode, (size_t)-1);
|
|
|
|
if (ai.handle[0] != '\0') {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.handle, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), ai.name, (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
formatMask = ai.oformats;
|
|
} else {
|
|
formatMask = ai.iformats;
|
|
}
|
|
|
|
if (((formatMask & AFMT_S16_LE) != 0 && ma_is_little_endian()) || (AFMT_S16_BE && ma_is_big_endian())) {
|
|
ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s16, pDeviceInfo);
|
|
}
|
|
if (((formatMask & AFMT_S32_LE) != 0 && ma_is_little_endian()) || (AFMT_S32_BE && ma_is_big_endian())) {
|
|
ma_context_add_native_data_format__oss(pContext, &ai, ma_format_s32, pDeviceInfo);
|
|
}
|
|
if ((formatMask & AFMT_U8) != 0) {
|
|
ma_context_add_native_data_format__oss(pContext, &ai, ma_format_u8, pDeviceInfo);
|
|
}
|
|
|
|
foundDevice = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
close(fdTemp);
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve system information for device enumeration.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
close(fdTemp);
|
|
|
|
if (!foundDevice) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__oss(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
close(pDevice->oss.fdCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
close(pDevice->oss.fdPlayback);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static int ma_format_to_oss(ma_format format)
|
|
{
|
|
int ossFormat = AFMT_U8;
|
|
switch (format) {
|
|
case ma_format_s16: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
|
|
case ma_format_s24: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
|
|
case ma_format_s32: ossFormat = (ma_is_little_endian()) ? AFMT_S32_LE : AFMT_S32_BE; break;
|
|
case ma_format_f32: ossFormat = (ma_is_little_endian()) ? AFMT_S16_LE : AFMT_S16_BE; break;
|
|
case ma_format_u8:
|
|
default: ossFormat = AFMT_U8; break;
|
|
}
|
|
|
|
return ossFormat;
|
|
}
|
|
|
|
static ma_format ma_format_from_oss(int ossFormat)
|
|
{
|
|
if (ossFormat == AFMT_U8) {
|
|
return ma_format_u8;
|
|
} else {
|
|
if (ma_is_little_endian()) {
|
|
switch (ossFormat) {
|
|
case AFMT_S16_LE: return ma_format_s16;
|
|
case AFMT_S32_LE: return ma_format_s32;
|
|
default: return ma_format_unknown;
|
|
}
|
|
} else {
|
|
switch (ossFormat) {
|
|
case AFMT_S16_BE: return ma_format_s16;
|
|
case AFMT_S32_BE: return ma_format_s32;
|
|
default: return ma_format_unknown;
|
|
}
|
|
}
|
|
}
|
|
|
|
return ma_format_unknown;
|
|
}
|
|
|
|
static ma_result ma_device_init_fd__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType)
|
|
{
|
|
ma_result result;
|
|
int ossResult;
|
|
int fd;
|
|
const ma_device_id* pDeviceID = NULL;
|
|
ma_share_mode shareMode;
|
|
int ossFormat;
|
|
int ossChannels;
|
|
int ossSampleRate;
|
|
int ossFragment;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(deviceType != ma_device_type_duplex);
|
|
|
|
pDeviceID = pDescriptor->pDeviceID;
|
|
shareMode = pDescriptor->shareMode;
|
|
ossFormat = ma_format_to_oss((pDescriptor->format != ma_format_unknown) ? pDescriptor->format : ma_format_s16);
|
|
ossChannels = (int)(pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS;
|
|
ossSampleRate = (int)(pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE;
|
|
|
|
result = ma_context_open_device__oss(pDevice->pContext, deviceType, pDeviceID, shareMode, &fd);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
|
|
return result;
|
|
}
|
|
|
|
ossResult = ioctl(fd, SNDCTL_DSP_SETFMT, &ossFormat);
|
|
if (ossResult == -1) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set format.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
ossResult = ioctl(fd, SNDCTL_DSP_CHANNELS, &ossChannels);
|
|
if (ossResult == -1) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set channel count.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
ossResult = ioctl(fd, SNDCTL_DSP_SPEED, &ossSampleRate);
|
|
if (ossResult == -1) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set sample rate.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
{
|
|
ma_uint32 periodSizeInFrames;
|
|
ma_uint32 periodSizeInBytes;
|
|
ma_uint32 ossFragmentSizePower;
|
|
|
|
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, (ma_uint32)ossSampleRate, pConfig->performanceProfile);
|
|
|
|
periodSizeInBytes = ma_round_to_power_of_2(periodSizeInFrames * ma_get_bytes_per_frame(ma_format_from_oss(ossFormat), ossChannels));
|
|
if (periodSizeInBytes < 16) {
|
|
periodSizeInBytes = 16;
|
|
}
|
|
|
|
ossFragmentSizePower = 4;
|
|
periodSizeInBytes >>= 4;
|
|
while (periodSizeInBytes >>= 1) {
|
|
ossFragmentSizePower += 1;
|
|
}
|
|
|
|
ossFragment = (int)((pConfig->periods << 16) | ossFragmentSizePower);
|
|
ossResult = ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &ossFragment);
|
|
if (ossResult == -1) {
|
|
close(fd);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to set fragment size and period count.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
pDevice->oss.fdCapture = fd;
|
|
} else {
|
|
pDevice->oss.fdPlayback = fd;
|
|
}
|
|
|
|
pDescriptor->format = ma_format_from_oss(ossFormat);
|
|
pDescriptor->channels = ossChannels;
|
|
pDescriptor->sampleRate = ossSampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_sound4, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
|
|
pDescriptor->periodCount = (ma_uint32)(ossFragment >> 16);
|
|
pDescriptor->periodSizeInFrames = (ma_uint32)(1 << (ossFragment & 0xFFFF)) / ma_get_bytes_per_frame(pDescriptor->format, pDescriptor->channels);
|
|
|
|
if (pDescriptor->format == ma_format_unknown) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] The device's internal format is not supported by miniaudio.");
|
|
return MA_FORMAT_NOT_SUPPORTED;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__oss(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
MA_ZERO_OBJECT(&pDevice->oss);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result result = ma_device_init_fd__oss(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open device.");
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__oss(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
(void)pDevice;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__oss(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
(void)pDevice;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_write__oss(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten)
|
|
{
|
|
int resultOSS;
|
|
ma_uint32 deviceState;
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
deviceState = ma_device_get_state(pDevice);
|
|
if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
resultOSS = write(pDevice->oss.fdPlayback, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
if (resultOSS < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to send data from the client to the device.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_read__oss(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead)
|
|
{
|
|
int resultOSS;
|
|
ma_uint32 deviceState;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
deviceState = ma_device_get_state(pDevice);
|
|
if (deviceState != ma_device_state_started && deviceState != ma_device_state_starting) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
resultOSS = read(pDevice->oss.fdCapture, pPCMFrames, frameCount * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels));
|
|
if (resultOSS < 0) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OSS] Failed to read data from the device to be sent to the client.");
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = (ma_uint32)resultOSS / ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__oss(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_oss);
|
|
|
|
(void)pContext;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__oss(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
int fd;
|
|
int ossVersion;
|
|
int result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
fd = ma_open_temp_device__oss();
|
|
if (fd == -1) {
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to open temporary device for retrieving system properties.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
ossVersion = 0;
|
|
result = ioctl(fd, OSS_GETVERSION, &ossVersion);
|
|
if (result == -1) {
|
|
close(fd);
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[OSS] Failed to retrieve OSS version.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
close(fd);
|
|
|
|
pContext->oss.versionMajor = ((ossVersion & 0xFF0000) >> 16);
|
|
pContext->oss.versionMinor = ((ossVersion & 0x00FF00) >> 8);
|
|
|
|
pCallbacks->onContextInit = ma_context_init__oss;
|
|
pCallbacks->onContextUninit = ma_context_uninit__oss;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__oss;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__oss;
|
|
pCallbacks->onDeviceInit = ma_device_init__oss;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__oss;
|
|
pCallbacks->onDeviceStart = ma_device_start__oss;
|
|
pCallbacks->onDeviceStop = ma_device_stop__oss;
|
|
pCallbacks->onDeviceRead = ma_device_read__oss;
|
|
pCallbacks->onDeviceWrite = ma_device_write__oss;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_AAUDIO
|
|
|
|
#ifdef MA_NO_RUNTIME_LINKING
|
|
#include <AAudio/AAudio.h>
|
|
#endif
|
|
|
|
typedef int32_t ma_aaudio_result_t;
|
|
typedef int32_t ma_aaudio_direction_t;
|
|
typedef int32_t ma_aaudio_sharing_mode_t;
|
|
typedef int32_t ma_aaudio_format_t;
|
|
typedef int32_t ma_aaudio_stream_state_t;
|
|
typedef int32_t ma_aaudio_performance_mode_t;
|
|
typedef int32_t ma_aaudio_usage_t;
|
|
typedef int32_t ma_aaudio_content_type_t;
|
|
typedef int32_t ma_aaudio_input_preset_t;
|
|
typedef int32_t ma_aaudio_allowed_capture_policy_t;
|
|
typedef int32_t ma_aaudio_data_callback_result_t;
|
|
typedef struct ma_AAudioStreamBuilder_t* ma_AAudioStreamBuilder;
|
|
typedef struct ma_AAudioStream_t* ma_AAudioStream;
|
|
|
|
#define MA_AAUDIO_UNSPECIFIED 0
|
|
|
|
#define MA_AAUDIO_OK 0
|
|
|
|
#define MA_AAUDIO_DIRECTION_OUTPUT 0
|
|
#define MA_AAUDIO_DIRECTION_INPUT 1
|
|
|
|
#define MA_AAUDIO_SHARING_MODE_EXCLUSIVE 0
|
|
#define MA_AAUDIO_SHARING_MODE_SHARED 1
|
|
|
|
#define MA_AAUDIO_FORMAT_PCM_I16 1
|
|
#define MA_AAUDIO_FORMAT_PCM_FLOAT 2
|
|
|
|
#define MA_AAUDIO_STREAM_STATE_UNINITIALIZED 0
|
|
#define MA_AAUDIO_STREAM_STATE_UNKNOWN 1
|
|
#define MA_AAUDIO_STREAM_STATE_OPEN 2
|
|
#define MA_AAUDIO_STREAM_STATE_STARTING 3
|
|
#define MA_AAUDIO_STREAM_STATE_STARTED 4
|
|
#define MA_AAUDIO_STREAM_STATE_PAUSING 5
|
|
#define MA_AAUDIO_STREAM_STATE_PAUSED 6
|
|
#define MA_AAUDIO_STREAM_STATE_FLUSHING 7
|
|
#define MA_AAUDIO_STREAM_STATE_FLUSHED 8
|
|
#define MA_AAUDIO_STREAM_STATE_STOPPING 9
|
|
#define MA_AAUDIO_STREAM_STATE_STOPPED 10
|
|
#define MA_AAUDIO_STREAM_STATE_CLOSING 11
|
|
#define MA_AAUDIO_STREAM_STATE_CLOSED 12
|
|
#define MA_AAUDIO_STREAM_STATE_DISCONNECTED 13
|
|
|
|
#define MA_AAUDIO_PERFORMANCE_MODE_NONE 10
|
|
#define MA_AAUDIO_PERFORMANCE_MODE_POWER_SAVING 11
|
|
#define MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 12
|
|
|
|
#define MA_AAUDIO_USAGE_MEDIA 1
|
|
#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION 2
|
|
#define MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING 3
|
|
#define MA_AAUDIO_USAGE_ALARM 4
|
|
#define MA_AAUDIO_USAGE_NOTIFICATION 5
|
|
#define MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE 6
|
|
#define MA_AAUDIO_USAGE_NOTIFICATION_EVENT 10
|
|
#define MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY 11
|
|
#define MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE 12
|
|
#define MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION 13
|
|
#define MA_AAUDIO_USAGE_GAME 14
|
|
#define MA_AAUDIO_USAGE_ASSISTANT 16
|
|
#define MA_AAUDIO_SYSTEM_USAGE_EMERGENCY 1000
|
|
#define MA_AAUDIO_SYSTEM_USAGE_SAFETY 1001
|
|
#define MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS 1002
|
|
#define MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT 1003
|
|
|
|
#define MA_AAUDIO_CONTENT_TYPE_SPEECH 1
|
|
#define MA_AAUDIO_CONTENT_TYPE_MUSIC 2
|
|
#define MA_AAUDIO_CONTENT_TYPE_MOVIE 3
|
|
#define MA_AAUDIO_CONTENT_TYPE_SONIFICATION 4
|
|
|
|
#define MA_AAUDIO_INPUT_PRESET_GENERIC 1
|
|
#define MA_AAUDIO_INPUT_PRESET_CAMCORDER 5
|
|
#define MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION 6
|
|
#define MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION 7
|
|
#define MA_AAUDIO_INPUT_PRESET_UNPROCESSED 9
|
|
#define MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE 10
|
|
|
|
#define MA_AAUDIO_ALLOW_CAPTURE_BY_ALL 1
|
|
#define MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM 2
|
|
#define MA_AAUDIO_ALLOW_CAPTURE_BY_NONE 3
|
|
|
|
#define MA_AAUDIO_CALLBACK_RESULT_CONTINUE 0
|
|
#define MA_AAUDIO_CALLBACK_RESULT_STOP 1
|
|
|
|
typedef ma_aaudio_data_callback_result_t (* ma_AAudioStream_dataCallback) (ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t numFrames);
|
|
typedef void (* ma_AAudioStream_errorCallback)(ma_AAudioStream *pStream, void *pUserData, ma_aaudio_result_t error);
|
|
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudio_createStreamBuilder) (ma_AAudioStreamBuilder** ppBuilder);
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_delete) (ma_AAudioStreamBuilder* pBuilder);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setDeviceId) (ma_AAudioStreamBuilder* pBuilder, int32_t deviceId);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setDirection) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_direction_t direction);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setSharingMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_sharing_mode_t sharingMode);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setFormat) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_format_t format);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setChannelCount) (ma_AAudioStreamBuilder* pBuilder, int32_t channelCount);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setSampleRate) (ma_AAudioStreamBuilder* pBuilder, int32_t sampleRate);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)(ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback) (ma_AAudioStreamBuilder* pBuilder, int32_t numFrames);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setDataCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_dataCallback callback, void* pUserData);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setErrorCallback) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream_errorCallback callback, void* pUserData);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setPerformanceMode) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_performance_mode_t mode);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setUsage) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_usage_t contentType);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setContentType) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_content_type_t contentType);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setInputPreset) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_input_preset_t inputPreset);
|
|
typedef void (* MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy) (ma_AAudioStreamBuilder* pBuilder, ma_aaudio_allowed_capture_policy_t policy);
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudioStreamBuilder_openStream) (ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream);
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_close) (ma_AAudioStream* pStream);
|
|
typedef ma_aaudio_stream_state_t (* MA_PFN_AAudioStream_getState) (ma_AAudioStream* pStream);
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_waitForStateChange) (ma_AAudioStream* pStream, ma_aaudio_stream_state_t inputState, ma_aaudio_stream_state_t* pNextState, int64_t timeoutInNanoseconds);
|
|
typedef ma_aaudio_format_t (* MA_PFN_AAudioStream_getFormat) (ma_AAudioStream* pStream);
|
|
typedef int32_t (* MA_PFN_AAudioStream_getChannelCount) (ma_AAudioStream* pStream);
|
|
typedef int32_t (* MA_PFN_AAudioStream_getSampleRate) (ma_AAudioStream* pStream);
|
|
typedef int32_t (* MA_PFN_AAudioStream_getBufferCapacityInFrames) (ma_AAudioStream* pStream);
|
|
typedef int32_t (* MA_PFN_AAudioStream_getFramesPerDataCallback) (ma_AAudioStream* pStream);
|
|
typedef int32_t (* MA_PFN_AAudioStream_getFramesPerBurst) (ma_AAudioStream* pStream);
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStart) (ma_AAudioStream* pStream);
|
|
typedef ma_aaudio_result_t (* MA_PFN_AAudioStream_requestStop) (ma_AAudioStream* pStream);
|
|
|
|
static ma_result ma_result_from_aaudio(ma_aaudio_result_t resultAA)
|
|
{
|
|
switch (resultAA)
|
|
{
|
|
case MA_AAUDIO_OK: return MA_SUCCESS;
|
|
default: break;
|
|
}
|
|
|
|
return MA_ERROR;
|
|
}
|
|
|
|
static ma_aaudio_usage_t ma_to_usage__aaudio(ma_aaudio_usage usage)
|
|
{
|
|
switch (usage) {
|
|
case ma_aaudio_usage_media: return MA_AAUDIO_USAGE_MEDIA;
|
|
case ma_aaudio_usage_voice_communication: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION;
|
|
case ma_aaudio_usage_voice_communication_signalling: return MA_AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
|
|
case ma_aaudio_usage_alarm: return MA_AAUDIO_USAGE_ALARM;
|
|
case ma_aaudio_usage_notification: return MA_AAUDIO_USAGE_NOTIFICATION;
|
|
case ma_aaudio_usage_notification_ringtone: return MA_AAUDIO_USAGE_NOTIFICATION_RINGTONE;
|
|
case ma_aaudio_usage_notification_event: return MA_AAUDIO_USAGE_NOTIFICATION_EVENT;
|
|
case ma_aaudio_usage_assistance_accessibility: return MA_AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
|
|
case ma_aaudio_usage_assistance_navigation_guidance: return MA_AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
|
|
case ma_aaudio_usage_assistance_sonification: return MA_AAUDIO_USAGE_ASSISTANCE_SONIFICATION;
|
|
case ma_aaudio_usage_game: return MA_AAUDIO_USAGE_GAME;
|
|
case ma_aaudio_usage_assitant: return MA_AAUDIO_USAGE_ASSISTANT;
|
|
case ma_aaudio_usage_emergency: return MA_AAUDIO_SYSTEM_USAGE_EMERGENCY;
|
|
case ma_aaudio_usage_safety: return MA_AAUDIO_SYSTEM_USAGE_SAFETY;
|
|
case ma_aaudio_usage_vehicle_status: return MA_AAUDIO_SYSTEM_USAGE_VEHICLE_STATUS;
|
|
case ma_aaudio_usage_announcement: return MA_AAUDIO_SYSTEM_USAGE_ANNOUNCEMENT;
|
|
default: break;
|
|
}
|
|
|
|
return MA_AAUDIO_USAGE_MEDIA;
|
|
}
|
|
|
|
static ma_aaudio_content_type_t ma_to_content_type__aaudio(ma_aaudio_content_type contentType)
|
|
{
|
|
switch (contentType) {
|
|
case ma_aaudio_content_type_speech: return MA_AAUDIO_CONTENT_TYPE_SPEECH;
|
|
case ma_aaudio_content_type_music: return MA_AAUDIO_CONTENT_TYPE_MUSIC;
|
|
case ma_aaudio_content_type_movie: return MA_AAUDIO_CONTENT_TYPE_MOVIE;
|
|
case ma_aaudio_content_type_sonification: return MA_AAUDIO_CONTENT_TYPE_SONIFICATION;
|
|
default: break;
|
|
}
|
|
|
|
return MA_AAUDIO_CONTENT_TYPE_SPEECH;
|
|
}
|
|
|
|
static ma_aaudio_input_preset_t ma_to_input_preset__aaudio(ma_aaudio_input_preset inputPreset)
|
|
{
|
|
switch (inputPreset) {
|
|
case ma_aaudio_input_preset_generic: return MA_AAUDIO_INPUT_PRESET_GENERIC;
|
|
case ma_aaudio_input_preset_camcorder: return MA_AAUDIO_INPUT_PRESET_CAMCORDER;
|
|
case ma_aaudio_input_preset_voice_recognition: return MA_AAUDIO_INPUT_PRESET_VOICE_RECOGNITION;
|
|
case ma_aaudio_input_preset_voice_communication: return MA_AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION;
|
|
case ma_aaudio_input_preset_unprocessed: return MA_AAUDIO_INPUT_PRESET_UNPROCESSED;
|
|
case ma_aaudio_input_preset_voice_performance: return MA_AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE;
|
|
default: break;
|
|
}
|
|
|
|
return MA_AAUDIO_INPUT_PRESET_GENERIC;
|
|
}
|
|
|
|
static ma_aaudio_allowed_capture_policy_t ma_to_allowed_capture_policy__aaudio(ma_aaudio_allowed_capture_policy allowedCapturePolicy)
|
|
{
|
|
switch (allowedCapturePolicy) {
|
|
case ma_aaudio_allow_capture_by_all: return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
|
|
case ma_aaudio_allow_capture_by_system: return MA_AAUDIO_ALLOW_CAPTURE_BY_SYSTEM;
|
|
case ma_aaudio_allow_capture_by_none: return MA_AAUDIO_ALLOW_CAPTURE_BY_NONE;
|
|
default: break;
|
|
}
|
|
|
|
return MA_AAUDIO_ALLOW_CAPTURE_BY_ALL;
|
|
}
|
|
|
|
static void ma_stream_error_callback__aaudio(ma_AAudioStream* pStream, void* pUserData, ma_aaudio_result_t error)
|
|
{
|
|
ma_result result;
|
|
ma_job job;
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
(void)error;
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] ERROR CALLBACK: error=%d, AAudioStream_getState()=%d\n", error, ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream));
|
|
|
|
if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Tearing down device.\n");
|
|
}
|
|
else {
|
|
job = ma_job_init(MA_JOB_TYPE_DEVICE_AAUDIO_REROUTE);
|
|
job.data.device.aaudio.reroute.pDevice = pDevice;
|
|
|
|
if (pStream == pDevice->aaudio.pStreamCapture) {
|
|
job.data.device.aaudio.reroute.deviceType = ma_device_type_capture;
|
|
} else {
|
|
job.data.device.aaudio.reroute.deviceType = ma_device_type_playback;
|
|
}
|
|
|
|
result = ma_device_job_thread_post(&pDevice->pContext->aaudio.jobThread, &job);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Device Disconnected. Failed to post job for rerouting.\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
static ma_aaudio_data_callback_result_t ma_stream_data_callback_capture__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (frameCount > 0) {
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, pAudioData, (ma_uint32)frameCount);
|
|
}
|
|
|
|
(void)pStream;
|
|
return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
|
|
}
|
|
|
|
static ma_aaudio_data_callback_result_t ma_stream_data_callback_playback__aaudio(ma_AAudioStream* pStream, void* pUserData, void* pAudioData, int32_t frameCount)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (frameCount > 0) {
|
|
ma_device_handle_backend_data_callback(pDevice, pAudioData, NULL, (ma_uint32)frameCount);
|
|
}
|
|
|
|
(void)pStream;
|
|
return MA_AAUDIO_CALLBACK_RESULT_CONTINUE;
|
|
}
|
|
|
|
static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, const ma_device_descriptor* pDescriptor, const ma_device_config* pConfig, ma_device* pDevice, ma_AAudioStreamBuilder** ppBuilder)
|
|
{
|
|
ma_AAudioStreamBuilder* pBuilder;
|
|
ma_aaudio_result_t resultAA;
|
|
|
|
*ppBuilder = NULL;
|
|
|
|
resultAA = ((MA_PFN_AAudio_createStreamBuilder)pContext->aaudio.AAudio_createStreamBuilder)(&pBuilder);
|
|
if (resultAA != MA_AAUDIO_OK) {
|
|
return ma_result_from_aaudio(resultAA);
|
|
}
|
|
|
|
if (pDeviceID != NULL) {
|
|
((MA_PFN_AAudioStreamBuilder_setDeviceId)pContext->aaudio.AAudioStreamBuilder_setDeviceId)(pBuilder, pDeviceID->aaudio);
|
|
}
|
|
|
|
((MA_PFN_AAudioStreamBuilder_setDirection)pContext->aaudio.AAudioStreamBuilder_setDirection)(pBuilder, (deviceType == ma_device_type_playback) ? MA_AAUDIO_DIRECTION_OUTPUT : MA_AAUDIO_DIRECTION_INPUT);
|
|
((MA_PFN_AAudioStreamBuilder_setSharingMode)pContext->aaudio.AAudioStreamBuilder_setSharingMode)(pBuilder, (shareMode == ma_share_mode_shared) ? MA_AAUDIO_SHARING_MODE_SHARED : MA_AAUDIO_SHARING_MODE_EXCLUSIVE);
|
|
|
|
if (pDescriptor != NULL) {
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
if (pDescriptor->sampleRate != 0) {
|
|
((MA_PFN_AAudioStreamBuilder_setSampleRate)pContext->aaudio.AAudioStreamBuilder_setSampleRate)(pBuilder, pDescriptor->sampleRate);
|
|
}
|
|
|
|
if (pDescriptor->channels != 0) {
|
|
((MA_PFN_AAudioStreamBuilder_setChannelCount)pContext->aaudio.AAudioStreamBuilder_setChannelCount)(pBuilder, pDescriptor->channels);
|
|
}
|
|
|
|
if (pDescriptor->format != ma_format_unknown) {
|
|
((MA_PFN_AAudioStreamBuilder_setFormat)pContext->aaudio.AAudioStreamBuilder_setFormat)(pBuilder, (pDescriptor->format == ma_format_s16) ? MA_AAUDIO_FORMAT_PCM_I16 : MA_AAUDIO_FORMAT_PCM_FLOAT);
|
|
}
|
|
|
|
if ((!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) && pConfig->aaudio.allowSetBufferCapacity) {
|
|
|
|
ma_uint32 bufferCapacityInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptor, pDescriptor->sampleRate, pConfig->performanceProfile) * pDescriptor->periodCount;
|
|
|
|
((MA_PFN_AAudioStreamBuilder_setBufferCapacityInFrames)pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames)(pBuilder, bufferCapacityInFrames);
|
|
((MA_PFN_AAudioStreamBuilder_setFramesPerDataCallback)pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback)(pBuilder, bufferCapacityInFrames / pDescriptor->periodCount);
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture) {
|
|
if (pConfig->aaudio.inputPreset != ma_aaudio_input_preset_default && pContext->aaudio.AAudioStreamBuilder_setInputPreset != NULL) {
|
|
((MA_PFN_AAudioStreamBuilder_setInputPreset)pContext->aaudio.AAudioStreamBuilder_setInputPreset)(pBuilder, ma_to_input_preset__aaudio(pConfig->aaudio.inputPreset));
|
|
}
|
|
|
|
((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_capture__aaudio, (void*)pDevice);
|
|
} else {
|
|
if (pConfig->aaudio.usage != ma_aaudio_usage_default && pContext->aaudio.AAudioStreamBuilder_setUsage != NULL) {
|
|
((MA_PFN_AAudioStreamBuilder_setUsage)pContext->aaudio.AAudioStreamBuilder_setUsage)(pBuilder, ma_to_usage__aaudio(pConfig->aaudio.usage));
|
|
}
|
|
|
|
if (pConfig->aaudio.contentType != ma_aaudio_content_type_default && pContext->aaudio.AAudioStreamBuilder_setContentType != NULL) {
|
|
((MA_PFN_AAudioStreamBuilder_setContentType)pContext->aaudio.AAudioStreamBuilder_setContentType)(pBuilder, ma_to_content_type__aaudio(pConfig->aaudio.contentType));
|
|
}
|
|
|
|
if (pConfig->aaudio.allowedCapturePolicy != ma_aaudio_allow_capture_default && pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy != NULL) {
|
|
((MA_PFN_AAudioStreamBuilder_setAllowedCapturePolicy)pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy)(pBuilder, ma_to_allowed_capture_policy__aaudio(pConfig->aaudio.allowedCapturePolicy));
|
|
}
|
|
|
|
((MA_PFN_AAudioStreamBuilder_setDataCallback)pContext->aaudio.AAudioStreamBuilder_setDataCallback)(pBuilder, ma_stream_data_callback_playback__aaudio, (void*)pDevice);
|
|
}
|
|
|
|
((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, (pConfig->performanceProfile == ma_performance_profile_low_latency) ? MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY : MA_AAUDIO_PERFORMANCE_MODE_NONE);
|
|
|
|
if (pDevice != NULL) {
|
|
((MA_PFN_AAudioStreamBuilder_setErrorCallback)pContext->aaudio.AAudioStreamBuilder_setErrorCallback)(pBuilder, ma_stream_error_callback__aaudio, (void*)pDevice);
|
|
}
|
|
}
|
|
|
|
*ppBuilder = pBuilder;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_open_stream_and_close_builder__aaudio(ma_context* pContext, ma_AAudioStreamBuilder* pBuilder, ma_AAudioStream** ppStream)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_result_from_aaudio(((MA_PFN_AAudioStreamBuilder_openStream)pContext->aaudio.AAudioStreamBuilder_openStream)(pBuilder, ppStream));
|
|
((MA_PFN_AAudioStreamBuilder_delete)pContext->aaudio.AAudioStreamBuilder_delete)(pBuilder);
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_open_stream_basic__aaudio(ma_context* pContext, const ma_device_id* pDeviceID, ma_device_type deviceType, ma_share_mode shareMode, ma_AAudioStream** ppStream)
|
|
{
|
|
ma_result result;
|
|
ma_AAudioStreamBuilder* pBuilder;
|
|
|
|
*ppStream = NULL;
|
|
|
|
result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pContext, pDeviceID, deviceType, shareMode, NULL, NULL, NULL, &pBuilder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
((MA_PFN_AAudioStreamBuilder_setPerformanceMode)pContext->aaudio.AAudioStreamBuilder_setPerformanceMode)(pBuilder, MA_AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
|
|
|
|
return ma_open_stream_and_close_builder__aaudio(pContext, pBuilder, ppStream);
|
|
}
|
|
|
|
static ma_result ma_open_stream__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, const ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream)
|
|
{
|
|
ma_result result;
|
|
ma_AAudioStreamBuilder* pBuilder;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pDescriptor != NULL);
|
|
MA_ASSERT(deviceType != ma_device_type_duplex);
|
|
|
|
*ppStream = NULL;
|
|
|
|
result = ma_create_and_configure_AAudioStreamBuilder__aaudio(pDevice->pContext, pDescriptor->pDeviceID, deviceType, pDescriptor->shareMode, pDescriptor, pConfig, pDevice, &pBuilder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_open_stream_and_close_builder__aaudio(pDevice->pContext, pBuilder, ppStream);
|
|
}
|
|
|
|
static ma_result ma_close_stream__aaudio(ma_context* pContext, ma_AAudioStream* pStream)
|
|
{
|
|
if (pStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_result_from_aaudio(((MA_PFN_AAudioStream_close)pContext->aaudio.AAudioStream_close)(pStream));
|
|
}
|
|
|
|
static ma_bool32 ma_has_default_device__aaudio(ma_context* pContext, ma_device_type deviceType)
|
|
{
|
|
ma_AAudioStream* pStream;
|
|
ma_result result = ma_open_stream_basic__aaudio(pContext, NULL, deviceType, ma_share_mode_shared, &pStream);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
ma_close_stream__aaudio(pContext, pStream);
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static ma_result ma_wait_for_simple_state_transition__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_aaudio_stream_state_t oldState, ma_aaudio_stream_state_t newState)
|
|
{
|
|
ma_aaudio_stream_state_t actualNewState;
|
|
ma_aaudio_result_t resultAA = ((MA_PFN_AAudioStream_waitForStateChange)pContext->aaudio.AAudioStream_waitForStateChange)(pStream, oldState, &actualNewState, 5000000000);
|
|
if (resultAA != MA_AAUDIO_OK) {
|
|
return ma_result_from_aaudio(resultAA);
|
|
}
|
|
|
|
if (newState != actualNewState) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__aaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_bool32 cbResult = MA_TRUE;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
|
|
if (ma_has_default_device__aaudio(pContext, ma_device_type_playback)) {
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
}
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.aaudio = MA_AAUDIO_UNSPECIFIED;
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
|
|
if (ma_has_default_device__aaudio(pContext, ma_device_type_capture)) {
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_format format, ma_uint32 flags, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pStream != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = ((MA_PFN_AAudioStream_getChannelCount)pContext->aaudio.AAudioStream_getChannelCount)(pStream);
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pContext->aaudio.AAudioStream_getSampleRate)(pStream);
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = flags;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
}
|
|
|
|
static void ma_context_add_native_data_format_from_AAudioStream__aaudio(ma_context* pContext, ma_AAudioStream* pStream, ma_uint32 flags, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_f32, flags, pDeviceInfo);
|
|
ma_context_add_native_data_format_from_AAudioStream_ex__aaudio(pContext, pStream, ma_format_s16, flags, pDeviceInfo);
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__aaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_AAudioStream* pStream;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pDeviceID != NULL) {
|
|
pDeviceInfo->id.aaudio = pDeviceID->aaudio;
|
|
} else {
|
|
pDeviceInfo->id.aaudio = MA_AAUDIO_UNSPECIFIED;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
|
|
result = ma_open_stream_basic__aaudio(pContext, pDeviceID, deviceType, ma_share_mode_shared, &pStream);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
ma_context_add_native_data_format_from_AAudioStream__aaudio(pContext, pStream, 0, pDeviceInfo);
|
|
|
|
ma_close_stream__aaudio(pContext, pStream);
|
|
pStream = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_close_streams__aaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
|
|
pDevice->aaudio.pStreamCapture = NULL;
|
|
}
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_close_stream__aaudio(pDevice->pContext, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
|
|
pDevice->aaudio.pStreamPlayback = NULL;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__aaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_atomic_bool32_set(&pDevice->aaudio.isTearingDown, MA_TRUE);
|
|
|
|
ma_mutex_lock(&pDevice->aaudio.rerouteLock);
|
|
{
|
|
ma_close_streams__aaudio(pDevice);
|
|
}
|
|
ma_mutex_unlock(&pDevice->aaudio.rerouteLock);
|
|
|
|
ma_mutex_uninit(&pDevice->aaudio.rerouteLock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init_by_type__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_type deviceType, ma_device_descriptor* pDescriptor, ma_AAudioStream** ppStream)
|
|
{
|
|
ma_result result;
|
|
int32_t bufferCapacityInFrames;
|
|
int32_t framesPerDataCallback;
|
|
ma_AAudioStream* pStream;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDescriptor != NULL);
|
|
|
|
*ppStream = NULL;
|
|
|
|
result = ma_open_stream__aaudio(pDevice, pConfig, deviceType, pDescriptor, &pStream);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDescriptor->format = (((MA_PFN_AAudioStream_getFormat)pDevice->pContext->aaudio.AAudioStream_getFormat)(pStream) == MA_AAUDIO_FORMAT_PCM_I16) ? ma_format_s16 : ma_format_f32;
|
|
pDescriptor->channels = ((MA_PFN_AAudioStream_getChannelCount)pDevice->pContext->aaudio.AAudioStream_getChannelCount)(pStream);
|
|
pDescriptor->sampleRate = ((MA_PFN_AAudioStream_getSampleRate)pDevice->pContext->aaudio.AAudioStream_getSampleRate)(pStream);
|
|
|
|
if (pDescriptor->channels <= MA_MAX_CHANNELS) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels);
|
|
} else {
|
|
ma_channel_map_init_blank(pDescriptor->channelMap, MA_MAX_CHANNELS);
|
|
}
|
|
|
|
bufferCapacityInFrames = ((MA_PFN_AAudioStream_getBufferCapacityInFrames)pDevice->pContext->aaudio.AAudioStream_getBufferCapacityInFrames)(pStream);
|
|
framesPerDataCallback = ((MA_PFN_AAudioStream_getFramesPerDataCallback)pDevice->pContext->aaudio.AAudioStream_getFramesPerDataCallback)(pStream);
|
|
|
|
if (framesPerDataCallback > 0) {
|
|
pDescriptor->periodSizeInFrames = framesPerDataCallback;
|
|
pDescriptor->periodCount = bufferCapacityInFrames / framesPerDataCallback;
|
|
} else {
|
|
pDescriptor->periodSizeInFrames = bufferCapacityInFrames;
|
|
pDescriptor->periodCount = 1;
|
|
}
|
|
|
|
*ppStream = pStream;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init_streams__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
pDevice->aaudio.usage = pConfig->aaudio.usage;
|
|
pDevice->aaudio.contentType = pConfig->aaudio.contentType;
|
|
pDevice->aaudio.inputPreset = pConfig->aaudio.inputPreset;
|
|
pDevice->aaudio.allowedCapturePolicy = pConfig->aaudio.allowedCapturePolicy;
|
|
pDevice->aaudio.noAutoStartAfterReroute = pConfig->aaudio.noAutoStartAfterReroute;
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_capture, pDescriptorCapture, (ma_AAudioStream**)&pDevice->aaudio.pStreamCapture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
result = ma_device_init_by_type__aaudio(pDevice, pConfig, ma_device_type_playback, pDescriptorPlayback, (ma_AAudioStream**)&pDevice->aaudio.pStreamPlayback);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__aaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
result = ma_device_init_streams__aaudio(pDevice, pConfig, pDescriptorPlayback, pDescriptorCapture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_mutex_init(&pDevice->aaudio.rerouteLock);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
|
|
{
|
|
ma_aaudio_result_t resultAA;
|
|
ma_aaudio_stream_state_t currentState;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
resultAA = ((MA_PFN_AAudioStream_requestStart)pDevice->pContext->aaudio.AAudioStream_requestStart)(pStream);
|
|
if (resultAA != MA_AAUDIO_OK) {
|
|
return ma_result_from_aaudio(resultAA);
|
|
}
|
|
|
|
currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
|
|
if (currentState != MA_AAUDIO_STREAM_STATE_STARTED) {
|
|
ma_result result;
|
|
|
|
if (currentState != MA_AAUDIO_STREAM_STATE_STARTING) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STARTED);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop_stream__aaudio(ma_device* pDevice, ma_AAudioStream* pStream)
|
|
{
|
|
ma_aaudio_result_t resultAA;
|
|
ma_aaudio_stream_state_t currentState;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
|
|
if (currentState == MA_AAUDIO_STREAM_STATE_DISCONNECTED) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
resultAA = ((MA_PFN_AAudioStream_requestStop)pDevice->pContext->aaudio.AAudioStream_requestStop)(pStream);
|
|
if (resultAA != MA_AAUDIO_OK) {
|
|
return ma_result_from_aaudio(resultAA);
|
|
}
|
|
|
|
currentState = ((MA_PFN_AAudioStream_getState)pDevice->pContext->aaudio.AAudioStream_getState)(pStream);
|
|
if (currentState != MA_AAUDIO_STREAM_STATE_STOPPED) {
|
|
ma_result result;
|
|
|
|
if (currentState != MA_AAUDIO_STREAM_STATE_STOPPING) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
result = ma_wait_for_simple_state_transition__aaudio(pDevice->pContext, pStream, currentState, MA_AAUDIO_STREAM_STATE_STOPPED);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_start__aaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_result result = ma_device_start_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
|
|
if (result != MA_SUCCESS) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__aaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamCapture);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_result result = ma_device_stop_stream__aaudio(pDevice, (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
ma_device__on_notification_stopped(pDevice);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_reinit__aaudio(ma_device* pDevice, ma_device_type deviceType)
|
|
{
|
|
const ma_int32 maxAttempts = 4;
|
|
|
|
ma_result result;
|
|
ma_int32 iAttempt;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
iAttempt = 0;
|
|
while (iAttempt++ < maxAttempts) {
|
|
if (ma_atomic_bool32_get(&pDevice->aaudio.isTearingDown)) {
|
|
result = MA_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
ma_close_streams__aaudio(pDevice);
|
|
|
|
ma_device_config deviceConfig;
|
|
ma_device_descriptor descriptorPlayback;
|
|
ma_device_descriptor descriptorCapture;
|
|
|
|
deviceConfig = ma_device_config_init(deviceType);
|
|
deviceConfig.playback.pDeviceID = NULL;
|
|
deviceConfig.playback.shareMode = pDevice->playback.shareMode;
|
|
deviceConfig.playback.format = pDevice->playback.format;
|
|
deviceConfig.playback.channels = pDevice->playback.channels;
|
|
deviceConfig.capture.pDeviceID = NULL;
|
|
deviceConfig.capture.shareMode = pDevice->capture.shareMode;
|
|
deviceConfig.capture.format = pDevice->capture.format;
|
|
deviceConfig.capture.channels = pDevice->capture.channels;
|
|
deviceConfig.sampleRate = pDevice->sampleRate;
|
|
deviceConfig.aaudio.usage = pDevice->aaudio.usage;
|
|
deviceConfig.aaudio.contentType = pDevice->aaudio.contentType;
|
|
deviceConfig.aaudio.inputPreset = pDevice->aaudio.inputPreset;
|
|
deviceConfig.aaudio.allowedCapturePolicy = pDevice->aaudio.allowedCapturePolicy;
|
|
deviceConfig.aaudio.noAutoStartAfterReroute = pDevice->aaudio.noAutoStartAfterReroute;
|
|
deviceConfig.periods = 1;
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
deviceConfig.periodSizeInFrames = pDevice->playback.internalPeriodSizeInFrames;
|
|
} else {
|
|
deviceConfig.periodSizeInFrames = pDevice->capture.internalPeriodSizeInFrames;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
|
|
descriptorCapture.pDeviceID = deviceConfig.capture.pDeviceID;
|
|
descriptorCapture.shareMode = deviceConfig.capture.shareMode;
|
|
descriptorCapture.format = deviceConfig.capture.format;
|
|
descriptorCapture.channels = deviceConfig.capture.channels;
|
|
descriptorCapture.sampleRate = deviceConfig.sampleRate;
|
|
descriptorCapture.periodSizeInFrames = deviceConfig.periodSizeInFrames;
|
|
descriptorCapture.periodCount = deviceConfig.periods;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
descriptorPlayback.pDeviceID = deviceConfig.playback.pDeviceID;
|
|
descriptorPlayback.shareMode = deviceConfig.playback.shareMode;
|
|
descriptorPlayback.format = deviceConfig.playback.format;
|
|
descriptorPlayback.channels = deviceConfig.playback.channels;
|
|
descriptorPlayback.sampleRate = deviceConfig.sampleRate;
|
|
descriptorPlayback.periodSizeInFrames = deviceConfig.periodSizeInFrames;
|
|
descriptorPlayback.periodCount = deviceConfig.periods;
|
|
}
|
|
|
|
result = ma_device_init_streams__aaudio(pDevice, &deviceConfig, &descriptorPlayback, &descriptorCapture);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to create stream after route change.");
|
|
break;
|
|
}
|
|
|
|
result = ma_device_post_init(pDevice, deviceType, &descriptorPlayback, &descriptorCapture);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[AAudio] Failed to initialize device after route change.");
|
|
ma_close_streams__aaudio(pDevice);
|
|
break;
|
|
}
|
|
|
|
ma_device__on_notification_rerouted(pDevice);
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
if (pDevice->aaudio.noAutoStartAfterReroute == MA_FALSE) {
|
|
result = ma_device_start__aaudio(pDevice);
|
|
if (result != MA_SUCCESS) {
|
|
if (iAttempt < maxAttempts) {
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, retrying(%d)", iAttempt);
|
|
} else {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[AAudio] Failed to start stream after route change, giving up.");
|
|
}
|
|
}
|
|
} else {
|
|
ma_device_stop(pDevice);
|
|
}
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_device_get_info__aaudio(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_AAudioStream* pStream = NULL;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ASSERT(type != ma_device_type_duplex);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
if (type == ma_device_type_capture) {
|
|
pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamCapture;
|
|
pDeviceInfo->id.aaudio = pDevice->capture.id.aaudio;
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
if (type == ma_device_type_playback) {
|
|
pStream = (ma_AAudioStream*)pDevice->aaudio.pStreamPlayback;
|
|
pDeviceInfo->id.aaudio = pDevice->playback.id.aaudio;
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
if (pStream == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
ma_context_add_native_data_format_from_AAudioStream__aaudio(pDevice->pContext, pStream, 0, pDeviceInfo);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__aaudio(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_aaudio);
|
|
|
|
ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks);
|
|
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);
|
|
pContext->aaudio.hAAudio = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
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"
|
|
};
|
|
|
|
for (i = 0; i < ma_countof(libNames); ++i) {
|
|
pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]);
|
|
if (pContext->aaudio.hAAudio != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pContext->aaudio.hAAudio == NULL) {
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder");
|
|
pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete");
|
|
pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId");
|
|
pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection");
|
|
pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode");
|
|
pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat");
|
|
pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount");
|
|
pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate");
|
|
pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames");
|
|
pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback");
|
|
pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback");
|
|
pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback");
|
|
pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode");
|
|
pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage");
|
|
pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType");
|
|
pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset");
|
|
pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy");
|
|
pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream");
|
|
pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close");
|
|
pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState");
|
|
pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange");
|
|
pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat");
|
|
pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount");
|
|
pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate");
|
|
pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames");
|
|
pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback");
|
|
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;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__aaudio;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__aaudio;
|
|
pCallbacks->onDeviceInit = ma_device_init__aaudio;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__aaudio;
|
|
pCallbacks->onDeviceStart = ma_device_start__aaudio;
|
|
pCallbacks->onDeviceStop = ma_device_stop__aaudio;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
pCallbacks->onDeviceGetInfo = ma_device_get_info__aaudio;
|
|
|
|
{
|
|
ma_result result;
|
|
ma_device_job_thread_config jobThreadConfig;
|
|
|
|
jobThreadConfig = ma_device_job_thread_config_init();
|
|
|
|
result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio);
|
|
pContext->aaudio.hAAudio = NULL;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
(void)pConfig;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_device* pDevice;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDevice = (ma_device*)pJob->data.device.aaudio.reroute.pDevice;
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
ma_mutex_lock(&pDevice->aaudio.rerouteLock);
|
|
{
|
|
result = ma_device_reinit__aaudio(pDevice, (ma_device_type)pJob->data.device.aaudio.reroute.deviceType);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[AAudio] Stopping device due to reroute failure.");
|
|
ma_device_stop(pDevice);
|
|
}
|
|
}
|
|
ma_mutex_unlock(&pDevice->aaudio.rerouteLock);
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
static ma_result ma_job_process__device__aaudio_reroute(ma_job* pJob)
|
|
{
|
|
return ma_job_process__noop(pJob);
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_OPENSL
|
|
#include <SLES/OpenSLES.h>
|
|
#ifdef MA_ANDROID
|
|
#include <SLES/OpenSLES_Android.h>
|
|
#endif
|
|
|
|
typedef SLresult (SLAPIENTRY * ma_slCreateEngine_proc)(SLObjectItf* pEngine, SLuint32 numOptions, SLEngineOption* pEngineOptions, SLuint32 numInterfaces, SLInterfaceID* pInterfaceIds, SLboolean* pInterfaceRequired);
|
|
|
|
static SLObjectItf g_maEngineObjectSL = NULL;
|
|
static SLEngineItf g_maEngineSL = NULL;
|
|
static ma_uint32 g_maOpenSLInitCounter = 0;
|
|
static ma_spinlock g_maOpenSLSpinlock = 0;
|
|
|
|
#define MA_OPENSL_OBJ(p) (*((SLObjectItf)(p)))
|
|
#define MA_OPENSL_OUTPUTMIX(p) (*((SLOutputMixItf)(p)))
|
|
#define MA_OPENSL_PLAY(p) (*((SLPlayItf)(p)))
|
|
#define MA_OPENSL_RECORD(p) (*((SLRecordItf)(p)))
|
|
|
|
#ifdef MA_ANDROID
|
|
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLAndroidSimpleBufferQueueItf)(p)))
|
|
#else
|
|
#define MA_OPENSL_BUFFERQUEUE(p) (*((SLBufferQueueItf)(p)))
|
|
#endif
|
|
|
|
static ma_result ma_result_from_OpenSL(SLuint32 result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case SL_RESULT_SUCCESS: return MA_SUCCESS;
|
|
case SL_RESULT_PRECONDITIONS_VIOLATED: return MA_ERROR;
|
|
case SL_RESULT_PARAMETER_INVALID: return MA_INVALID_ARGS;
|
|
case SL_RESULT_MEMORY_FAILURE: return MA_OUT_OF_MEMORY;
|
|
case SL_RESULT_RESOURCE_ERROR: return MA_INVALID_DATA;
|
|
case SL_RESULT_RESOURCE_LOST: return MA_ERROR;
|
|
case SL_RESULT_IO_ERROR: return MA_IO_ERROR;
|
|
case SL_RESULT_BUFFER_INSUFFICIENT: return MA_NO_SPACE;
|
|
case SL_RESULT_CONTENT_CORRUPTED: return MA_INVALID_DATA;
|
|
case SL_RESULT_CONTENT_UNSUPPORTED: return MA_FORMAT_NOT_SUPPORTED;
|
|
case SL_RESULT_CONTENT_NOT_FOUND: return MA_ERROR;
|
|
case SL_RESULT_PERMISSION_DENIED: return MA_ACCESS_DENIED;
|
|
case SL_RESULT_FEATURE_UNSUPPORTED: return MA_NOT_IMPLEMENTED;
|
|
case SL_RESULT_INTERNAL_ERROR: return MA_ERROR;
|
|
case SL_RESULT_UNKNOWN_ERROR: return MA_ERROR;
|
|
case SL_RESULT_OPERATION_ABORTED: return MA_ERROR;
|
|
case SL_RESULT_CONTROL_LOST: return MA_ERROR;
|
|
default: return MA_ERROR;
|
|
}
|
|
}
|
|
|
|
static ma_uint8 ma_channel_id_to_ma__opensl(SLuint32 id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case SL_SPEAKER_FRONT_LEFT: return MA_CHANNEL_FRONT_LEFT;
|
|
case SL_SPEAKER_FRONT_RIGHT: return MA_CHANNEL_FRONT_RIGHT;
|
|
case SL_SPEAKER_FRONT_CENTER: return MA_CHANNEL_FRONT_CENTER;
|
|
case SL_SPEAKER_LOW_FREQUENCY: return MA_CHANNEL_LFE;
|
|
case SL_SPEAKER_BACK_LEFT: return MA_CHANNEL_BACK_LEFT;
|
|
case SL_SPEAKER_BACK_RIGHT: return MA_CHANNEL_BACK_RIGHT;
|
|
case SL_SPEAKER_FRONT_LEFT_OF_CENTER: return MA_CHANNEL_FRONT_LEFT_CENTER;
|
|
case SL_SPEAKER_FRONT_RIGHT_OF_CENTER: return MA_CHANNEL_FRONT_RIGHT_CENTER;
|
|
case SL_SPEAKER_BACK_CENTER: return MA_CHANNEL_BACK_CENTER;
|
|
case SL_SPEAKER_SIDE_LEFT: return MA_CHANNEL_SIDE_LEFT;
|
|
case SL_SPEAKER_SIDE_RIGHT: return MA_CHANNEL_SIDE_RIGHT;
|
|
case SL_SPEAKER_TOP_CENTER: return MA_CHANNEL_TOP_CENTER;
|
|
case SL_SPEAKER_TOP_FRONT_LEFT: return MA_CHANNEL_TOP_FRONT_LEFT;
|
|
case SL_SPEAKER_TOP_FRONT_CENTER: return MA_CHANNEL_TOP_FRONT_CENTER;
|
|
case SL_SPEAKER_TOP_FRONT_RIGHT: return MA_CHANNEL_TOP_FRONT_RIGHT;
|
|
case SL_SPEAKER_TOP_BACK_LEFT: return MA_CHANNEL_TOP_BACK_LEFT;
|
|
case SL_SPEAKER_TOP_BACK_CENTER: return MA_CHANNEL_TOP_BACK_CENTER;
|
|
case SL_SPEAKER_TOP_BACK_RIGHT: return MA_CHANNEL_TOP_BACK_RIGHT;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static SLuint32 ma_channel_id_to_opensl(ma_uint8 id)
|
|
{
|
|
switch (id)
|
|
{
|
|
case MA_CHANNEL_MONO: return SL_SPEAKER_FRONT_CENTER;
|
|
case MA_CHANNEL_FRONT_LEFT: return SL_SPEAKER_FRONT_LEFT;
|
|
case MA_CHANNEL_FRONT_RIGHT: return SL_SPEAKER_FRONT_RIGHT;
|
|
case MA_CHANNEL_FRONT_CENTER: return SL_SPEAKER_FRONT_CENTER;
|
|
case MA_CHANNEL_LFE: return SL_SPEAKER_LOW_FREQUENCY;
|
|
case MA_CHANNEL_BACK_LEFT: return SL_SPEAKER_BACK_LEFT;
|
|
case MA_CHANNEL_BACK_RIGHT: return SL_SPEAKER_BACK_RIGHT;
|
|
case MA_CHANNEL_FRONT_LEFT_CENTER: return SL_SPEAKER_FRONT_LEFT_OF_CENTER;
|
|
case MA_CHANNEL_FRONT_RIGHT_CENTER: return SL_SPEAKER_FRONT_RIGHT_OF_CENTER;
|
|
case MA_CHANNEL_BACK_CENTER: return SL_SPEAKER_BACK_CENTER;
|
|
case MA_CHANNEL_SIDE_LEFT: return SL_SPEAKER_SIDE_LEFT;
|
|
case MA_CHANNEL_SIDE_RIGHT: return SL_SPEAKER_SIDE_RIGHT;
|
|
case MA_CHANNEL_TOP_CENTER: return SL_SPEAKER_TOP_CENTER;
|
|
case MA_CHANNEL_TOP_FRONT_LEFT: return SL_SPEAKER_TOP_FRONT_LEFT;
|
|
case MA_CHANNEL_TOP_FRONT_CENTER: return SL_SPEAKER_TOP_FRONT_CENTER;
|
|
case MA_CHANNEL_TOP_FRONT_RIGHT: return SL_SPEAKER_TOP_FRONT_RIGHT;
|
|
case MA_CHANNEL_TOP_BACK_LEFT: return SL_SPEAKER_TOP_BACK_LEFT;
|
|
case MA_CHANNEL_TOP_BACK_CENTER: return SL_SPEAKER_TOP_BACK_CENTER;
|
|
case MA_CHANNEL_TOP_BACK_RIGHT: return SL_SPEAKER_TOP_BACK_RIGHT;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static SLuint32 ma_channel_map_to_channel_mask__opensl(const ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
SLuint32 channelMask = 0;
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
channelMask |= ma_channel_id_to_opensl(pChannelMap[iChannel]);
|
|
}
|
|
|
|
return channelMask;
|
|
}
|
|
|
|
static void ma_channel_mask_to_channel_map__opensl(SLuint32 channelMask, ma_uint32 channels, ma_channel* pChannelMap)
|
|
{
|
|
if (channels == 1 && channelMask == 0) {
|
|
pChannelMap[0] = MA_CHANNEL_MONO;
|
|
} else if (channels == 2 && channelMask == 0) {
|
|
pChannelMap[0] = MA_CHANNEL_FRONT_LEFT;
|
|
pChannelMap[1] = MA_CHANNEL_FRONT_RIGHT;
|
|
} else {
|
|
if (channels == 1 && (channelMask & SL_SPEAKER_FRONT_CENTER) != 0) {
|
|
pChannelMap[0] = MA_CHANNEL_MONO;
|
|
} else {
|
|
ma_uint32 iChannel = 0;
|
|
ma_uint32 iBit;
|
|
for (iBit = 0; iBit < 32 && iChannel < channels; ++iBit) {
|
|
SLuint32 bitValue = (channelMask & (1UL << iBit));
|
|
if (bitValue != 0) {
|
|
pChannelMap[iChannel] = ma_channel_id_to_ma__opensl(bitValue);
|
|
iChannel += 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static SLuint32 ma_round_to_standard_sample_rate__opensl(SLuint32 samplesPerSec)
|
|
{
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_8) {
|
|
return SL_SAMPLINGRATE_8;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_11_025) {
|
|
return SL_SAMPLINGRATE_11_025;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_12) {
|
|
return SL_SAMPLINGRATE_12;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_16) {
|
|
return SL_SAMPLINGRATE_16;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_22_05) {
|
|
return SL_SAMPLINGRATE_22_05;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_24) {
|
|
return SL_SAMPLINGRATE_24;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_32) {
|
|
return SL_SAMPLINGRATE_32;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_44_1) {
|
|
return SL_SAMPLINGRATE_44_1;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_48) {
|
|
return SL_SAMPLINGRATE_48;
|
|
}
|
|
|
|
#ifndef MA_ANDROID
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_64) {
|
|
return SL_SAMPLINGRATE_64;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_88_2) {
|
|
return SL_SAMPLINGRATE_88_2;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_96) {
|
|
return SL_SAMPLINGRATE_96;
|
|
}
|
|
if (samplesPerSec <= SL_SAMPLINGRATE_192) {
|
|
return SL_SAMPLINGRATE_192;
|
|
}
|
|
#endif
|
|
|
|
return SL_SAMPLINGRATE_16;
|
|
}
|
|
|
|
static SLint32 ma_to_stream_type__opensl(ma_opensl_stream_type streamType)
|
|
{
|
|
switch (streamType) {
|
|
case ma_opensl_stream_type_voice: return SL_ANDROID_STREAM_VOICE;
|
|
case ma_opensl_stream_type_system: return SL_ANDROID_STREAM_SYSTEM;
|
|
case ma_opensl_stream_type_ring: return SL_ANDROID_STREAM_RING;
|
|
case ma_opensl_stream_type_media: return SL_ANDROID_STREAM_MEDIA;
|
|
case ma_opensl_stream_type_alarm: return SL_ANDROID_STREAM_ALARM;
|
|
case ma_opensl_stream_type_notification: return SL_ANDROID_STREAM_NOTIFICATION;
|
|
default: break;
|
|
}
|
|
|
|
return SL_ANDROID_STREAM_VOICE;
|
|
}
|
|
|
|
static SLint32 ma_to_recording_preset__opensl(ma_opensl_recording_preset recordingPreset)
|
|
{
|
|
switch (recordingPreset) {
|
|
case ma_opensl_recording_preset_generic: return SL_ANDROID_RECORDING_PRESET_GENERIC;
|
|
case ma_opensl_recording_preset_camcorder: return SL_ANDROID_RECORDING_PRESET_CAMCORDER;
|
|
case ma_opensl_recording_preset_voice_recognition: return SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
|
|
case ma_opensl_recording_preset_voice_communication: return SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
|
|
case ma_opensl_recording_preset_voice_unprocessed: return SL_ANDROID_RECORDING_PRESET_UNPROCESSED;
|
|
default: break;
|
|
}
|
|
|
|
return SL_ANDROID_RECORDING_PRESET_NONE;
|
|
}
|
|
|
|
static ma_result ma_context_enumerate_devices__opensl(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_bool32 cbResult;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
#if 0 && !defined(MA_ANDROID)
|
|
ma_bool32 isTerminated = MA_FALSE;
|
|
|
|
SLuint32 pDeviceIDs[128];
|
|
SLint32 deviceCount = sizeof(pDeviceIDs) / sizeof(pDeviceIDs[0]);
|
|
|
|
SLAudioIODeviceCapabilitiesItf deviceCaps;
|
|
SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
goto return_default_device;
|
|
}
|
|
|
|
if (!isTerminated) {
|
|
resultSL = (*deviceCaps)->GetAvailableAudioOutputs(deviceCaps, &deviceCount, pDeviceIDs);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.opensl = pDeviceIDs[iDevice];
|
|
|
|
SLAudioOutputDescriptor desc;
|
|
resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
|
|
if (resultSL == SL_RESULT_SUCCESS) {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.pDeviceName, (size_t)-1);
|
|
|
|
ma_bool32 cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
if (cbResult == MA_FALSE) {
|
|
isTerminated = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!isTerminated) {
|
|
resultSL = (*deviceCaps)->GetAvailableAudioInputs(deviceCaps, &deviceCount, pDeviceIDs);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
for (SLint32 iDevice = 0; iDevice < deviceCount; ++iDevice) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.opensl = pDeviceIDs[iDevice];
|
|
|
|
SLAudioInputDescriptor desc;
|
|
resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, deviceInfo.id.opensl, &desc);
|
|
if (resultSL == SL_RESULT_SUCCESS) {
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), (const char*)desc.deviceName, (size_t)-1);
|
|
|
|
ma_bool32 cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
if (cbResult == MA_FALSE) {
|
|
isTerminated = MA_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
#else
|
|
goto return_default_device;
|
|
#endif
|
|
|
|
return_default_device:;
|
|
cbResult = MA_TRUE;
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
deviceInfo.id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_context_add_data_format_ex__opensl(ma_context* pContext, ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].format = format;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].channels = channels;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].sampleRate = sampleRate;
|
|
pDeviceInfo->nativeDataFormats[pDeviceInfo->nativeDataFormatCount].flags = 0;
|
|
pDeviceInfo->nativeDataFormatCount += 1;
|
|
}
|
|
|
|
static void ma_context_add_data_format__opensl(ma_context* pContext, ma_format format, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_uint32 minChannels = 1;
|
|
ma_uint32 maxChannels = 2;
|
|
ma_uint32 minSampleRate = (ma_uint32)ma_standard_sample_rate_8000;
|
|
ma_uint32 maxSampleRate = (ma_uint32)ma_standard_sample_rate_48000;
|
|
ma_uint32 iChannel;
|
|
ma_uint32 iSampleRate;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pDeviceInfo != NULL);
|
|
|
|
for (iChannel = minChannels; iChannel < maxChannels; iChannel += 1) {
|
|
for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); iSampleRate += 1) {
|
|
ma_uint32 standardSampleRate = g_maStandardSampleRatePriorities[iSampleRate];
|
|
if (standardSampleRate >= minSampleRate && standardSampleRate <= maxSampleRate) {
|
|
ma_context_add_data_format_ex__opensl(pContext, format, iChannel, standardSampleRate, pDeviceInfo);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__opensl(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
#if 0 && !defined(MA_ANDROID)
|
|
SLAudioIODeviceCapabilitiesItf deviceCaps;
|
|
SLresult resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES, &deviceCaps);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
goto return_default_device;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
SLAudioOutputDescriptor desc;
|
|
resultSL = (*deviceCaps)->QueryAudioOutputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.pDeviceName, (size_t)-1);
|
|
} else {
|
|
SLAudioInputDescriptor desc;
|
|
resultSL = (*deviceCaps)->QueryAudioInputCapabilities(deviceCaps, pDeviceID->opensl, &desc);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), (const char*)desc.deviceName, (size_t)-1);
|
|
}
|
|
|
|
goto return_detailed_info;
|
|
#else
|
|
goto return_default_device;
|
|
#endif
|
|
|
|
return_default_device:
|
|
if (pDeviceID != NULL) {
|
|
if ((deviceType == ma_device_type_playback && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOOUTPUT) ||
|
|
(deviceType == ma_device_type_capture && pDeviceID->opensl != SL_DEFAULTDEVICEID_AUDIOINPUT)) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOOUTPUT;
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
pDeviceInfo->id.opensl = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
|
|
goto return_detailed_info;
|
|
|
|
return_detailed_info:
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 0;
|
|
#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
|
|
ma_context_add_data_format__opensl(pContext, ma_format_f32, pDeviceInfo);
|
|
#endif
|
|
ma_context_add_data_format__opensl(pContext, ma_format_s16, pDeviceInfo);
|
|
ma_context_add_data_format__opensl(pContext, ma_format_u8, pDeviceInfo);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#ifdef MA_ANDROID
|
|
static void ma_buffer_queue_callback_capture__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
size_t periodSizeInBytes;
|
|
ma_uint8* pBuffer;
|
|
SLresult resultSL;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
(void)pBufferQueue;
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_started) {
|
|
return;
|
|
}
|
|
|
|
if (pDevice->opensl.isDrainingCapture) {
|
|
return;
|
|
}
|
|
|
|
periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
pBuffer = pDevice->opensl.pBufferCapture + (pDevice->opensl.currentBufferIndexCapture * periodSizeInBytes);
|
|
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, pBuffer, pDevice->capture.internalPeriodSizeInFrames);
|
|
|
|
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pBuffer, periodSizeInBytes);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
pDevice->opensl.currentBufferIndexCapture = (pDevice->opensl.currentBufferIndexCapture + 1) % pDevice->capture.internalPeriods;
|
|
}
|
|
|
|
static void ma_buffer_queue_callback_playback__opensl_android(SLAndroidSimpleBufferQueueItf pBufferQueue, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
size_t periodSizeInBytes;
|
|
ma_uint8* pBuffer;
|
|
SLresult resultSL;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
(void)pBufferQueue;
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_started) {
|
|
return;
|
|
}
|
|
|
|
if (pDevice->opensl.isDrainingPlayback) {
|
|
return;
|
|
}
|
|
|
|
periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
pBuffer = pDevice->opensl.pBufferPlayback + (pDevice->opensl.currentBufferIndexPlayback * periodSizeInBytes);
|
|
|
|
ma_device_handle_backend_data_callback(pDevice, pBuffer, NULL, pDevice->playback.internalPeriodSizeInFrames);
|
|
|
|
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pBuffer, periodSizeInBytes);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
pDevice->opensl.currentBufferIndexPlayback = (pDevice->opensl.currentBufferIndexPlayback + 1) % pDevice->playback.internalPeriods;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_device_uninit__opensl(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->opensl.pAudioRecorderObj) {
|
|
MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioRecorderObj);
|
|
}
|
|
|
|
ma_free(pDevice->opensl.pBufferCapture, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
if (pDevice->opensl.pAudioPlayerObj) {
|
|
MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Destroy((SLObjectItf)pDevice->opensl.pAudioPlayerObj);
|
|
}
|
|
if (pDevice->opensl.pOutputMixObj) {
|
|
MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Destroy((SLObjectItf)pDevice->opensl.pOutputMixObj);
|
|
}
|
|
|
|
ma_free(pDevice->opensl.pBufferPlayback, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
|
|
typedef SLAndroidDataFormat_PCM_EX ma_SLDataFormat_PCM;
|
|
#else
|
|
typedef SLDataFormat_PCM ma_SLDataFormat_PCM;
|
|
#endif
|
|
|
|
static ma_result ma_SLDataFormat_PCM_init__opensl(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* channelMap, ma_SLDataFormat_PCM* pDataFormat)
|
|
{
|
|
if (format == ma_format_unknown) {
|
|
format = MA_DEFAULT_FORMAT;
|
|
}
|
|
if (channels == 0) {
|
|
channels = MA_DEFAULT_CHANNELS;
|
|
}
|
|
if (sampleRate == 0) {
|
|
sampleRate = MA_DEFAULT_SAMPLE_RATE;
|
|
}
|
|
|
|
#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
|
|
if (format == ma_format_f32) {
|
|
pDataFormat->formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
|
|
pDataFormat->representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
|
|
} else {
|
|
pDataFormat->formatType = SL_DATAFORMAT_PCM;
|
|
}
|
|
#else
|
|
pDataFormat->formatType = SL_DATAFORMAT_PCM;
|
|
#endif
|
|
|
|
pDataFormat->numChannels = channels;
|
|
((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = ma_round_to_standard_sample_rate__opensl(sampleRate * 1000);
|
|
pDataFormat->bitsPerSample = ma_get_bytes_per_sample(format) * 8;
|
|
pDataFormat->channelMask = ma_channel_map_to_channel_mask__opensl(channelMap, channels);
|
|
pDataFormat->endianness = (ma_is_little_endian()) ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
|
|
|
|
#ifdef MA_ANDROID
|
|
if (pDataFormat->numChannels > 2) {
|
|
pDataFormat->numChannels = 2;
|
|
}
|
|
#if __ANDROID_API__ >= 21
|
|
if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
|
|
MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
|
|
if (pDataFormat->bitsPerSample > 32) {
|
|
pDataFormat->bitsPerSample = 32;
|
|
}
|
|
} else {
|
|
if (pDataFormat->bitsPerSample > 16) {
|
|
pDataFormat->bitsPerSample = 16;
|
|
}
|
|
}
|
|
#else
|
|
if (pDataFormat->bitsPerSample > 16) {
|
|
pDataFormat->bitsPerSample = 16;
|
|
}
|
|
#endif
|
|
if (((SLDataFormat_PCM*)pDataFormat)->samplesPerSec > SL_SAMPLINGRATE_48) {
|
|
((SLDataFormat_PCM*)pDataFormat)->samplesPerSec = SL_SAMPLINGRATE_48;
|
|
}
|
|
#endif
|
|
|
|
pDataFormat->containerSize = pDataFormat->bitsPerSample;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_deconstruct_SLDataFormat_PCM__opensl(ma_SLDataFormat_PCM* pDataFormat, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_bool32 isFloatingPoint = MA_FALSE;
|
|
#if defined(MA_ANDROID) && __ANDROID_API__ >= 21
|
|
if (pDataFormat->formatType == SL_ANDROID_DATAFORMAT_PCM_EX) {
|
|
MA_ASSERT(pDataFormat->representation == SL_ANDROID_PCM_REPRESENTATION_FLOAT);
|
|
isFloatingPoint = MA_TRUE;
|
|
}
|
|
#endif
|
|
if (isFloatingPoint) {
|
|
if (pDataFormat->bitsPerSample == 32) {
|
|
*pFormat = ma_format_f32;
|
|
}
|
|
} else {
|
|
if (pDataFormat->bitsPerSample == 8) {
|
|
*pFormat = ma_format_u8;
|
|
} else if (pDataFormat->bitsPerSample == 16) {
|
|
*pFormat = ma_format_s16;
|
|
} else if (pDataFormat->bitsPerSample == 24) {
|
|
*pFormat = ma_format_s24;
|
|
} else if (pDataFormat->bitsPerSample == 32) {
|
|
*pFormat = ma_format_s32;
|
|
}
|
|
}
|
|
|
|
*pChannels = pDataFormat->numChannels;
|
|
*pSampleRate = ((SLDataFormat_PCM*)pDataFormat)->samplesPerSec / 1000;
|
|
ma_channel_mask_to_channel_map__opensl(pDataFormat->channelMask, ma_min(pDataFormat->numChannels, channelMapCap), pChannelMap);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_init__opensl(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
#ifdef MA_ANDROID
|
|
SLDataLocator_AndroidSimpleBufferQueue queue;
|
|
SLresult resultSL;
|
|
size_t bufferSizeInBytes;
|
|
SLInterfaceID itfIDs[2];
|
|
const SLboolean itfIDsRequired[] = {
|
|
SL_BOOLEAN_TRUE,
|
|
SL_BOOLEAN_FALSE
|
|
};
|
|
#endif
|
|
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
#ifdef MA_ANDROID
|
|
itfIDs[0] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
|
itfIDs[1] = (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION;
|
|
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
MA_ZERO_OBJECT(&pDevice->opensl);
|
|
|
|
queue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_SLDataFormat_PCM pcm;
|
|
SLDataLocator_IODevice locatorDevice;
|
|
SLDataSource source;
|
|
SLDataSink sink;
|
|
SLAndroidConfigurationItf pRecorderConfig;
|
|
|
|
ma_SLDataFormat_PCM_init__opensl(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &pcm);
|
|
|
|
locatorDevice.locatorType = SL_DATALOCATOR_IODEVICE;
|
|
locatorDevice.deviceType = SL_IODEVICE_AUDIOINPUT;
|
|
locatorDevice.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
|
|
locatorDevice.device = NULL;
|
|
|
|
source.pLocator = &locatorDevice;
|
|
source.pFormat = NULL;
|
|
|
|
queue.numBuffers = pDescriptorCapture->periodCount;
|
|
|
|
sink.pLocator = &queue;
|
|
sink.pFormat = (SLDataFormat_PCM*)&pcm;
|
|
|
|
resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
|
|
if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
|
|
pcm.formatType = SL_DATAFORMAT_PCM;
|
|
pcm.numChannels = 1;
|
|
((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
|
|
pcm.bitsPerSample = 16;
|
|
pcm.containerSize = pcm.bitsPerSample;
|
|
pcm.channelMask = 0;
|
|
resultSL = (*g_maEngineSL)->CreateAudioRecorder(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioRecorderObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
|
|
}
|
|
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio recorder.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
if (pConfig->opensl.recordingPreset != ma_opensl_recording_preset_default) {
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pRecorderConfig);
|
|
if (resultSL == SL_RESULT_SUCCESS) {
|
|
SLint32 recordingPreset = ma_to_recording_preset__opensl(pConfig->opensl.recordingPreset);
|
|
resultSL = (*pRecorderConfig)->SetConfiguration(pRecorderConfig, SL_ANDROID_KEY_RECORDING_PRESET, &recordingPreset, sizeof(SLint32));
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
}
|
|
}
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->Realize((SLObjectItf)pDevice->opensl.pAudioRecorderObj, SL_BOOLEAN_FALSE);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio recorder.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_RECORD, &pDevice->opensl.pAudioRecorder);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_RECORD interface.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioRecorderObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioRecorderObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueueCapture);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, ma_buffer_queue_callback_capture__opensl_android, pDevice);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorCapture->format, &pDescriptorCapture->channels, &pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap));
|
|
|
|
pDescriptorCapture->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorCapture, pDescriptorCapture->sampleRate, pConfig->performanceProfile);
|
|
pDevice->opensl.currentBufferIndexCapture = 0;
|
|
|
|
bufferSizeInBytes = pDescriptorCapture->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels) * pDescriptorCapture->periodCount;
|
|
pDevice->opensl.pBufferCapture = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->opensl.pBufferCapture == NULL) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
MA_ZERO_MEMORY(pDevice->opensl.pBufferCapture, bufferSizeInBytes);
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_SLDataFormat_PCM pcm;
|
|
SLDataSource source;
|
|
SLDataLocator_OutputMix outmixLocator;
|
|
SLDataSink sink;
|
|
SLAndroidConfigurationItf pPlayerConfig;
|
|
|
|
ma_SLDataFormat_PCM_init__opensl(pDescriptorPlayback->format, pDescriptorPlayback->channels, pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, &pcm);
|
|
|
|
resultSL = (*g_maEngineSL)->CreateOutputMix(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pOutputMixObj, 0, NULL, NULL);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create output mix.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->Realize((SLObjectItf)pDevice->opensl.pOutputMixObj, SL_BOOLEAN_FALSE);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize output mix object.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pOutputMixObj)->GetInterface((SLObjectItf)pDevice->opensl.pOutputMixObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_OUTPUTMIX, &pDevice->opensl.pOutputMix);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_OUTPUTMIX interface.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
if (pDescriptorPlayback->pDeviceID != NULL) {
|
|
SLuint32 deviceID_OpenSL = pDescriptorPlayback->pDeviceID->opensl;
|
|
MA_OPENSL_OUTPUTMIX(pDevice->opensl.pOutputMix)->ReRoute((SLOutputMixItf)pDevice->opensl.pOutputMix, 1, &deviceID_OpenSL);
|
|
}
|
|
|
|
queue.numBuffers = pDescriptorPlayback->periodCount;
|
|
|
|
source.pLocator = &queue;
|
|
source.pFormat = (SLDataFormat_PCM*)&pcm;
|
|
|
|
outmixLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
|
outmixLocator.outputMix = (SLObjectItf)pDevice->opensl.pOutputMixObj;
|
|
|
|
sink.pLocator = &outmixLocator;
|
|
sink.pFormat = NULL;
|
|
|
|
resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
|
|
if (resultSL == SL_RESULT_CONTENT_UNSUPPORTED || resultSL == SL_RESULT_PARAMETER_INVALID) {
|
|
pcm.formatType = SL_DATAFORMAT_PCM;
|
|
pcm.numChannels = 2;
|
|
((SLDataFormat_PCM*)&pcm)->samplesPerSec = SL_SAMPLINGRATE_16;
|
|
pcm.bitsPerSample = 16;
|
|
pcm.containerSize = pcm.bitsPerSample;
|
|
pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
|
resultSL = (*g_maEngineSL)->CreateAudioPlayer(g_maEngineSL, (SLObjectItf*)&pDevice->opensl.pAudioPlayerObj, &source, &sink, ma_countof(itfIDs), itfIDs, itfIDsRequired);
|
|
}
|
|
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to create audio player.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
if (pConfig->opensl.streamType != ma_opensl_stream_type_default) {
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDCONFIGURATION, &pPlayerConfig);
|
|
if (resultSL == SL_RESULT_SUCCESS) {
|
|
SLint32 streamType = ma_to_stream_type__opensl(pConfig->opensl.streamType);
|
|
resultSL = (*pPlayerConfig)->SetConfiguration(pPlayerConfig, SL_ANDROID_KEY_STREAM_TYPE, &streamType, sizeof(SLint32));
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
}
|
|
}
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->Realize((SLObjectItf)pDevice->opensl.pAudioPlayerObj, SL_BOOLEAN_FALSE);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to realize audio player.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_PLAY, &pDevice->opensl.pAudioPlayer);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_PLAY interface.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_OBJ(pDevice->opensl.pAudioPlayerObj)->GetInterface((SLObjectItf)pDevice->opensl.pAudioPlayerObj, (SLInterfaceID)pDevice->pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &pDevice->opensl.pBufferQueuePlayback);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to retrieve SL_IID_ANDROIDSIMPLEBUFFERQUEUE interface.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->RegisterCallback((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, ma_buffer_queue_callback_playback__opensl_android, pDevice);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to register buffer queue callback.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
ma_deconstruct_SLDataFormat_PCM__opensl(&pcm, &pDescriptorPlayback->format, &pDescriptorPlayback->channels, &pDescriptorPlayback->sampleRate, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap));
|
|
|
|
pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
|
|
pDevice->opensl.currentBufferIndexPlayback = 0;
|
|
|
|
bufferSizeInBytes = pDescriptorPlayback->periodSizeInFrames * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels) * pDescriptorPlayback->periodCount;
|
|
pDevice->opensl.pBufferPlayback = (ma_uint8*)ma_calloc(bufferSizeInBytes, &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->opensl.pBufferPlayback == NULL) {
|
|
ma_device_uninit__opensl(pDevice);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to allocate memory for data buffer.");
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, bufferSizeInBytes);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
#else
|
|
return MA_NO_BACKEND;
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_device_start__opensl(ma_device* pDevice)
|
|
{
|
|
SLresult resultSL;
|
|
size_t periodSizeInBytes;
|
|
ma_uint32 iPeriod;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_RECORDING);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal capture device.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
periodSizeInBytes = pDevice->capture.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->capture.internalFormat, pDevice->capture.internalChannels);
|
|
for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) {
|
|
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture, pDevice->opensl.pBufferCapture + (periodSizeInBytes * iPeriod), periodSizeInBytes);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for capture device.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_PLAYING);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to start internal playback device.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels));
|
|
} else {
|
|
ma_device__read_frames_from_client(pDevice, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods, pDevice->opensl.pBufferPlayback);
|
|
}
|
|
|
|
periodSizeInBytes = pDevice->playback.internalPeriodSizeInFrames * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels);
|
|
for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; ++iPeriod) {
|
|
resultSL = MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Enqueue((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback, pDevice->opensl.pBufferPlayback + (periodSizeInBytes * iPeriod), periodSizeInBytes);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to enqueue buffer for playback device.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_drain__opensl(ma_device* pDevice, ma_device_type deviceType)
|
|
{
|
|
SLAndroidSimpleBufferQueueItf pBufferQueue;
|
|
|
|
MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback);
|
|
|
|
if (pDevice->type == ma_device_type_capture) {
|
|
pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture;
|
|
pDevice->opensl.isDrainingCapture = MA_TRUE;
|
|
} else {
|
|
pBufferQueue = (SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback;
|
|
pDevice->opensl.isDrainingPlayback = MA_TRUE;
|
|
}
|
|
|
|
for (;;) {
|
|
SLAndroidSimpleBufferQueueState state;
|
|
|
|
MA_OPENSL_BUFFERQUEUE(pBufferQueue)->GetState(pBufferQueue, &state);
|
|
if (state.count == 0) {
|
|
break;
|
|
}
|
|
|
|
ma_sleep(10);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture) {
|
|
pDevice->opensl.isDrainingCapture = MA_FALSE;
|
|
} else {
|
|
pDevice->opensl.isDrainingPlayback = MA_FALSE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__opensl(ma_device* pDevice)
|
|
{
|
|
SLresult resultSL;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
|
|
ma_device_drain__opensl(pDevice, ma_device_type_capture);
|
|
|
|
resultSL = MA_OPENSL_RECORD(pDevice->opensl.pAudioRecorder)->SetRecordState((SLRecordItf)pDevice->opensl.pAudioRecorder, SL_RECORDSTATE_STOPPED);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal capture device.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueueCapture)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueueCapture);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_device_drain__opensl(pDevice, ma_device_type_playback);
|
|
|
|
resultSL = MA_OPENSL_PLAY(pDevice->opensl.pAudioPlayer)->SetPlayState((SLPlayItf)pDevice->opensl.pAudioPlayer, SL_PLAYSTATE_STOPPED);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[OpenSL] Failed to stop internal playback device.");
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
MA_OPENSL_BUFFERQUEUE(pDevice->opensl.pBufferQueuePlayback)->Clear((SLAndroidSimpleBufferQueueItf)pDevice->opensl.pBufferQueuePlayback);
|
|
}
|
|
|
|
ma_device__on_notification_stopped(pDevice);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__opensl(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_opensl);
|
|
(void)pContext;
|
|
|
|
ma_spinlock_lock(&g_maOpenSLSpinlock);
|
|
{
|
|
MA_ASSERT(g_maOpenSLInitCounter > 0);
|
|
|
|
g_maOpenSLInitCounter -= 1;
|
|
if (g_maOpenSLInitCounter == 0) {
|
|
(*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
|
|
}
|
|
}
|
|
ma_spinlock_unlock(&g_maOpenSLSpinlock);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle)
|
|
{
|
|
ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName);
|
|
if (p == NULL) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName);
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
*pHandle = *p;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init_engine_nolock__opensl(ma_context* pContext)
|
|
{
|
|
g_maOpenSLInitCounter += 1;
|
|
if (g_maOpenSLInitCounter == 1) {
|
|
SLresult resultSL;
|
|
|
|
resultSL = ((ma_slCreateEngine_proc)pContext->opensl.slCreateEngine)(&g_maEngineObjectSL, 0, NULL, 0, NULL, NULL);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
g_maOpenSLInitCounter -= 1;
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
|
|
(*g_maEngineObjectSL)->Realize(g_maEngineObjectSL, SL_BOOLEAN_FALSE);
|
|
|
|
resultSL = (*g_maEngineObjectSL)->GetInterface(g_maEngineObjectSL, (SLInterfaceID)pContext->opensl.SL_IID_ENGINE, &g_maEngineSL);
|
|
if (resultSL != SL_RESULT_SUCCESS) {
|
|
(*g_maEngineObjectSL)->Destroy(g_maEngineObjectSL);
|
|
g_maOpenSLInitCounter -= 1;
|
|
return ma_result_from_OpenSL(resultSL);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
ma_result result;
|
|
|
|
#if !defined(MA_NO_RUNTIME_LINKING)
|
|
size_t i;
|
|
const char* libOpenSLESNames[] = {
|
|
"libOpenSLES.so"
|
|
};
|
|
#endif
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
#if !defined(MA_NO_RUNTIME_LINKING)
|
|
for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) {
|
|
pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]);
|
|
if (pContext->opensl.libOpenSLES != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pContext->opensl.libOpenSLES == NULL) {
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Could not find libOpenSLES.so");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION);
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
return result;
|
|
}
|
|
|
|
pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine");
|
|
if (pContext->opensl.slCreateEngine == NULL) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine.");
|
|
return MA_NO_BACKEND;
|
|
}
|
|
#else
|
|
pContext->opensl.SL_IID_ENGINE = (ma_handle)SL_IID_ENGINE;
|
|
pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES = (ma_handle)SL_IID_AUDIOIODEVICECAPABILITIES;
|
|
pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (ma_handle)SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
|
|
pContext->opensl.SL_IID_RECORD = (ma_handle)SL_IID_RECORD;
|
|
pContext->opensl.SL_IID_PLAY = (ma_handle)SL_IID_PLAY;
|
|
pContext->opensl.SL_IID_OUTPUTMIX = (ma_handle)SL_IID_OUTPUTMIX;
|
|
pContext->opensl.SL_IID_ANDROIDCONFIGURATION = (ma_handle)SL_IID_ANDROIDCONFIGURATION;
|
|
pContext->opensl.slCreateEngine = (ma_proc)slCreateEngine;
|
|
#endif
|
|
|
|
ma_spinlock_lock(&g_maOpenSLSpinlock);
|
|
{
|
|
result = ma_context_init_engine_nolock__opensl(pContext);
|
|
}
|
|
ma_spinlock_unlock(&g_maOpenSLSpinlock);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES);
|
|
ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine.");
|
|
return result;
|
|
}
|
|
|
|
pCallbacks->onContextInit = ma_context_init__opensl;
|
|
pCallbacks->onContextUninit = ma_context_uninit__opensl;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__opensl;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__opensl;
|
|
pCallbacks->onDeviceInit = ma_device_init__opensl;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__opensl;
|
|
pCallbacks->onDeviceStart = ma_device_start__opensl;
|
|
pCallbacks->onDeviceStop = ma_device_stop__opensl;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifdef MA_HAS_WEBAUDIO
|
|
#include <emscripten/emscripten.h>
|
|
|
|
#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 32)))
|
|
#include <emscripten/webaudio.h>
|
|
#define MA_SUPPORT_AUDIO_WORKLETS
|
|
|
|
#if (__EMSCRIPTEN_major__ > 3) || (__EMSCRIPTEN_major__ == 3 && (__EMSCRIPTEN_minor__ > 1 || (__EMSCRIPTEN_minor__ == 1 && __EMSCRIPTEN_tiny__ >= 70)))
|
|
#define MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(MA_ENABLE_AUDIO_WORKLETS) && defined(MA_SUPPORT_AUDIO_WORKLETS)
|
|
#define MA_USE_AUDIO_WORKLETS
|
|
#endif
|
|
|
|
#ifndef MA_AUDIO_WORKLETS_THREAD_STACK_SIZE
|
|
#define MA_AUDIO_WORKLETS_THREAD_STACK_SIZE 131072
|
|
#endif
|
|
|
|
#if defined(MA_USE_AUDIO_WORKLETS)
|
|
#define MA_WEBAUDIO_LATENCY_HINT_BALANCED "balanced"
|
|
#define MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE "interactive"
|
|
#define MA_WEBAUDIO_LATENCY_HINT_PLAYBACK "playback"
|
|
#endif
|
|
|
|
static ma_bool32 ma_is_capture_supported__webaudio()
|
|
{
|
|
return EM_ASM_INT({
|
|
return (navigator.mediaDevices !== undefined && navigator.mediaDevices.getUserMedia !== undefined);
|
|
}, 0) != 0;
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
void* EMSCRIPTEN_KEEPALIVE ma_malloc_emscripten(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_malloc(sz, pAllocationCallbacks);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE ma_free_emscripten(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_free(p, pAllocationCallbacks);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_capture__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
|
|
{
|
|
ma_device_handle_backend_data_callback(pDevice, NULL, pFrames, (ma_uint32)frameCount);
|
|
}
|
|
|
|
void EMSCRIPTEN_KEEPALIVE ma_device_process_pcm_frames_playback__webaudio(ma_device* pDevice, int frameCount, float* pFrames)
|
|
{
|
|
ma_device_handle_backend_data_callback(pDevice, pFrames, NULL, (ma_uint32)frameCount);
|
|
}
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_context_enumerate_devices__webaudio(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_bool32 cbResult = MA_TRUE;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(callback != NULL);
|
|
|
|
if (cbResult) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_playback, &deviceInfo, pUserData);
|
|
}
|
|
|
|
if (cbResult) {
|
|
if (ma_is_capture_supported__webaudio()) {
|
|
ma_device_info deviceInfo;
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
ma_strncpy_s(deviceInfo.name, sizeof(deviceInfo.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
deviceInfo.isDefault = MA_TRUE;
|
|
cbResult = callback(pContext, ma_device_type_capture, &deviceInfo, pUserData);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(pDeviceInfo->id.webaudio, sizeof(pDeviceInfo->id.webaudio));
|
|
|
|
(void)pDeviceID;
|
|
if (deviceType == ma_device_type_playback) {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDeviceInfo->name, sizeof(pDeviceInfo->name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
}
|
|
|
|
pDeviceInfo->isDefault = MA_TRUE;
|
|
|
|
pDeviceInfo->nativeDataFormats[0].flags = 0;
|
|
pDeviceInfo->nativeDataFormats[0].format = ma_format_unknown;
|
|
pDeviceInfo->nativeDataFormats[0].channels = 0;
|
|
pDeviceInfo->nativeDataFormats[0].sampleRate = EM_ASM_INT({
|
|
try {
|
|
var temp = new (window.AudioContext || window.webkitAudioContext)();
|
|
var sampleRate = temp.sampleRate;
|
|
temp.close();
|
|
return sampleRate;
|
|
} catch(e) {
|
|
return 0;
|
|
}
|
|
}, 0);
|
|
|
|
if (pDeviceInfo->nativeDataFormats[0].sampleRate == 0) {
|
|
return MA_NO_DEVICE;
|
|
}
|
|
|
|
pDeviceInfo->nativeDataFormatCount = 1;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_uninit__webaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
#if defined(MA_USE_AUDIO_WORKLETS)
|
|
{
|
|
EM_ASM({
|
|
var device = window.miniaudio.get_device_by_index($0);
|
|
|
|
if (device.streamNode !== undefined) {
|
|
device.streamNode.disconnect();
|
|
device.streamNode = undefined;
|
|
}
|
|
|
|
device.pDevice = undefined;
|
|
}, pDevice->webaudio.deviceIndex);
|
|
|
|
emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet);
|
|
emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
|
|
ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
#else
|
|
{
|
|
EM_ASM({
|
|
var device = window.miniaudio.get_device_by_index($0);
|
|
|
|
if (device.scriptNode !== undefined) {
|
|
device.scriptNode.onaudioprocess = function(e) {};
|
|
device.scriptNode.disconnect();
|
|
device.scriptNode = undefined;
|
|
}
|
|
|
|
if (device.streamNode !== undefined) {
|
|
device.streamNode.disconnect();
|
|
device.streamNode = undefined;
|
|
}
|
|
|
|
device.webaudio.close();
|
|
device.webaudio = undefined;
|
|
device.pDevice = undefined;
|
|
}, pDevice->webaudio.deviceIndex);
|
|
}
|
|
#endif
|
|
|
|
EM_ASM({
|
|
window.miniaudio.untrack_device_by_index($0);
|
|
}, pDevice->webaudio.deviceIndex);
|
|
|
|
ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if !defined(MA_USE_AUDIO_WORKLETS)
|
|
static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
|
|
{
|
|
ma_uint32 periodSizeInFrames;
|
|
|
|
if (nativeSampleRate == 0) {
|
|
nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
|
|
}
|
|
|
|
if (pDescriptor->periodSizeInFrames == 0) {
|
|
if (pDescriptor->periodSizeInMilliseconds == 0) {
|
|
if (performanceProfile == ma_performance_profile_low_latency) {
|
|
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(33, nativeSampleRate);
|
|
} else {
|
|
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(333, nativeSampleRate);
|
|
}
|
|
} else {
|
|
periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
|
|
}
|
|
} else {
|
|
periodSizeInFrames = pDescriptor->periodSizeInFrames;
|
|
}
|
|
|
|
if (periodSizeInFrames < 256) {
|
|
periodSizeInFrames = 256;
|
|
} else if (periodSizeInFrames > 16384) {
|
|
periodSizeInFrames = 16384;
|
|
} else {
|
|
periodSizeInFrames = ma_next_power_of_2(periodSizeInFrames);
|
|
}
|
|
|
|
return periodSizeInFrames;
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_USE_AUDIO_WORKLETS)
|
|
typedef struct
|
|
{
|
|
ma_device* pDevice;
|
|
const ma_device_config* pConfig;
|
|
ma_device_descriptor* pDescriptorPlayback;
|
|
ma_device_descriptor* pDescriptorCapture;
|
|
} ma_audio_worklet_thread_initialized_data;
|
|
|
|
static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pUserData;
|
|
ma_uint32 frameCount;
|
|
|
|
(void)paramCount;
|
|
(void)pParams;
|
|
|
|
if (pDevice->type == ma_device_type_playback) {
|
|
frameCount = pDevice->playback.internalPeriodSizeInFrames;
|
|
} else {
|
|
frameCount = pDevice->capture.internalPeriodSizeInFrames;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_started) {
|
|
for (int i = 0; i < outputCount; i += 1) {
|
|
MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float));
|
|
}
|
|
|
|
return EM_TRUE;
|
|
}
|
|
|
|
if (inputCount > 0) {
|
|
for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) {
|
|
for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame];
|
|
}
|
|
}
|
|
|
|
ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);
|
|
}
|
|
|
|
if (outputCount > 0) {
|
|
if (pDevice->type == ma_device_type_capture) {
|
|
for (int i = 0; i < outputCount; i += 1) {
|
|
MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float));
|
|
}
|
|
} else {
|
|
ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer);
|
|
|
|
for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) {
|
|
for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel];
|
|
}
|
|
}
|
|
|
|
for (int i = 1; i < outputCount; i += 1) {
|
|
MA_ZERO_MEMORY(pOutputs[i].data, pOutputs[i].numberOfChannels * frameCount * sizeof(float));
|
|
}
|
|
}
|
|
}
|
|
|
|
return EM_TRUE;
|
|
}
|
|
|
|
static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
|
|
{
|
|
ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
|
|
EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions;
|
|
int channels = 0;
|
|
size_t intermediaryBufferSizeInFrames;
|
|
int sampleRate;
|
|
|
|
if (success == EM_FALSE) {
|
|
pParameters->pDevice->webaudio.initResult = MA_ERROR;
|
|
ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
|
|
return;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&audioWorkletOptions);
|
|
|
|
if (pParameters->pConfig->deviceType == ma_device_type_capture) {
|
|
channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS);
|
|
audioWorkletOptions.numberOfInputs = 1;
|
|
} else {
|
|
channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS);
|
|
|
|
if (pParameters->pConfig->deviceType == ma_device_type_duplex) {
|
|
audioWorkletOptions.numberOfInputs = 1;
|
|
} else {
|
|
audioWorkletOptions.numberOfInputs = 0;
|
|
}
|
|
}
|
|
|
|
audioWorkletOptions.numberOfOutputs = 1;
|
|
audioWorkletOptions.outputChannelCounts = &channels;
|
|
|
|
#if defined(MA_SUPPORT_AUDIO_WORKLETS_VARIABLE_BUFFER_SIZE)
|
|
{
|
|
intermediaryBufferSizeInFrames = (size_t)emscripten_audio_context_quantum_size(audioContext);
|
|
}
|
|
#else
|
|
{
|
|
intermediaryBufferSizeInFrames = 128;
|
|
}
|
|
#endif
|
|
|
|
pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks);
|
|
if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) {
|
|
pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY;
|
|
ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
|
|
return;
|
|
}
|
|
|
|
pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice);
|
|
|
|
if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_result attachmentResult = (ma_result)EM_ASM_INT({
|
|
var getUserMediaResult = 0;
|
|
var audioWorklet = emscriptenGetAudioObject($0);
|
|
var audioContext = emscriptenGetAudioObject($1);
|
|
|
|
navigator.mediaDevices.getUserMedia({audio:true, video:false})
|
|
.then(function(stream) {
|
|
audioContext.streamNode = audioContext.createMediaStreamSource(stream);
|
|
audioContext.streamNode.connect(audioWorklet);
|
|
audioWorklet.connect(audioContext.destination);
|
|
getUserMediaResult = 0;
|
|
})
|
|
.catch(function(error) {
|
|
console.log("navigator.mediaDevices.getUserMedia Failed: " + error);
|
|
getUserMediaResult = -1;
|
|
});
|
|
|
|
return getUserMediaResult;
|
|
}, pParameters->pDevice->webaudio.audioWorklet, audioContext);
|
|
|
|
if (attachmentResult != MA_SUCCESS) {
|
|
ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node.");
|
|
emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet);
|
|
pParameters->pDevice->webaudio.initResult = attachmentResult;
|
|
ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (pParameters->pConfig->deviceType == ma_device_type_playback) {
|
|
ma_result attachmentResult = (ma_result)EM_ASM_INT({
|
|
var audioWorklet = emscriptenGetAudioObject($0);
|
|
var audioContext = emscriptenGetAudioObject($1);
|
|
audioWorklet.connect(audioContext.destination);
|
|
return 0;
|
|
}, pParameters->pDevice->webaudio.audioWorklet, audioContext);
|
|
|
|
if (attachmentResult != MA_SUCCESS) {
|
|
ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node.");
|
|
pParameters->pDevice->webaudio.initResult = attachmentResult;
|
|
ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
|
|
return;
|
|
}
|
|
}
|
|
|
|
sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext);
|
|
|
|
if (pParameters->pDescriptorCapture != NULL) {
|
|
pParameters->pDescriptorCapture->format = ma_format_f32;
|
|
pParameters->pDescriptorCapture->channels = (ma_uint32)channels;
|
|
pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels);
|
|
pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames;
|
|
pParameters->pDescriptorCapture->periodCount = 1;
|
|
}
|
|
|
|
if (pParameters->pDescriptorPlayback != NULL) {
|
|
pParameters->pDescriptorPlayback->format = ma_format_f32;
|
|
pParameters->pDescriptorPlayback->channels = (ma_uint32)channels;
|
|
pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels);
|
|
pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames;
|
|
pParameters->pDescriptorPlayback->periodCount = 1;
|
|
}
|
|
|
|
ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet);
|
|
pParameters->pDevice->webaudio.initResult = MA_SUCCESS;
|
|
ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData)
|
|
{
|
|
ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData;
|
|
WebAudioWorkletProcessorCreateOptions workletProcessorOptions;
|
|
|
|
MA_ASSERT(pParameters != NULL);
|
|
|
|
if (success == EM_FALSE) {
|
|
pParameters->pDevice->webaudio.initResult = MA_ERROR;
|
|
return;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&workletProcessorOptions);
|
|
workletProcessorOptions.name = "miniaudio";
|
|
|
|
emscripten_create_wasm_audio_worklet_processor_async(audioContext, &workletProcessorOptions, ma_audio_worklet_processor_created__webaudio, pParameters);
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
if (pConfig->deviceType == ma_device_type_loopback) {
|
|
return MA_DEVICE_TYPE_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (((pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) && pDescriptorPlayback->shareMode == ma_share_mode_exclusive) ||
|
|
((pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) && pDescriptorCapture->shareMode == ma_share_mode_exclusive)) {
|
|
return MA_SHARE_MODE_NOT_SUPPORTED;
|
|
}
|
|
|
|
#if defined(MA_USE_AUDIO_WORKLETS)
|
|
{
|
|
EmscriptenWebAudioCreateAttributes audioContextAttributes;
|
|
ma_audio_worklet_thread_initialized_data* pInitParameters;
|
|
void* pStackBuffer;
|
|
|
|
if (pConfig->performanceProfile == ma_performance_profile_conservative) {
|
|
audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK;
|
|
} else {
|
|
audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback) {
|
|
audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate;
|
|
} else {
|
|
audioContextAttributes.sampleRate = 0;
|
|
}
|
|
|
|
pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes);
|
|
|
|
pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks);
|
|
if (pStackBuffer == NULL) {
|
|
emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks);
|
|
if (pInitParameters == NULL) {
|
|
ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
|
|
emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pInitParameters->pDevice = pDevice;
|
|
pInitParameters->pConfig = pConfig;
|
|
pInitParameters->pDescriptorPlayback = pDescriptorPlayback;
|
|
pInitParameters->pDescriptorCapture = pDescriptorCapture;
|
|
|
|
pDevice->webaudio.initResult = MA_BUSY;
|
|
{
|
|
emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters);
|
|
}
|
|
while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); }
|
|
|
|
if (pDevice->webaudio.initResult != MA_SUCCESS) {
|
|
ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks);
|
|
emscripten_destroy_audio_context(pDevice->webaudio.audioContext);
|
|
return pDevice->webaudio.initResult;
|
|
}
|
|
|
|
pDevice->webaudio.deviceIndex = EM_ASM_INT({
|
|
return window.miniaudio.track_device({
|
|
webaudio: emscriptenGetAudioObject($0),
|
|
state: 1,
|
|
pDevice: $1
|
|
});
|
|
}, pDevice->webaudio.audioContext, pDevice);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
ma_uint32 deviceIndex;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 periodSizeInFrames;
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture) {
|
|
channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS;
|
|
} else {
|
|
channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback) {
|
|
sampleRate = pDescriptorPlayback->sampleRate;
|
|
} else {
|
|
sampleRate = 0;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture) {
|
|
periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile);
|
|
} else {
|
|
periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile);
|
|
}
|
|
|
|
pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks);
|
|
if (pDevice->webaudio.pIntermediaryBuffer == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
deviceIndex = EM_ASM_INT({
|
|
var deviceType = $0;
|
|
var channels = $1;
|
|
var sampleRate = $2;
|
|
var bufferSize = $3;
|
|
var pIntermediaryBuffer = $4;
|
|
var pDevice = $5;
|
|
|
|
if (typeof(window.miniaudio) === 'undefined') {
|
|
return -1;
|
|
}
|
|
|
|
var device = {};
|
|
|
|
var audioContextOptions = {};
|
|
if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) {
|
|
audioContextOptions.sampleRate = sampleRate;
|
|
}
|
|
|
|
device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions);
|
|
device.webaudio.suspend();
|
|
device.state = window.miniaudio.device_state.stopped;
|
|
|
|
var channelCountIn = 0;
|
|
var channelCountOut = channels;
|
|
if (deviceType != window.miniaudio.device_type.playback) {
|
|
channelCountIn = channels;
|
|
}
|
|
|
|
device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut);
|
|
|
|
device.scriptNode.onaudioprocess = function(e) {
|
|
if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) {
|
|
device.intermediaryBufferView = new Float32Array(HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels);
|
|
}
|
|
|
|
if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
|
|
for (var iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
var inputBuffer = e.inputBuffer.getChannelData(iChannel);
|
|
var intermediaryBuffer = device.intermediaryBufferView;
|
|
|
|
for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {
|
|
intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame];
|
|
}
|
|
}
|
|
|
|
_ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
|
|
}
|
|
|
|
if (deviceType == window.miniaudio.device_type.playback || deviceType == window.miniaudio.device_type.duplex) {
|
|
_ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer);
|
|
|
|
for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
|
|
var outputBuffer = e.outputBuffer.getChannelData(iChannel);
|
|
var intermediaryBuffer = device.intermediaryBufferView;
|
|
|
|
for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) {
|
|
outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel];
|
|
}
|
|
}
|
|
} else {
|
|
for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) {
|
|
e.outputBuffer.getChannelData(iChannel).fill(0.0);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (deviceType == window.miniaudio.device_type.capture || deviceType == window.miniaudio.device_type.duplex) {
|
|
navigator.mediaDevices.getUserMedia({audio:true, video:false})
|
|
.then(function(stream) {
|
|
device.streamNode = device.webaudio.createMediaStreamSource(stream);
|
|
device.streamNode.connect(device.scriptNode);
|
|
device.scriptNode.connect(device.webaudio.destination);
|
|
})
|
|
.catch(function(error) {
|
|
console.log("Failed to get user media: " + error);
|
|
});
|
|
}
|
|
|
|
if (deviceType == window.miniaudio.device_type.playback) {
|
|
device.scriptNode.connect(device.webaudio.destination);
|
|
}
|
|
|
|
device.pDevice = pDevice;
|
|
|
|
return window.miniaudio.track_device(device);
|
|
}, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice);
|
|
|
|
if (deviceIndex < 0) {
|
|
return MA_FAILED_TO_OPEN_BACKEND_DEVICE;
|
|
}
|
|
|
|
pDevice->webaudio.deviceIndex = deviceIndex;
|
|
|
|
sampleRate = (ma_uint32)EM_ASM_INT({ return window.miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex);
|
|
|
|
if (pDescriptorCapture != NULL) {
|
|
pDescriptorCapture->format = ma_format_f32;
|
|
pDescriptorCapture->channels = channels;
|
|
pDescriptorCapture->sampleRate = sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels);
|
|
pDescriptorCapture->periodSizeInFrames = periodSizeInFrames;
|
|
pDescriptorCapture->periodCount = 1;
|
|
}
|
|
|
|
if (pDescriptorPlayback != NULL) {
|
|
pDescriptorPlayback->format = ma_format_f32;
|
|
pDescriptorPlayback->channels = channels;
|
|
pDescriptorPlayback->sampleRate = sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels);
|
|
pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames;
|
|
pDescriptorPlayback->periodCount = 1;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_device_start__webaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
EM_ASM({
|
|
var device = window.miniaudio.get_device_by_index($0);
|
|
device.webaudio.resume();
|
|
device.state = window.miniaudio.device_state.started;
|
|
}, pDevice->webaudio.deviceIndex);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_device_stop__webaudio(ma_device* pDevice)
|
|
{
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
EM_ASM({
|
|
var device = window.miniaudio.get_device_by_index($0);
|
|
device.webaudio.suspend();
|
|
device.state = window.miniaudio.device_state.stopped;
|
|
}, pDevice->webaudio.deviceIndex);
|
|
|
|
ma_device__on_notification_stopped(pDevice);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_uninit__webaudio(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
MA_ASSERT(pContext->backend == ma_backend_webaudio);
|
|
|
|
(void)pContext;
|
|
|
|
EM_ASM({
|
|
if (typeof(window.miniaudio) !== 'undefined') {
|
|
window.miniaudio.unlock_event_types.map(function(event_type) {
|
|
document.removeEventListener(event_type, window.miniaudio.unlock, true);
|
|
});
|
|
|
|
window.miniaudio.referenceCount -= 1;
|
|
if (window.miniaudio.referenceCount === 0) {
|
|
delete window.miniaudio;
|
|
}
|
|
}
|
|
});
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_context_config* pConfig, ma_backend_callbacks* pCallbacks)
|
|
{
|
|
int resultFromJS;
|
|
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
(void)pConfig;
|
|
|
|
resultFromJS = EM_ASM_INT({
|
|
if (typeof window === 'undefined' || (window.AudioContext || window.webkitAudioContext) === undefined) {
|
|
return 0;
|
|
}
|
|
|
|
if (typeof(window.miniaudio) === 'undefined') {
|
|
window.miniaudio = {
|
|
referenceCount: 0
|
|
};
|
|
|
|
window.miniaudio.device_type = {};
|
|
window.miniaudio.device_type.playback = $0;
|
|
window.miniaudio.device_type.capture = $1;
|
|
window.miniaudio.device_type.duplex = $2;
|
|
|
|
window.miniaudio.device_state = {};
|
|
window.miniaudio.device_state.stopped = $3;
|
|
window.miniaudio.device_state.started = $4;
|
|
|
|
let miniaudio = window.miniaudio;
|
|
miniaudio.devices = [];
|
|
|
|
miniaudio.track_device = function(device) {
|
|
for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
|
|
if (miniaudio.devices[iDevice] == null) {
|
|
miniaudio.devices[iDevice] = device;
|
|
return iDevice;
|
|
}
|
|
}
|
|
|
|
miniaudio.devices.push(device);
|
|
return miniaudio.devices.length - 1;
|
|
};
|
|
|
|
miniaudio.untrack_device_by_index = function(deviceIndex) {
|
|
miniaudio.devices[deviceIndex] = null;
|
|
|
|
while (miniaudio.devices.length > 0) {
|
|
if (miniaudio.devices[miniaudio.devices.length-1] == null) {
|
|
miniaudio.devices.pop();
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
miniaudio.untrack_device = function(device) {
|
|
for (var iDevice = 0; iDevice < miniaudio.devices.length; ++iDevice) {
|
|
if (miniaudio.devices[iDevice] == device) {
|
|
return miniaudio.untrack_device_by_index(iDevice);
|
|
}
|
|
}
|
|
};
|
|
|
|
miniaudio.get_device_by_index = function(deviceIndex) {
|
|
return miniaudio.devices[deviceIndex];
|
|
};
|
|
|
|
miniaudio.unlock_event_types = (function(){
|
|
return ['touchend', 'click'];
|
|
})();
|
|
|
|
miniaudio.unlock = function() {
|
|
for(var i = 0; i < miniaudio.devices.length; ++i) {
|
|
var device = miniaudio.devices[i];
|
|
if (device != null &&
|
|
device.webaudio != null &&
|
|
device.state === miniaudio.device_state.started) {
|
|
|
|
device.webaudio.resume().then(() => {
|
|
_ma_device__on_notification_unlocked(device.pDevice);
|
|
},
|
|
(error) => {console.error("Failed to resume audiocontext", error);
|
|
});
|
|
}
|
|
}
|
|
miniaudio.unlock_event_types.map(function(event_type) {
|
|
document.removeEventListener(event_type, miniaudio.unlock, true);
|
|
});
|
|
};
|
|
|
|
miniaudio.unlock_event_types.map(function(event_type) {
|
|
document.addEventListener(event_type, miniaudio.unlock, true);
|
|
});
|
|
}
|
|
|
|
window.miniaudio.referenceCount += 1;
|
|
|
|
return 1;
|
|
}, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started);
|
|
|
|
if (resultFromJS != 1) {
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
pCallbacks->onContextInit = ma_context_init__webaudio;
|
|
pCallbacks->onContextUninit = ma_context_uninit__webaudio;
|
|
pCallbacks->onContextEnumerateDevices = ma_context_enumerate_devices__webaudio;
|
|
pCallbacks->onContextGetDeviceInfo = ma_context_get_device_info__webaudio;
|
|
pCallbacks->onDeviceInit = ma_device_init__webaudio;
|
|
pCallbacks->onDeviceUninit = ma_device_uninit__webaudio;
|
|
pCallbacks->onDeviceStart = ma_device_start__webaudio;
|
|
pCallbacks->onDeviceStop = ma_device_stop__webaudio;
|
|
pCallbacks->onDeviceRead = NULL;
|
|
pCallbacks->onDeviceWrite = NULL;
|
|
pCallbacks->onDeviceDataLoop = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static ma_bool32 ma__is_channel_map_valid(const ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
if (pChannelMap != NULL && pChannelMap[0] != MA_CHANNEL_NONE) {
|
|
ma_uint32 iChannel;
|
|
|
|
if (channels == 0 || channels > MA_MAX_CHANNELS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
ma_uint32 jChannel;
|
|
for (jChannel = iChannel + 1; jChannel < channels; ++jChannel) {
|
|
if (pChannelMap[iChannel] == pChannelMap[jChannel]) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static ma_bool32 ma_context_is_backend_asynchronous(ma_context* pContext)
|
|
{
|
|
MA_ASSERT(pContext != NULL);
|
|
|
|
if (pContext->callbacks.onDeviceRead == NULL && pContext->callbacks.onDeviceWrite == NULL) {
|
|
if (pContext->callbacks.onDeviceDataLoop == NULL) {
|
|
return MA_TRUE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type deviceType)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
|
|
if (pDevice->capture.format == ma_format_unknown) {
|
|
pDevice->capture.format = pDevice->capture.internalFormat;
|
|
}
|
|
if (pDevice->capture.channels == 0) {
|
|
pDevice->capture.channels = pDevice->capture.internalChannels;
|
|
}
|
|
if (pDevice->capture.channelMap[0] == MA_CHANNEL_NONE) {
|
|
MA_ASSERT(pDevice->capture.channels <= MA_MAX_CHANNELS);
|
|
if (pDevice->capture.internalChannels == pDevice->capture.channels) {
|
|
ma_channel_map_copy(pDevice->capture.channelMap, pDevice->capture.internalChannelMap, pDevice->capture.channels);
|
|
} else {
|
|
if (pDevice->capture.channelMixMode == ma_channel_mix_mode_simple) {
|
|
ma_channel_map_init_blank(pDevice->capture.channelMap, pDevice->capture.channels);
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pDevice->capture.channels);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
if (pDevice->playback.format == ma_format_unknown) {
|
|
pDevice->playback.format = pDevice->playback.internalFormat;
|
|
}
|
|
if (pDevice->playback.channels == 0) {
|
|
pDevice->playback.channels = pDevice->playback.internalChannels;
|
|
}
|
|
if (pDevice->playback.channelMap[0] == MA_CHANNEL_NONE) {
|
|
MA_ASSERT(pDevice->playback.channels <= MA_MAX_CHANNELS);
|
|
if (pDevice->playback.internalChannels == pDevice->playback.channels) {
|
|
ma_channel_map_copy(pDevice->playback.channelMap, pDevice->playback.internalChannelMap, pDevice->playback.channels);
|
|
} else {
|
|
if (pDevice->playback.channelMixMode == ma_channel_mix_mode_simple) {
|
|
ma_channel_map_init_blank(pDevice->playback.channelMap, pDevice->playback.channels);
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pDevice->playback.channels);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pDevice->sampleRate == 0) {
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
|
|
pDevice->sampleRate = pDevice->capture.internalSampleRate;
|
|
} else {
|
|
pDevice->sampleRate = pDevice->playback.internalSampleRate;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
|
|
ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
|
|
converterConfig.formatIn = pDevice->capture.internalFormat;
|
|
converterConfig.channelsIn = pDevice->capture.internalChannels;
|
|
converterConfig.sampleRateIn = pDevice->capture.internalSampleRate;
|
|
converterConfig.pChannelMapIn = pDevice->capture.internalChannelMap;
|
|
converterConfig.formatOut = pDevice->capture.format;
|
|
converterConfig.channelsOut = pDevice->capture.channels;
|
|
converterConfig.sampleRateOut = pDevice->sampleRate;
|
|
converterConfig.pChannelMapOut = pDevice->capture.channelMap;
|
|
converterConfig.channelMixMode = pDevice->capture.channelMixMode;
|
|
converterConfig.calculateLFEFromSpatialChannels = pDevice->capture.calculateLFEFromSpatialChannels;
|
|
converterConfig.allowDynamicSampleRate = MA_FALSE;
|
|
converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
|
|
converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
|
|
converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
|
|
converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {
|
|
ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->capture.converter);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
ma_data_converter_config converterConfig = ma_data_converter_config_init_default();
|
|
converterConfig.formatIn = pDevice->playback.format;
|
|
converterConfig.channelsIn = pDevice->playback.channels;
|
|
converterConfig.sampleRateIn = pDevice->sampleRate;
|
|
converterConfig.pChannelMapIn = pDevice->playback.channelMap;
|
|
converterConfig.formatOut = pDevice->playback.internalFormat;
|
|
converterConfig.channelsOut = pDevice->playback.internalChannels;
|
|
converterConfig.sampleRateOut = pDevice->playback.internalSampleRate;
|
|
converterConfig.pChannelMapOut = pDevice->playback.internalChannelMap;
|
|
converterConfig.channelMixMode = pDevice->playback.channelMixMode;
|
|
converterConfig.calculateLFEFromSpatialChannels = pDevice->playback.calculateLFEFromSpatialChannels;
|
|
converterConfig.allowDynamicSampleRate = MA_FALSE;
|
|
converterConfig.resampling.algorithm = pDevice->resampling.algorithm;
|
|
converterConfig.resampling.linear.lpfOrder = pDevice->resampling.linear.lpfOrder;
|
|
converterConfig.resampling.pBackendVTable = pDevice->resampling.pBackendVTable;
|
|
converterConfig.resampling.pBackendUserData = pDevice->resampling.pBackendUserData;
|
|
|
|
if (ma_device_get_state(pDevice) != ma_device_state_uninitialized) {
|
|
ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
result = ma_data_converter_init(&converterConfig, &pDevice->pContext->allocationCallbacks, &pDevice->playback.converter);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
ma_uint64 unused;
|
|
|
|
pDevice->playback.inputCacheConsumed = 0;
|
|
pDevice->playback.inputCacheRemaining = 0;
|
|
|
|
if (pDevice->type == ma_device_type_duplex ||
|
|
ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS)
|
|
{
|
|
void* pNewInputCache;
|
|
ma_uint64 newInputCacheCap;
|
|
ma_uint64 newInputCacheSizeInBytes;
|
|
|
|
newInputCacheCap = ma_calculate_frame_count_after_resampling(pDevice->playback.internalSampleRate, pDevice->sampleRate, pDevice->playback.internalPeriodSizeInFrames);
|
|
|
|
newInputCacheSizeInBytes = newInputCacheCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
|
|
if (newInputCacheSizeInBytes > MA_SIZE_MAX) {
|
|
ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
|
|
pDevice->playback.pInputCache = NULL;
|
|
pDevice->playback.inputCacheCap = 0;
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pNewInputCache = ma_realloc(pDevice->playback.pInputCache, (size_t)newInputCacheSizeInBytes, &pDevice->pContext->allocationCallbacks);
|
|
if (pNewInputCache == NULL) {
|
|
ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
|
|
pDevice->playback.pInputCache = NULL;
|
|
pDevice->playback.inputCacheCap = 0;
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pDevice->playback.pInputCache = pNewInputCache;
|
|
pDevice->playback.inputCacheCap = newInputCacheCap;
|
|
} else {
|
|
ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
|
|
pDevice->playback.pInputCache = NULL;
|
|
pDevice->playback.inputCacheCap = 0;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceType, const ma_device_descriptor* pDescriptorPlayback, const ma_device_descriptor* pDescriptorCapture)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
|
|
if (ma_device_descriptor_is_valid(pDescriptorCapture) == MA_FALSE) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDevice->capture.internalFormat = pDescriptorCapture->format;
|
|
pDevice->capture.internalChannels = pDescriptorCapture->channels;
|
|
pDevice->capture.internalSampleRate = pDescriptorCapture->sampleRate;
|
|
MA_COPY_MEMORY(pDevice->capture.internalChannelMap, pDescriptorCapture->channelMap, sizeof(pDescriptorCapture->channelMap));
|
|
pDevice->capture.internalPeriodSizeInFrames = pDescriptorCapture->periodSizeInFrames;
|
|
pDevice->capture.internalPeriods = pDescriptorCapture->periodCount;
|
|
|
|
if (pDevice->capture.internalPeriodSizeInFrames == 0) {
|
|
pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorCapture->periodSizeInMilliseconds, pDescriptorCapture->sampleRate);
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
if (ma_device_descriptor_is_valid(pDescriptorPlayback) == MA_FALSE) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDevice->playback.internalFormat = pDescriptorPlayback->format;
|
|
pDevice->playback.internalChannels = pDescriptorPlayback->channels;
|
|
pDevice->playback.internalSampleRate = pDescriptorPlayback->sampleRate;
|
|
MA_COPY_MEMORY(pDevice->playback.internalChannelMap, pDescriptorPlayback->channelMap, sizeof(pDescriptorPlayback->channelMap));
|
|
pDevice->playback.internalPeriodSizeInFrames = pDescriptorPlayback->periodSizeInFrames;
|
|
pDevice->playback.internalPeriods = pDescriptorPlayback->periodCount;
|
|
|
|
if (pDevice->playback.internalPeriodSizeInFrames == 0) {
|
|
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptorPlayback->periodSizeInMilliseconds, pDescriptorPlayback->sampleRate);
|
|
}
|
|
}
|
|
|
|
{
|
|
ma_device_info deviceInfo;
|
|
|
|
if (deviceType == ma_device_type_capture || deviceType == ma_device_type_duplex || deviceType == ma_device_type_loopback) {
|
|
result = ma_device_get_info(pDevice, ma_device_type_capture, &deviceInfo);
|
|
if (result == MA_SUCCESS) {
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
|
|
} else {
|
|
if (pDescriptorCapture->pDeviceID == NULL) {
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback || deviceType == ma_device_type_duplex) {
|
|
result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
|
|
if (result == MA_SUCCESS) {
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
|
|
} else {
|
|
if (pDescriptorPlayback->pDeviceID == NULL) {
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return ma_device__post_init_setup(pDevice, deviceType);
|
|
}
|
|
|
|
static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData)
|
|
{
|
|
ma_device* pDevice = (ma_device*)pData;
|
|
#if defined(MA_WIN32) && !defined(MA_XBOX)
|
|
HRESULT CoInitializeResult;
|
|
#endif
|
|
|
|
MA_ASSERT(pDevice != NULL);
|
|
|
|
#if defined(MA_WIN32) && !defined(MA_XBOX)
|
|
CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE);
|
|
#endif
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
ma_event_signal(&pDevice->stopEvent);
|
|
|
|
for (;;) {
|
|
ma_result startResult;
|
|
ma_result stopResult;
|
|
|
|
ma_event_wait(&pDevice->wakeupEvent);
|
|
|
|
pDevice->workResult = MA_SUCCESS;
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
|
|
break;
|
|
}
|
|
|
|
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_starting);
|
|
|
|
if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
|
|
startResult = pDevice->pContext->callbacks.onDeviceStart(pDevice);
|
|
} else {
|
|
startResult = MA_SUCCESS;
|
|
}
|
|
|
|
if (startResult != MA_SUCCESS) {
|
|
pDevice->workResult = startResult;
|
|
ma_event_signal(&pDevice->startEvent);
|
|
continue;
|
|
}
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_started);
|
|
ma_event_signal(&pDevice->startEvent);
|
|
|
|
ma_device__on_notification_started(pDevice);
|
|
|
|
if (pDevice->pContext->callbacks.onDeviceDataLoop != NULL) {
|
|
pDevice->pContext->callbacks.onDeviceDataLoop(pDevice);
|
|
} else {
|
|
ma_device_audio_thread__default_read_write(pDevice);
|
|
}
|
|
|
|
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
|
|
stopResult = pDevice->pContext->callbacks.onDeviceStop(pDevice);
|
|
} else {
|
|
stopResult = MA_SUCCESS;
|
|
}
|
|
|
|
if (stopResult == MA_SUCCESS) {
|
|
ma_device__on_notification_stopped(pDevice);
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
|
|
break;
|
|
}
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
ma_event_signal(&pDevice->stopEvent);
|
|
}
|
|
|
|
#if defined(MA_WIN32) && !defined(MA_XBOX)
|
|
if (CoInitializeResult == S_OK || CoInitializeResult == S_FALSE) {
|
|
ma_CoUninitialize(pDevice->pContext);
|
|
}
|
|
#endif
|
|
|
|
return (ma_thread_result)0;
|
|
}
|
|
|
|
static ma_bool32 ma_device__is_initialized(ma_device* pDevice)
|
|
{
|
|
if (pDevice == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_device_get_state(pDevice) != ma_device_state_uninitialized;
|
|
}
|
|
|
|
#ifdef MA_WIN32
|
|
static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext)
|
|
{
|
|
#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)
|
|
{
|
|
#if !defined(MA_XBOX)
|
|
{
|
|
if (pContext->win32.CoInitializeResult == S_OK || pContext->win32.CoInitializeResult == S_FALSE) {
|
|
ma_CoUninitialize(pContext);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_WIN32_DESKTOP)
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL);
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL);
|
|
#endif
|
|
|
|
ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL);
|
|
}
|
|
#else
|
|
{
|
|
(void)pContext;
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init_backend_apis__win32(ma_context* pContext)
|
|
{
|
|
#if (defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK)) && !defined(MA_XBOX)
|
|
{
|
|
#if defined(MA_WIN32_DESKTOP)
|
|
{
|
|
pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll");
|
|
if (pContext->win32.hUser32DLL == NULL) {
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow");
|
|
pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow");
|
|
|
|
pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll");
|
|
if (pContext->win32.hAdvapi32DLL == NULL) {
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA");
|
|
pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey");
|
|
pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA");
|
|
}
|
|
#endif
|
|
|
|
pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll");
|
|
if (pContext->win32.hOle32DLL == NULL) {
|
|
return MA_FAILED_TO_INIT_BACKEND;
|
|
}
|
|
|
|
pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize");
|
|
pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx");
|
|
pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize");
|
|
pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance");
|
|
pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree");
|
|
pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear");
|
|
pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2");
|
|
}
|
|
#else
|
|
{
|
|
(void)pContext;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_XBOX)
|
|
{
|
|
pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE);
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
static ma_result ma_context_uninit_backend_apis__nix(ma_context* pContext)
|
|
{
|
|
(void)pContext;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_context_init_backend_apis__nix(ma_context* pContext)
|
|
{
|
|
(void)pContext;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_context_init_backend_apis(ma_context* pContext)
|
|
{
|
|
ma_result result;
|
|
#ifdef MA_WIN32
|
|
result = ma_context_init_backend_apis__win32(pContext);
|
|
#else
|
|
result = ma_context_init_backend_apis__nix(pContext);
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_context_uninit_backend_apis(ma_context* pContext)
|
|
{
|
|
ma_result result;
|
|
#ifdef MA_WIN32
|
|
result = ma_context_uninit_backend_apis__win32(pContext);
|
|
#else
|
|
result = ma_context_uninit_backend_apis__nix(pContext);
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
#ifndef MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY
|
|
#define MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY 32
|
|
#endif
|
|
|
|
MA_API ma_device_job_thread_config ma_device_job_thread_config_init(void)
|
|
{
|
|
ma_device_job_thread_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.noThread = MA_FALSE;
|
|
config.jobQueueCapacity = MA_DEFAULT_DEVICE_JOB_QUEUE_CAPACITY;
|
|
config.jobQueueFlags = 0;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_thread_result MA_THREADCALL ma_device_job_thread_entry(void* pUserData)
|
|
{
|
|
ma_device_job_thread* pJobThread = (ma_device_job_thread*)pUserData;
|
|
MA_ASSERT(pJobThread != NULL);
|
|
|
|
for (;;) {
|
|
ma_result result;
|
|
ma_job job;
|
|
|
|
result = ma_device_job_thread_next(pJobThread, &job);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
|
|
break;
|
|
}
|
|
|
|
ma_job_process(&job);
|
|
}
|
|
|
|
return (ma_thread_result)0;
|
|
}
|
|
|
|
MA_API ma_result ma_device_job_thread_init(const ma_device_job_thread_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_device_job_thread* pJobThread)
|
|
{
|
|
ma_result result;
|
|
ma_job_queue_config jobQueueConfig;
|
|
|
|
if (pJobThread == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pJobThread);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
jobQueueConfig = ma_job_queue_config_init(pConfig->jobQueueFlags, pConfig->jobQueueCapacity);
|
|
|
|
result = ma_job_queue_init(&jobQueueConfig, pAllocationCallbacks, &pJobThread->jobQueue);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->noThread == MA_FALSE) {
|
|
result = ma_thread_create(&pJobThread->thread, ma_thread_priority_normal, 0, ma_device_job_thread_entry, pJobThread, pAllocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pJobThread->_hasThread = MA_TRUE;
|
|
} else {
|
|
pJobThread->_hasThread = MA_FALSE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_device_job_thread_uninit(ma_device_job_thread* pJobThread, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pJobThread == NULL) {
|
|
return;
|
|
}
|
|
|
|
{
|
|
ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
|
|
ma_device_job_thread_post(pJobThread, &job);
|
|
}
|
|
|
|
if (pJobThread->_hasThread) {
|
|
ma_thread_wait(&pJobThread->thread);
|
|
}
|
|
|
|
ma_job_queue_uninit(&pJobThread->jobQueue, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_device_job_thread_post(ma_device_job_thread* pJobThread, const ma_job* pJob)
|
|
{
|
|
if (pJobThread == NULL || pJob == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_job_queue_post(&pJobThread->jobQueue, pJob);
|
|
}
|
|
|
|
MA_API ma_result ma_device_job_thread_next(ma_device_job_thread* pJobThread, ma_job* pJob)
|
|
{
|
|
if (pJob == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pJob);
|
|
|
|
if (pJobThread == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_job_queue_next(&pJobThread->jobQueue, pJob);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_device_id_equal(const ma_device_id* pA, const ma_device_id* pB)
|
|
{
|
|
size_t i;
|
|
|
|
if (pA == NULL || pB == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
for (i = 0; i < sizeof(ma_device_id); i += 1) {
|
|
if (((const char*)pA)[i] != ((const char*)pB)[i]) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
MA_API ma_context_config ma_context_config_init(void)
|
|
{
|
|
ma_context_config config;
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_context_init(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pConfig, ma_context* pContext)
|
|
{
|
|
ma_result result;
|
|
ma_context_config defaultConfig;
|
|
ma_backend defaultBackends[ma_backend_null+1];
|
|
ma_uint32 iBackend;
|
|
ma_backend* pBackendsToIterate;
|
|
ma_uint32 backendsToIterateCount;
|
|
|
|
if (pContext == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pContext);
|
|
|
|
if (pConfig == NULL) {
|
|
defaultConfig = ma_context_config_init();
|
|
pConfig = &defaultConfig;
|
|
}
|
|
|
|
result = ma_allocation_callbacks_init_copy(&pContext->allocationCallbacks, &pConfig->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->pLog != NULL) {
|
|
pContext->pLog = pConfig->pLog;
|
|
} else {
|
|
result = ma_log_init(&pContext->allocationCallbacks, &pContext->log);
|
|
if (result == MA_SUCCESS) {
|
|
pContext->pLog = &pContext->log;
|
|
} else {
|
|
pContext->pLog = NULL;
|
|
}
|
|
}
|
|
|
|
pContext->threadPriority = pConfig->threadPriority;
|
|
pContext->threadStackSize = pConfig->threadStackSize;
|
|
pContext->pUserData = pConfig->pUserData;
|
|
|
|
result = ma_context_init_backend_apis(pContext);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
|
|
defaultBackends[iBackend] = (ma_backend)iBackend;
|
|
}
|
|
|
|
pBackendsToIterate = (ma_backend*)backends;
|
|
backendsToIterateCount = backendCount;
|
|
if (pBackendsToIterate == NULL) {
|
|
pBackendsToIterate = (ma_backend*)defaultBackends;
|
|
backendsToIterateCount = ma_countof(defaultBackends);
|
|
}
|
|
|
|
MA_ASSERT(pBackendsToIterate != NULL);
|
|
|
|
for (iBackend = 0; iBackend < backendsToIterateCount; iBackend += 1) {
|
|
ma_backend backend = pBackendsToIterate[iBackend];
|
|
|
|
MA_ZERO_OBJECT(&pContext->callbacks);
|
|
|
|
switch (backend) {
|
|
#ifdef MA_HAS_WASAPI
|
|
case ma_backend_wasapi:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__wasapi;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_DSOUND
|
|
case ma_backend_dsound:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__dsound;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_WINMM
|
|
case ma_backend_winmm:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__winmm;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_COREAUDIO
|
|
case ma_backend_coreaudio:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__coreaudio;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_SNDIO
|
|
case ma_backend_sndio:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__sndio;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_AUDIO4
|
|
case ma_backend_audio4:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__audio4;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_OSS
|
|
case ma_backend_oss:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__oss;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_PULSEAUDIO
|
|
case ma_backend_pulseaudio:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__pulse;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_ALSA
|
|
case ma_backend_alsa:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__alsa;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_JACK
|
|
case ma_backend_jack:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__jack;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_AAUDIO
|
|
case ma_backend_aaudio:
|
|
{
|
|
if (ma_is_backend_enabled(backend)) {
|
|
pContext->callbacks.onContextInit = ma_context_init__aaudio;
|
|
}
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_OPENSL
|
|
case ma_backend_opensl:
|
|
{
|
|
if (ma_is_backend_enabled(backend)) {
|
|
pContext->callbacks.onContextInit = ma_context_init__opensl;
|
|
}
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_WEBAUDIO
|
|
case ma_backend_webaudio:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__webaudio;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_CUSTOM
|
|
case ma_backend_custom:
|
|
{
|
|
pContext->callbacks = pConfig->custom;
|
|
} break;
|
|
#endif
|
|
#ifdef MA_HAS_NULL
|
|
case ma_backend_null:
|
|
{
|
|
pContext->callbacks.onContextInit = ma_context_init__null;
|
|
} break;
|
|
#endif
|
|
|
|
default: break;
|
|
}
|
|
|
|
if (pContext->callbacks.onContextInit != NULL) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Attempting to initialize %s backend...\n", ma_get_backend_name(backend));
|
|
result = pContext->callbacks.onContextInit(pContext, pConfig, &pContext->callbacks);
|
|
} else {
|
|
if (backend != ma_backend_custom) {
|
|
result = MA_BACKEND_NOT_ENABLED;
|
|
} else {
|
|
#if !defined(MA_HAS_CUSTOM)
|
|
result = MA_BACKEND_NOT_ENABLED;
|
|
#else
|
|
result = MA_NO_BACKEND;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_mutex_init(&pContext->deviceEnumLock);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device enumeration. ma_context_get_devices() is not thread safe.\n");
|
|
}
|
|
|
|
result = ma_mutex_init(&pContext->deviceInfoLock);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to initialize mutex for device info retrieval. ma_context_get_device_info() is not thread safe.\n");
|
|
}
|
|
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "System Architecture:\n");
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " Endian: %s\n", ma_is_little_endian() ? "LE" : "BE");
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " SSE2: %s\n", ma_has_sse2() ? "YES" : "NO");
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " AVX2: %s\n", ma_has_avx2() ? "YES" : "NO");
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, " NEON: %s\n", ma_has_neon() ? "YES" : "NO");
|
|
|
|
pContext->backend = backend;
|
|
return result;
|
|
} else {
|
|
if (result == MA_BACKEND_NOT_ENABLED) {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "%s backend is disabled.\n", ma_get_backend_name(backend));
|
|
} else {
|
|
ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Failed to initialize %s backend.\n", ma_get_backend_name(backend));
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pContext);
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
MA_API ma_result ma_context_uninit(ma_context* pContext)
|
|
{
|
|
if (pContext == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pContext->callbacks.onContextUninit != NULL) {
|
|
pContext->callbacks.onContextUninit(pContext);
|
|
}
|
|
|
|
ma_mutex_uninit(&pContext->deviceEnumLock);
|
|
ma_mutex_uninit(&pContext->deviceInfoLock);
|
|
ma_free(pContext->pDeviceInfos, &pContext->allocationCallbacks);
|
|
ma_context_uninit_backend_apis(pContext);
|
|
|
|
if (pContext->pLog == &pContext->log) {
|
|
ma_log_uninit(&pContext->log);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API size_t ma_context_sizeof(void)
|
|
{
|
|
return sizeof(ma_context);
|
|
}
|
|
|
|
MA_API ma_log* ma_context_get_log(ma_context* pContext)
|
|
{
|
|
if (pContext == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pContext->pLog;
|
|
}
|
|
|
|
MA_API ma_result ma_context_enumerate_devices(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pContext == NULL || callback == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pContext->callbacks.onContextEnumerateDevices == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_mutex_lock(&pContext->deviceEnumLock);
|
|
{
|
|
result = pContext->callbacks.onContextEnumerateDevices(pContext, callback, pUserData);
|
|
}
|
|
ma_mutex_unlock(&pContext->deviceEnumLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_bool32 ma_context_get_devices__enum_callback(ma_context* pContext, ma_device_type deviceType, const ma_device_info* pInfo, void* pUserData)
|
|
{
|
|
|
|
const ma_uint32 bufferExpansionCount = 2;
|
|
const ma_uint32 totalDeviceInfoCount = pContext->playbackDeviceInfoCount + pContext->captureDeviceInfoCount;
|
|
|
|
if (totalDeviceInfoCount >= pContext->deviceInfoCapacity) {
|
|
ma_uint32 newCapacity = pContext->deviceInfoCapacity + bufferExpansionCount;
|
|
ma_device_info* pNewInfos = (ma_device_info*)ma_realloc(pContext->pDeviceInfos, sizeof(*pContext->pDeviceInfos)*newCapacity, &pContext->allocationCallbacks);
|
|
if (pNewInfos == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
pContext->pDeviceInfos = pNewInfos;
|
|
pContext->deviceInfoCapacity = newCapacity;
|
|
}
|
|
|
|
if (deviceType == ma_device_type_playback) {
|
|
|
|
ma_uint32 iFirstCaptureDevice = pContext->playbackDeviceInfoCount;
|
|
size_t iCaptureDevice;
|
|
for (iCaptureDevice = totalDeviceInfoCount; iCaptureDevice > iFirstCaptureDevice; --iCaptureDevice) {
|
|
pContext->pDeviceInfos[iCaptureDevice] = pContext->pDeviceInfos[iCaptureDevice-1];
|
|
}
|
|
|
|
pContext->pDeviceInfos[iFirstCaptureDevice] = *pInfo;
|
|
pContext->playbackDeviceInfoCount += 1;
|
|
} else {
|
|
pContext->pDeviceInfos[totalDeviceInfoCount] = *pInfo;
|
|
pContext->captureDeviceInfoCount += 1;
|
|
}
|
|
|
|
(void)pUserData;
|
|
return MA_TRUE;
|
|
}
|
|
|
|
MA_API ma_result ma_context_get_devices(ma_context* pContext, ma_device_info** ppPlaybackDeviceInfos, ma_uint32* pPlaybackDeviceCount, ma_device_info** ppCaptureDeviceInfos, ma_uint32* pCaptureDeviceCount)
|
|
{
|
|
ma_result result;
|
|
|
|
if (ppPlaybackDeviceInfos != NULL) *ppPlaybackDeviceInfos = NULL;
|
|
if (pPlaybackDeviceCount != NULL) *pPlaybackDeviceCount = 0;
|
|
if (ppCaptureDeviceInfos != NULL) *ppCaptureDeviceInfos = NULL;
|
|
if (pCaptureDeviceCount != NULL) *pCaptureDeviceCount = 0;
|
|
|
|
if (pContext == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pContext->callbacks.onContextEnumerateDevices == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_mutex_lock(&pContext->deviceEnumLock);
|
|
{
|
|
pContext->playbackDeviceInfoCount = 0;
|
|
pContext->captureDeviceInfoCount = 0;
|
|
|
|
result = pContext->callbacks.onContextEnumerateDevices(pContext, ma_context_get_devices__enum_callback, NULL);
|
|
if (result == MA_SUCCESS) {
|
|
if (ppPlaybackDeviceInfos != NULL) {
|
|
*ppPlaybackDeviceInfos = pContext->pDeviceInfos;
|
|
}
|
|
if (pPlaybackDeviceCount != NULL) {
|
|
*pPlaybackDeviceCount = pContext->playbackDeviceInfoCount;
|
|
}
|
|
|
|
if (ppCaptureDeviceInfos != NULL) {
|
|
*ppCaptureDeviceInfos = pContext->pDeviceInfos;
|
|
if (pContext->playbackDeviceInfoCount > 0) {
|
|
*ppCaptureDeviceInfos += pContext->playbackDeviceInfoCount;
|
|
}
|
|
}
|
|
if (pCaptureDeviceCount != NULL) {
|
|
*pCaptureDeviceCount = pContext->captureDeviceInfoCount;
|
|
}
|
|
}
|
|
}
|
|
ma_mutex_unlock(&pContext->deviceEnumLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_context_get_device_info(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, ma_device_info* pDeviceInfo)
|
|
{
|
|
ma_result result;
|
|
ma_device_info deviceInfo;
|
|
|
|
if (pContext == NULL || pDeviceInfo == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&deviceInfo);
|
|
|
|
if (pDeviceID != NULL) {
|
|
MA_COPY_MEMORY(&deviceInfo.id, pDeviceID, sizeof(*pDeviceID));
|
|
}
|
|
|
|
if (pContext->callbacks.onContextGetDeviceInfo == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_mutex_lock(&pContext->deviceInfoLock);
|
|
{
|
|
result = pContext->callbacks.onContextGetDeviceInfo(pContext, deviceType, pDeviceID, &deviceInfo);
|
|
}
|
|
ma_mutex_unlock(&pContext->deviceInfoLock);
|
|
|
|
*pDeviceInfo = deviceInfo;
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_context_is_loopback_supported(ma_context* pContext)
|
|
{
|
|
if (pContext == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_is_loopback_supported(pContext->backend);
|
|
}
|
|
|
|
MA_API ma_device_config ma_device_config_init(ma_device_type deviceType)
|
|
{
|
|
ma_device_config config;
|
|
MA_ZERO_OBJECT(&config);
|
|
config.deviceType = deviceType;
|
|
config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear);
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pConfig, ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
ma_device_descriptor descriptorPlayback;
|
|
ma_device_descriptor descriptorCapture;
|
|
|
|
if (pContext == NULL) {
|
|
return ma_device_init_ex(NULL, 0, NULL, pConfig, pDevice);
|
|
}
|
|
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDevice);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pContext->callbacks.onDeviceInit == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) {
|
|
if (pConfig->capture.channels > MA_MAX_CHANNELS) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (!ma__is_channel_map_valid(pConfig->capture.pChannelMap, pConfig->capture.channels)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
|
if (pConfig->playback.channels > MA_MAX_CHANNELS) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (!ma__is_channel_map_valid(pConfig->playback.pChannelMap, pConfig->playback.channels)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
pDevice->pContext = pContext;
|
|
|
|
pDevice->pUserData = pConfig->pUserData;
|
|
pDevice->onData = pConfig->dataCallback;
|
|
pDevice->onNotification = pConfig->notificationCallback;
|
|
pDevice->onStop = pConfig->stopCallback;
|
|
|
|
if (pConfig->playback.pDeviceID != NULL) {
|
|
MA_COPY_MEMORY(&pDevice->playback.id, pConfig->playback.pDeviceID, sizeof(pDevice->playback.id));
|
|
pDevice->playback.pID = &pDevice->playback.id;
|
|
} else {
|
|
pDevice->playback.pID = NULL;
|
|
}
|
|
|
|
if (pConfig->capture.pDeviceID != NULL) {
|
|
MA_COPY_MEMORY(&pDevice->capture.id, pConfig->capture.pDeviceID, sizeof(pDevice->capture.id));
|
|
pDevice->capture.pID = &pDevice->capture.id;
|
|
} else {
|
|
pDevice->capture.pID = NULL;
|
|
}
|
|
|
|
pDevice->noPreSilencedOutputBuffer = pConfig->noPreSilencedOutputBuffer;
|
|
pDevice->noClip = pConfig->noClip;
|
|
pDevice->noDisableDenormals = pConfig->noDisableDenormals;
|
|
pDevice->noFixedSizedCallback = pConfig->noFixedSizedCallback;
|
|
ma_atomic_float_set(&pDevice->masterVolumeFactor, 1);
|
|
|
|
pDevice->type = pConfig->deviceType;
|
|
pDevice->sampleRate = pConfig->sampleRate;
|
|
pDevice->resampling.algorithm = pConfig->resampling.algorithm;
|
|
pDevice->resampling.linear.lpfOrder = pConfig->resampling.linear.lpfOrder;
|
|
pDevice->resampling.pBackendVTable = pConfig->resampling.pBackendVTable;
|
|
pDevice->resampling.pBackendUserData = pConfig->resampling.pBackendUserData;
|
|
|
|
pDevice->capture.shareMode = pConfig->capture.shareMode;
|
|
pDevice->capture.format = pConfig->capture.format;
|
|
pDevice->capture.channels = pConfig->capture.channels;
|
|
ma_channel_map_copy_or_default(pDevice->capture.channelMap, ma_countof(pDevice->capture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
|
|
pDevice->capture.channelMixMode = pConfig->capture.channelMixMode;
|
|
pDevice->capture.calculateLFEFromSpatialChannels = pConfig->capture.calculateLFEFromSpatialChannels;
|
|
|
|
pDevice->playback.shareMode = pConfig->playback.shareMode;
|
|
pDevice->playback.format = pConfig->playback.format;
|
|
pDevice->playback.channels = pConfig->playback.channels;
|
|
ma_channel_map_copy_or_default(pDevice->playback.channelMap, ma_countof(pDevice->playback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
|
|
pDevice->playback.channelMixMode = pConfig->playback.channelMixMode;
|
|
pDevice->playback.calculateLFEFromSpatialChannels = pConfig->playback.calculateLFEFromSpatialChannels;
|
|
|
|
result = ma_mutex_init(&pDevice->startStopLock);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_event_init(&pDevice->wakeupEvent);
|
|
if (result != MA_SUCCESS) {
|
|
ma_mutex_uninit(&pDevice->startStopLock);
|
|
return result;
|
|
}
|
|
|
|
result = ma_event_init(&pDevice->startEvent);
|
|
if (result != MA_SUCCESS) {
|
|
ma_event_uninit(&pDevice->wakeupEvent);
|
|
ma_mutex_uninit(&pDevice->startStopLock);
|
|
return result;
|
|
}
|
|
|
|
result = ma_event_init(&pDevice->stopEvent);
|
|
if (result != MA_SUCCESS) {
|
|
ma_event_uninit(&pDevice->startEvent);
|
|
ma_event_uninit(&pDevice->wakeupEvent);
|
|
ma_mutex_uninit(&pDevice->startStopLock);
|
|
return result;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&descriptorPlayback);
|
|
descriptorPlayback.pDeviceID = pConfig->playback.pDeviceID;
|
|
descriptorPlayback.shareMode = pConfig->playback.shareMode;
|
|
descriptorPlayback.format = pConfig->playback.format;
|
|
descriptorPlayback.channels = pConfig->playback.channels;
|
|
descriptorPlayback.sampleRate = pConfig->sampleRate;
|
|
ma_channel_map_copy_or_default(descriptorPlayback.channelMap, ma_countof(descriptorPlayback.channelMap), pConfig->playback.pChannelMap, pConfig->playback.channels);
|
|
descriptorPlayback.periodSizeInFrames = pConfig->periodSizeInFrames;
|
|
descriptorPlayback.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
|
|
descriptorPlayback.periodCount = pConfig->periods;
|
|
|
|
if (descriptorPlayback.periodCount == 0) {
|
|
descriptorPlayback.periodCount = MA_DEFAULT_PERIODS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(&descriptorCapture);
|
|
descriptorCapture.pDeviceID = pConfig->capture.pDeviceID;
|
|
descriptorCapture.shareMode = pConfig->capture.shareMode;
|
|
descriptorCapture.format = pConfig->capture.format;
|
|
descriptorCapture.channels = pConfig->capture.channels;
|
|
descriptorCapture.sampleRate = pConfig->sampleRate;
|
|
ma_channel_map_copy_or_default(descriptorCapture.channelMap, ma_countof(descriptorCapture.channelMap), pConfig->capture.pChannelMap, pConfig->capture.channels);
|
|
descriptorCapture.periodSizeInFrames = pConfig->periodSizeInFrames;
|
|
descriptorCapture.periodSizeInMilliseconds = pConfig->periodSizeInMilliseconds;
|
|
descriptorCapture.periodCount = pConfig->periods;
|
|
|
|
if (descriptorCapture.periodCount == 0) {
|
|
descriptorCapture.periodCount = MA_DEFAULT_PERIODS;
|
|
}
|
|
|
|
result = pContext->callbacks.onDeviceInit(pDevice, pConfig, &descriptorPlayback, &descriptorCapture);
|
|
if (result != MA_SUCCESS) {
|
|
ma_event_uninit(&pDevice->startEvent);
|
|
ma_event_uninit(&pDevice->wakeupEvent);
|
|
ma_mutex_uninit(&pDevice->startStopLock);
|
|
return result;
|
|
}
|
|
|
|
#if 0
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
|
if (!ma_device_descriptor_is_valid(&descriptorCapture)) {
|
|
ma_device_uninit(pDevice);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDevice->capture.internalFormat = descriptorCapture.format;
|
|
pDevice->capture.internalChannels = descriptorCapture.channels;
|
|
pDevice->capture.internalSampleRate = descriptorCapture.sampleRate;
|
|
ma_channel_map_copy(pDevice->capture.internalChannelMap, descriptorCapture.channelMap, descriptorCapture.channels);
|
|
pDevice->capture.internalPeriodSizeInFrames = descriptorCapture.periodSizeInFrames;
|
|
pDevice->capture.internalPeriods = descriptorCapture.periodCount;
|
|
|
|
if (pDevice->capture.internalPeriodSizeInFrames == 0) {
|
|
pDevice->capture.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorCapture.periodSizeInMilliseconds, descriptorCapture.sampleRate);
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
if (!ma_device_descriptor_is_valid(&descriptorPlayback)) {
|
|
ma_device_uninit(pDevice);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDevice->playback.internalFormat = descriptorPlayback.format;
|
|
pDevice->playback.internalChannels = descriptorPlayback.channels;
|
|
pDevice->playback.internalSampleRate = descriptorPlayback.sampleRate;
|
|
ma_channel_map_copy(pDevice->playback.internalChannelMap, descriptorPlayback.channelMap, descriptorPlayback.channels);
|
|
pDevice->playback.internalPeriodSizeInFrames = descriptorPlayback.periodSizeInFrames;
|
|
pDevice->playback.internalPeriods = descriptorPlayback.periodCount;
|
|
|
|
if (pDevice->playback.internalPeriodSizeInFrames == 0) {
|
|
pDevice->playback.internalPeriodSizeInFrames = ma_calculate_buffer_size_in_frames_from_milliseconds(descriptorPlayback.periodSizeInMilliseconds, descriptorPlayback.sampleRate);
|
|
}
|
|
}
|
|
|
|
{
|
|
ma_device_info deviceInfo;
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
|
result = ma_device_get_info(pDevice, (pConfig->deviceType == ma_device_type_loopback) ? ma_device_type_playback : ma_device_type_capture, &deviceInfo);
|
|
if (result == MA_SUCCESS) {
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), deviceInfo.name, (size_t)-1);
|
|
} else {
|
|
if (descriptorCapture.pDeviceID == NULL) {
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), MA_DEFAULT_CAPTURE_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), "Capture Device", (size_t)-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
result = ma_device_get_info(pDevice, ma_device_type_playback, &deviceInfo);
|
|
if (result == MA_SUCCESS) {
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), deviceInfo.name, (size_t)-1);
|
|
} else {
|
|
if (descriptorPlayback.pDeviceID == NULL) {
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), MA_DEFAULT_PLAYBACK_DEVICE_NAME, (size_t)-1);
|
|
} else {
|
|
ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "Playback Device", (size_t)-1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_device__post_init_setup(pDevice, pConfig->deviceType);
|
|
#endif
|
|
|
|
result = ma_device_post_init(pDevice, pConfig->deviceType, &descriptorPlayback, &descriptorCapture);
|
|
if (result != MA_SUCCESS) {
|
|
ma_device_uninit(pDevice);
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->noFixedSizedCallback == MA_FALSE) {
|
|
ma_uint32 intermediaryBufferCap = pConfig->periodSizeInFrames;
|
|
if (intermediaryBufferCap == 0) {
|
|
intermediaryBufferCap = ma_calculate_buffer_size_in_frames_from_milliseconds(pConfig->periodSizeInMilliseconds, pDevice->sampleRate);
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex || pConfig->deviceType == ma_device_type_loopback) {
|
|
ma_uint32 intermediaryBufferSizeInBytes;
|
|
|
|
pDevice->capture.intermediaryBufferLen = 0;
|
|
pDevice->capture.intermediaryBufferCap = intermediaryBufferCap;
|
|
if (pDevice->capture.intermediaryBufferCap == 0) {
|
|
pDevice->capture.intermediaryBufferCap = pDevice->capture.internalPeriodSizeInFrames;
|
|
}
|
|
|
|
intermediaryBufferSizeInBytes = pDevice->capture.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->capture.format, pDevice->capture.channels);
|
|
|
|
pDevice->capture.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
|
|
if (pDevice->capture.pIntermediaryBuffer == NULL) {
|
|
ma_device_uninit(pDevice);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_silence_pcm_frames(pDevice->capture.pIntermediaryBuffer, pDevice->capture.intermediaryBufferCap, pDevice->capture.format, pDevice->capture.channels);
|
|
pDevice->capture.intermediaryBufferLen = pDevice->capture.intermediaryBufferCap;
|
|
}
|
|
|
|
if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
|
|
ma_uint64 intermediaryBufferSizeInBytes;
|
|
|
|
pDevice->playback.intermediaryBufferLen = 0;
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
pDevice->playback.intermediaryBufferCap = pDevice->capture.intermediaryBufferCap;
|
|
} else {
|
|
pDevice->playback.intermediaryBufferCap = intermediaryBufferCap;
|
|
if (pDevice->playback.intermediaryBufferCap == 0) {
|
|
pDevice->playback.intermediaryBufferCap = pDevice->playback.internalPeriodSizeInFrames;
|
|
}
|
|
}
|
|
|
|
intermediaryBufferSizeInBytes = pDevice->playback.intermediaryBufferCap * ma_get_bytes_per_frame(pDevice->playback.format, pDevice->playback.channels);
|
|
|
|
pDevice->playback.pIntermediaryBuffer = ma_malloc((size_t)intermediaryBufferSizeInBytes, &pContext->allocationCallbacks);
|
|
if (pDevice->playback.pIntermediaryBuffer == NULL) {
|
|
ma_device_uninit(pDevice);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_silence_pcm_frames(pDevice->playback.pIntermediaryBuffer, pDevice->playback.intermediaryBufferCap, pDevice->playback.format, pDevice->playback.channels);
|
|
pDevice->playback.intermediaryBufferLen = 0;
|
|
}
|
|
} else {
|
|
}
|
|
|
|
if (!ma_context_is_backend_asynchronous(pContext)) {
|
|
result = ma_thread_create(&pDevice->thread, pContext->threadPriority, pContext->threadStackSize, ma_worker_thread, pDevice, &pContext->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
ma_device_uninit(pDevice);
|
|
return result;
|
|
}
|
|
|
|
ma_event_wait(&pDevice->stopEvent);
|
|
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
|
|
} else {
|
|
if (ma_context_is_backend_asynchronous(pContext)) {
|
|
if (pConfig->deviceType == ma_device_type_duplex) {
|
|
result = ma_duplex_rb_init(pDevice->capture.format, pDevice->capture.channels, pDevice->sampleRate, pDevice->capture.internalSampleRate, pDevice->capture.internalPeriodSizeInFrames, &pDevice->pContext->allocationCallbacks, &pDevice->duplexRB);
|
|
if (result != MA_SUCCESS) {
|
|
ma_device_uninit(pDevice);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
}
|
|
|
|
{
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[%s]\n", ma_get_backend_name(pDevice->pContext->backend));
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
|
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
|
|
ma_device_get_name(pDevice, ma_device_type_capture, name, sizeof(name), NULL);
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Capture");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->capture.internalFormat), ma_get_format_name(pDevice->capture.format));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->capture.internalChannels, pDevice->capture.channels);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->capture.internalSampleRate, pDevice->sampleRate);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->capture.internalPeriodSizeInFrames, pDevice->capture.internalPeriods, (pDevice->capture.internalPeriodSizeInFrames * pDevice->capture.internalPeriods));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->capture.converter.hasPreFormatConversion ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->capture.converter.hasPostFormatConversion ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->capture.converter.hasChannelConverter ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->capture.converter.hasResampler ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->capture.converter.isPassthrough ? "YES" : "NO");
|
|
{
|
|
char channelMapStr[1024];
|
|
ma_channel_map_to_string(pDevice->capture.internalChannelMap, pDevice->capture.internalChannels, channelMapStr, sizeof(channelMapStr));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
|
|
|
|
ma_channel_map_to_string(pDevice->capture.channelMap, pDevice->capture.channels, channelMapStr, sizeof(channelMapStr));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
|
|
}
|
|
}
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
char name[MA_MAX_DEVICE_NAME_LENGTH + 1];
|
|
ma_device_get_name(pDevice, ma_device_type_playback, name, sizeof(name), NULL);
|
|
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " %s (%s)\n", name, "Playback");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Format: %s -> %s\n", ma_get_format_name(pDevice->playback.format), ma_get_format_name(pDevice->playback.internalFormat));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channels: %d -> %d\n", pDevice->playback.channels, pDevice->playback.internalChannels);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Sample Rate: %d -> %d\n", pDevice->sampleRate, pDevice->playback.internalSampleRate);
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Buffer Size: %d*%d (%d)\n", pDevice->playback.internalPeriodSizeInFrames, pDevice->playback.internalPeriods, (pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Conversion:\n");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Pre Format Conversion: %s\n", pDevice->playback.converter.hasPreFormatConversion ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Post Format Conversion: %s\n", pDevice->playback.converter.hasPostFormatConversion ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Routing: %s\n", pDevice->playback.converter.hasChannelConverter ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Resampling: %s\n", pDevice->playback.converter.hasResampler ? "YES" : "NO");
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Passthrough: %s\n", pDevice->playback.converter.isPassthrough ? "YES" : "NO");
|
|
{
|
|
char channelMapStr[1024];
|
|
ma_channel_map_to_string(pDevice->playback.channelMap, pDevice->playback.channels, channelMapStr, sizeof(channelMapStr));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map In: {%s}\n", channelMapStr);
|
|
|
|
ma_channel_map_to_string(pDevice->playback.internalChannelMap, pDevice->playback.internalChannels, channelMapStr, sizeof(channelMapStr));
|
|
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, " Channel Map Out: {%s}\n", channelMapStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_device_init_ex(const ma_backend backends[], ma_uint32 backendCount, const ma_context_config* pContextConfig, const ma_device_config* pConfig, ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
ma_context* pContext;
|
|
ma_backend defaultBackends[ma_backend_null+1];
|
|
ma_uint32 iBackend;
|
|
ma_backend* pBackendsToIterate;
|
|
ma_uint32 backendsToIterateCount;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pContextConfig != NULL) {
|
|
result = ma_allocation_callbacks_init_copy(&allocationCallbacks, &pContextConfig->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
} else {
|
|
allocationCallbacks = ma_allocation_callbacks_init_default();
|
|
}
|
|
|
|
pContext = (ma_context*)ma_malloc(sizeof(*pContext), &allocationCallbacks);
|
|
if (pContext == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
for (iBackend = 0; iBackend <= ma_backend_null; ++iBackend) {
|
|
defaultBackends[iBackend] = (ma_backend)iBackend;
|
|
}
|
|
|
|
pBackendsToIterate = (ma_backend*)backends;
|
|
backendsToIterateCount = backendCount;
|
|
if (pBackendsToIterate == NULL) {
|
|
pBackendsToIterate = (ma_backend*)defaultBackends;
|
|
backendsToIterateCount = ma_countof(defaultBackends);
|
|
}
|
|
|
|
result = MA_NO_BACKEND;
|
|
|
|
for (iBackend = 0; iBackend < backendsToIterateCount; ++iBackend) {
|
|
#if defined(MA_APPLE_MOBILE)
|
|
ma_context_config contextConfig;
|
|
|
|
if (pContextConfig == NULL) {
|
|
contextConfig = ma_context_config_init();
|
|
switch (pConfig->deviceType) {
|
|
case ma_device_type_duplex: {
|
|
contextConfig.coreaudio.sessionCategory = ma_ios_session_category_play_and_record;
|
|
} break;
|
|
case ma_device_type_capture: {
|
|
contextConfig.coreaudio.sessionCategory = ma_ios_session_category_record;
|
|
} break;
|
|
case ma_device_type_playback:
|
|
default: {
|
|
contextConfig.coreaudio.sessionCategory = ma_ios_session_category_playback;
|
|
} break;
|
|
}
|
|
|
|
pContextConfig = &contextConfig;
|
|
}
|
|
#endif
|
|
|
|
result = ma_context_init(&pBackendsToIterate[iBackend], 1, pContextConfig, pContext);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_device_init(pContext, pConfig, pDevice);
|
|
if (result == MA_SUCCESS) {
|
|
break;
|
|
} else {
|
|
ma_context_uninit(pContext);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pContext, &allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pDevice->isOwnerOfContext = MA_TRUE;
|
|
return result;
|
|
}
|
|
|
|
MA_API void ma_device_uninit(ma_device* pDevice)
|
|
{
|
|
if (!ma_device__is_initialized(pDevice)) {
|
|
return;
|
|
}
|
|
|
|
#if 0
|
|
{
|
|
if (ma_device_is_started(pDevice)) {
|
|
ma_device_stop(pDevice);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_uninitialized);
|
|
|
|
if (!ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
|
ma_event_signal(&pDevice->wakeupEvent);
|
|
ma_thread_wait(&pDevice->thread);
|
|
}
|
|
|
|
if (pDevice->pContext->callbacks.onDeviceUninit != NULL) {
|
|
pDevice->pContext->callbacks.onDeviceUninit(pDevice);
|
|
}
|
|
|
|
ma_event_uninit(&pDevice->stopEvent);
|
|
ma_event_uninit(&pDevice->startEvent);
|
|
ma_event_uninit(&pDevice->wakeupEvent);
|
|
ma_mutex_uninit(&pDevice->startStopLock);
|
|
|
|
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
ma_duplex_rb_uninit(&pDevice->duplexRB);
|
|
}
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) {
|
|
ma_data_converter_uninit(&pDevice->capture.converter, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) {
|
|
ma_data_converter_uninit(&pDevice->playback.converter, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
if (pDevice->playback.pInputCache != NULL) {
|
|
ma_free(pDevice->playback.pInputCache, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
if (pDevice->capture.pIntermediaryBuffer != NULL) {
|
|
ma_free(pDevice->capture.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
if (pDevice->playback.pIntermediaryBuffer != NULL) {
|
|
ma_free(pDevice->playback.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks);
|
|
}
|
|
|
|
if (pDevice->isOwnerOfContext) {
|
|
ma_allocation_callbacks allocationCallbacks = pDevice->pContext->allocationCallbacks;
|
|
|
|
ma_context_uninit(pDevice->pContext);
|
|
ma_free(pDevice->pContext, &allocationCallbacks);
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDevice);
|
|
}
|
|
|
|
MA_API ma_context* ma_device_get_context(ma_device* pDevice)
|
|
{
|
|
if (pDevice == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pDevice->pContext;
|
|
}
|
|
|
|
MA_API ma_log* ma_device_get_log(ma_device* pDevice)
|
|
{
|
|
return ma_context_get_log(ma_device_get_context(pDevice));
|
|
}
|
|
|
|
MA_API ma_result ma_device_get_info(ma_device* pDevice, ma_device_type type, ma_device_info* pDeviceInfo)
|
|
{
|
|
if (pDeviceInfo == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDeviceInfo);
|
|
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDevice->pContext->callbacks.onDeviceGetInfo != NULL) {
|
|
return pDevice->pContext->callbacks.onDeviceGetInfo(pDevice, type, pDeviceInfo);
|
|
}
|
|
|
|
if (type == ma_device_type_playback) {
|
|
return ma_context_get_device_info(pDevice->pContext, type, pDevice->playback.pID, pDeviceInfo);
|
|
} else {
|
|
if (pDevice->type == ma_device_type_loopback && pDevice->capture.pID == NULL) {
|
|
type = ma_device_type_playback;
|
|
}
|
|
|
|
return ma_context_get_device_info(pDevice->pContext, type, pDevice->capture.pID, pDeviceInfo);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_device_get_name(ma_device* pDevice, ma_device_type type, char* pName, size_t nameCap, size_t* pLengthNotIncludingNullTerminator)
|
|
{
|
|
ma_result result;
|
|
ma_device_info deviceInfo;
|
|
|
|
if (pLengthNotIncludingNullTerminator != NULL) {
|
|
*pLengthNotIncludingNullTerminator = 0;
|
|
}
|
|
|
|
if (pName != NULL && nameCap > 0) {
|
|
pName[0] = '\0';
|
|
}
|
|
|
|
result = ma_device_get_info(pDevice, type, &deviceInfo);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pName != NULL) {
|
|
ma_strncpy_s(pName, nameCap, deviceInfo.name, (size_t)-1);
|
|
|
|
if (pLengthNotIncludingNullTerminator != NULL) {
|
|
*pLengthNotIncludingNullTerminator = strlen(pName);
|
|
}
|
|
} else {
|
|
if (pLengthNotIncludingNullTerminator != NULL) {
|
|
*pLengthNotIncludingNullTerminator = strlen(deviceInfo.name);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_device_start(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
ma_mutex_lock(&pDevice->startStopLock);
|
|
{
|
|
if (ma_device_get_state(pDevice) == ma_device_state_started) {
|
|
ma_mutex_unlock(&pDevice->startStopLock);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_stopped);
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_starting);
|
|
|
|
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
|
if (pDevice->pContext->callbacks.onDeviceStart != NULL) {
|
|
result = pDevice->pContext->callbacks.onDeviceStart(pDevice);
|
|
} else {
|
|
result = MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
ma_device__set_state(pDevice, ma_device_state_started);
|
|
ma_device__on_notification_started(pDevice);
|
|
}
|
|
} else {
|
|
ma_event_signal(&pDevice->wakeupEvent);
|
|
|
|
ma_event_wait(&pDevice->startEvent);
|
|
result = pDevice->workResult;
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
}
|
|
}
|
|
ma_mutex_unlock(&pDevice->startStopLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_device_stop(ma_device* pDevice)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
ma_mutex_lock(&pDevice->startStopLock);
|
|
{
|
|
if (ma_device_get_state(pDevice) == ma_device_state_stopped) {
|
|
ma_mutex_unlock(&pDevice->startStopLock);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_ASSERT(ma_device_get_state(pDevice) == ma_device_state_started);
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_stopping);
|
|
|
|
if (ma_context_is_backend_asynchronous(pDevice->pContext)) {
|
|
if (pDevice->pContext->callbacks.onDeviceStop != NULL) {
|
|
result = pDevice->pContext->callbacks.onDeviceStop(pDevice);
|
|
} else {
|
|
result = MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_device__set_state(pDevice, ma_device_state_stopped);
|
|
} else {
|
|
MA_ASSERT(ma_device_get_state(pDevice) != ma_device_state_started);
|
|
|
|
if (pDevice->pContext->callbacks.onDeviceDataLoopWakeup != NULL) {
|
|
pDevice->pContext->callbacks.onDeviceDataLoopWakeup(pDevice);
|
|
}
|
|
|
|
ma_event_wait(&pDevice->stopEvent);
|
|
result = MA_SUCCESS;
|
|
}
|
|
|
|
pDevice->playback.intermediaryBufferLen = 0;
|
|
pDevice->playback.inputCacheConsumed = 0;
|
|
pDevice->playback.inputCacheRemaining = 0;
|
|
}
|
|
ma_mutex_unlock(&pDevice->startStopLock);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_device_is_started(const ma_device* pDevice)
|
|
{
|
|
return ma_device_get_state(pDevice) == ma_device_state_started;
|
|
}
|
|
|
|
MA_API ma_device_state ma_device_get_state(const ma_device* pDevice)
|
|
{
|
|
if (pDevice == NULL) {
|
|
return ma_device_state_uninitialized;
|
|
}
|
|
|
|
return ma_atomic_device_state_get((ma_atomic_device_state*)&pDevice->state);
|
|
}
|
|
|
|
MA_API ma_result ma_device_set_master_volume(ma_device* pDevice, float volume)
|
|
{
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (volume < 0.0f) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_float_set(&pDevice->masterVolumeFactor, volume);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_device_get_master_volume(ma_device* pDevice, float* pVolume)
|
|
{
|
|
if (pVolume == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDevice == NULL) {
|
|
*pVolume = 0;
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pVolume = ma_atomic_float_get(&pDevice->masterVolumeFactor);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_device_set_master_volume_db(ma_device* pDevice, float gainDB)
|
|
{
|
|
if (gainDB > 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_device_set_master_volume(pDevice, ma_volume_db_to_linear(gainDB));
|
|
}
|
|
|
|
MA_API ma_result ma_device_get_master_volume_db(ma_device* pDevice, float* pGainDB)
|
|
{
|
|
float factor;
|
|
ma_result result;
|
|
|
|
if (pGainDB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_device_get_master_volume(pDevice, &factor);
|
|
if (result != MA_SUCCESS) {
|
|
*pGainDB = 0;
|
|
return result;
|
|
}
|
|
|
|
*pGainDB = ma_volume_linear_to_db(factor);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_device_handle_backend_data_callback(ma_device* pDevice, void* pOutput, const void* pInput, ma_uint32 frameCount)
|
|
{
|
|
if (pDevice == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pOutput == NULL && pInput == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_duplex) {
|
|
if (pInput != NULL) {
|
|
ma_device__handle_duplex_callback_capture(pDevice, frameCount, pInput, &pDevice->duplexRB.rb);
|
|
}
|
|
|
|
if (pOutput != NULL) {
|
|
ma_device__handle_duplex_callback_playback(pDevice, frameCount, pOutput, &pDevice->duplexRB.rb);
|
|
}
|
|
} else {
|
|
if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_loopback) {
|
|
if (pInput == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_device__send_frames_to_client(pDevice, frameCount, pInput);
|
|
}
|
|
|
|
if (pDevice->type == ma_device_type_playback) {
|
|
if (pOutput == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_device__read_frames_from_client(pDevice, frameCount, pOutput);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_descriptor(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile)
|
|
{
|
|
if (pDescriptor == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (nativeSampleRate == 0) {
|
|
nativeSampleRate = pDescriptor->sampleRate;
|
|
}
|
|
if (nativeSampleRate == 0) {
|
|
nativeSampleRate = MA_DEFAULT_SAMPLE_RATE;
|
|
}
|
|
|
|
MA_ASSERT(nativeSampleRate != 0);
|
|
|
|
if (pDescriptor->periodSizeInFrames == 0) {
|
|
if (pDescriptor->periodSizeInMilliseconds == 0) {
|
|
if (performanceProfile == ma_performance_profile_low_latency) {
|
|
return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, nativeSampleRate);
|
|
} else {
|
|
return ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, nativeSampleRate);
|
|
}
|
|
} else {
|
|
return ma_calculate_buffer_size_in_frames_from_milliseconds(pDescriptor->periodSizeInMilliseconds, nativeSampleRate);
|
|
}
|
|
} else {
|
|
return pDescriptor->periodSizeInFrames;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_uint32 ma_calculate_buffer_size_in_milliseconds_from_frames(ma_uint32 bufferSizeInFrames, ma_uint32 sampleRate)
|
|
{
|
|
if (sampleRate == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return (bufferSizeInFrames*1000 + (sampleRate - 1)) / sampleRate;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_calculate_buffer_size_in_frames_from_milliseconds(ma_uint32 bufferSizeInMilliseconds, ma_uint32 sampleRate)
|
|
{
|
|
if (sampleRate == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return bufferSizeInMilliseconds*sampleRate / 1000;
|
|
}
|
|
|
|
MA_API void ma_copy_pcm_frames(void* dst, const void* src, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
|
|
{
|
|
if (dst == src) {
|
|
return;
|
|
}
|
|
|
|
ma_copy_memory_64(dst, src, frameCount * ma_get_bytes_per_frame(format, channels));
|
|
}
|
|
|
|
MA_API void ma_silence_pcm_frames(void* p, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
|
|
{
|
|
if (format == ma_format_u8) {
|
|
ma_uint64 sampleCount = frameCount * channels;
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
((ma_uint8*)p)[iSample] = 128;
|
|
}
|
|
} else {
|
|
ma_zero_memory_64(p, frameCount * ma_get_bytes_per_frame(format, channels));
|
|
}
|
|
}
|
|
|
|
MA_API void* ma_offset_pcm_frames_ptr(void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
|
|
{
|
|
return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
|
|
}
|
|
|
|
MA_API const void* ma_offset_pcm_frames_const_ptr(const void* p, ma_uint64 offsetInFrames, ma_format format, ma_uint32 channels)
|
|
{
|
|
return ma_offset_ptr(p, offsetInFrames * ma_get_bytes_per_frame(format, channels));
|
|
}
|
|
|
|
MA_API void ma_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_u8(pSrc[iSample]);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_s16(pSrc[iSample]);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
ma_int64 s = ma_clip_s24(pSrc[iSample]);
|
|
pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
|
|
pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
|
|
pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_s32(pSrc[iSample]);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_f32(pSrc[iSample]);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels)
|
|
{
|
|
ma_uint64 sampleCount;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
sampleCount = frameCount * channels;
|
|
|
|
switch (format) {
|
|
case ma_format_u8: ma_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount); break;
|
|
case ma_format_s16: ma_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount); break;
|
|
case ma_format_s24: ma_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount); break;
|
|
case ma_format_s32: ma_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount); break;
|
|
case ma_format_f32: ma_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount); break;
|
|
|
|
case ma_format_unknown:
|
|
case ma_format_count:
|
|
break;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_u8(ma_uint8* pSamplesOut, const ma_uint8* pSamplesIn, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
if (pSamplesOut == NULL || pSamplesIn == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamplesOut[iSample] = (ma_uint8)(pSamplesIn[iSample] * factor);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_s16(ma_int16* pSamplesOut, const ma_int16* pSamplesIn, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
if (pSamplesOut == NULL || pSamplesIn == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamplesOut[iSample] = (ma_int16)(pSamplesIn[iSample] * factor);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_s24(void* pSamplesOut, const void* pSamplesIn, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_uint64 iSample;
|
|
ma_uint8* pSamplesOut8;
|
|
ma_uint8* pSamplesIn8;
|
|
|
|
if (pSamplesOut == NULL || pSamplesIn == NULL) {
|
|
return;
|
|
}
|
|
|
|
pSamplesOut8 = (ma_uint8*)pSamplesOut;
|
|
pSamplesIn8 = (ma_uint8*)pSamplesIn;
|
|
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
ma_int32 sampleS32;
|
|
|
|
sampleS32 = (ma_int32)(((ma_uint32)(pSamplesIn8[iSample*3+0]) << 8) | ((ma_uint32)(pSamplesIn8[iSample*3+1]) << 16) | ((ma_uint32)(pSamplesIn8[iSample*3+2])) << 24);
|
|
sampleS32 = (ma_int32)(sampleS32 * factor);
|
|
|
|
pSamplesOut8[iSample*3+0] = (ma_uint8)(((ma_uint32)sampleS32 & 0x0000FF00) >> 8);
|
|
pSamplesOut8[iSample*3+1] = (ma_uint8)(((ma_uint32)sampleS32 & 0x00FF0000) >> 16);
|
|
pSamplesOut8[iSample*3+2] = (ma_uint8)(((ma_uint32)sampleS32 & 0xFF000000) >> 24);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_s32(ma_int32* pSamplesOut, const ma_int32* pSamplesIn, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
if (pSamplesOut == NULL || pSamplesIn == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamplesOut[iSample] = (ma_int32)(pSamplesIn[iSample] * factor);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_f32(float* pSamplesOut, const float* pSamplesIn, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
if (pSamplesOut == NULL || pSamplesIn == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (factor == 1) {
|
|
if (pSamplesOut == pSamplesIn) {
|
|
} else {
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamplesOut[iSample] = pSamplesIn[iSample];
|
|
}
|
|
}
|
|
} else {
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamplesOut[iSample] = pSamplesIn[iSample] * factor;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_u8(ma_uint8* pSamples, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_u8(pSamples, pSamples, sampleCount, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_s16(ma_int16* pSamples, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_s16(pSamples, pSamples, sampleCount, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_s24(void* pSamples, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_s24(pSamples, pSamples, sampleCount, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_s32(ma_int32* pSamples, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_s32(pSamples, pSamples, sampleCount, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_f32(float* pSamples, ma_uint64 sampleCount, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_f32(pSamples, pSamples, sampleCount, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_u8(ma_uint8* pFramesOut, const ma_uint8* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_u8(pFramesOut, pFramesIn, frameCount*channels, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s16(ma_int16* pFramesOut, const ma_int16* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_s16(pFramesOut, pFramesIn, frameCount*channels, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s24(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_s24(pFramesOut, pFramesIn, frameCount*channels, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_s32(ma_int32* pFramesOut, const ma_int32* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_s32(pFramesOut, pFramesIn, frameCount*channels, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_f32(pFramesOut, pFramesIn, frameCount*channels, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
|
|
{
|
|
switch (format)
|
|
{
|
|
case ma_format_u8: ma_copy_and_apply_volume_factor_pcm_frames_u8 ((ma_uint8*)pFramesOut, (const ma_uint8*)pFramesIn, frameCount, channels, factor); return;
|
|
case ma_format_s16: ma_copy_and_apply_volume_factor_pcm_frames_s16((ma_int16*)pFramesOut, (const ma_int16*)pFramesIn, frameCount, channels, factor); return;
|
|
case ma_format_s24: ma_copy_and_apply_volume_factor_pcm_frames_s24( pFramesOut, pFramesIn, frameCount, channels, factor); return;
|
|
case ma_format_s32: ma_copy_and_apply_volume_factor_pcm_frames_s32((ma_int32*)pFramesOut, (const ma_int32*)pFramesIn, frameCount, channels, factor); return;
|
|
case ma_format_f32: ma_copy_and_apply_volume_factor_pcm_frames_f32( (float*)pFramesOut, (const float*)pFramesIn, frameCount, channels, factor); return;
|
|
default: return;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames_u8(ma_uint8* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_pcm_frames_u8(pFrames, pFrames, frameCount, channels, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames_s16(ma_int16* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_pcm_frames_s16(pFrames, pFrames, frameCount, channels, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames_s24(void* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_pcm_frames_s24(pFrames, pFrames, frameCount, channels, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames_s32(ma_int32* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_pcm_frames_s32(pFrames, pFrames, frameCount, channels, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames_f32(float* pFrames, ma_uint64 frameCount, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_pcm_frames_f32(pFrames, pFrames, frameCount, channels, factor);
|
|
}
|
|
|
|
MA_API void ma_apply_volume_factor_pcm_frames(void* pFramesOut, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float factor)
|
|
{
|
|
ma_copy_and_apply_volume_factor_pcm_frames(pFramesOut, pFramesOut, frameCount, format, channels, factor);
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_factor_per_channel_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, ma_uint32 channels, float* pChannelGains)
|
|
{
|
|
ma_uint64 iFrame;
|
|
|
|
if (channels == 2) {
|
|
}
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOut[iFrame * channels + iChannel] = pFramesIn[iFrame * channels + iChannel] * pChannelGains[iChannel];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_apply_volume_unclipped_u8(ma_int16 x, ma_int16 volume)
|
|
{
|
|
return (ma_int16)(((ma_int32)x * (ma_int32)volume) >> 8);
|
|
}
|
|
|
|
static MA_INLINE ma_int32 ma_apply_volume_unclipped_s16(ma_int32 x, ma_int16 volume)
|
|
{
|
|
return (ma_int32)((x * volume) >> 8);
|
|
}
|
|
|
|
static MA_INLINE ma_int64 ma_apply_volume_unclipped_s24(ma_int64 x, ma_int16 volume)
|
|
{
|
|
return (ma_int64)((x * volume) >> 8);
|
|
}
|
|
|
|
static MA_INLINE ma_int64 ma_apply_volume_unclipped_s32(ma_int64 x, ma_int16 volume)
|
|
{
|
|
return (ma_int64)((x * volume) >> 8);
|
|
}
|
|
|
|
static MA_INLINE float ma_apply_volume_unclipped_f32(float x, float volume)
|
|
{
|
|
return x * volume;
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_u8(ma_uint8* pDst, const ma_int16* pSrc, ma_uint64 count, float volume)
|
|
{
|
|
ma_uint64 iSample;
|
|
ma_int16 volumeFixed;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
volumeFixed = ma_float_to_fixed_16(volume);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_u8(ma_apply_volume_unclipped_u8(pSrc[iSample], volumeFixed));
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_s16(ma_int16* pDst, const ma_int32* pSrc, ma_uint64 count, float volume)
|
|
{
|
|
ma_uint64 iSample;
|
|
ma_int16 volumeFixed;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
volumeFixed = ma_float_to_fixed_16(volume);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_s16(ma_apply_volume_unclipped_s16(pSrc[iSample], volumeFixed));
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_s24(ma_uint8* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
|
|
{
|
|
ma_uint64 iSample;
|
|
ma_int16 volumeFixed;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
volumeFixed = ma_float_to_fixed_16(volume);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
ma_int64 s = ma_clip_s24(ma_apply_volume_unclipped_s24(pSrc[iSample], volumeFixed));
|
|
pDst[iSample*3 + 0] = (ma_uint8)((s & 0x000000FF) >> 0);
|
|
pDst[iSample*3 + 1] = (ma_uint8)((s & 0x0000FF00) >> 8);
|
|
pDst[iSample*3 + 2] = (ma_uint8)((s & 0x00FF0000) >> 16);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_s32(ma_int32* pDst, const ma_int64* pSrc, ma_uint64 count, float volume)
|
|
{
|
|
ma_uint64 iSample;
|
|
ma_int16 volumeFixed;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
volumeFixed = ma_float_to_fixed_16(volume);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_s32(ma_apply_volume_unclipped_s32(pSrc[iSample], volumeFixed));
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_samples_f32(float* pDst, const float* pSrc, ma_uint64 count, float volume)
|
|
{
|
|
ma_uint64 iSample;
|
|
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
for (iSample = 0; iSample < count; iSample += 1) {
|
|
pDst[iSample] = ma_clip_f32(ma_apply_volume_unclipped_f32(pSrc[iSample], volume));
|
|
}
|
|
}
|
|
|
|
MA_API void ma_copy_and_apply_volume_and_clip_pcm_frames(void* pDst, const void* pSrc, ma_uint64 frameCount, ma_format format, ma_uint32 channels, float volume)
|
|
{
|
|
MA_ASSERT(pDst != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
if (volume == 1) {
|
|
ma_clip_pcm_frames(pDst, pSrc, frameCount, format, channels);
|
|
} else if (volume == 0) {
|
|
ma_silence_pcm_frames(pDst, frameCount, format, channels);
|
|
} else {
|
|
ma_uint64 sampleCount = frameCount * channels;
|
|
|
|
switch (format) {
|
|
case ma_format_u8: ma_copy_and_apply_volume_and_clip_samples_u8( (ma_uint8*)pDst, (const ma_int16*)pSrc, sampleCount, volume); break;
|
|
case ma_format_s16: ma_copy_and_apply_volume_and_clip_samples_s16((ma_int16*)pDst, (const ma_int32*)pSrc, sampleCount, volume); break;
|
|
case ma_format_s24: ma_copy_and_apply_volume_and_clip_samples_s24((ma_uint8*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
|
|
case ma_format_s32: ma_copy_and_apply_volume_and_clip_samples_s32((ma_int32*)pDst, (const ma_int64*)pSrc, sampleCount, volume); break;
|
|
case ma_format_f32: ma_copy_and_apply_volume_and_clip_samples_f32(( float*)pDst, (const float*)pSrc, sampleCount, volume); break;
|
|
|
|
case ma_format_unknown:
|
|
case ma_format_count:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API float ma_volume_linear_to_db(float factor)
|
|
{
|
|
return 20*ma_log10f(factor);
|
|
}
|
|
|
|
MA_API float ma_volume_db_to_linear(float gain)
|
|
{
|
|
return ma_powf(10, gain/20.0f);
|
|
}
|
|
|
|
MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume)
|
|
{
|
|
ma_uint64 iSample;
|
|
ma_uint64 sampleCount;
|
|
|
|
if (pDst == NULL || pSrc == NULL || channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (volume == 0) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
sampleCount = frameCount * channels;
|
|
|
|
if (volume == 1) {
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pDst[iSample] += pSrc[iSample];
|
|
}
|
|
} else {
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pDst[iSample] += ma_apply_volume_unclipped_f32(pSrc[iSample], volume);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_pcm_sample_f32_to_s16(float x)
|
|
{
|
|
return (ma_int16)(x * 32767.0f);
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_pcm_sample_u8_to_s16_no_scale(ma_uint8 x)
|
|
{
|
|
return (ma_int16)((ma_int16)x - 128);
|
|
}
|
|
|
|
static MA_INLINE ma_int64 ma_pcm_sample_s24_to_s32_no_scale(const ma_uint8* x)
|
|
{
|
|
return (ma_int64)(((ma_uint64)x[0] << 40) | ((ma_uint64)x[1] << 48) | ((ma_uint64)x[2] << 56)) >> 40;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_sample_s32_to_s24_no_scale(ma_int64 x, ma_uint8* s24)
|
|
{
|
|
s24[0] = (ma_uint8)((x & 0x000000FF) >> 0);
|
|
s24[1] = (ma_uint8)((x & 0x0000FF00) >> 8);
|
|
s24[2] = (ma_uint8)((x & 0x00FF0000) >> 16);
|
|
}
|
|
|
|
MA_API void ma_pcm_u8_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
(void)ditherMode;
|
|
ma_copy_memory_64(dst, src, count * sizeof(ma_uint8));
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int16* dst_s16 = (ma_int16*)dst;
|
|
const ma_uint8* src_u8 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int16 x = src_u8[i];
|
|
x = (ma_int16)(x - 128);
|
|
x = (ma_int16)(x << 8);
|
|
dst_s16[i] = x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_u8_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_u8_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_u8_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_u8_to_s16__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_u8_to_s16__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_u8_to_s16__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_u8_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_s24 = (ma_uint8*)dst;
|
|
const ma_uint8* src_u8 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int16 x = src_u8[i];
|
|
x = (ma_int16)(x - 128);
|
|
|
|
dst_s24[i*3+0] = 0;
|
|
dst_s24[i*3+1] = 0;
|
|
dst_s24[i*3+2] = (ma_uint8)((ma_int8)x);
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_u8_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_u8_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_u8_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_u8_to_s24__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_u8_to_s24__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_u8_to_s24__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_u8_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int32* dst_s32 = (ma_int32*)dst;
|
|
const ma_uint8* src_u8 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = src_u8[i];
|
|
x = x - 128;
|
|
x = x << 24;
|
|
dst_s32[i] = x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_u8_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_u8_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_u8_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_u8_to_s32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_u8_to_s32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_u8_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
float* dst_f32 = (float*)dst;
|
|
const ma_uint8* src_u8 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
float x = (float)src_u8[i];
|
|
x = x * 0.00784313725490196078f;
|
|
x = x - 1;
|
|
|
|
dst_f32[i] = x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_u8_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_u8_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_u8_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_u8_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_u8_to_f32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_u8_to_f32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_u8_to_f32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
static MA_INLINE void ma_pcm_interleave_u8__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_uint8* dst_u8 = (ma_uint8*)dst;
|
|
const ma_uint8** src_u8 = (const ma_uint8**)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
static MA_INLINE void ma_pcm_interleave_u8__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_uint8* dst_u8 = (ma_uint8*)dst;
|
|
const ma_uint8** src_u8 = (const ma_uint8**)src;
|
|
|
|
if (channels == 1) {
|
|
ma_copy_memory_64(dst, src[0], frameCount * sizeof(ma_uint8));
|
|
} else if (channels == 2) {
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
dst_u8[iFrame*2 + 0] = src_u8[0][iFrame];
|
|
dst_u8[iFrame*2 + 1] = src_u8[1][iFrame];
|
|
}
|
|
} else {
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_interleave_u8(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_interleave_u8__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_interleave_u8__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_u8__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_uint8** dst_u8 = (ma_uint8**)dst;
|
|
const ma_uint8* src_u8 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_u8__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_deinterleave_u8(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_deinterleave_u8__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_u8 = (ma_uint8*)dst;
|
|
const ma_int16* src_s16 = (const ma_int16*)src;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int16 x = src_s16[i];
|
|
x = (ma_int16)(x >> 8);
|
|
x = (ma_int16)(x + 128);
|
|
dst_u8[i] = (ma_uint8)x;
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int16 x = src_s16[i];
|
|
|
|
ma_int32 dither = ma_dither_s32(ditherMode, -0x80, 0x7F);
|
|
if ((x + dither) <= 0x7FFF) {
|
|
x = (ma_int16)(x + dither);
|
|
} else {
|
|
x = 0x7FFF;
|
|
}
|
|
|
|
x = (ma_int16)(x >> 8);
|
|
x = (ma_int16)(x + 128);
|
|
dst_u8[i] = (ma_uint8)x;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s16_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s16_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s16_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s16_to_u8__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s16_to_u8__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s16_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_pcm_s16_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
(void)ditherMode;
|
|
ma_copy_memory_64(dst, src, count * sizeof(ma_int16));
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_s24 = (ma_uint8*)dst;
|
|
const ma_int16* src_s16 = (const ma_int16*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
dst_s24[i*3+0] = 0;
|
|
dst_s24[i*3+1] = (ma_uint8)(src_s16[i] & 0xFF);
|
|
dst_s24[i*3+2] = (ma_uint8)(src_s16[i] >> 8);
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s16_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s16_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s16_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s16_to_s24__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s16_to_s24__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s16_to_s24__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s16_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int32* dst_s32 = (ma_int32*)dst;
|
|
const ma_int16* src_s16 = (const ma_int16*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
dst_s32[i] = (ma_int32)src_s16[i] << 16;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s16_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s16_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s16_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s16_to_s32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s16_to_s32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s16_to_s32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s16_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
float* dst_f32 = (float*)dst;
|
|
const ma_int16* src_s16 = (const ma_int16*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
float x = (float)src_s16[i];
|
|
|
|
#if 0
|
|
x = x + 32768.0f;
|
|
x = x * 0.00003051804379339284f;
|
|
x = x - 1;
|
|
#else
|
|
x = x * 0.000030517578125f;
|
|
#endif
|
|
|
|
dst_f32[i] = x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s16_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s16_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s16_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s16_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s16_to_f32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s16_to_f32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s16_to_f32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s16_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_interleave_s16__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_int16* dst_s16 = (ma_int16*)dst;
|
|
const ma_int16** src_s16 = (const ma_int16**)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_interleave_s16__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_interleave_s16(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_interleave_s16__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_interleave_s16__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_s16__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_int16** dst_s16 = (ma_int16**)dst;
|
|
const ma_int16* src_s16 = (const ma_int16*)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_s16__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_deinterleave_s16(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_deinterleave_s16__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_u8 = (ma_uint8*)dst;
|
|
const ma_uint8* src_s24 = (const ma_uint8*)src;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
dst_u8[i] = (ma_uint8)((ma_int8)src_s24[i*3 + 2] + 128);
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);
|
|
|
|
ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
|
|
if ((ma_int64)x + dither <= 0x7FFFFFFF) {
|
|
x = x + dither;
|
|
} else {
|
|
x = 0x7FFFFFFF;
|
|
}
|
|
|
|
x = x >> 24;
|
|
x = x + 128;
|
|
dst_u8[i] = (ma_uint8)x;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s24_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s24_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s24_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s24_to_u8__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s24_to_u8__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s24_to_u8__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s24_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int16* dst_s16 = (ma_int16*)dst;
|
|
const ma_uint8* src_s24 = (const ma_uint8*)src;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_uint16 dst_lo = ((ma_uint16)src_s24[i*3 + 1]);
|
|
ma_uint16 dst_hi = (ma_uint16)((ma_uint16)src_s24[i*3 + 2] << 8);
|
|
dst_s16[i] = (ma_int16)(dst_lo | dst_hi);
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);
|
|
|
|
ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
|
|
if ((ma_int64)x + dither <= 0x7FFFFFFF) {
|
|
x = x + dither;
|
|
} else {
|
|
x = 0x7FFFFFFF;
|
|
}
|
|
|
|
x = x >> 16;
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s24_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s24_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s24_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s24_to_s16__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s24_to_s16__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s24_to_s16__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s24_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_pcm_s24_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
(void)ditherMode;
|
|
|
|
ma_copy_memory_64(dst, src, count * 3);
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int32* dst_s32 = (ma_int32*)dst;
|
|
const ma_uint8* src_s24 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
dst_s32[i] = (ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24);
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s24_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s24_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s24_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s24_to_s32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s24_to_s32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s24_to_s32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s24_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
float* dst_f32 = (float*)dst;
|
|
const ma_uint8* src_s24 = (const ma_uint8*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
float x = (float)(((ma_int32)(((ma_uint32)(src_s24[i*3+0]) << 8) | ((ma_uint32)(src_s24[i*3+1]) << 16) | ((ma_uint32)(src_s24[i*3+2])) << 24)) >> 8);
|
|
|
|
#if 0
|
|
x = x + 8388608.0f;
|
|
x = x * 0.00000011920929665621f;
|
|
x = x - 1;
|
|
#else
|
|
x = x * 0.00000011920928955078125f;
|
|
#endif
|
|
|
|
dst_f32[i] = x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s24_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s24_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s24_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s24_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s24_to_f32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s24_to_f32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s24_to_f32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s24_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_interleave_s24__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_uint8* dst8 = (ma_uint8*)dst;
|
|
const ma_uint8** src8 = (const ma_uint8**)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst8[iFrame*3*channels + iChannel*3 + 0] = src8[iChannel][iFrame*3 + 0];
|
|
dst8[iFrame*3*channels + iChannel*3 + 1] = src8[iChannel][iFrame*3 + 1];
|
|
dst8[iFrame*3*channels + iChannel*3 + 2] = src8[iChannel][iFrame*3 + 2];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_interleave_s24__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_interleave_s24(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_interleave_s24__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_interleave_s24__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_s24__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_uint8** dst8 = (ma_uint8**)dst;
|
|
const ma_uint8* src8 = (const ma_uint8*)src;
|
|
|
|
ma_uint32 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel*3 + 0];
|
|
dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel*3 + 1];
|
|
dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel*3 + 2];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_s24__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_deinterleave_s24(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_deinterleave_s24__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_u8 = (ma_uint8*)dst;
|
|
const ma_int32* src_s32 = (const ma_int32*)src;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = src_s32[i];
|
|
x = x >> 24;
|
|
x = x + 128;
|
|
dst_u8[i] = (ma_uint8)x;
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = src_s32[i];
|
|
|
|
ma_int32 dither = ma_dither_s32(ditherMode, -0x800000, 0x7FFFFF);
|
|
if ((ma_int64)x + dither <= 0x7FFFFFFF) {
|
|
x = x + dither;
|
|
} else {
|
|
x = 0x7FFFFFFF;
|
|
}
|
|
|
|
x = x >> 24;
|
|
x = x + 128;
|
|
dst_u8[i] = (ma_uint8)x;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s32_to_u8__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s32_to_u8__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s32_to_u8__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s32_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int16* dst_s16 = (ma_int16*)dst;
|
|
const ma_int32* src_s32 = (const ma_int32*)src;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = src_s32[i];
|
|
x = x >> 16;
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 x = src_s32[i];
|
|
|
|
ma_int32 dither = ma_dither_s32(ditherMode, -0x8000, 0x7FFF);
|
|
if ((ma_int64)x + dither <= 0x7FFFFFFF) {
|
|
x = x + dither;
|
|
} else {
|
|
x = 0x7FFFFFFF;
|
|
}
|
|
|
|
x = x >> 16;
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s32_to_s16__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s32_to_s16__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s32_to_s16__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s32_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_s24 = (ma_uint8*)dst;
|
|
const ma_int32* src_s32 = (const ma_int32*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_uint32 x = (ma_uint32)src_s32[i];
|
|
dst_s24[i*3+0] = (ma_uint8)((x & 0x0000FF00) >> 8);
|
|
dst_s24[i*3+1] = (ma_uint8)((x & 0x00FF0000) >> 16);
|
|
dst_s24[i*3+2] = (ma_uint8)((x & 0xFF000000) >> 24);
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s32_to_s24__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s32_to_s24__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s32_to_s24__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s32_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_pcm_s32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
(void)ditherMode;
|
|
|
|
ma_copy_memory_64(dst, src, count * sizeof(ma_int32));
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_f32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
float* dst_f32 = (float*)dst;
|
|
const ma_int32* src_s32 = (const ma_int32*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
double x = src_s32[i];
|
|
|
|
#if 0
|
|
x = x + 2147483648.0;
|
|
x = x * 0.0000000004656612873077392578125;
|
|
x = x - 1;
|
|
#else
|
|
x = x / 2147483648.0;
|
|
#endif
|
|
|
|
dst_f32[i] = (float)x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_s32_to_f32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_s32_to_f32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_s32_to_f32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_s32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_s32_to_f32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_s32_to_f32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_s32_to_f32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_s32_to_f32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_interleave_s32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_int32* dst_s32 = (ma_int32*)dst;
|
|
const ma_int32** src_s32 = (const ma_int32**)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_interleave_s32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_interleave_s32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_interleave_s32__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_interleave_s32__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_s32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_int32** dst_s32 = (ma_int32**)dst;
|
|
const ma_int32* src_s32 = (const ma_int32*)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel];
|
|
}
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_deinterleave_s32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_deinterleave_s32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_deinterleave_s32__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_f32_to_u8__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint64 i;
|
|
|
|
ma_uint8* dst_u8 = (ma_uint8*)dst;
|
|
const float* src_f32 = (const float*)src;
|
|
|
|
float ditherMin = 0;
|
|
float ditherMax = 0;
|
|
if (ditherMode != ma_dither_mode_none) {
|
|
ditherMin = 1.0f / -128;
|
|
ditherMax = 1.0f / 127;
|
|
}
|
|
|
|
for (i = 0; i < count; i += 1) {
|
|
float x = src_f32[i];
|
|
x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
x = x + 1;
|
|
x = x * 127.5f;
|
|
|
|
dst_u8[i] = (ma_uint8)x;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_f32_to_u8__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_f32_to_u8__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_f32_to_u8__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_f32_to_u8(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_f32_to_u8__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_f32_to_u8__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_f32_to_u8__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_f32_to_u8__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
static MA_INLINE void ma_pcm_f32_to_s16__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint64 i;
|
|
|
|
ma_int16* dst_s16 = (ma_int16*)dst;
|
|
const float* src_f32 = (const float*)src;
|
|
|
|
float ditherMin = 0;
|
|
float ditherMax = 0;
|
|
if (ditherMode != ma_dither_mode_none) {
|
|
ditherMin = 1.0f / -32768;
|
|
ditherMax = 1.0f / 32767;
|
|
}
|
|
|
|
for (i = 0; i < count; i += 1) {
|
|
float x = src_f32[i];
|
|
x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
|
|
#if 0
|
|
x = x + 1;
|
|
x = x * 32767.5f;
|
|
x = x - 32768.0f;
|
|
#else
|
|
x = x * 32767.0f;
|
|
#endif
|
|
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
#else
|
|
static MA_INLINE void ma_pcm_f32_to_s16__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 i4;
|
|
ma_uint64 count4;
|
|
|
|
ma_int16* dst_s16 = (ma_int16*)dst;
|
|
const float* src_f32 = (const float*)src;
|
|
|
|
float ditherMin = 0;
|
|
float ditherMax = 0;
|
|
if (ditherMode != ma_dither_mode_none) {
|
|
ditherMin = 1.0f / -32768;
|
|
ditherMax = 1.0f / 32767;
|
|
}
|
|
|
|
i = 0;
|
|
count4 = count >> 2;
|
|
for (i4 = 0; i4 < count4; i4 += 1) {
|
|
float d0 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
float d1 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
float d2 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
float d3 = ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
|
|
float x0 = src_f32[i+0];
|
|
float x1 = src_f32[i+1];
|
|
float x2 = src_f32[i+2];
|
|
float x3 = src_f32[i+3];
|
|
|
|
x0 = x0 + d0;
|
|
x1 = x1 + d1;
|
|
x2 = x2 + d2;
|
|
x3 = x3 + d3;
|
|
|
|
x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
|
|
x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
|
|
x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
|
|
x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
|
|
|
|
x0 = x0 * 32767.0f;
|
|
x1 = x1 * 32767.0f;
|
|
x2 = x2 * 32767.0f;
|
|
x3 = x3 * 32767.0f;
|
|
|
|
dst_s16[i+0] = (ma_int16)x0;
|
|
dst_s16[i+1] = (ma_int16)x1;
|
|
dst_s16[i+2] = (ma_int16)x2;
|
|
dst_s16[i+3] = (ma_int16)x3;
|
|
|
|
i += 4;
|
|
}
|
|
|
|
for (; i < count; i += 1) {
|
|
float x = src_f32[i];
|
|
x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
x = x * 32767.0f;
|
|
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_f32_to_s16__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 i8;
|
|
ma_uint64 count8;
|
|
ma_int16* dst_s16;
|
|
const float* src_f32;
|
|
float ditherMin;
|
|
float ditherMax;
|
|
|
|
if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
|
|
ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
|
|
return;
|
|
}
|
|
|
|
dst_s16 = (ma_int16*)dst;
|
|
src_f32 = (const float*)src;
|
|
|
|
ditherMin = 0;
|
|
ditherMax = 0;
|
|
if (ditherMode != ma_dither_mode_none) {
|
|
ditherMin = 1.0f / -32768;
|
|
ditherMax = 1.0f / 32767;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
count8 = count >> 3;
|
|
for (i8 = 0; i8 < count8; i8 += 1) {
|
|
__m128 d0;
|
|
__m128 d1;
|
|
__m128 x0;
|
|
__m128 x1;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
d0 = _mm_set1_ps(0);
|
|
d1 = _mm_set1_ps(0);
|
|
} else if (ditherMode == ma_dither_mode_rectangle) {
|
|
d0 = _mm_set_ps(
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax),
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax),
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax),
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax)
|
|
);
|
|
d1 = _mm_set_ps(
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax),
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax),
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax),
|
|
ma_dither_f32_rectangle(ditherMin, ditherMax)
|
|
);
|
|
} else {
|
|
d0 = _mm_set_ps(
|
|
ma_dither_f32_triangle(ditherMin, ditherMax),
|
|
ma_dither_f32_triangle(ditherMin, ditherMax),
|
|
ma_dither_f32_triangle(ditherMin, ditherMax),
|
|
ma_dither_f32_triangle(ditherMin, ditherMax)
|
|
);
|
|
d1 = _mm_set_ps(
|
|
ma_dither_f32_triangle(ditherMin, ditherMax),
|
|
ma_dither_f32_triangle(ditherMin, ditherMax),
|
|
ma_dither_f32_triangle(ditherMin, ditherMax),
|
|
ma_dither_f32_triangle(ditherMin, ditherMax)
|
|
);
|
|
}
|
|
|
|
x0 = *((__m128*)(src_f32 + i) + 0);
|
|
x1 = *((__m128*)(src_f32 + i) + 1);
|
|
|
|
x0 = _mm_add_ps(x0, d0);
|
|
x1 = _mm_add_ps(x1, d1);
|
|
|
|
x0 = _mm_mul_ps(x0, _mm_set1_ps(32767.0f));
|
|
x1 = _mm_mul_ps(x1, _mm_set1_ps(32767.0f));
|
|
|
|
_mm_stream_si128(((__m128i*)(dst_s16 + i)), _mm_packs_epi32(_mm_cvttps_epi32(x0), _mm_cvttps_epi32(x1)));
|
|
|
|
i += 8;
|
|
}
|
|
|
|
for (; i < count; i += 1) {
|
|
float x = src_f32[i];
|
|
x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
x = x * 32767.0f;
|
|
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 i8;
|
|
ma_uint64 count8;
|
|
ma_int16* dst_s16;
|
|
const float* src_f32;
|
|
float ditherMin;
|
|
float ditherMax;
|
|
|
|
if (!ma_has_neon()) {
|
|
ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
|
|
return;
|
|
}
|
|
|
|
if ((((ma_uintptr)dst & 15) != 0) || (((ma_uintptr)src & 15) != 0)) {
|
|
ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
|
|
return;
|
|
}
|
|
|
|
dst_s16 = (ma_int16*)dst;
|
|
src_f32 = (const float*)src;
|
|
|
|
ditherMin = 0;
|
|
ditherMax = 0;
|
|
if (ditherMode != ma_dither_mode_none) {
|
|
ditherMin = 1.0f / -32768;
|
|
ditherMax = 1.0f / 32767;
|
|
}
|
|
|
|
i = 0;
|
|
|
|
count8 = count >> 3;
|
|
for (i8 = 0; i8 < count8; i8 += 1) {
|
|
float32x4_t d0;
|
|
float32x4_t d1;
|
|
float32x4_t x0;
|
|
float32x4_t x1;
|
|
int32x4_t i0;
|
|
int32x4_t i1;
|
|
|
|
if (ditherMode == ma_dither_mode_none) {
|
|
d0 = vmovq_n_f32(0);
|
|
d1 = vmovq_n_f32(0);
|
|
} else if (ditherMode == ma_dither_mode_rectangle) {
|
|
float d0v[4];
|
|
float d1v[4];
|
|
|
|
d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d0 = vld1q_f32(d0v);
|
|
|
|
d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d1v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax);
|
|
d1 = vld1q_f32(d1v);
|
|
} else {
|
|
float d0v[4];
|
|
float d1v[4];
|
|
|
|
d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d0 = vld1q_f32(d0v);
|
|
|
|
d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d1v[3] = ma_dither_f32_triangle(ditherMin, ditherMax);
|
|
d1 = vld1q_f32(d1v);
|
|
}
|
|
|
|
x0 = *((float32x4_t*)(src_f32 + i) + 0);
|
|
x1 = *((float32x4_t*)(src_f32 + i) + 1);
|
|
|
|
x0 = vaddq_f32(x0, d0);
|
|
x1 = vaddq_f32(x1, d1);
|
|
|
|
x0 = vmulq_n_f32(x0, 32767.0f);
|
|
x1 = vmulq_n_f32(x1, 32767.0f);
|
|
|
|
i0 = vcvtq_s32_f32(x0);
|
|
i1 = vcvtq_s32_f32(x1);
|
|
*((int16x8_t*)(dst_s16 + i)) = vcombine_s16(vqmovn_s32(i0), vqmovn_s32(i1));
|
|
|
|
i += 8;
|
|
}
|
|
|
|
for (; i < count; i += 1) {
|
|
float x = src_f32[i];
|
|
x = x + ma_dither_f32(ditherMode, ditherMin, ditherMax);
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
x = x * 32767.0f;
|
|
|
|
dst_s16[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
MA_API void ma_pcm_f32_to_s16(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_f32_to_s16__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_f32_to_s16__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_f32_to_s16__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_f32_to_s16__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_f32_to_s24__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_uint8* dst_s24 = (ma_uint8*)dst;
|
|
const float* src_f32 = (const float*)src;
|
|
|
|
ma_uint64 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
ma_int32 r;
|
|
float x = src_f32[i];
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
|
|
#if 0
|
|
x = x + 1;
|
|
x = x * 8388607.5f;
|
|
x = x - 8388608.0f;
|
|
#else
|
|
x = x * 8388607.0f;
|
|
#endif
|
|
|
|
r = (ma_int32)x;
|
|
dst_s24[(i*3)+0] = (ma_uint8)((r & 0x0000FF) >> 0);
|
|
dst_s24[(i*3)+1] = (ma_uint8)((r & 0x00FF00) >> 8);
|
|
dst_s24[(i*3)+2] = (ma_uint8)((r & 0xFF0000) >> 16);
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_f32_to_s24__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_f32_to_s24__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_f32_to_s24__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_f32_to_s24(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_f32_to_s24__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_f32_to_s24__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_f32_to_s24__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_f32_to_s24__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_f32_to_s32__reference(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_int32* dst_s32 = (ma_int32*)dst;
|
|
const float* src_f32 = (const float*)src;
|
|
|
|
ma_uint32 i;
|
|
for (i = 0; i < count; i += 1) {
|
|
double x = src_f32[i];
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
|
|
#if 0
|
|
x = x + 1;
|
|
x = x * 2147483647.5;
|
|
x = x - 2147483648.0;
|
|
#else
|
|
x = x * 2147483647.0;
|
|
#endif
|
|
|
|
dst_s32[i] = (ma_int32)x;
|
|
}
|
|
|
|
(void)ditherMode;
|
|
}
|
|
|
|
static MA_INLINE void ma_pcm_f32_to_s32__optimized(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
|
|
}
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_pcm_f32_to_s32__sse2(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
#if defined(MA_SUPPORT_NEON)
|
|
static MA_INLINE void ma_pcm_f32_to_s32__neon(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
|
|
MA_API void ma_pcm_f32_to_s32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_f32_to_s32__reference(dst, src, count, ditherMode);
|
|
#else
|
|
# if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_pcm_f32_to_s32__sse2(dst, src, count, ditherMode);
|
|
} else
|
|
#elif defined(MA_SUPPORT_NEON)
|
|
if (ma_has_neon()) {
|
|
ma_pcm_f32_to_s32__neon(dst, src, count, ditherMode);
|
|
} else
|
|
#endif
|
|
{
|
|
ma_pcm_f32_to_s32__optimized(dst, src, count, ditherMode);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_pcm_f32_to_f32(void* dst, const void* src, ma_uint64 count, ma_dither_mode ditherMode)
|
|
{
|
|
(void)ditherMode;
|
|
|
|
ma_copy_memory_64(dst, src, count * sizeof(float));
|
|
}
|
|
|
|
static void ma_pcm_interleave_f32__reference(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
float* dst_f32 = (float*)dst;
|
|
const float** src_f32 = (const float**)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_pcm_interleave_f32__optimized(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_interleave_f32(void* dst, const void** src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_interleave_f32__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_interleave_f32__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
static void ma_pcm_deinterleave_f32__reference(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
float** dst_f32 = (float**)dst;
|
|
const float* src_f32 = (const float*)src;
|
|
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_pcm_deinterleave_f32__optimized(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
|
|
}
|
|
|
|
MA_API void ma_pcm_deinterleave_f32(void** dst, const void* src, ma_uint64 frameCount, ma_uint32 channels)
|
|
{
|
|
#ifdef MA_USE_REFERENCE_CONVERSION_APIS
|
|
ma_pcm_deinterleave_f32__reference(dst, src, frameCount, channels);
|
|
#else
|
|
ma_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels);
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_pcm_convert(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 sampleCount, ma_dither_mode ditherMode)
|
|
{
|
|
if (formatOut == formatIn) {
|
|
ma_copy_memory_64(pOut, pIn, sampleCount * ma_get_bytes_per_sample(formatOut));
|
|
return;
|
|
}
|
|
|
|
switch (formatIn)
|
|
{
|
|
case ma_format_u8:
|
|
{
|
|
switch (formatOut)
|
|
{
|
|
case ma_format_s16: ma_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s24: ma_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s32: ma_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_f32: ma_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return;
|
|
default: break;
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
switch (formatOut)
|
|
{
|
|
case ma_format_u8: ma_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s24: ma_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s32: ma_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_f32: ma_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return;
|
|
default: break;
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
switch (formatOut)
|
|
{
|
|
case ma_format_u8: ma_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s16: ma_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s32: ma_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_f32: ma_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return;
|
|
default: break;
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
switch (formatOut)
|
|
{
|
|
case ma_format_u8: ma_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s16: ma_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s24: ma_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_f32: ma_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return;
|
|
default: break;
|
|
}
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
switch (formatOut)
|
|
{
|
|
case ma_format_u8: ma_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s16: ma_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s24: ma_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return;
|
|
case ma_format_s32: ma_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return;
|
|
default: break;
|
|
}
|
|
} break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_convert_pcm_frames_format(void* pOut, ma_format formatOut, const void* pIn, ma_format formatIn, ma_uint64 frameCount, ma_uint32 channels, ma_dither_mode ditherMode)
|
|
{
|
|
ma_pcm_convert(pOut, formatOut, pIn, formatIn, frameCount * channels, ditherMode);
|
|
}
|
|
|
|
MA_API void ma_deinterleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void* pInterleavedPCMFrames, void** ppDeinterleavedPCMFrames)
|
|
{
|
|
if (pInterleavedPCMFrames == NULL || ppDeinterleavedPCMFrames == NULL) {
|
|
return;
|
|
}
|
|
|
|
switch (format) {
|
|
case ma_format_s16:
|
|
{
|
|
const ma_int16* pSrcS16 = (const ma_int16*)pInterleavedPCMFrames;
|
|
ma_uint64 iPCMFrame;
|
|
for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
ma_int16* pDstS16 = (ma_int16*)ppDeinterleavedPCMFrames[iChannel];
|
|
pDstS16[iPCMFrame] = pSrcS16[iPCMFrame*channels+iChannel];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
const float* pSrcF32 = (const float*)pInterleavedPCMFrames;
|
|
ma_uint64 iPCMFrame;
|
|
for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
float* pDstF32 = (float*)ppDeinterleavedPCMFrames[iChannel];
|
|
pDstF32[iPCMFrame] = pSrcF32[iPCMFrame*channels+iChannel];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
|
|
ma_uint64 iPCMFrame;
|
|
for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
void* pDst = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
|
|
const void* pSrc = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
|
|
memcpy(pDst, pSrc, sampleSizeInBytes);
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_interleave_pcm_frames(ma_format format, ma_uint32 channels, ma_uint64 frameCount, const void** ppDeinterleavedPCMFrames, void* pInterleavedPCMFrames)
|
|
{
|
|
switch (format)
|
|
{
|
|
case ma_format_s16:
|
|
{
|
|
ma_int16* pDstS16 = (ma_int16*)pInterleavedPCMFrames;
|
|
ma_uint64 iPCMFrame;
|
|
for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
const ma_int16* pSrcS16 = (const ma_int16*)ppDeinterleavedPCMFrames[iChannel];
|
|
pDstS16[iPCMFrame*channels+iChannel] = pSrcS16[iPCMFrame];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
float* pDstF32 = (float*)pInterleavedPCMFrames;
|
|
ma_uint64 iPCMFrame;
|
|
for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
const float* pSrcF32 = (const float*)ppDeinterleavedPCMFrames[iChannel];
|
|
pDstF32[iPCMFrame*channels+iChannel] = pSrcF32[iPCMFrame];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
ma_uint32 sampleSizeInBytes = ma_get_bytes_per_sample(format);
|
|
ma_uint64 iPCMFrame;
|
|
for (iPCMFrame = 0; iPCMFrame < frameCount; ++iPCMFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
void* pDst = ma_offset_ptr(pInterleavedPCMFrames, (iPCMFrame*channels+iChannel)*sampleSizeInBytes);
|
|
const void* pSrc = ma_offset_ptr(ppDeinterleavedPCMFrames[iChannel], iPCMFrame*sampleSizeInBytes);
|
|
memcpy(pDst, pSrc, sampleSizeInBytes);
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
#ifndef MA_BIQUAD_FIXED_POINT_SHIFT
|
|
#define MA_BIQUAD_FIXED_POINT_SHIFT 14
|
|
#endif
|
|
|
|
static ma_int32 ma_biquad_float_to_fp(double x)
|
|
{
|
|
return (ma_int32)(x * (1 << MA_BIQUAD_FIXED_POINT_SHIFT));
|
|
}
|
|
|
|
MA_API ma_biquad_config ma_biquad_config_init(ma_format format, ma_uint32 channels, double b0, double b1, double b2, double a0, double a1, double a2)
|
|
{
|
|
ma_biquad_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.b0 = b0;
|
|
config.b1 = b1;
|
|
config.b2 = b2;
|
|
config.a0 = a0;
|
|
config.a1 = a1;
|
|
config.a2 = a2;
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t r1Offset;
|
|
size_t r2Offset;
|
|
} ma_biquad_heap_layout;
|
|
|
|
static ma_result ma_biquad_get_heap_layout(const ma_biquad_config* pConfig, ma_biquad_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
|
|
|
|
pHeapLayout->r2Offset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_get_heap_size(const ma_biquad_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_init_preallocated(const ma_biquad_config* pConfig, void* pHeap, ma_biquad* pBQ)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_heap_layout heapLayout;
|
|
|
|
if (pBQ == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pBQ);
|
|
|
|
result = ma_biquad_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pBQ->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pBQ->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
|
|
pBQ->pR2 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r2Offset);
|
|
|
|
return ma_biquad_reinit(pConfig, pBQ);
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_init(const ma_biquad_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad* pBQ)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_biquad_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_biquad_init_preallocated(pConfig, pHeap, pBQ);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pBQ->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_biquad_uninit(ma_biquad* pBQ, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pBQ == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pBQ->_ownsHeap) {
|
|
ma_free(pBQ->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_reinit(const ma_biquad_config* pConfig, ma_biquad* pBQ)
|
|
{
|
|
if (pBQ == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->a0 == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pBQ->format != ma_format_unknown && pBQ->format != pConfig->format) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pBQ->channels != 0 && pBQ->channels != pConfig->channels) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pBQ->format = pConfig->format;
|
|
pBQ->channels = pConfig->channels;
|
|
|
|
if (pConfig->format == ma_format_f32) {
|
|
pBQ->b0.f32 = (float)(pConfig->b0 / pConfig->a0);
|
|
pBQ->b1.f32 = (float)(pConfig->b1 / pConfig->a0);
|
|
pBQ->b2.f32 = (float)(pConfig->b2 / pConfig->a0);
|
|
pBQ->a1.f32 = (float)(pConfig->a1 / pConfig->a0);
|
|
pBQ->a2.f32 = (float)(pConfig->a2 / pConfig->a0);
|
|
} else {
|
|
pBQ->b0.s32 = ma_biquad_float_to_fp(pConfig->b0 / pConfig->a0);
|
|
pBQ->b1.s32 = ma_biquad_float_to_fp(pConfig->b1 / pConfig->a0);
|
|
pBQ->b2.s32 = ma_biquad_float_to_fp(pConfig->b2 / pConfig->a0);
|
|
pBQ->a1.s32 = ma_biquad_float_to_fp(pConfig->a1 / pConfig->a0);
|
|
pBQ->a2.s32 = ma_biquad_float_to_fp(pConfig->a2 / pConfig->a0);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_clear_cache(ma_biquad* pBQ)
|
|
{
|
|
if (pBQ == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pBQ->format == ma_format_f32) {
|
|
pBQ->pR1->f32 = 0;
|
|
pBQ->pR2->f32 = 0;
|
|
} else {
|
|
pBQ->pR1->s32 = 0;
|
|
pBQ->pR2->s32 = 0;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(ma_biquad* pBQ, float* pY, const float* pX)
|
|
{
|
|
ma_uint32 c;
|
|
const ma_uint32 channels = pBQ->channels;
|
|
const float b0 = pBQ->b0.f32;
|
|
const float b1 = pBQ->b1.f32;
|
|
const float b2 = pBQ->b2.f32;
|
|
const float a1 = pBQ->a1.f32;
|
|
const float a2 = pBQ->a2.f32;
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
float r1 = pBQ->pR1[c].f32;
|
|
float r2 = pBQ->pR2[c].f32;
|
|
float x = pX[c];
|
|
float y;
|
|
|
|
y = b0*x + r1;
|
|
r1 = b1*x - a1*y + r2;
|
|
r2 = b2*x - a2*y;
|
|
|
|
pY[c] = y;
|
|
pBQ->pR1[c].f32 = r1;
|
|
pBQ->pR2[c].f32 = r2;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_biquad_process_pcm_frame_f32(ma_biquad* pBQ, float* pY, const float* pX)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
|
|
}
|
|
|
|
static MA_INLINE void ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
|
|
{
|
|
ma_uint32 c;
|
|
const ma_uint32 channels = pBQ->channels;
|
|
const ma_int32 b0 = pBQ->b0.s32;
|
|
const ma_int32 b1 = pBQ->b1.s32;
|
|
const ma_int32 b2 = pBQ->b2.s32;
|
|
const ma_int32 a1 = pBQ->a1.s32;
|
|
const ma_int32 a2 = pBQ->a2.s32;
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
ma_int32 r1 = pBQ->pR1[c].s32;
|
|
ma_int32 r2 = pBQ->pR2[c].s32;
|
|
ma_int32 x = pX[c];
|
|
ma_int32 y;
|
|
|
|
y = (b0*x + r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
|
|
r1 = (b1*x - a1*y + r2);
|
|
r2 = (b2*x - a2*y);
|
|
|
|
pY[c] = (ma_int16)ma_clamp(y, -32768, 32767);
|
|
pBQ->pR1[c].s32 = r1;
|
|
pBQ->pR2[c].s32 = r2;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_biquad_process_pcm_frame_s16(ma_biquad* pBQ, ma_int16* pY, const ma_int16* pX)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_process_pcm_frames(ma_biquad* pBQ, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint32 n;
|
|
|
|
if (pBQ == NULL || pFramesOut == NULL || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pBQ->format == ma_format_f32) {
|
|
float* pY = ( float*)pFramesOut;
|
|
const float* pX = (const float*)pFramesIn;
|
|
|
|
for (n = 0; n < frameCount; n += 1) {
|
|
ma_biquad_process_pcm_frame_f32__direct_form_2_transposed(pBQ, pY, pX);
|
|
pY += pBQ->channels;
|
|
pX += pBQ->channels;
|
|
}
|
|
} else if (pBQ->format == ma_format_s16) {
|
|
ma_int16* pY = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pX = (const ma_int16*)pFramesIn;
|
|
|
|
for (n = 0; n < frameCount; n += 1) {
|
|
ma_biquad_process_pcm_frame_s16__direct_form_2_transposed(pBQ, pY, pX);
|
|
pY += pBQ->channels;
|
|
pX += pBQ->channels;
|
|
}
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_biquad_get_latency(const ma_biquad* pBQ)
|
|
{
|
|
if (pBQ == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return 2;
|
|
}
|
|
|
|
MA_API ma_lpf1_config ma_lpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
|
|
{
|
|
ma_lpf1_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.q = 0.5;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_lpf2_config ma_lpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
|
|
{
|
|
ma_lpf2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.q = q;
|
|
|
|
if (config.q == 0) {
|
|
config.q = 0.707107;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t r1Offset;
|
|
} ma_lpf1_heap_layout;
|
|
|
|
static ma_result ma_lpf1_get_heap_layout(const ma_lpf1_config* pConfig, ma_lpf1_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf1_get_heap_size(const ma_lpf1_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_lpf1_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf1_init_preallocated(const ma_lpf1_config* pConfig, void* pHeap, ma_lpf1* pLPF)
|
|
{
|
|
ma_result result;
|
|
ma_lpf1_heap_layout heapLayout;
|
|
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pLPF);
|
|
|
|
result = ma_lpf1_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pLPF->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
|
|
|
|
return ma_lpf1_reinit(pConfig, pLPF);
|
|
}
|
|
|
|
MA_API ma_result ma_lpf1_init(const ma_lpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf1* pLPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_lpf1_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_lpf1_init_preallocated(pConfig, pHeap, pLPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pLPF->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_lpf1_uninit(ma_lpf1* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pLPF->_ownsHeap) {
|
|
ma_free(pLPF->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_lpf1_reinit(const ma_lpf1_config* pConfig, ma_lpf1* pLPF)
|
|
{
|
|
double a;
|
|
|
|
if (pLPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pLPF->format = pConfig->format;
|
|
pLPF->channels = pConfig->channels;
|
|
|
|
a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
|
|
if (pConfig->format == ma_format_f32) {
|
|
pLPF->a.f32 = (float)a;
|
|
} else {
|
|
pLPF->a.s32 = ma_biquad_float_to_fp(a);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf1_clear_cache(ma_lpf1* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pLPF->format == ma_format_f32) {
|
|
pLPF->a.f32 = 0;
|
|
} else {
|
|
pLPF->a.s32 = 0;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_lpf1_process_pcm_frame_f32(ma_lpf1* pLPF, float* pY, const float* pX)
|
|
{
|
|
ma_uint32 c;
|
|
const ma_uint32 channels = pLPF->channels;
|
|
const float a = pLPF->a.f32;
|
|
const float b = 1 - a;
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
float r1 = pLPF->pR1[c].f32;
|
|
float x = pX[c];
|
|
float y;
|
|
|
|
y = b*x + a*r1;
|
|
|
|
pY[c] = y;
|
|
pLPF->pR1[c].f32 = y;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_lpf1_process_pcm_frame_s16(ma_lpf1* pLPF, ma_int16* pY, const ma_int16* pX)
|
|
{
|
|
ma_uint32 c;
|
|
const ma_uint32 channels = pLPF->channels;
|
|
const ma_int32 a = pLPF->a.s32;
|
|
const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
ma_int32 r1 = pLPF->pR1[c].s32;
|
|
ma_int32 x = pX[c];
|
|
ma_int32 y;
|
|
|
|
y = (b*x + a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
|
|
|
|
pY[c] = (ma_int16)y;
|
|
pLPF->pR1[c].s32 = (ma_int32)y;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_lpf1_process_pcm_frames(ma_lpf1* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint32 n;
|
|
|
|
if (pLPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pLPF->format == ma_format_f32) {
|
|
float* pY = ( float*)pFramesOut;
|
|
const float* pX = (const float*)pFramesIn;
|
|
|
|
for (n = 0; n < frameCount; n += 1) {
|
|
ma_lpf1_process_pcm_frame_f32(pLPF, pY, pX);
|
|
pY += pLPF->channels;
|
|
pX += pLPF->channels;
|
|
}
|
|
} else if (pLPF->format == ma_format_s16) {
|
|
ma_int16* pY = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pX = (const ma_int16*)pFramesIn;
|
|
|
|
for (n = 0; n < frameCount; n += 1) {
|
|
ma_lpf1_process_pcm_frame_s16(pLPF, pY, pX);
|
|
pY += pLPF->channels;
|
|
pX += pLPF->channels;
|
|
}
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_lpf1_get_latency(const ma_lpf1* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_lpf2__get_biquad_config(const ma_lpf2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double q;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double a;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
q = pConfig->q;
|
|
w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
a = s / (2*q);
|
|
|
|
bqConfig.b0 = (1 - c) / 2;
|
|
bqConfig.b1 = 1 - c;
|
|
bqConfig.b2 = (1 - c) / 2;
|
|
bqConfig.a0 = 1 + a;
|
|
bqConfig.a1 = -2 * c;
|
|
bqConfig.a2 = 1 - a;
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf2_get_heap_size(const ma_lpf2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_lpf2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_lpf2_init_preallocated(const ma_lpf2_config* pConfig, void* pHeap, ma_lpf2* pLPF)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pLPF);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_lpf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pLPF->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf2_init(const ma_lpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf2* pLPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_lpf2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_lpf2_init_preallocated(pConfig, pHeap, pLPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pLPF->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_lpf2_uninit(ma_lpf2* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pLPF->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_lpf2_reinit(const ma_lpf2_config* pConfig, ma_lpf2* pLPF)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pLPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_lpf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pLPF->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf2_clear_cache(ma_lpf2* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_biquad_clear_cache(&pLPF->bq);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_lpf2_process_pcm_frame_s16(ma_lpf2* pLPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pLPF->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_lpf2_process_pcm_frame_f32(ma_lpf2* pLPF, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pLPF->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_lpf2_process_pcm_frames(ma_lpf2* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pLPF->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_lpf2_get_latency(const ma_lpf2* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pLPF->bq);
|
|
}
|
|
|
|
MA_API ma_lpf_config ma_lpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
|
|
{
|
|
ma_lpf_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.order = ma_min(order, MA_MAX_FILTER_ORDER);
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t lpf1Offset;
|
|
size_t lpf2Offset;
|
|
} ma_lpf_heap_layout;
|
|
|
|
static void ma_lpf_calculate_sub_lpf_counts(ma_uint32 order, ma_uint32* pLPF1Count, ma_uint32* pLPF2Count)
|
|
{
|
|
MA_ASSERT(pLPF1Count != NULL);
|
|
MA_ASSERT(pLPF2Count != NULL);
|
|
|
|
*pLPF1Count = order % 2;
|
|
*pLPF2Count = order / 2;
|
|
}
|
|
|
|
static ma_result ma_lpf_get_heap_layout(const ma_lpf_config* pConfig, ma_lpf_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 lpf1Count;
|
|
ma_uint32 lpf2Count;
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->order > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->lpf1Offset = pHeapLayout->sizeInBytes;
|
|
for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
|
|
size_t lpf1HeapSizeInBytes;
|
|
ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
|
|
|
|
result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += sizeof(ma_lpf1) + lpf1HeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->lpf2Offset = pHeapLayout->sizeInBytes;
|
|
for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
|
|
size_t lpf2HeapSizeInBytes;
|
|
ma_lpf2_config lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107);
|
|
|
|
result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += sizeof(ma_lpf2) + lpf2HeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_lpf_reinit__internal(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF, ma_bool32 isNew)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 lpf1Count;
|
|
ma_uint32 lpf2Count;
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
ma_lpf_heap_layout heapLayout;
|
|
|
|
if (pLPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pLPF->format != ma_format_unknown && pLPF->format != pConfig->format) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pLPF->channels != 0 && pLPF->channels != pConfig->channels) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pConfig->order > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_lpf_calculate_sub_lpf_counts(pConfig->order, &lpf1Count, &lpf2Count);
|
|
|
|
if (!isNew) {
|
|
if (pLPF->lpf1Count != lpf1Count || pLPF->lpf2Count != lpf2Count) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
if (isNew) {
|
|
result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pLPF->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pLPF->pLPF1 = (ma_lpf1*)ma_offset_ptr(pHeap, heapLayout.lpf1Offset);
|
|
pLPF->pLPF2 = (ma_lpf2*)ma_offset_ptr(pHeap, heapLayout.lpf2Offset);
|
|
} else {
|
|
MA_ZERO_OBJECT(&heapLayout);
|
|
}
|
|
|
|
for (ilpf1 = 0; ilpf1 < lpf1Count; ilpf1 += 1) {
|
|
ma_lpf1_config lpf1Config = ma_lpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
|
|
|
|
if (isNew) {
|
|
size_t lpf1HeapSizeInBytes;
|
|
|
|
result = ma_lpf1_get_heap_size(&lpf1Config, &lpf1HeapSizeInBytes);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_lpf1_init_preallocated(&lpf1Config, ma_offset_ptr(pHeap, heapLayout.lpf1Offset + (sizeof(ma_lpf1) * lpf1Count) + (ilpf1 * lpf1HeapSizeInBytes)), &pLPF->pLPF1[ilpf1]);
|
|
}
|
|
} else {
|
|
result = ma_lpf1_reinit(&lpf1Config, &pLPF->pLPF1[ilpf1]);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_uint32 jlpf1;
|
|
|
|
for (jlpf1 = 0; jlpf1 < ilpf1; jlpf1 += 1) {
|
|
ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (ilpf2 = 0; ilpf2 < lpf2Count; ilpf2 += 1) {
|
|
ma_lpf2_config lpf2Config;
|
|
double q;
|
|
double a;
|
|
|
|
if (lpf1Count == 1) {
|
|
a = (1 + ilpf2*1) * (MA_PI_D/(pConfig->order*1));
|
|
} else {
|
|
a = (1 + ilpf2*2) * (MA_PI_D/(pConfig->order*2));
|
|
}
|
|
q = 1 / (2*ma_cosd(a));
|
|
|
|
lpf2Config = ma_lpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
|
|
|
|
if (isNew) {
|
|
size_t lpf2HeapSizeInBytes;
|
|
|
|
result = ma_lpf2_get_heap_size(&lpf2Config, &lpf2HeapSizeInBytes);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_lpf2_init_preallocated(&lpf2Config, ma_offset_ptr(pHeap, heapLayout.lpf2Offset + (sizeof(ma_lpf2) * lpf2Count) + (ilpf2 * lpf2HeapSizeInBytes)), &pLPF->pLPF2[ilpf2]);
|
|
}
|
|
} else {
|
|
result = ma_lpf2_reinit(&lpf2Config, &pLPF->pLPF2[ilpf2]);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_uint32 jlpf1;
|
|
ma_uint32 jlpf2;
|
|
|
|
for (jlpf1 = 0; jlpf1 < lpf1Count; jlpf1 += 1) {
|
|
ma_lpf1_uninit(&pLPF->pLPF1[jlpf1], NULL);
|
|
}
|
|
|
|
for (jlpf2 = 0; jlpf2 < ilpf2; jlpf2 += 1) {
|
|
ma_lpf2_uninit(&pLPF->pLPF2[jlpf2], NULL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
pLPF->lpf1Count = lpf1Count;
|
|
pLPF->lpf2Count = lpf2Count;
|
|
pLPF->format = pConfig->format;
|
|
pLPF->channels = pConfig->channels;
|
|
pLPF->sampleRate = pConfig->sampleRate;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_get_heap_size(const ma_lpf_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_lpf_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_lpf_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_init_preallocated(const ma_lpf_config* pConfig, void* pHeap, ma_lpf* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pLPF);
|
|
|
|
return ma_lpf_reinit__internal(pConfig, pHeap, pLPF, MA_TRUE);
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_init(const ma_lpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf* pLPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_lpf_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_lpf_init_preallocated(pConfig, pHeap, pLPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pLPF->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_lpf_uninit(ma_lpf* pLPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
|
|
if (pLPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
|
|
ma_lpf1_uninit(&pLPF->pLPF1[ilpf1], pAllocationCallbacks);
|
|
}
|
|
|
|
for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
|
|
ma_lpf2_uninit(&pLPF->pLPF2[ilpf2], pAllocationCallbacks);
|
|
}
|
|
|
|
if (pLPF->_ownsHeap) {
|
|
ma_free(pLPF->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_reinit(const ma_lpf_config* pConfig, ma_lpf* pLPF)
|
|
{
|
|
return ma_lpf_reinit__internal(pConfig, NULL, pLPF, MA_FALSE);
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_clear_cache(ma_lpf* pLPF)
|
|
{
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
|
|
ma_lpf1_clear_cache(&pLPF->pLPF1[ilpf1]);
|
|
}
|
|
|
|
for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
|
|
ma_lpf2_clear_cache(&pLPF->pLPF2[ilpf2]);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_lpf_process_pcm_frame_f32(ma_lpf* pLPF, float* pY, const void* pX)
|
|
{
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
|
|
MA_ASSERT(pLPF->format == ma_format_f32);
|
|
|
|
MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
|
|
|
|
for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
|
|
ma_lpf1_process_pcm_frame_f32(&pLPF->pLPF1[ilpf1], pY, pY);
|
|
}
|
|
|
|
for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
|
|
ma_lpf2_process_pcm_frame_f32(&pLPF->pLPF2[ilpf2], pY, pY);
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_lpf_process_pcm_frame_s16(ma_lpf* pLPF, ma_int16* pY, const ma_int16* pX)
|
|
{
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
|
|
MA_ASSERT(pLPF->format == ma_format_s16);
|
|
|
|
MA_MOVE_MEMORY(pY, pX, ma_get_bytes_per_frame(pLPF->format, pLPF->channels));
|
|
|
|
for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
|
|
ma_lpf1_process_pcm_frame_s16(&pLPF->pLPF1[ilpf1], pY, pY);
|
|
}
|
|
|
|
for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
|
|
ma_lpf2_process_pcm_frame_s16(&pLPF->pLPF2[ilpf2], pY, pY);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_process_pcm_frames(ma_lpf* pLPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 ilpf1;
|
|
ma_uint32 ilpf2;
|
|
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut == pFramesIn) {
|
|
for (ilpf1 = 0; ilpf1 < pLPF->lpf1Count; ilpf1 += 1) {
|
|
result = ma_lpf1_process_pcm_frames(&pLPF->pLPF1[ilpf1], pFramesOut, pFramesOut, frameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (ilpf2 = 0; ilpf2 < pLPF->lpf2Count; ilpf2 += 1) {
|
|
result = ma_lpf2_process_pcm_frames(&pLPF->pLPF2[ilpf2], pFramesOut, pFramesOut, frameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesOut != pFramesIn) {
|
|
ma_uint32 iFrame;
|
|
|
|
if (pLPF->format == ma_format_f32) {
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_lpf_process_pcm_frame_f32(pLPF, pFramesOutF32, pFramesInF32);
|
|
pFramesOutF32 += pLPF->channels;
|
|
pFramesInF32 += pLPF->channels;
|
|
}
|
|
} else if (pLPF->format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_lpf_process_pcm_frame_s16(pLPF, pFramesOutS16, pFramesInS16);
|
|
pFramesOutS16 += pLPF->channels;
|
|
pFramesInS16 += pLPF->channels;
|
|
}
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_lpf_get_latency(const ma_lpf* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pLPF->lpf2Count*2 + pLPF->lpf1Count;
|
|
}
|
|
|
|
MA_API ma_hpf1_config ma_hpf1_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency)
|
|
{
|
|
ma_hpf1_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_hpf2_config ma_hpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
|
|
{
|
|
ma_hpf2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.q = q;
|
|
|
|
if (config.q == 0) {
|
|
config.q = 0.707107;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t r1Offset;
|
|
} ma_hpf1_heap_layout;
|
|
|
|
static ma_result ma_hpf1_get_heap_layout(const ma_hpf1_config* pConfig, ma_hpf1_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->r1Offset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(ma_biquad_coefficient) * pConfig->channels;
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf1_get_heap_size(const ma_hpf1_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_hpf1_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf1_init_preallocated(const ma_hpf1_config* pConfig, void* pHeap, ma_hpf1* pLPF)
|
|
{
|
|
ma_result result;
|
|
ma_hpf1_heap_layout heapLayout;
|
|
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pLPF);
|
|
|
|
result = ma_hpf1_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pLPF->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pLPF->pR1 = (ma_biquad_coefficient*)ma_offset_ptr(pHeap, heapLayout.r1Offset);
|
|
|
|
return ma_hpf1_reinit(pConfig, pLPF);
|
|
}
|
|
|
|
MA_API ma_result ma_hpf1_init(const ma_hpf1_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf1* pLPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_hpf1_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_hpf1_init_preallocated(pConfig, pHeap, pLPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pLPF->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_hpf1_uninit(ma_hpf1* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pHPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pHPF->_ownsHeap) {
|
|
ma_free(pHPF->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_hpf1_reinit(const ma_hpf1_config* pConfig, ma_hpf1* pHPF)
|
|
{
|
|
double a;
|
|
|
|
if (pHPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pHPF->format = pConfig->format;
|
|
pHPF->channels = pConfig->channels;
|
|
|
|
a = ma_expd(-2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate);
|
|
if (pConfig->format == ma_format_f32) {
|
|
pHPF->a.f32 = (float)a;
|
|
} else {
|
|
pHPF->a.s32 = ma_biquad_float_to_fp(a);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_hpf1_process_pcm_frame_f32(ma_hpf1* pHPF, float* pY, const float* pX)
|
|
{
|
|
ma_uint32 c;
|
|
const ma_uint32 channels = pHPF->channels;
|
|
const float a = 1 - pHPF->a.f32;
|
|
const float b = 1 - a;
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
float r1 = pHPF->pR1[c].f32;
|
|
float x = pX[c];
|
|
float y;
|
|
|
|
y = b*x - a*r1;
|
|
|
|
pY[c] = y;
|
|
pHPF->pR1[c].f32 = y;
|
|
}
|
|
}
|
|
|
|
static MA_INLINE void ma_hpf1_process_pcm_frame_s16(ma_hpf1* pHPF, ma_int16* pY, const ma_int16* pX)
|
|
{
|
|
ma_uint32 c;
|
|
const ma_uint32 channels = pHPF->channels;
|
|
const ma_int32 a = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - pHPF->a.s32);
|
|
const ma_int32 b = ((1 << MA_BIQUAD_FIXED_POINT_SHIFT) - a);
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
ma_int32 r1 = pHPF->pR1[c].s32;
|
|
ma_int32 x = pX[c];
|
|
ma_int32 y;
|
|
|
|
y = (b*x - a*r1) >> MA_BIQUAD_FIXED_POINT_SHIFT;
|
|
|
|
pY[c] = (ma_int16)y;
|
|
pHPF->pR1[c].s32 = (ma_int32)y;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_hpf1_process_pcm_frames(ma_hpf1* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint32 n;
|
|
|
|
if (pHPF == NULL || pFramesOut == NULL || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pHPF->format == ma_format_f32) {
|
|
float* pY = ( float*)pFramesOut;
|
|
const float* pX = (const float*)pFramesIn;
|
|
|
|
for (n = 0; n < frameCount; n += 1) {
|
|
ma_hpf1_process_pcm_frame_f32(pHPF, pY, pX);
|
|
pY += pHPF->channels;
|
|
pX += pHPF->channels;
|
|
}
|
|
} else if (pHPF->format == ma_format_s16) {
|
|
ma_int16* pY = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pX = (const ma_int16*)pFramesIn;
|
|
|
|
for (n = 0; n < frameCount; n += 1) {
|
|
ma_hpf1_process_pcm_frame_s16(pHPF, pY, pX);
|
|
pY += pHPF->channels;
|
|
pX += pHPF->channels;
|
|
}
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_hpf1_get_latency(const ma_hpf1* pHPF)
|
|
{
|
|
if (pHPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_hpf2__get_biquad_config(const ma_hpf2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double q;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double a;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
q = pConfig->q;
|
|
w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
a = s / (2*q);
|
|
|
|
bqConfig.b0 = (1 + c) / 2;
|
|
bqConfig.b1 = -(1 + c);
|
|
bqConfig.b2 = (1 + c) / 2;
|
|
bqConfig.a0 = 1 + a;
|
|
bqConfig.a1 = -2 * c;
|
|
bqConfig.a2 = 1 - a;
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf2_get_heap_size(const ma_hpf2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_hpf2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_hpf2_init_preallocated(const ma_hpf2_config* pConfig, void* pHeap, ma_hpf2* pHPF)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pHPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pHPF);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_hpf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pHPF->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf2_init(const ma_hpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf2* pHPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_hpf2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_hpf2_init_preallocated(pConfig, pHeap, pHPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pHPF->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_hpf2_uninit(ma_hpf2* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pHPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pHPF->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_hpf2_reinit(const ma_hpf2_config* pConfig, ma_hpf2* pHPF)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pHPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_hpf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pHPF->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_hpf2_process_pcm_frame_s16(ma_hpf2* pHPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pHPF->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_hpf2_process_pcm_frame_f32(ma_hpf2* pHPF, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pHPF->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_hpf2_process_pcm_frames(ma_hpf2* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pHPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pHPF->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_hpf2_get_latency(const ma_hpf2* pHPF)
|
|
{
|
|
if (pHPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pHPF->bq);
|
|
}
|
|
|
|
MA_API ma_hpf_config ma_hpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
|
|
{
|
|
ma_hpf_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.order = ma_min(order, MA_MAX_FILTER_ORDER);
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t hpf1Offset;
|
|
size_t hpf2Offset;
|
|
} ma_hpf_heap_layout;
|
|
|
|
static void ma_hpf_calculate_sub_hpf_counts(ma_uint32 order, ma_uint32* pHPF1Count, ma_uint32* pHPF2Count)
|
|
{
|
|
MA_ASSERT(pHPF1Count != NULL);
|
|
MA_ASSERT(pHPF2Count != NULL);
|
|
|
|
*pHPF1Count = order % 2;
|
|
*pHPF2Count = order / 2;
|
|
}
|
|
|
|
static ma_result ma_hpf_get_heap_layout(const ma_hpf_config* pConfig, ma_hpf_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 hpf1Count;
|
|
ma_uint32 hpf2Count;
|
|
ma_uint32 ihpf1;
|
|
ma_uint32 ihpf2;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->order > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->hpf1Offset = pHeapLayout->sizeInBytes;
|
|
for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
|
|
size_t hpf1HeapSizeInBytes;
|
|
ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
|
|
|
|
result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += sizeof(ma_hpf1) + hpf1HeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->hpf2Offset = pHeapLayout->sizeInBytes;
|
|
for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
|
|
size_t hpf2HeapSizeInBytes;
|
|
ma_hpf2_config hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107);
|
|
|
|
result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += sizeof(ma_hpf2) + hpf2HeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_hpf_reinit__internal(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pHPF, ma_bool32 isNew)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 hpf1Count;
|
|
ma_uint32 hpf2Count;
|
|
ma_uint32 ihpf1;
|
|
ma_uint32 ihpf2;
|
|
ma_hpf_heap_layout heapLayout;
|
|
|
|
if (pHPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pHPF->format != ma_format_unknown && pHPF->format != pConfig->format) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pHPF->channels != 0 && pHPF->channels != pConfig->channels) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pConfig->order > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_hpf_calculate_sub_hpf_counts(pConfig->order, &hpf1Count, &hpf2Count);
|
|
|
|
if (!isNew) {
|
|
if (pHPF->hpf1Count != hpf1Count || pHPF->hpf2Count != hpf2Count) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
if (isNew) {
|
|
result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHPF->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pHPF->pHPF1 = (ma_hpf1*)ma_offset_ptr(pHeap, heapLayout.hpf1Offset);
|
|
pHPF->pHPF2 = (ma_hpf2*)ma_offset_ptr(pHeap, heapLayout.hpf2Offset);
|
|
} else {
|
|
MA_ZERO_OBJECT(&heapLayout);
|
|
}
|
|
|
|
for (ihpf1 = 0; ihpf1 < hpf1Count; ihpf1 += 1) {
|
|
ma_hpf1_config hpf1Config = ma_hpf1_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency);
|
|
|
|
if (isNew) {
|
|
size_t hpf1HeapSizeInBytes;
|
|
|
|
result = ma_hpf1_get_heap_size(&hpf1Config, &hpf1HeapSizeInBytes);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_hpf1_init_preallocated(&hpf1Config, ma_offset_ptr(pHeap, heapLayout.hpf1Offset + (sizeof(ma_hpf1) * hpf1Count) + (ihpf1 * hpf1HeapSizeInBytes)), &pHPF->pHPF1[ihpf1]);
|
|
}
|
|
} else {
|
|
result = ma_hpf1_reinit(&hpf1Config, &pHPF->pHPF1[ihpf1]);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_uint32 jhpf1;
|
|
|
|
for (jhpf1 = 0; jhpf1 < ihpf1; jhpf1 += 1) {
|
|
ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (ihpf2 = 0; ihpf2 < hpf2Count; ihpf2 += 1) {
|
|
ma_hpf2_config hpf2Config;
|
|
double q;
|
|
double a;
|
|
|
|
if (hpf1Count == 1) {
|
|
a = (1 + ihpf2*1) * (MA_PI_D/(pConfig->order*1));
|
|
} else {
|
|
a = (1 + ihpf2*2) * (MA_PI_D/(pConfig->order*2));
|
|
}
|
|
q = 1 / (2*ma_cosd(a));
|
|
|
|
hpf2Config = ma_hpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
|
|
|
|
if (isNew) {
|
|
size_t hpf2HeapSizeInBytes;
|
|
|
|
result = ma_hpf2_get_heap_size(&hpf2Config, &hpf2HeapSizeInBytes);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_hpf2_init_preallocated(&hpf2Config, ma_offset_ptr(pHeap, heapLayout.hpf2Offset + (sizeof(ma_hpf2) * hpf2Count) + (ihpf2 * hpf2HeapSizeInBytes)), &pHPF->pHPF2[ihpf2]);
|
|
}
|
|
} else {
|
|
result = ma_hpf2_reinit(&hpf2Config, &pHPF->pHPF2[ihpf2]);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_uint32 jhpf1;
|
|
ma_uint32 jhpf2;
|
|
|
|
for (jhpf1 = 0; jhpf1 < hpf1Count; jhpf1 += 1) {
|
|
ma_hpf1_uninit(&pHPF->pHPF1[jhpf1], NULL);
|
|
}
|
|
|
|
for (jhpf2 = 0; jhpf2 < ihpf2; jhpf2 += 1) {
|
|
ma_hpf2_uninit(&pHPF->pHPF2[jhpf2], NULL);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
pHPF->hpf1Count = hpf1Count;
|
|
pHPF->hpf2Count = hpf2Count;
|
|
pHPF->format = pConfig->format;
|
|
pHPF->channels = pConfig->channels;
|
|
pHPF->sampleRate = pConfig->sampleRate;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf_get_heap_size(const ma_hpf_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_hpf_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_hpf_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf_init_preallocated(const ma_hpf_config* pConfig, void* pHeap, ma_hpf* pLPF)
|
|
{
|
|
if (pLPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pLPF);
|
|
|
|
return ma_hpf_reinit__internal(pConfig, pHeap, pLPF, MA_TRUE);
|
|
}
|
|
|
|
MA_API ma_result ma_hpf_init(const ma_hpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf* pHPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_hpf_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_hpf_init_preallocated(pConfig, pHeap, pHPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pHPF->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_hpf_uninit(ma_hpf* pHPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_uint32 ihpf1;
|
|
ma_uint32 ihpf2;
|
|
|
|
if (pHPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
|
|
ma_hpf1_uninit(&pHPF->pHPF1[ihpf1], pAllocationCallbacks);
|
|
}
|
|
|
|
for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
|
|
ma_hpf2_uninit(&pHPF->pHPF2[ihpf2], pAllocationCallbacks);
|
|
}
|
|
|
|
if (pHPF->_ownsHeap) {
|
|
ma_free(pHPF->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_hpf_reinit(const ma_hpf_config* pConfig, ma_hpf* pHPF)
|
|
{
|
|
return ma_hpf_reinit__internal(pConfig, NULL, pHPF, MA_FALSE);
|
|
}
|
|
|
|
MA_API ma_result ma_hpf_process_pcm_frames(ma_hpf* pHPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 ihpf1;
|
|
ma_uint32 ihpf2;
|
|
|
|
if (pHPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut == pFramesIn) {
|
|
for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
|
|
result = ma_hpf1_process_pcm_frames(&pHPF->pHPF1[ihpf1], pFramesOut, pFramesOut, frameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
|
|
result = ma_hpf2_process_pcm_frames(&pHPF->pHPF2[ihpf2], pFramesOut, pFramesOut, frameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesOut != pFramesIn) {
|
|
ma_uint32 iFrame;
|
|
|
|
if (pHPF->format == ma_format_f32) {
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
|
|
|
|
for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
|
|
ma_hpf1_process_pcm_frame_f32(&pHPF->pHPF1[ihpf1], pFramesOutF32, pFramesOutF32);
|
|
}
|
|
|
|
for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
|
|
ma_hpf2_process_pcm_frame_f32(&pHPF->pHPF2[ihpf2], pFramesOutF32, pFramesOutF32);
|
|
}
|
|
|
|
pFramesOutF32 += pHPF->channels;
|
|
pFramesInF32 += pHPF->channels;
|
|
}
|
|
} else if (pHPF->format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pHPF->format, pHPF->channels));
|
|
|
|
for (ihpf1 = 0; ihpf1 < pHPF->hpf1Count; ihpf1 += 1) {
|
|
ma_hpf1_process_pcm_frame_s16(&pHPF->pHPF1[ihpf1], pFramesOutS16, pFramesOutS16);
|
|
}
|
|
|
|
for (ihpf2 = 0; ihpf2 < pHPF->hpf2Count; ihpf2 += 1) {
|
|
ma_hpf2_process_pcm_frame_s16(&pHPF->pHPF2[ihpf2], pFramesOutS16, pFramesOutS16);
|
|
}
|
|
|
|
pFramesOutS16 += pHPF->channels;
|
|
pFramesInS16 += pHPF->channels;
|
|
}
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_hpf_get_latency(const ma_hpf* pHPF)
|
|
{
|
|
if (pHPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pHPF->hpf2Count*2 + pHPF->hpf1Count;
|
|
}
|
|
|
|
MA_API ma_bpf2_config ma_bpf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, double q)
|
|
{
|
|
ma_bpf2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.q = q;
|
|
|
|
if (config.q == 0) {
|
|
config.q = 0.707107;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_bpf2__get_biquad_config(const ma_bpf2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double q;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double a;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
q = pConfig->q;
|
|
w = 2 * MA_PI_D * pConfig->cutoffFrequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
a = s / (2*q);
|
|
|
|
bqConfig.b0 = q * a;
|
|
bqConfig.b1 = 0;
|
|
bqConfig.b2 = -q * a;
|
|
bqConfig.a0 = 1 + a;
|
|
bqConfig.a1 = -2 * c;
|
|
bqConfig.a2 = 1 - a;
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_bpf2_get_heap_size(const ma_bpf2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_bpf2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_bpf2_init_preallocated(const ma_bpf2_config* pConfig, void* pHeap, ma_bpf2* pBPF)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pBPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pBPF);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_bpf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pBPF->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_bpf2_init(const ma_bpf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf2* pBPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_bpf2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_bpf2_init_preallocated(pConfig, pHeap, pBPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pBPF->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_bpf2_uninit(ma_bpf2* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pBPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pBPF->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_bpf2_reinit(const ma_bpf2_config* pConfig, ma_bpf2* pBPF)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pBPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_bpf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pBPF->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_bpf2_process_pcm_frame_s16(ma_bpf2* pBPF, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pBPF->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_bpf2_process_pcm_frame_f32(ma_bpf2* pBPF, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pBPF->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_bpf2_process_pcm_frames(ma_bpf2* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pBPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pBPF->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_bpf2_get_latency(const ma_bpf2* pBPF)
|
|
{
|
|
if (pBPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pBPF->bq);
|
|
}
|
|
|
|
MA_API ma_bpf_config ma_bpf_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
|
|
{
|
|
ma_bpf_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.cutoffFrequency = cutoffFrequency;
|
|
config.order = ma_min(order, MA_MAX_FILTER_ORDER);
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t bpf2Offset;
|
|
} ma_bpf_heap_layout;
|
|
|
|
static ma_result ma_bpf_get_heap_layout(const ma_bpf_config* pConfig, ma_bpf_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 bpf2Count;
|
|
ma_uint32 ibpf2;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->order > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pConfig->order & 0x1) != 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bpf2Count = pConfig->order / 2;
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->bpf2Offset = pHeapLayout->sizeInBytes;
|
|
for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
|
|
size_t bpf2HeapSizeInBytes;
|
|
ma_bpf2_config bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, 0.707107);
|
|
|
|
result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += sizeof(ma_bpf2) + bpf2HeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_bpf_reinit__internal(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF, ma_bool32 isNew)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 bpf2Count;
|
|
ma_uint32 ibpf2;
|
|
ma_bpf_heap_layout heapLayout;
|
|
|
|
if (pBPF == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pBPF->format != ma_format_unknown && pBPF->format != pConfig->format) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pBPF->channels != 0 && pBPF->channels != pConfig->channels) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pConfig->order > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pConfig->order & 0x1) != 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bpf2Count = pConfig->order / 2;
|
|
|
|
if (!isNew) {
|
|
if (pBPF->bpf2Count != bpf2Count) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
if (isNew) {
|
|
result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pBPF->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pBPF->pBPF2 = (ma_bpf2*)ma_offset_ptr(pHeap, heapLayout.bpf2Offset);
|
|
} else {
|
|
MA_ZERO_OBJECT(&heapLayout);
|
|
}
|
|
|
|
for (ibpf2 = 0; ibpf2 < bpf2Count; ibpf2 += 1) {
|
|
ma_bpf2_config bpf2Config;
|
|
double q;
|
|
|
|
q = 0.707107;
|
|
|
|
bpf2Config = ma_bpf2_config_init(pConfig->format, pConfig->channels, pConfig->sampleRate, pConfig->cutoffFrequency, q);
|
|
|
|
if (isNew) {
|
|
size_t bpf2HeapSizeInBytes;
|
|
|
|
result = ma_bpf2_get_heap_size(&bpf2Config, &bpf2HeapSizeInBytes);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_bpf2_init_preallocated(&bpf2Config, ma_offset_ptr(pHeap, heapLayout.bpf2Offset + (sizeof(ma_bpf2) * bpf2Count) + (ibpf2 * bpf2HeapSizeInBytes)), &pBPF->pBPF2[ibpf2]);
|
|
}
|
|
} else {
|
|
result = ma_bpf2_reinit(&bpf2Config, &pBPF->pBPF2[ibpf2]);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
pBPF->bpf2Count = bpf2Count;
|
|
pBPF->format = pConfig->format;
|
|
pBPF->channels = pConfig->channels;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_bpf_get_heap_size(const ma_bpf_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_bpf_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_bpf_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_bpf_init_preallocated(const ma_bpf_config* pConfig, void* pHeap, ma_bpf* pBPF)
|
|
{
|
|
if (pBPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pBPF);
|
|
|
|
return ma_bpf_reinit__internal(pConfig, pHeap, pBPF, MA_TRUE);
|
|
}
|
|
|
|
MA_API ma_result ma_bpf_init(const ma_bpf_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf* pBPF)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_bpf_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_bpf_init_preallocated(pConfig, pHeap, pBPF);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pBPF->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_bpf_uninit(ma_bpf* pBPF, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_uint32 ibpf2;
|
|
|
|
if (pBPF == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
|
|
ma_bpf2_uninit(&pBPF->pBPF2[ibpf2], pAllocationCallbacks);
|
|
}
|
|
|
|
if (pBPF->_ownsHeap) {
|
|
ma_free(pBPF->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_bpf_reinit(const ma_bpf_config* pConfig, ma_bpf* pBPF)
|
|
{
|
|
return ma_bpf_reinit__internal(pConfig, NULL, pBPF, MA_FALSE);
|
|
}
|
|
|
|
MA_API ma_result ma_bpf_process_pcm_frames(ma_bpf* pBPF, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 ibpf2;
|
|
|
|
if (pBPF == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut == pFramesIn) {
|
|
for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
|
|
result = ma_bpf2_process_pcm_frames(&pBPF->pBPF2[ibpf2], pFramesOut, pFramesOut, frameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesOut != pFramesIn) {
|
|
ma_uint32 iFrame;
|
|
|
|
if (pBPF->format == ma_format_f32) {
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
MA_COPY_MEMORY(pFramesOutF32, pFramesInF32, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
|
|
|
|
for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
|
|
ma_bpf2_process_pcm_frame_f32(&pBPF->pBPF2[ibpf2], pFramesOutF32, pFramesOutF32);
|
|
}
|
|
|
|
pFramesOutF32 += pBPF->channels;
|
|
pFramesInF32 += pBPF->channels;
|
|
}
|
|
} else if (pBPF->format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
MA_COPY_MEMORY(pFramesOutS16, pFramesInS16, ma_get_bytes_per_frame(pBPF->format, pBPF->channels));
|
|
|
|
for (ibpf2 = 0; ibpf2 < pBPF->bpf2Count; ibpf2 += 1) {
|
|
ma_bpf2_process_pcm_frame_s16(&pBPF->pBPF2[ibpf2], pFramesOutS16, pFramesOutS16);
|
|
}
|
|
|
|
pFramesOutS16 += pBPF->channels;
|
|
pFramesInS16 += pBPF->channels;
|
|
}
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_bpf_get_latency(const ma_bpf* pBPF)
|
|
{
|
|
if (pBPF == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pBPF->bpf2Count*2;
|
|
}
|
|
|
|
MA_API ma_notch2_config ma_notch2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
|
|
{
|
|
ma_notch2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.q = q;
|
|
config.frequency = frequency;
|
|
|
|
if (config.q == 0) {
|
|
config.q = 0.707107;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_notch2__get_biquad_config(const ma_notch2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double q;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double a;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
q = pConfig->q;
|
|
w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
a = s / (2*q);
|
|
|
|
bqConfig.b0 = 1;
|
|
bqConfig.b1 = -2 * c;
|
|
bqConfig.b2 = 1;
|
|
bqConfig.a0 = 1 + a;
|
|
bqConfig.a1 = -2 * c;
|
|
bqConfig.a2 = 1 - a;
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_notch2_get_heap_size(const ma_notch2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_notch2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_notch2_init_preallocated(const ma_notch2_config* pConfig, void* pHeap, ma_notch2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFilter);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_notch2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_notch2_init(const ma_notch2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch2* pFilter)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_notch2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_notch2_init_preallocated(pConfig, pHeap, pFilter);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pFilter->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_notch2_uninit(ma_notch2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_notch2_reinit(const ma_notch2_config* pConfig, ma_notch2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_notch2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_notch2_process_pcm_frame_s16(ma_notch2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_notch2_process_pcm_frame_f32(ma_notch2* pFilter, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_notch2_process_pcm_frames(ma_notch2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_notch2_get_latency(const ma_notch2* pFilter)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pFilter->bq);
|
|
}
|
|
|
|
MA_API ma_peak2_config ma_peak2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
|
|
{
|
|
ma_peak2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.gainDB = gainDB;
|
|
config.q = q;
|
|
config.frequency = frequency;
|
|
|
|
if (config.q == 0) {
|
|
config.q = 0.707107;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_peak2__get_biquad_config(const ma_peak2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double q;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double a;
|
|
double A;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
q = pConfig->q;
|
|
w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
a = s / (2*q);
|
|
A = ma_powd(10, (pConfig->gainDB / 40));
|
|
|
|
bqConfig.b0 = 1 + (a * A);
|
|
bqConfig.b1 = -2 * c;
|
|
bqConfig.b2 = 1 - (a * A);
|
|
bqConfig.a0 = 1 + (a / A);
|
|
bqConfig.a1 = -2 * c;
|
|
bqConfig.a2 = 1 - (a / A);
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_peak2_get_heap_size(const ma_peak2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_peak2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_peak2_init_preallocated(const ma_peak2_config* pConfig, void* pHeap, ma_peak2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFilter);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_peak2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_peak2_init(const ma_peak2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak2* pFilter)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_peak2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_peak2_init_preallocated(pConfig, pHeap, pFilter);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pFilter->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_peak2_uninit(ma_peak2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_peak2_reinit(const ma_peak2_config* pConfig, ma_peak2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_peak2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_peak2_process_pcm_frame_s16(ma_peak2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_peak2_process_pcm_frame_f32(ma_peak2* pFilter, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_peak2_process_pcm_frames(ma_peak2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_peak2_get_latency(const ma_peak2* pFilter)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pFilter->bq);
|
|
}
|
|
|
|
MA_API ma_loshelf2_config ma_loshelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
|
|
{
|
|
ma_loshelf2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.gainDB = gainDB;
|
|
config.shelfSlope = shelfSlope;
|
|
config.frequency = frequency;
|
|
|
|
return config;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_loshelf2__get_biquad_config(const ma_loshelf2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double A;
|
|
double S;
|
|
double a;
|
|
double sqrtA;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
A = ma_powd(10, (pConfig->gainDB / 40));
|
|
S = pConfig->shelfSlope;
|
|
a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
|
|
sqrtA = 2*ma_sqrtd(A)*a;
|
|
|
|
bqConfig.b0 = A * ((A + 1) - (A - 1)*c + sqrtA);
|
|
bqConfig.b1 = 2 * A * ((A - 1) - (A + 1)*c);
|
|
bqConfig.b2 = A * ((A + 1) - (A - 1)*c - sqrtA);
|
|
bqConfig.a0 = (A + 1) + (A - 1)*c + sqrtA;
|
|
bqConfig.a1 = -2 * ((A - 1) + (A + 1)*c);
|
|
bqConfig.a2 = (A + 1) + (A - 1)*c - sqrtA;
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_loshelf2_get_heap_size(const ma_loshelf2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_loshelf2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_loshelf2_init_preallocated(const ma_loshelf2_config* pConfig, void* pHeap, ma_loshelf2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFilter);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_loshelf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_loshelf2_init(const ma_loshelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf2* pFilter)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_loshelf2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_loshelf2_init_preallocated(pConfig, pHeap, pFilter);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pFilter->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_loshelf2_uninit(ma_loshelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_loshelf2_reinit(const ma_loshelf2_config* pConfig, ma_loshelf2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_loshelf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_loshelf2_process_pcm_frame_s16(ma_loshelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_loshelf2_process_pcm_frame_f32(ma_loshelf2* pFilter, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_loshelf2_process_pcm_frames(ma_loshelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_loshelf2_get_latency(const ma_loshelf2* pFilter)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pFilter->bq);
|
|
}
|
|
|
|
MA_API ma_hishelf2_config ma_hishelf2_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double shelfSlope, double frequency)
|
|
{
|
|
ma_hishelf2_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.gainDB = gainDB;
|
|
config.shelfSlope = shelfSlope;
|
|
config.frequency = frequency;
|
|
|
|
return config;
|
|
}
|
|
|
|
static MA_INLINE ma_biquad_config ma_hishelf2__get_biquad_config(const ma_hishelf2_config* pConfig)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
double w;
|
|
double s;
|
|
double c;
|
|
double A;
|
|
double S;
|
|
double a;
|
|
double sqrtA;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
w = 2 * MA_PI_D * pConfig->frequency / pConfig->sampleRate;
|
|
s = ma_sind(w);
|
|
c = ma_cosd(w);
|
|
A = ma_powd(10, (pConfig->gainDB / 40));
|
|
S = pConfig->shelfSlope;
|
|
a = s/2 * ma_sqrtd((A + 1/A) * (1/S - 1) + 2);
|
|
sqrtA = 2*ma_sqrtd(A)*a;
|
|
|
|
bqConfig.b0 = A * ((A + 1) + (A - 1)*c + sqrtA);
|
|
bqConfig.b1 = -2 * A * ((A - 1) + (A + 1)*c);
|
|
bqConfig.b2 = A * ((A + 1) + (A - 1)*c - sqrtA);
|
|
bqConfig.a0 = (A + 1) - (A - 1)*c + sqrtA;
|
|
bqConfig.a1 = 2 * ((A - 1) - (A + 1)*c);
|
|
bqConfig.a2 = (A + 1) - (A - 1)*c - sqrtA;
|
|
|
|
bqConfig.format = pConfig->format;
|
|
bqConfig.channels = pConfig->channels;
|
|
|
|
return bqConfig;
|
|
}
|
|
|
|
MA_API ma_result ma_hishelf2_get_heap_size(const ma_hishelf2_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_biquad_config bqConfig;
|
|
bqConfig = ma_hishelf2__get_biquad_config(pConfig);
|
|
|
|
return ma_biquad_get_heap_size(&bqConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_hishelf2_init_preallocated(const ma_hishelf2_config* pConfig, void* pHeap, ma_hishelf2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFilter);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_hishelf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_init_preallocated(&bqConfig, pHeap, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_hishelf2_init(const ma_hishelf2_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf2* pFilter)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_hishelf2_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_hishelf2_init_preallocated(pConfig, pHeap, pFilter);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pFilter->bq._ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_hishelf2_uninit(ma_hishelf2* pFilter, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_biquad_uninit(&pFilter->bq, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_hishelf2_reinit(const ma_hishelf2_config* pConfig, ma_hishelf2* pFilter)
|
|
{
|
|
ma_result result;
|
|
ma_biquad_config bqConfig;
|
|
|
|
if (pFilter == NULL || pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
bqConfig = ma_hishelf2__get_biquad_config(pConfig);
|
|
result = ma_biquad_reinit(&bqConfig, &pFilter->bq);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static MA_INLINE void ma_hishelf2_process_pcm_frame_s16(ma_hishelf2* pFilter, ma_int16* pFrameOut, const ma_int16* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_s16(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
static MA_INLINE void ma_hishelf2_process_pcm_frame_f32(ma_hishelf2* pFilter, float* pFrameOut, const float* pFrameIn)
|
|
{
|
|
ma_biquad_process_pcm_frame_f32(&pFilter->bq, pFrameOut, pFrameIn);
|
|
}
|
|
|
|
MA_API ma_result ma_hishelf2_process_pcm_frames(ma_hishelf2* pFilter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_biquad_process_pcm_frames(&pFilter->bq, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_hishelf2_get_latency(const ma_hishelf2* pFilter)
|
|
{
|
|
if (pFilter == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_biquad_get_latency(&pFilter->bq);
|
|
}
|
|
|
|
MA_API ma_delay_config ma_delay_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
|
|
{
|
|
ma_delay_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.delayInFrames = delayInFrames;
|
|
config.delayStart = (decay == 0) ? MA_TRUE : MA_FALSE;
|
|
config.wet = 1;
|
|
config.dry = 1;
|
|
config.decay = decay;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_delay_init(const ma_delay_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay* pDelay)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDelay);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->decay < 0 || pConfig->decay > 1) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDelay->config = *pConfig;
|
|
pDelay->bufferSizeInFrames = pConfig->delayInFrames;
|
|
pDelay->cursor = 0;
|
|
|
|
pDelay->pBuffer = (float*)ma_malloc((size_t)(pDelay->bufferSizeInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->channels)), pAllocationCallbacks);
|
|
if (pDelay->pBuffer == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_silence_pcm_frames(pDelay->pBuffer, pDelay->bufferSizeInFrames, ma_format_f32, pConfig->channels);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_delay_uninit(ma_delay* pDelay, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_free(pDelay->pBuffer, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_delay_process_pcm_frames(ma_delay* pDelay, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
|
{
|
|
ma_uint32 iFrame;
|
|
ma_uint32 iChannel;
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
if (pDelay == NULL || pFramesOut == NULL || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < pDelay->config.channels; iChannel += 1) {
|
|
ma_uint32 iBuffer = (pDelay->cursor * pDelay->config.channels) + iChannel;
|
|
|
|
if (pDelay->config.delayStart) {
|
|
|
|
pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
|
|
|
|
pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
|
|
} else {
|
|
|
|
pDelay->pBuffer[iBuffer] = (pDelay->pBuffer[iBuffer] * pDelay->config.decay) + (pFramesInF32[iChannel] * pDelay->config.dry);
|
|
|
|
pFramesOutF32[iChannel] = pDelay->pBuffer[iBuffer] * pDelay->config.wet;
|
|
}
|
|
}
|
|
|
|
pDelay->cursor = (pDelay->cursor + 1) % pDelay->bufferSizeInFrames;
|
|
|
|
pFramesOutF32 += pDelay->config.channels;
|
|
pFramesInF32 += pDelay->config.channels;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_delay_set_wet(ma_delay* pDelay, float value)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return;
|
|
}
|
|
|
|
pDelay->config.wet = value;
|
|
}
|
|
|
|
MA_API float ma_delay_get_wet(const ma_delay* pDelay)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pDelay->config.wet;
|
|
}
|
|
|
|
MA_API void ma_delay_set_dry(ma_delay* pDelay, float value)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return;
|
|
}
|
|
|
|
pDelay->config.dry = value;
|
|
}
|
|
|
|
MA_API float ma_delay_get_dry(const ma_delay* pDelay)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pDelay->config.dry;
|
|
}
|
|
|
|
MA_API void ma_delay_set_decay(ma_delay* pDelay, float value)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return;
|
|
}
|
|
|
|
pDelay->config.decay = value;
|
|
}
|
|
|
|
MA_API float ma_delay_get_decay(const ma_delay* pDelay)
|
|
{
|
|
if (pDelay == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pDelay->config.decay;
|
|
}
|
|
|
|
MA_API ma_gainer_config ma_gainer_config_init(ma_uint32 channels, ma_uint32 smoothTimeInFrames)
|
|
{
|
|
ma_gainer_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.channels = channels;
|
|
config.smoothTimeInFrames = smoothTimeInFrames;
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t oldGainsOffset;
|
|
size_t newGainsOffset;
|
|
} ma_gainer_heap_layout;
|
|
|
|
static ma_result ma_gainer_get_heap_layout(const ma_gainer_config* pConfig, ma_gainer_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->oldGainsOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
|
|
|
|
pHeapLayout->newGainsOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_get_heap_size(const ma_gainer_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_gainer_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_init_preallocated(const ma_gainer_config* pConfig, void* pHeap, ma_gainer* pGainer)
|
|
{
|
|
ma_result result;
|
|
ma_gainer_heap_layout heapLayout;
|
|
ma_uint32 iChannel;
|
|
|
|
if (pGainer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pGainer);
|
|
|
|
if (pConfig == NULL || pHeap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_gainer_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pGainer->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pGainer->pOldGains = (float*)ma_offset_ptr(pHeap, heapLayout.oldGainsOffset);
|
|
pGainer->pNewGains = (float*)ma_offset_ptr(pHeap, heapLayout.newGainsOffset);
|
|
pGainer->masterVolume = 1;
|
|
|
|
pGainer->config = *pConfig;
|
|
pGainer->t = (ma_uint32)-1;
|
|
|
|
for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
|
|
pGainer->pOldGains[iChannel] = 1;
|
|
pGainer->pNewGains[iChannel] = 1;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_init(const ma_gainer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_gainer* pGainer)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_gainer_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_gainer_init_preallocated(pConfig, pHeap, pGainer);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pGainer->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_gainer_uninit(ma_gainer* pGainer, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pGainer == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pGainer->_ownsHeap) {
|
|
ma_free(pGainer->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
static float ma_gainer_calculate_current_gain(const ma_gainer* pGainer, ma_uint32 channel)
|
|
{
|
|
float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
|
|
return ma_mix_f32_fast(pGainer->pOldGains[channel], pGainer->pNewGains[channel], a);
|
|
}
|
|
|
|
static ma_result ma_gainer_process_pcm_frames_internal(ma_gainer * pGainer, void* MA_RESTRICT pFramesOut, const void* MA_RESTRICT pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannel;
|
|
ma_uint64 interpolatedFrameCount;
|
|
|
|
MA_ASSERT(pGainer != NULL);
|
|
|
|
if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
|
|
interpolatedFrameCount = 0;
|
|
} else {
|
|
interpolatedFrameCount = pGainer->t - pGainer->config.smoothTimeInFrames;
|
|
if (interpolatedFrameCount > frameCount) {
|
|
interpolatedFrameCount = frameCount;
|
|
}
|
|
}
|
|
|
|
if (interpolatedFrameCount > 0) {
|
|
if (pFramesOut != NULL && pFramesIn != NULL) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
|
|
float d = 1.0f / pGainer->config.smoothTimeInFrames;
|
|
|
|
if (pGainer->config.channels <= 32) {
|
|
float pRunningGain[32];
|
|
float pRunningGainDelta[32];
|
|
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume;
|
|
pRunningGainDelta[iChannel] = t * d;
|
|
pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a);
|
|
}
|
|
|
|
iFrame = 0;
|
|
|
|
if (pGainer->config.channels == 2) {
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
|
|
|
|
__m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[1], pRunningGainDelta[0]);
|
|
__m128 runningGain0 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[1], pRunningGain[0]);
|
|
|
|
for (; iFrame < unrolledLoopCount; iFrame += 1) {
|
|
_mm_storeu_ps(&pFramesOutF32[iFrame*4 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*4 + 0]), runningGain0));
|
|
runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
|
|
}
|
|
|
|
iFrame = unrolledLoopCount << 1;
|
|
} else
|
|
#endif
|
|
{
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
|
|
|
|
pRunningGainDelta[2] = pRunningGainDelta[0];
|
|
pRunningGainDelta[3] = pRunningGainDelta[1];
|
|
pRunningGain[2] = pRunningGain[0] + pRunningGainDelta[0];
|
|
pRunningGain[3] = pRunningGain[1] + pRunningGainDelta[1];
|
|
|
|
for (; iFrame < unrolledLoopCount; iFrame += 1) {
|
|
pFramesOutF32[iFrame*4 + 0] = pFramesInF32[iFrame*4 + 0] * pRunningGain[0];
|
|
pFramesOutF32[iFrame*4 + 1] = pFramesInF32[iFrame*4 + 1] * pRunningGain[1];
|
|
pFramesOutF32[iFrame*4 + 2] = pFramesInF32[iFrame*4 + 2] * pRunningGain[2];
|
|
pFramesOutF32[iFrame*4 + 3] = pFramesInF32[iFrame*4 + 3] * pRunningGain[3];
|
|
|
|
pRunningGain[0] += pRunningGainDelta[0];
|
|
pRunningGain[1] += pRunningGainDelta[1];
|
|
pRunningGain[2] += pRunningGainDelta[2];
|
|
pRunningGain[3] += pRunningGainDelta[3];
|
|
}
|
|
|
|
iFrame = unrolledLoopCount << 1;
|
|
#else
|
|
for (; iFrame < interpolatedFrameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < 2; iChannel += 1) {
|
|
pFramesOutF32[iFrame*2 + iChannel] = pFramesInF32[iFrame*2 + iChannel] * pRunningGain[iChannel];
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < 2; iChannel += 1) {
|
|
pRunningGain[iChannel] += pRunningGainDelta[iChannel];
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} else if (pGainer->config.channels == 6) {
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_uint64 unrolledLoopCount = interpolatedFrameCount >> 1;
|
|
|
|
__m128 runningGainDelta0 = _mm_set_ps(pRunningGainDelta[3], pRunningGainDelta[2], pRunningGainDelta[1], pRunningGainDelta[0]);
|
|
__m128 runningGainDelta1 = _mm_set_ps(pRunningGainDelta[1], pRunningGainDelta[0], pRunningGainDelta[5], pRunningGainDelta[4]);
|
|
__m128 runningGainDelta2 = _mm_set_ps(pRunningGainDelta[5], pRunningGainDelta[4], pRunningGainDelta[3], pRunningGainDelta[2]);
|
|
|
|
__m128 runningGain0 = _mm_set_ps(pRunningGain[3], pRunningGain[2], pRunningGain[1], pRunningGain[0]);
|
|
__m128 runningGain1 = _mm_set_ps(pRunningGain[1] + pRunningGainDelta[1], pRunningGain[0] + pRunningGainDelta[0], pRunningGain[5], pRunningGain[4]);
|
|
__m128 runningGain2 = _mm_set_ps(pRunningGain[5] + pRunningGainDelta[5], pRunningGain[4] + pRunningGainDelta[4], pRunningGain[3] + pRunningGainDelta[3], pRunningGain[2] + pRunningGainDelta[2]);
|
|
|
|
for (; iFrame < unrolledLoopCount; iFrame += 1) {
|
|
_mm_storeu_ps(&pFramesOutF32[iFrame*12 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 0]), runningGain0));
|
|
_mm_storeu_ps(&pFramesOutF32[iFrame*12 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 4]), runningGain1));
|
|
_mm_storeu_ps(&pFramesOutF32[iFrame*12 + 8], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*12 + 8]), runningGain2));
|
|
|
|
runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
|
|
runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
|
|
runningGain2 = _mm_add_ps(runningGain2, runningGainDelta2);
|
|
}
|
|
|
|
iFrame = unrolledLoopCount << 1;
|
|
} else
|
|
#endif
|
|
{
|
|
for (; iFrame < interpolatedFrameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < 6; iChannel += 1) {
|
|
pFramesOutF32[iFrame*6 + iChannel] = pFramesInF32[iFrame*6 + iChannel] * pRunningGain[iChannel];
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < 6; iChannel += 1) {
|
|
pRunningGain[iChannel] += pRunningGainDelta[iChannel];
|
|
}
|
|
}
|
|
}
|
|
} else if (pGainer->config.channels == 8) {
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
__m128 runningGainDelta0 = _mm_loadu_ps(&pRunningGainDelta[0]);
|
|
__m128 runningGainDelta1 = _mm_loadu_ps(&pRunningGainDelta[4]);
|
|
__m128 runningGain0 = _mm_loadu_ps(&pRunningGain[0]);
|
|
__m128 runningGain1 = _mm_loadu_ps(&pRunningGain[4]);
|
|
|
|
for (; iFrame < interpolatedFrameCount; iFrame += 1) {
|
|
_mm_storeu_ps(&pFramesOutF32[iFrame*8 + 0], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 0]), runningGain0));
|
|
_mm_storeu_ps(&pFramesOutF32[iFrame*8 + 4], _mm_mul_ps(_mm_loadu_ps(&pFramesInF32[iFrame*8 + 4]), runningGain1));
|
|
|
|
runningGain0 = _mm_add_ps(runningGain0, runningGainDelta0);
|
|
runningGain1 = _mm_add_ps(runningGain1, runningGainDelta1);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
for (; iFrame < interpolatedFrameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < 8; iChannel += 1) {
|
|
pFramesOutF32[iFrame*8 + iChannel] = pFramesInF32[iFrame*8 + iChannel] * pRunningGain[iChannel];
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < 8; iChannel += 1) {
|
|
pRunningGain[iChannel] += pRunningGainDelta[iChannel];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (; iFrame < interpolatedFrameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * pRunningGain[iChannel];
|
|
pRunningGain[iChannel] += pRunningGainDelta[iChannel];
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < interpolatedFrameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;
|
|
}
|
|
|
|
a += d;
|
|
}
|
|
}
|
|
|
|
pFramesOut = ma_offset_ptr(pFramesOut, interpolatedFrameCount * sizeof(float));
|
|
pFramesIn = ma_offset_ptr(pFramesIn, interpolatedFrameCount * sizeof(float));
|
|
}
|
|
|
|
frameCount -= interpolatedFrameCount;
|
|
|
|
pGainer->t = (ma_uint32)ma_min(pGainer->t + interpolatedFrameCount, pGainer->config.smoothTimeInFrames);
|
|
}
|
|
|
|
if (pFramesOut != NULL && pFramesIn != NULL) {
|
|
if (pGainer->config.channels <= 32) {
|
|
float gains[32];
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
gains[iChannel] = pGainer->pNewGains[iChannel] * pGainer->masterVolume;
|
|
}
|
|
|
|
ma_copy_and_apply_volume_factor_per_channel_f32((float*)pFramesOut, (const float*)pFramesIn, frameCount, pGainer->config.channels, gains);
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
((float*)pFramesOut)[iFrame*pGainer->config.channels + iChannel] = ((const float*)pFramesIn)[iFrame*pGainer->config.channels + iChannel] * pGainer->pNewGains[iChannel] * pGainer->masterVolume;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pGainer->t == (ma_uint32)-1) {
|
|
pGainer->t = (ma_uint32)ma_min(pGainer->config.smoothTimeInFrames, frameCount);
|
|
}
|
|
|
|
#if 0
|
|
if (pGainer->t >= pGainer->config.smoothTimeInFrames) {
|
|
ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);
|
|
ma_apply_volume_factor_f32(pFramesOutF32, frameCount * pGainer->config.channels, pGainer->masterVolume);
|
|
|
|
if (pGainer->t == (ma_uint32)-1) {
|
|
pGainer->t = pGainer->config.smoothTimeInFrames;
|
|
}
|
|
} else {
|
|
|
|
if (pFramesOut != NULL && pFramesIn != NULL) {
|
|
float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
|
|
float d = 1.0f / pGainer->config.smoothTimeInFrames;
|
|
ma_uint32 channelCount = pGainer->config.channels;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
|
|
pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a) * pGainer->masterVolume;
|
|
}
|
|
|
|
pFramesOutF32 += channelCount;
|
|
pFramesInF32 += channelCount;
|
|
|
|
a += d;
|
|
if (a > 1) {
|
|
a = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);
|
|
|
|
#if 0
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
if (pFramesOut != NULL && pFramesIn != NULL) {
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame * pGainer->config.channels + iChannel] = pFramesInF32[iFrame * pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel) * pGainer->masterVolume;
|
|
}
|
|
}
|
|
|
|
pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_process_pcm_frames(ma_gainer* pGainer, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pGainer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_gainer_process_pcm_frames_internal(pGainer, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
|
|
static void ma_gainer_set_gain_by_index(ma_gainer* pGainer, float newGain, ma_uint32 iChannel)
|
|
{
|
|
pGainer->pOldGains[iChannel] = ma_gainer_calculate_current_gain(pGainer, iChannel);
|
|
pGainer->pNewGains[iChannel] = newGain;
|
|
}
|
|
|
|
static void ma_gainer_reset_smoothing_time(ma_gainer* pGainer)
|
|
{
|
|
if (pGainer->t == (ma_uint32)-1) {
|
|
pGainer->t = pGainer->config.smoothTimeInFrames;
|
|
} else {
|
|
pGainer->t = 0;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_set_gain(ma_gainer* pGainer, float newGain)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pGainer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
ma_gainer_set_gain_by_index(pGainer, newGain, iChannel);
|
|
}
|
|
|
|
ma_gainer_reset_smoothing_time(pGainer);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_set_gains(ma_gainer* pGainer, float* pNewGains)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pGainer == NULL || pNewGains == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
|
|
ma_gainer_set_gain_by_index(pGainer, pNewGains[iChannel], iChannel);
|
|
}
|
|
|
|
ma_gainer_reset_smoothing_time(pGainer);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_set_master_volume(ma_gainer* pGainer, float volume)
|
|
{
|
|
if (pGainer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pGainer->masterVolume = volume;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_gainer_get_master_volume(const ma_gainer* pGainer, float* pVolume)
|
|
{
|
|
if (pGainer == NULL || pVolume == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pVolume = pGainer->masterVolume;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_panner_config ma_panner_config_init(ma_format format, ma_uint32 channels)
|
|
{
|
|
ma_panner_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.mode = ma_pan_mode_balance;
|
|
config.pan = 0;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_panner_init(const ma_panner_config* pConfig, ma_panner* pPanner)
|
|
{
|
|
if (pPanner == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pPanner);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pPanner->format = pConfig->format;
|
|
pPanner->channels = pConfig->channels;
|
|
pPanner->mode = pConfig->mode;
|
|
pPanner->pan = pConfig->pan;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_stereo_balance_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
|
|
{
|
|
ma_uint64 iFrame;
|
|
|
|
if (pan > 0) {
|
|
float factor = 1.0f - pan;
|
|
if (pFramesOut == pFramesIn) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0] * factor;
|
|
pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1];
|
|
}
|
|
}
|
|
} else {
|
|
float factor = 1.0f + pan;
|
|
if (pFramesOut == pFramesIn) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
pFramesOut[iFrame*2 + 0] = pFramesIn[iFrame*2 + 0];
|
|
pFramesOut[iFrame*2 + 1] = pFramesIn[iFrame*2 + 1] * factor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_stereo_balance_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
|
|
{
|
|
if (pan == 0) {
|
|
if (pFramesOut == pFramesIn) {
|
|
} else {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
switch (format) {
|
|
case ma_format_f32: ma_stereo_balance_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
|
|
|
|
default:
|
|
{
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
static void ma_stereo_pan_pcm_frames_f32(float* pFramesOut, const float* pFramesIn, ma_uint64 frameCount, float pan)
|
|
{
|
|
ma_uint64 iFrame;
|
|
|
|
if (pan > 0) {
|
|
float factorL0 = 1.0f - pan;
|
|
float factorL1 = 0.0f + pan;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float sample0 = (pFramesIn[iFrame*2 + 0] * factorL0);
|
|
float sample1 = (pFramesIn[iFrame*2 + 0] * factorL1) + pFramesIn[iFrame*2 + 1];
|
|
|
|
pFramesOut[iFrame*2 + 0] = sample0;
|
|
pFramesOut[iFrame*2 + 1] = sample1;
|
|
}
|
|
} else {
|
|
float factorR0 = 0.0f - pan;
|
|
float factorR1 = 1.0f + pan;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float sample0 = pFramesIn[iFrame*2 + 0] + (pFramesIn[iFrame*2 + 1] * factorR0);
|
|
float sample1 = (pFramesIn[iFrame*2 + 1] * factorR1);
|
|
|
|
pFramesOut[iFrame*2 + 0] = sample0;
|
|
pFramesOut[iFrame*2 + 1] = sample1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_stereo_pan_pcm_frames(void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount, ma_format format, float pan)
|
|
{
|
|
if (pan == 0) {
|
|
if (pFramesOut == pFramesIn) {
|
|
} else {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
switch (format) {
|
|
case ma_format_f32: ma_stereo_pan_pcm_frames_f32((float*)pFramesOut, (float*)pFramesIn, frameCount, pan); break;
|
|
|
|
default:
|
|
{
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, format, 2);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_panner_process_pcm_frames(ma_panner* pPanner, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pPanner == NULL || pFramesOut == NULL || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pPanner->channels == 2) {
|
|
if (pPanner->mode == ma_pan_mode_balance) {
|
|
ma_stereo_balance_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
|
|
} else {
|
|
ma_stereo_pan_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->pan);
|
|
}
|
|
} else {
|
|
if (pPanner->channels == 1) {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
|
|
} else {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pPanner->format, pPanner->channels);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_panner_set_mode(ma_panner* pPanner, ma_pan_mode mode)
|
|
{
|
|
if (pPanner == NULL) {
|
|
return;
|
|
}
|
|
|
|
pPanner->mode = mode;
|
|
}
|
|
|
|
MA_API ma_pan_mode ma_panner_get_mode(const ma_panner* pPanner)
|
|
{
|
|
if (pPanner == NULL) {
|
|
return ma_pan_mode_balance;
|
|
}
|
|
|
|
return pPanner->mode;
|
|
}
|
|
|
|
MA_API void ma_panner_set_pan(ma_panner* pPanner, float pan)
|
|
{
|
|
if (pPanner == NULL) {
|
|
return;
|
|
}
|
|
|
|
pPanner->pan = ma_clamp(pan, -1.0f, 1.0f);
|
|
}
|
|
|
|
MA_API float ma_panner_get_pan(const ma_panner* pPanner)
|
|
{
|
|
if (pPanner == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pPanner->pan;
|
|
}
|
|
|
|
MA_API ma_fader_config ma_fader_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
|
|
{
|
|
ma_fader_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader)
|
|
{
|
|
if (pFader == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFader);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pFader->config = *pConfig;
|
|
pFader->volumeBeg = 1;
|
|
pFader->volumeEnd = 1;
|
|
pFader->lengthInFrames = 0;
|
|
pFader->cursorInFrames = 0;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pFader == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFader->cursorInFrames < 0) {
|
|
ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames;
|
|
if (absCursorInFrames > frameCount) {
|
|
absCursorInFrames = frameCount;
|
|
}
|
|
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels);
|
|
|
|
pFader->cursorInFrames += absCursorInFrames;
|
|
frameCount -= absCursorInFrames;
|
|
pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);
|
|
pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames);
|
|
}
|
|
|
|
if (pFader->cursorInFrames >= 0) {
|
|
if (frameCount + pFader->cursorInFrames > UINT_MAX) {
|
|
frameCount = UINT_MAX - pFader->cursorInFrames;
|
|
}
|
|
|
|
if (pFader->volumeBeg == pFader->volumeEnd) {
|
|
if (pFader->volumeBeg == 1) {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels);
|
|
} else {
|
|
ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg);
|
|
}
|
|
} else {
|
|
if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {
|
|
ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd);
|
|
} else {
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannel;
|
|
|
|
if (pFader->config.format == ma_format_f32) {
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames);
|
|
float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a);
|
|
|
|
for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume;
|
|
}
|
|
}
|
|
} else {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pFader->cursorInFrames += frameCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
|
|
{
|
|
if (pFader == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pFader->config.format;
|
|
}
|
|
|
|
if (pChannels != NULL) {
|
|
*pChannels = pFader->config.channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pFader->config.sampleRate;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames)
|
|
{
|
|
ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0);
|
|
}
|
|
|
|
MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames)
|
|
{
|
|
if (pFader == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (volumeBeg < 0) {
|
|
volumeBeg = ma_fader_get_current_volume(pFader);
|
|
}
|
|
|
|
if (lengthInFrames > UINT_MAX) {
|
|
lengthInFrames = UINT_MAX;
|
|
}
|
|
|
|
if (startOffsetInFrames > INT_MAX) {
|
|
startOffsetInFrames = INT_MAX;
|
|
}
|
|
|
|
pFader->volumeBeg = volumeBeg;
|
|
pFader->volumeEnd = volumeEnd;
|
|
pFader->lengthInFrames = lengthInFrames;
|
|
pFader->cursorInFrames = -startOffsetInFrames;
|
|
}
|
|
|
|
MA_API float ma_fader_get_current_volume(const ma_fader* pFader)
|
|
{
|
|
if (pFader == NULL) {
|
|
return 0.0f;
|
|
}
|
|
|
|
if (pFader->cursorInFrames < 0) {
|
|
return 1.0f;
|
|
}
|
|
|
|
if (pFader->cursorInFrames == 0) {
|
|
return pFader->volumeBeg;
|
|
} else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) {
|
|
return pFader->volumeEnd;
|
|
} else {
|
|
return ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, (ma_uint32)pFader->cursorInFrames / (float)((ma_uint32)pFader->lengthInFrames));
|
|
}
|
|
}
|
|
|
|
MA_API ma_vec3f ma_vec3f_init_3f(float x, float y, float z)
|
|
{
|
|
ma_vec3f v;
|
|
|
|
v.x = x;
|
|
v.y = y;
|
|
v.z = z;
|
|
|
|
return v;
|
|
}
|
|
|
|
MA_API ma_vec3f ma_vec3f_sub(ma_vec3f a, ma_vec3f b)
|
|
{
|
|
return ma_vec3f_init_3f(
|
|
a.x - b.x,
|
|
a.y - b.y,
|
|
a.z - b.z
|
|
);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_vec3f_neg(ma_vec3f a)
|
|
{
|
|
return ma_vec3f_init_3f(
|
|
-a.x,
|
|
-a.y,
|
|
-a.z
|
|
);
|
|
}
|
|
|
|
MA_API float ma_vec3f_dot(ma_vec3f a, ma_vec3f b)
|
|
{
|
|
return a.x*b.x + a.y*b.y + a.z*b.z;
|
|
}
|
|
|
|
MA_API float ma_vec3f_len2(ma_vec3f v)
|
|
{
|
|
return ma_vec3f_dot(v, v);
|
|
}
|
|
|
|
MA_API float ma_vec3f_len(ma_vec3f v)
|
|
{
|
|
return (float)ma_sqrtd(ma_vec3f_len2(v));
|
|
}
|
|
|
|
MA_API float ma_vec3f_dist(ma_vec3f a, ma_vec3f b)
|
|
{
|
|
return ma_vec3f_len(ma_vec3f_sub(a, b));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_vec3f_normalize(ma_vec3f v)
|
|
{
|
|
float invLen;
|
|
float len2 = ma_vec3f_len2(v);
|
|
if (len2 == 0) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
invLen = ma_rsqrtf(len2);
|
|
v.x *= invLen;
|
|
v.y *= invLen;
|
|
v.z *= invLen;
|
|
|
|
return v;
|
|
}
|
|
|
|
MA_API ma_vec3f ma_vec3f_cross(ma_vec3f a, ma_vec3f b)
|
|
{
|
|
return ma_vec3f_init_3f(
|
|
a.y*b.z - a.z*b.y,
|
|
a.z*b.x - a.x*b.z,
|
|
a.x*b.y - a.y*b.x
|
|
);
|
|
}
|
|
|
|
MA_API void ma_atomic_vec3f_init(ma_atomic_vec3f* v, ma_vec3f value)
|
|
{
|
|
v->v = value;
|
|
v->lock = 0;
|
|
}
|
|
|
|
MA_API void ma_atomic_vec3f_set(ma_atomic_vec3f* v, ma_vec3f value)
|
|
{
|
|
ma_spinlock_lock(&v->lock);
|
|
{
|
|
v->v = value;
|
|
}
|
|
ma_spinlock_unlock(&v->lock);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_atomic_vec3f_get(ma_atomic_vec3f* v)
|
|
{
|
|
ma_vec3f r;
|
|
|
|
ma_spinlock_lock(&v->lock);
|
|
{
|
|
r = v->v;
|
|
}
|
|
ma_spinlock_unlock(&v->lock);
|
|
|
|
return r;
|
|
}
|
|
|
|
static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode);
|
|
static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition);
|
|
|
|
#ifndef MA_DEFAULT_SPEED_OF_SOUND
|
|
#define MA_DEFAULT_SPEED_OF_SOUND 343.3f
|
|
#endif
|
|
|
|
static ma_vec3f g_maChannelDirections[MA_CHANNEL_POSITION_COUNT] = {
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{-0.7071f, 0.0f, -0.7071f },
|
|
{+0.7071f, 0.0f, -0.7071f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{-0.7071f, 0.0f, +0.7071f },
|
|
{+0.7071f, 0.0f, +0.7071f },
|
|
{-0.3162f, 0.0f, -0.9487f },
|
|
{+0.3162f, 0.0f, -0.9487f },
|
|
{ 0.0f, 0.0f, +1.0f },
|
|
{-1.0f, 0.0f, 0.0f },
|
|
{+1.0f, 0.0f, 0.0f },
|
|
{ 0.0f, +1.0f, 0.0f },
|
|
{-0.5774f, +0.5774f, -0.5774f },
|
|
{ 0.0f, +0.7071f, -0.7071f },
|
|
{+0.5774f, +0.5774f, -0.5774f },
|
|
{-0.5774f, +0.5774f, +0.5774f },
|
|
{ 0.0f, +0.7071f, +0.7071f },
|
|
{+0.5774f, +0.5774f, +0.5774f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f },
|
|
{ 0.0f, 0.0f, -1.0f }
|
|
};
|
|
|
|
static ma_vec3f ma_get_channel_direction(ma_channel channel)
|
|
{
|
|
if (channel >= MA_CHANNEL_POSITION_COUNT) {
|
|
return ma_vec3f_init_3f(0, 0, -1);
|
|
} else {
|
|
return g_maChannelDirections[channel];
|
|
}
|
|
}
|
|
|
|
static float ma_attenuation_inverse(float distance, float minDistance, float maxDistance, float rolloff)
|
|
{
|
|
if (minDistance >= maxDistance) {
|
|
return 1;
|
|
}
|
|
|
|
return minDistance / (minDistance + rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance));
|
|
}
|
|
|
|
static float ma_attenuation_linear(float distance, float minDistance, float maxDistance, float rolloff)
|
|
{
|
|
if (minDistance >= maxDistance) {
|
|
return 1;
|
|
}
|
|
|
|
return 1 - rolloff * (ma_clamp(distance, minDistance, maxDistance) - minDistance) / (maxDistance - minDistance);
|
|
}
|
|
|
|
static float ma_attenuation_exponential(float distance, float minDistance, float maxDistance, float rolloff)
|
|
{
|
|
if (minDistance >= maxDistance) {
|
|
return 1;
|
|
}
|
|
|
|
return (float)ma_powd(ma_clamp(distance, minDistance, maxDistance) / minDistance, -rolloff);
|
|
}
|
|
|
|
static float ma_doppler_pitch(ma_vec3f relativePosition, ma_vec3f sourceVelocity, ma_vec3f listenVelocity, float speedOfSound, float dopplerFactor)
|
|
{
|
|
float len;
|
|
float vls;
|
|
float vss;
|
|
|
|
len = ma_vec3f_len(relativePosition);
|
|
|
|
if (len == 0) {
|
|
return 1.0;
|
|
}
|
|
|
|
vls = ma_vec3f_dot(relativePosition, listenVelocity) / len;
|
|
vss = ma_vec3f_dot(relativePosition, sourceVelocity) / len;
|
|
|
|
vls = ma_min(vls, speedOfSound / dopplerFactor);
|
|
vss = ma_min(vss, speedOfSound / dopplerFactor);
|
|
|
|
return (speedOfSound - dopplerFactor*vls) / (speedOfSound - dopplerFactor*vss);
|
|
}
|
|
|
|
static void ma_get_default_channel_map_for_spatializer(ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channelCount)
|
|
{
|
|
if (channelCount == 2) {
|
|
pChannelMap[0] = MA_CHANNEL_SIDE_LEFT;
|
|
pChannelMap[1] = MA_CHANNEL_SIDE_RIGHT;
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channelCount);
|
|
}
|
|
}
|
|
|
|
MA_API ma_spatializer_listener_config ma_spatializer_listener_config_init(ma_uint32 channelsOut)
|
|
{
|
|
ma_spatializer_listener_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.channelsOut = channelsOut;
|
|
config.pChannelMapOut = NULL;
|
|
config.handedness = ma_handedness_right;
|
|
config.worldUp = ma_vec3f_init_3f(0, 1, 0);
|
|
config.coneInnerAngleInRadians = 6.283185f;
|
|
config.coneOuterAngleInRadians = 6.283185f;
|
|
config.coneOuterGain = 0;
|
|
config.speedOfSound = 343.3f;
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t channelMapOutOffset;
|
|
} ma_spatializer_listener_heap_layout;
|
|
|
|
static ma_result ma_spatializer_listener_get_heap_layout(const ma_spatializer_listener_config* pConfig, ma_spatializer_listener_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channelsOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapOut) * pConfig->channelsOut);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_listener_get_heap_size(const ma_spatializer_listener_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_spatializer_listener_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_listener_init_preallocated(const ma_spatializer_listener_config* pConfig, void* pHeap, ma_spatializer_listener* pListener)
|
|
{
|
|
ma_result result;
|
|
ma_spatializer_listener_heap_layout heapLayout;
|
|
|
|
if (pListener == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pListener);
|
|
|
|
result = ma_spatializer_listener_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pListener->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pListener->config = *pConfig;
|
|
ma_atomic_vec3f_init(&pListener->position, ma_vec3f_init_3f(0, 0, 0));
|
|
ma_atomic_vec3f_init(&pListener->direction, ma_vec3f_init_3f(0, 0, -1));
|
|
ma_atomic_vec3f_init(&pListener->velocity, ma_vec3f_init_3f(0, 0, 0));
|
|
pListener->isEnabled = MA_TRUE;
|
|
|
|
if (pListener->config.handedness == ma_handedness_left) {
|
|
ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_listener_get_direction(pListener));
|
|
ma_spatializer_listener_set_direction(pListener, negDir.x, negDir.y, negDir.z);
|
|
}
|
|
|
|
pListener->config.pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
|
|
|
|
if (pConfig->pChannelMapOut == NULL) {
|
|
ma_get_default_channel_map_for_spatializer(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->channelsOut);
|
|
} else {
|
|
ma_channel_map_copy_or_default(pListener->config.pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_listener_init(const ma_spatializer_listener_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer_listener* pListener)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_spatializer_listener_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_spatializer_listener_init_preallocated(pConfig, pHeap, pListener);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pListener->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_uninit(ma_spatializer_listener* pListener, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pListener->_ownsHeap) {
|
|
ma_free(pListener->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_channel* ma_spatializer_listener_get_channel_map(ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pListener->config.pChannelMapOut;
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_cone(ma_spatializer_listener* pListener, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
pListener->config.coneInnerAngleInRadians = innerAngleInRadians;
|
|
pListener->config.coneOuterAngleInRadians = outerAngleInRadians;
|
|
pListener->config.coneOuterGain = outerGain;
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_get_cone(const ma_spatializer_listener* pListener, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pInnerAngleInRadians != NULL) {
|
|
*pInnerAngleInRadians = pListener->config.coneInnerAngleInRadians;
|
|
}
|
|
|
|
if (pOuterAngleInRadians != NULL) {
|
|
*pOuterAngleInRadians = pListener->config.coneOuterAngleInRadians;
|
|
}
|
|
|
|
if (pOuterGain != NULL) {
|
|
*pOuterGain = pListener->config.coneOuterGain;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_position(ma_spatializer_listener* pListener, float x, float y, float z)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_vec3f_set(&pListener->position, ma_vec3f_init_3f(x, y, z));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_listener_get_position(const ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->position);
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_direction(ma_spatializer_listener* pListener, float x, float y, float z)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_vec3f_set(&pListener->direction, ma_vec3f_init_3f(x, y, z));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_listener_get_direction(const ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, -1);
|
|
}
|
|
|
|
return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->direction);
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_velocity(ma_spatializer_listener* pListener, float x, float y, float z)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_vec3f_set(&pListener->velocity, ma_vec3f_init_3f(x, y, z));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_listener_get_velocity(const ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pListener->velocity);
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_speed_of_sound(ma_spatializer_listener* pListener, float speedOfSound)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
pListener->config.speedOfSound = speedOfSound;
|
|
}
|
|
|
|
MA_API float ma_spatializer_listener_get_speed_of_sound(const ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pListener->config.speedOfSound;
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_world_up(ma_spatializer_listener* pListener, float x, float y, float z)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
pListener->config.worldUp = ma_vec3f_init_3f(x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_listener_get_world_up(const ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return ma_vec3f_init_3f(0, 1, 0);
|
|
}
|
|
|
|
return pListener->config.worldUp;
|
|
}
|
|
|
|
MA_API void ma_spatializer_listener_set_enabled(ma_spatializer_listener* pListener, ma_bool32 isEnabled)
|
|
{
|
|
if (pListener == NULL) {
|
|
return;
|
|
}
|
|
|
|
pListener->isEnabled = isEnabled;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_spatializer_listener_is_enabled(const ma_spatializer_listener* pListener)
|
|
{
|
|
if (pListener == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return pListener->isEnabled;
|
|
}
|
|
|
|
MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma_uint32 channelsOut)
|
|
{
|
|
ma_spatializer_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.channelsIn = channelsIn;
|
|
config.channelsOut = channelsOut;
|
|
config.pChannelMapIn = NULL;
|
|
config.attenuationModel = ma_attenuation_model_inverse;
|
|
config.positioning = ma_positioning_absolute;
|
|
config.handedness = ma_handedness_right;
|
|
config.minGain = 0;
|
|
config.maxGain = 1;
|
|
config.minDistance = 1;
|
|
config.maxDistance = MA_FLT_MAX;
|
|
config.rolloff = 1;
|
|
config.coneInnerAngleInRadians = 6.283185f;
|
|
config.coneOuterAngleInRadians = 6.283185f;
|
|
config.coneOuterGain = 0.0f;
|
|
config.dopplerFactor = 1;
|
|
config.directionalAttenuationFactor = 1;
|
|
config.minSpatializationChannelGain = 0.2f;
|
|
config.gainSmoothTimeInFrames = 360;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_gainer_config ma_spatializer_gainer_config_init(const ma_spatializer_config* pConfig)
|
|
{
|
|
MA_ASSERT(pConfig != NULL);
|
|
return ma_gainer_config_init(pConfig->channelsOut, pConfig->gainSmoothTimeInFrames);
|
|
}
|
|
|
|
static ma_result ma_spatializer_validate_config(const ma_spatializer_config* pConfig)
|
|
{
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t channelMapInOffset;
|
|
size_t newChannelGainsOffset;
|
|
size_t gainerOffset;
|
|
} ma_spatializer_heap_layout;
|
|
|
|
static ma_result ma_spatializer_get_heap_layout(const ma_spatializer_config* pConfig, ma_spatializer_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_spatializer_validate_config(pConfig);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->channelMapInOffset = MA_SIZE_MAX;
|
|
if (pConfig->pChannelMapIn != NULL) {
|
|
pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(sizeof(*pConfig->pChannelMapIn) * pConfig->channelsIn);
|
|
}
|
|
|
|
pHeapLayout->newChannelGainsOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(sizeof(float) * pConfig->channelsOut);
|
|
|
|
{
|
|
size_t gainerHeapSizeInBytes;
|
|
ma_gainer_config gainerConfig;
|
|
|
|
gainerConfig = ma_spatializer_gainer_config_init(pConfig);
|
|
|
|
result = ma_gainer_get_heap_size(&gainerConfig, &gainerHeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(gainerHeapSizeInBytes);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_get_heap_size(const ma_spatializer_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_spatializer_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* pConfig, void* pHeap, ma_spatializer* pSpatializer)
|
|
{
|
|
ma_result result;
|
|
ma_spatializer_heap_layout heapLayout;
|
|
ma_gainer_config gainerConfig;
|
|
|
|
if (pSpatializer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pSpatializer);
|
|
|
|
if (pConfig == NULL || pHeap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_spatializer_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pSpatializer->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pSpatializer->channelsIn = pConfig->channelsIn;
|
|
pSpatializer->channelsOut = pConfig->channelsOut;
|
|
pSpatializer->attenuationModel = pConfig->attenuationModel;
|
|
pSpatializer->positioning = pConfig->positioning;
|
|
pSpatializer->handedness = pConfig->handedness;
|
|
pSpatializer->minGain = pConfig->minGain;
|
|
pSpatializer->maxGain = pConfig->maxGain;
|
|
pSpatializer->minDistance = pConfig->minDistance;
|
|
pSpatializer->maxDistance = pConfig->maxDistance;
|
|
pSpatializer->rolloff = pConfig->rolloff;
|
|
pSpatializer->coneInnerAngleInRadians = pConfig->coneInnerAngleInRadians;
|
|
pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians;
|
|
pSpatializer->coneOuterGain = pConfig->coneOuterGain;
|
|
pSpatializer->dopplerFactor = pConfig->dopplerFactor;
|
|
pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain;
|
|
pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor;
|
|
pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames;
|
|
ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0));
|
|
ma_atomic_vec3f_init(&pSpatializer->direction, ma_vec3f_init_3f(0, 0, -1));
|
|
ma_atomic_vec3f_init(&pSpatializer->velocity, ma_vec3f_init_3f(0, 0, 0));
|
|
pSpatializer->dopplerPitch = 1;
|
|
|
|
if (pSpatializer->handedness == ma_handedness_left) {
|
|
ma_vec3f negDir = ma_vec3f_neg(ma_spatializer_get_direction(pSpatializer));
|
|
ma_spatializer_set_direction(pSpatializer, negDir.x, negDir.y, negDir.z);
|
|
}
|
|
|
|
if (pConfig->pChannelMapIn != NULL) {
|
|
pSpatializer->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
|
|
ma_channel_map_copy_or_default(pSpatializer->pChannelMapIn, pSpatializer->channelsIn, pConfig->pChannelMapIn, pSpatializer->channelsIn);
|
|
}
|
|
|
|
pSpatializer->pNewChannelGainsOut = (float*)ma_offset_ptr(pHeap, heapLayout.newChannelGainsOffset);
|
|
|
|
gainerConfig = ma_spatializer_gainer_config_init(pConfig);
|
|
|
|
result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pSpatializer->gainer);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_init(const ma_spatializer_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_spatializer* pSpatializer)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_spatializer_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_spatializer_init_preallocated(pConfig, pHeap, pSpatializer);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pSpatializer->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_spatializer_uninit(ma_spatializer* pSpatializer, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_gainer_uninit(&pSpatializer->gainer, pAllocationCallbacks);
|
|
|
|
if (pSpatializer->_ownsHeap) {
|
|
ma_free(pSpatializer->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
static float ma_calculate_angular_gain(ma_vec3f dirA, ma_vec3f dirB, float coneInnerAngleInRadians, float coneOuterAngleInRadians, float coneOuterGain)
|
|
{
|
|
|
|
if (coneInnerAngleInRadians < 6.283185f) {
|
|
float angularGain = 1;
|
|
float cutoffInner = (float)ma_cosd(coneInnerAngleInRadians*0.5f);
|
|
float cutoffOuter = (float)ma_cosd(coneOuterAngleInRadians*0.5f);
|
|
float d;
|
|
|
|
d = ma_vec3f_dot(dirA, dirB);
|
|
|
|
if (d > cutoffInner) {
|
|
angularGain = 1;
|
|
} else {
|
|
if (d > cutoffOuter) {
|
|
angularGain = ma_mix_f32(coneOuterGain, 1, (d - cutoffOuter) / (cutoffInner - cutoffOuter));
|
|
} else {
|
|
angularGain = coneOuterGain;
|
|
}
|
|
}
|
|
|
|
return angularGain;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, ma_spatializer_listener* pListener, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_channel* pChannelMapIn;
|
|
ma_channel* pChannelMapOut;
|
|
|
|
if (pSpatializer == NULL || pListener == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pChannelMapIn = pSpatializer->pChannelMapIn;
|
|
pChannelMapOut = pListener->config.pChannelMapOut;
|
|
|
|
if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) {
|
|
if (ma_spatializer_listener_is_enabled(pListener)) {
|
|
if (pSpatializer->channelsIn == pSpatializer->channelsOut) {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, pSpatializer->channelsIn);
|
|
} else {
|
|
ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, pSpatializer->channelsOut, (const float*)pFramesIn, pChannelMapIn, pSpatializer->channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default);
|
|
}
|
|
} else {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
|
|
}
|
|
|
|
pSpatializer->dopplerPitch = 1;
|
|
} else {
|
|
ma_vec3f relativePosNormalized;
|
|
ma_vec3f relativePos;
|
|
ma_vec3f relativeDir;
|
|
ma_vec3f listenerVel;
|
|
float speedOfSound;
|
|
float distance = 0;
|
|
float gain = 1;
|
|
ma_uint32 iChannel;
|
|
const ma_uint32 channelsOut = pSpatializer->channelsOut;
|
|
const ma_uint32 channelsIn = pSpatializer->channelsIn;
|
|
float minDistance = ma_spatializer_get_min_distance(pSpatializer);
|
|
float maxDistance = ma_spatializer_get_max_distance(pSpatializer);
|
|
float rolloff = ma_spatializer_get_rolloff(pSpatializer);
|
|
float dopplerFactor = ma_spatializer_get_doppler_factor(pSpatializer);
|
|
|
|
listenerVel = ma_spatializer_listener_get_velocity(pListener);
|
|
speedOfSound = pListener->config.speedOfSound;
|
|
|
|
if (ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
|
|
relativePos = ma_spatializer_get_position(pSpatializer);
|
|
relativeDir = ma_spatializer_get_direction(pSpatializer);
|
|
} else {
|
|
ma_spatializer_get_relative_position_and_direction(pSpatializer, pListener, &relativePos, &relativeDir);
|
|
}
|
|
|
|
distance = ma_vec3f_len(relativePos);
|
|
|
|
switch (ma_spatializer_get_attenuation_model(pSpatializer)) {
|
|
case ma_attenuation_model_inverse:
|
|
{
|
|
gain = ma_attenuation_inverse(distance, minDistance, maxDistance, rolloff);
|
|
} break;
|
|
case ma_attenuation_model_linear:
|
|
{
|
|
gain = ma_attenuation_linear(distance, minDistance, maxDistance, rolloff);
|
|
} break;
|
|
case ma_attenuation_model_exponential:
|
|
{
|
|
gain = ma_attenuation_exponential(distance, minDistance, maxDistance, rolloff);
|
|
} break;
|
|
case ma_attenuation_model_none:
|
|
default:
|
|
{
|
|
gain = 1;
|
|
} break;
|
|
}
|
|
|
|
if (distance > 0.001f) {
|
|
float distanceInv = 1/distance;
|
|
relativePosNormalized = relativePos;
|
|
relativePosNormalized.x *= distanceInv;
|
|
relativePosNormalized.y *= distanceInv;
|
|
relativePosNormalized.z *= distanceInv;
|
|
} else {
|
|
distance = 0;
|
|
relativePosNormalized = ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
if (distance > 0) {
|
|
float spatializerConeInnerAngle;
|
|
float spatializerConeOuterAngle;
|
|
float spatializerConeOuterGain;
|
|
ma_spatializer_get_cone(pSpatializer, &spatializerConeInnerAngle, &spatializerConeOuterAngle, &spatializerConeOuterGain);
|
|
|
|
gain *= ma_calculate_angular_gain(relativeDir, ma_vec3f_neg(relativePosNormalized), spatializerConeInnerAngle, spatializerConeOuterAngle, spatializerConeOuterGain);
|
|
|
|
if (pListener != NULL && pListener->config.coneInnerAngleInRadians < 6.283185f) {
|
|
ma_vec3f listenerDirection;
|
|
float listenerInnerAngle;
|
|
float listenerOuterAngle;
|
|
float listenerOuterGain;
|
|
|
|
if (pListener->config.handedness == ma_handedness_right) {
|
|
listenerDirection = ma_vec3f_init_3f(0, 0, -1);
|
|
} else {
|
|
listenerDirection = ma_vec3f_init_3f(0, 0, +1);
|
|
}
|
|
|
|
listenerInnerAngle = pListener->config.coneInnerAngleInRadians;
|
|
listenerOuterAngle = pListener->config.coneOuterAngleInRadians;
|
|
listenerOuterGain = pListener->config.coneOuterGain;
|
|
|
|
gain *= ma_calculate_angular_gain(listenerDirection, relativePosNormalized, listenerInnerAngle, listenerOuterAngle, listenerOuterGain);
|
|
}
|
|
} else {
|
|
}
|
|
|
|
gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer));
|
|
|
|
for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
|
|
pSpatializer->pNewChannelGainsOut[iChannel] = gain;
|
|
}
|
|
|
|
if (ma_spatializer_listener_is_enabled(pListener)) {
|
|
ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default);
|
|
} else {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut);
|
|
}
|
|
|
|
MA_ASSERT(pChannelMapOut != NULL);
|
|
|
|
if (distance > 0) {
|
|
ma_vec3f unitPos = relativePos;
|
|
float distanceInv = 1/distance;
|
|
unitPos.x *= distanceInv;
|
|
unitPos.y *= distanceInv;
|
|
unitPos.z *= distanceInv;
|
|
|
|
for (iChannel = 0; iChannel < channelsOut; iChannel += 1) {
|
|
ma_channel channelOut;
|
|
float d;
|
|
float dMin;
|
|
|
|
channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannel);
|
|
if (ma_is_spatial_channel_position(channelOut)) {
|
|
d = ma_mix_f32_fast(1, ma_vec3f_dot(unitPos, ma_get_channel_direction(channelOut)), ma_spatializer_get_directional_attenuation_factor(pSpatializer));
|
|
} else {
|
|
d = 1;
|
|
}
|
|
|
|
dMin = pSpatializer->minSpatializationChannelGain;
|
|
|
|
#if 1
|
|
{
|
|
d = (d + 1) * 0.5f;
|
|
d = ma_max(d, dMin);
|
|
pSpatializer->pNewChannelGainsOut[iChannel] *= d;
|
|
}
|
|
#else
|
|
{
|
|
if (d < 0) {
|
|
d += 1;
|
|
d = ma_max(d, dMin);
|
|
channelGainsOut[iChannel] *= d;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
} else {
|
|
}
|
|
|
|
ma_gainer_set_gains(&pSpatializer->gainer, pSpatializer->pNewChannelGainsOut);
|
|
ma_gainer_process_pcm_frames(&pSpatializer->gainer, pFramesOut, pFramesOut, frameCount);
|
|
|
|
if (dopplerFactor > 0) {
|
|
pSpatializer->dopplerPitch = ma_doppler_pitch(ma_vec3f_sub(ma_spatializer_listener_get_position(pListener), ma_spatializer_get_position(pSpatializer)), ma_spatializer_get_velocity(pSpatializer), listenerVel, speedOfSound, dopplerFactor);
|
|
} else {
|
|
pSpatializer->dopplerPitch = 1;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_set_master_volume(ma_spatializer* pSpatializer, float volume)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_gainer_set_master_volume(&pSpatializer->gainer, volume);
|
|
}
|
|
|
|
MA_API ma_result ma_spatializer_get_master_volume(const ma_spatializer* pSpatializer, float* pVolume)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_gainer_get_master_volume(&pSpatializer->gainer, pVolume);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_spatializer_get_input_channels(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pSpatializer->channelsIn;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_spatializer_get_output_channels(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pSpatializer->channelsOut;
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, ma_attenuation_model attenuationModel)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel);
|
|
}
|
|
|
|
MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return ma_attenuation_model_none;
|
|
}
|
|
|
|
return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_i32(&pSpatializer->positioning, positioning);
|
|
}
|
|
|
|
MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return ma_positioning_absolute;
|
|
}
|
|
|
|
return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->rolloff);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->minGain, minGain);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->minGain);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->maxGain);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->minDistance);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->maxDistance);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians);
|
|
ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians);
|
|
ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain);
|
|
}
|
|
|
|
MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pInnerAngleInRadians != NULL) {
|
|
*pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians);
|
|
}
|
|
|
|
if (pOuterAngleInRadians != NULL) {
|
|
*pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians);
|
|
}
|
|
|
|
if (pOuterGain != NULL) {
|
|
*pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, float dopplerFactor)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->dopplerFactor);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor);
|
|
}
|
|
|
|
MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_vec3f_set(&pSpatializer->position, ma_vec3f_init_3f(x, y, z));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_get_position(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->position);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_direction(ma_spatializer* pSpatializer, float x, float y, float z)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_vec3f_set(&pSpatializer->direction, ma_vec3f_init_3f(x, y, z));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_get_direction(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, -1);
|
|
}
|
|
|
|
return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->direction);
|
|
}
|
|
|
|
MA_API void ma_spatializer_set_velocity(ma_spatializer* pSpatializer, float x, float y, float z)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_vec3f_set(&pSpatializer->velocity, ma_vec3f_init_3f(x, y, z));
|
|
}
|
|
|
|
MA_API ma_vec3f ma_spatializer_get_velocity(const ma_spatializer* pSpatializer)
|
|
{
|
|
if (pSpatializer == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_atomic_vec3f_get((ma_atomic_vec3f*)&pSpatializer->velocity);
|
|
}
|
|
|
|
MA_API void ma_spatializer_get_relative_position_and_direction(const ma_spatializer* pSpatializer, const ma_spatializer_listener* pListener, ma_vec3f* pRelativePos, ma_vec3f* pRelativeDir)
|
|
{
|
|
if (pRelativePos != NULL) {
|
|
pRelativePos->x = 0;
|
|
pRelativePos->y = 0;
|
|
pRelativePos->z = 0;
|
|
}
|
|
|
|
if (pRelativeDir != NULL) {
|
|
pRelativeDir->x = 0;
|
|
pRelativeDir->y = 0;
|
|
pRelativeDir->z = -1;
|
|
}
|
|
|
|
if (pSpatializer == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pListener == NULL || ma_spatializer_get_positioning(pSpatializer) == ma_positioning_relative) {
|
|
if (pRelativePos != NULL) {
|
|
*pRelativePos = ma_spatializer_get_position(pSpatializer);
|
|
}
|
|
if (pRelativeDir != NULL) {
|
|
*pRelativeDir = ma_spatializer_get_direction(pSpatializer);
|
|
}
|
|
} else {
|
|
ma_vec3f spatializerPosition;
|
|
ma_vec3f spatializerDirection;
|
|
ma_vec3f listenerPosition;
|
|
ma_vec3f listenerDirection;
|
|
ma_vec3f v;
|
|
ma_vec3f axisX;
|
|
ma_vec3f axisY;
|
|
ma_vec3f axisZ;
|
|
float m[4][4];
|
|
|
|
spatializerPosition = ma_spatializer_get_position(pSpatializer);
|
|
spatializerDirection = ma_spatializer_get_direction(pSpatializer);
|
|
listenerPosition = ma_spatializer_listener_get_position(pListener);
|
|
listenerDirection = ma_spatializer_listener_get_direction(pListener);
|
|
|
|
axisZ = ma_vec3f_normalize(listenerDirection);
|
|
axisX = ma_vec3f_normalize(ma_vec3f_cross(axisZ, pListener->config.worldUp));
|
|
|
|
if (ma_vec3f_len2(axisX) == 0) {
|
|
axisX = ma_vec3f_init_3f(1, 0, 0);
|
|
}
|
|
|
|
axisY = ma_vec3f_cross(axisX, axisZ);
|
|
|
|
if (pListener->config.handedness == ma_handedness_left) {
|
|
axisX = ma_vec3f_neg(axisX);
|
|
}
|
|
|
|
m[0][0] = axisX.x; m[1][0] = axisX.y; m[2][0] = axisX.z; m[3][0] = -ma_vec3f_dot(axisX, listenerPosition);
|
|
m[0][1] = axisY.x; m[1][1] = axisY.y; m[2][1] = axisY.z; m[3][1] = -ma_vec3f_dot(axisY, listenerPosition);
|
|
m[0][2] = -axisZ.x; m[1][2] = -axisZ.y; m[2][2] = -axisZ.z; m[3][2] = -ma_vec3f_dot(ma_vec3f_neg(axisZ), listenerPosition);
|
|
m[0][3] = 0; m[1][3] = 0; m[2][3] = 0; m[3][3] = 1;
|
|
|
|
if (pRelativePos != NULL) {
|
|
v = spatializerPosition;
|
|
pRelativePos->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * 1;
|
|
pRelativePos->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * 1;
|
|
pRelativePos->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * 1;
|
|
}
|
|
|
|
if (pRelativeDir != NULL) {
|
|
v = spatializerDirection;
|
|
pRelativeDir->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z;
|
|
pRelativeDir->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z;
|
|
pRelativeDir->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API ma_linear_resampler_config ma_linear_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
|
{
|
|
ma_linear_resampler_config config;
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRateIn = sampleRateIn;
|
|
config.sampleRateOut = sampleRateOut;
|
|
config.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
|
|
config.lpfNyquistFactor = 1;
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t x0Offset;
|
|
size_t x1Offset;
|
|
size_t lpfOffset;
|
|
} ma_linear_resampler_heap_layout;
|
|
|
|
static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
|
|
{
|
|
ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut;
|
|
ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;
|
|
|
|
pResampler->inTimeFrac =
|
|
(oldRateTimeWhole * newSampleRateOut) +
|
|
((oldRateTimeFract * newSampleRateOut) / oldSampleRateOut);
|
|
|
|
pResampler->inTimeInt += pResampler->inTimeFrac / pResampler->config.sampleRateOut;
|
|
pResampler->inTimeFrac = pResampler->inTimeFrac % pResampler->config.sampleRateOut;
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_set_rate_internal(ma_linear_resampler* pResampler, void* pHeap, ma_linear_resampler_heap_layout* pHeapLayout, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_bool32 isResamplerAlreadyInitialized)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 gcf;
|
|
ma_uint32 lpfSampleRate;
|
|
double lpfCutoffFrequency;
|
|
ma_lpf_config lpfConfig;
|
|
ma_uint32 oldSampleRateOut;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (sampleRateIn == 0 || sampleRateOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
oldSampleRateOut = pResampler->config.sampleRateOut;
|
|
|
|
pResampler->config.sampleRateIn = sampleRateIn;
|
|
pResampler->config.sampleRateOut = sampleRateOut;
|
|
|
|
gcf = ma_gcf_u32(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut);
|
|
pResampler->config.sampleRateIn /= gcf;
|
|
pResampler->config.sampleRateOut /= gcf;
|
|
|
|
if (pResampler->config.lpfOrder > MA_MAX_FILTER_ORDER) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
lpfSampleRate = (ma_uint32)(ma_max(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut));
|
|
lpfCutoffFrequency = ( double)(ma_min(pResampler->config.sampleRateIn, pResampler->config.sampleRateOut) * 0.5 * pResampler->config.lpfNyquistFactor);
|
|
|
|
lpfConfig = ma_lpf_config_init(pResampler->config.format, pResampler->config.channels, lpfSampleRate, lpfCutoffFrequency, pResampler->config.lpfOrder);
|
|
|
|
if (isResamplerAlreadyInitialized) {
|
|
result = ma_lpf_reinit(&lpfConfig, &pResampler->lpf);
|
|
} else {
|
|
result = ma_lpf_init_preallocated(&lpfConfig, ma_offset_ptr(pHeap, pHeapLayout->lpfOffset), &pResampler->lpf);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pResampler->inAdvanceInt = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
|
|
pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;
|
|
|
|
ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format != ma_format_f32 && pConfig->format != ma_format_s16) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->x0Offset = pHeapLayout->sizeInBytes;
|
|
if (pConfig->format == ma_format_f32) {
|
|
pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
|
|
} else {
|
|
pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
|
|
}
|
|
|
|
pHeapLayout->x1Offset = pHeapLayout->sizeInBytes;
|
|
if (pConfig->format == ma_format_f32) {
|
|
pHeapLayout->sizeInBytes += sizeof(float) * pConfig->channels;
|
|
} else {
|
|
pHeapLayout->sizeInBytes += sizeof(ma_int16) * pConfig->channels;
|
|
}
|
|
|
|
pHeapLayout->lpfOffset = ma_align_64(pHeapLayout->sizeInBytes);
|
|
{
|
|
ma_result result;
|
|
size_t lpfHeapSizeInBytes;
|
|
ma_lpf_config lpfConfig = ma_lpf_config_init(pConfig->format, pConfig->channels, 1, 1, pConfig->lpfOrder);
|
|
|
|
result = ma_lpf_get_heap_size(&lpfConfig, &lpfHeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += lpfHeapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_get_heap_size(const ma_linear_resampler_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_linear_resampler_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_init_preallocated(const ma_linear_resampler_config* pConfig, void* pHeap, ma_linear_resampler* pResampler)
|
|
{
|
|
ma_result result;
|
|
ma_linear_resampler_heap_layout heapLayout;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pResampler);
|
|
|
|
result = ma_linear_resampler_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pResampler->config = *pConfig;
|
|
|
|
pResampler->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
if (pConfig->format == ma_format_f32) {
|
|
pResampler->x0.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
|
|
pResampler->x1.f32 = (float*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
|
|
} else {
|
|
pResampler->x0.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x0Offset);
|
|
pResampler->x1.s16 = (ma_int16*)ma_offset_ptr(pHeap, heapLayout.x1Offset);
|
|
}
|
|
|
|
result = ma_linear_resampler_set_rate_internal(pResampler, pHeap, &heapLayout, pConfig->sampleRateIn, pConfig->sampleRateOut, MA_FALSE);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pResampler->inTimeInt = 1;
|
|
pResampler->inTimeFrac = 0;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_linear_resampler* pResampler)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_linear_resampler_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_linear_resampler_init_preallocated(pConfig, pHeap, pResampler);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pResampler->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_linear_resampler_uninit(ma_linear_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_lpf_uninit(&pResampler->lpf, pAllocationCallbacks);
|
|
|
|
if (pResampler->_ownsHeap) {
|
|
ma_free(pResampler->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_linear_resampler_mix_s16(ma_int16 x, ma_int16 y, ma_int32 a, const ma_int32 shift)
|
|
{
|
|
ma_int32 b;
|
|
ma_int32 c;
|
|
ma_int32 r;
|
|
|
|
MA_ASSERT(a <= (1<<shift));
|
|
|
|
b = x * ((1<<shift) - a);
|
|
c = y * a;
|
|
r = b + c;
|
|
|
|
return (ma_int16)(r >> shift);
|
|
}
|
|
|
|
static void ma_linear_resampler_interpolate_frame_s16(ma_linear_resampler* pResampler, ma_int16* MA_RESTRICT pFrameOut)
|
|
{
|
|
ma_uint32 c;
|
|
ma_uint32 a;
|
|
const ma_uint32 channels = pResampler->config.channels;
|
|
const ma_uint32 shift = 12;
|
|
|
|
MA_ASSERT(pResampler != NULL);
|
|
MA_ASSERT(pFrameOut != NULL);
|
|
|
|
a = (pResampler->inTimeFrac << shift) / pResampler->config.sampleRateOut;
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
ma_int16 s = ma_linear_resampler_mix_s16(pResampler->x0.s16[c], pResampler->x1.s16[c], a, shift);
|
|
pFrameOut[c] = s;
|
|
}
|
|
}
|
|
|
|
static void ma_linear_resampler_interpolate_frame_f32(ma_linear_resampler* pResampler, float* MA_RESTRICT pFrameOut)
|
|
{
|
|
ma_uint32 c;
|
|
float a;
|
|
const ma_uint32 channels = pResampler->config.channels;
|
|
|
|
MA_ASSERT(pResampler != NULL);
|
|
MA_ASSERT(pFrameOut != NULL);
|
|
|
|
a = (float)pResampler->inTimeFrac / pResampler->config.sampleRateOut;
|
|
|
|
MA_ASSUME(channels > 0);
|
|
for (c = 0; c < channels; c += 1) {
|
|
float s = ma_mix_f32_fast(pResampler->x0.f32[c], pResampler->x1.f32[c], a);
|
|
pFrameOut[c] = s;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
const ma_int16* pFramesInS16;
|
|
ma_int16* pFramesOutS16;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
|
|
MA_ASSERT(pResampler != NULL);
|
|
MA_ASSERT(pFrameCountIn != NULL);
|
|
MA_ASSERT(pFrameCountOut != NULL);
|
|
|
|
pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
frameCountIn = *pFrameCountIn;
|
|
frameCountOut = *pFrameCountOut;
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
|
|
ma_uint32 iChannel;
|
|
|
|
if (pFramesInS16 != NULL) {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
|
|
pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
|
|
}
|
|
pFramesInS16 += pResampler->config.channels;
|
|
} else {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
|
|
pResampler->x1.s16[iChannel] = 0;
|
|
}
|
|
}
|
|
|
|
if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
|
|
ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16);
|
|
}
|
|
|
|
framesProcessedIn += 1;
|
|
pResampler->inTimeInt -= 1;
|
|
}
|
|
|
|
if (pResampler->inTimeInt > 0) {
|
|
break;
|
|
}
|
|
|
|
if (pFramesOutS16 != NULL) {
|
|
MA_ASSERT(pResampler->inTimeInt == 0);
|
|
ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
|
|
|
|
pFramesOutS16 += pResampler->config.channels;
|
|
}
|
|
|
|
framesProcessedOut += 1;
|
|
|
|
pResampler->inTimeInt += pResampler->inAdvanceInt;
|
|
pResampler->inTimeFrac += pResampler->inAdvanceFrac;
|
|
if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
|
|
pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
|
|
pResampler->inTimeInt += 1;
|
|
}
|
|
}
|
|
|
|
*pFrameCountIn = framesProcessedIn;
|
|
*pFrameCountOut = framesProcessedOut;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
const ma_int16* pFramesInS16;
|
|
ma_int16* pFramesOutS16;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
|
|
MA_ASSERT(pResampler != NULL);
|
|
MA_ASSERT(pFrameCountIn != NULL);
|
|
MA_ASSERT(pFrameCountOut != NULL);
|
|
|
|
pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
frameCountIn = *pFrameCountIn;
|
|
frameCountOut = *pFrameCountOut;
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
|
|
ma_uint32 iChannel;
|
|
|
|
if (pFramesInS16 != NULL) {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
|
|
pResampler->x1.s16[iChannel] = pFramesInS16[iChannel];
|
|
}
|
|
pFramesInS16 += pResampler->config.channels;
|
|
} else {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.s16[iChannel] = pResampler->x1.s16[iChannel];
|
|
pResampler->x1.s16[iChannel] = 0;
|
|
}
|
|
}
|
|
|
|
framesProcessedIn += 1;
|
|
pResampler->inTimeInt -= 1;
|
|
}
|
|
|
|
if (pResampler->inTimeInt > 0) {
|
|
break;
|
|
}
|
|
|
|
if (pFramesOutS16 != NULL) {
|
|
MA_ASSERT(pResampler->inTimeInt == 0);
|
|
ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16);
|
|
|
|
if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
|
|
ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16);
|
|
}
|
|
|
|
pFramesOutS16 += pResampler->config.channels;
|
|
}
|
|
|
|
framesProcessedOut += 1;
|
|
|
|
pResampler->inTimeInt += pResampler->inAdvanceInt;
|
|
pResampler->inTimeFrac += pResampler->inAdvanceFrac;
|
|
if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
|
|
pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
|
|
pResampler->inTimeInt += 1;
|
|
}
|
|
}
|
|
|
|
*pFrameCountIn = framesProcessedIn;
|
|
*pFrameCountOut = framesProcessedOut;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_process_pcm_frames_s16(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
MA_ASSERT(pResampler != NULL);
|
|
|
|
if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
|
|
return ma_linear_resampler_process_pcm_frames_s16_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
} else {
|
|
return ma_linear_resampler_process_pcm_frames_s16_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
const float* pFramesInF32;
|
|
float* pFramesOutF32;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
|
|
MA_ASSERT(pResampler != NULL);
|
|
MA_ASSERT(pFrameCountIn != NULL);
|
|
MA_ASSERT(pFrameCountOut != NULL);
|
|
|
|
pFramesInF32 = (const float*)pFramesIn;
|
|
pFramesOutF32 = ( float*)pFramesOut;
|
|
frameCountIn = *pFrameCountIn;
|
|
frameCountOut = *pFrameCountOut;
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
|
|
ma_uint32 iChannel;
|
|
|
|
if (pFramesInF32 != NULL) {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
|
|
pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
|
|
}
|
|
pFramesInF32 += pResampler->config.channels;
|
|
} else {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
|
|
pResampler->x1.f32[iChannel] = 0;
|
|
}
|
|
}
|
|
|
|
if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
|
|
ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32);
|
|
}
|
|
|
|
framesProcessedIn += 1;
|
|
pResampler->inTimeInt -= 1;
|
|
}
|
|
|
|
if (pResampler->inTimeInt > 0) {
|
|
break;
|
|
}
|
|
|
|
if (pFramesOutF32 != NULL) {
|
|
MA_ASSERT(pResampler->inTimeInt == 0);
|
|
ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
|
|
|
|
pFramesOutF32 += pResampler->config.channels;
|
|
}
|
|
|
|
framesProcessedOut += 1;
|
|
|
|
pResampler->inTimeInt += pResampler->inAdvanceInt;
|
|
pResampler->inTimeFrac += pResampler->inAdvanceFrac;
|
|
if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
|
|
pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
|
|
pResampler->inTimeInt += 1;
|
|
}
|
|
}
|
|
|
|
*pFrameCountIn = framesProcessedIn;
|
|
*pFrameCountOut = framesProcessedOut;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
const float* pFramesInF32;
|
|
float* pFramesOutF32;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
|
|
MA_ASSERT(pResampler != NULL);
|
|
MA_ASSERT(pFrameCountIn != NULL);
|
|
MA_ASSERT(pFrameCountOut != NULL);
|
|
|
|
pFramesInF32 = (const float*)pFramesIn;
|
|
pFramesOutF32 = ( float*)pFramesOut;
|
|
frameCountIn = *pFrameCountIn;
|
|
frameCountOut = *pFrameCountOut;
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
while (pResampler->inTimeInt > 0 && frameCountIn > framesProcessedIn) {
|
|
ma_uint32 iChannel;
|
|
|
|
if (pFramesInF32 != NULL) {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
|
|
pResampler->x1.f32[iChannel] = pFramesInF32[iChannel];
|
|
}
|
|
pFramesInF32 += pResampler->config.channels;
|
|
} else {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.f32[iChannel] = pResampler->x1.f32[iChannel];
|
|
pResampler->x1.f32[iChannel] = 0;
|
|
}
|
|
}
|
|
|
|
framesProcessedIn += 1;
|
|
pResampler->inTimeInt -= 1;
|
|
}
|
|
|
|
if (pResampler->inTimeInt > 0) {
|
|
break;
|
|
}
|
|
|
|
if (pFramesOutF32 != NULL) {
|
|
MA_ASSERT(pResampler->inTimeInt == 0);
|
|
ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32);
|
|
|
|
if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) {
|
|
ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32);
|
|
}
|
|
|
|
pFramesOutF32 += pResampler->config.channels;
|
|
}
|
|
|
|
framesProcessedOut += 1;
|
|
|
|
pResampler->inTimeInt += pResampler->inAdvanceInt;
|
|
pResampler->inTimeFrac += pResampler->inAdvanceFrac;
|
|
if (pResampler->inTimeFrac >= pResampler->config.sampleRateOut) {
|
|
pResampler->inTimeFrac -= pResampler->config.sampleRateOut;
|
|
pResampler->inTimeInt += 1;
|
|
}
|
|
}
|
|
|
|
*pFrameCountIn = framesProcessedIn;
|
|
*pFrameCountOut = framesProcessedOut;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_linear_resampler_process_pcm_frames_f32(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
MA_ASSERT(pResampler != NULL);
|
|
|
|
if (pResampler->config.sampleRateIn > pResampler->config.sampleRateOut) {
|
|
return ma_linear_resampler_process_pcm_frames_f32_downsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
} else {
|
|
return ma_linear_resampler_process_pcm_frames_f32_upsample(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_process_pcm_frames(ma_linear_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResampler->config.format == ma_format_s16) {
|
|
return ma_linear_resampler_process_pcm_frames_s16(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
} else if (pResampler->config.format == ma_format_f32) {
|
|
return ma_linear_resampler_process_pcm_frames_f32(pResampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
} else {
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_set_rate(ma_linear_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
|
{
|
|
return ma_linear_resampler_set_rate_internal(pResampler, NULL, NULL, sampleRateIn, sampleRateOut, MA_TRUE);
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResampler, float ratioInOut)
|
|
{
|
|
ma_uint32 n;
|
|
ma_uint32 d;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ratioInOut <= 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
d = 1000000;
|
|
n = (ma_uint32)(ratioInOut * d);
|
|
|
|
if (n == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(n != 0);
|
|
|
|
return ma_linear_resampler_set_rate(pResampler, n, d);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_linear_resampler_get_input_latency(const ma_linear_resampler* pResampler)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return 1 + ma_lpf_get_latency(&pResampler->lpf);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_linear_resampler_get_output_latency(const ma_linear_resampler* pResampler)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_linear_resampler_get_input_latency(pResampler) * pResampler->config.sampleRateOut / pResampler->config.sampleRateIn;
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_get_required_input_frame_count(const ma_linear_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
|
|
{
|
|
ma_uint64 inputFrameCount;
|
|
|
|
if (pInputFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pInputFrameCount = 0;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (outputFrameCount == 0) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
inputFrameCount = pResampler->inTimeInt;
|
|
outputFrameCount -= 1;
|
|
|
|
inputFrameCount += outputFrameCount * pResampler->inAdvanceInt;
|
|
inputFrameCount += (pResampler->inTimeFrac + (outputFrameCount * pResampler->inAdvanceFrac)) / pResampler->config.sampleRateOut;
|
|
|
|
*pInputFrameCount = inputFrameCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_get_expected_output_frame_count(const ma_linear_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
|
|
{
|
|
ma_uint64 outputFrameCount;
|
|
ma_uint64 preliminaryInputFrameCountFromFrac;
|
|
ma_uint64 preliminaryInputFrameCount;
|
|
|
|
if (pOutputFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pOutputFrameCount = 0;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
outputFrameCount = (inputFrameCount * pResampler->config.sampleRateOut) / pResampler->config.sampleRateIn;
|
|
|
|
preliminaryInputFrameCountFromFrac = (pResampler->inTimeFrac + outputFrameCount*pResampler->inAdvanceFrac) / pResampler->config.sampleRateOut;
|
|
preliminaryInputFrameCount = (pResampler->inTimeInt + outputFrameCount*pResampler->inAdvanceInt ) + preliminaryInputFrameCountFromFrac;
|
|
|
|
if (preliminaryInputFrameCount <= inputFrameCount) {
|
|
outputFrameCount += 1;
|
|
}
|
|
|
|
*pOutputFrameCount = outputFrameCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_linear_resampler_reset(ma_linear_resampler* pResampler)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pResampler->inTimeInt = 1;
|
|
pResampler->inTimeFrac = 0;
|
|
|
|
if (pResampler->config.format == ma_format_f32) {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.f32[iChannel] = 0;
|
|
pResampler->x1.f32[iChannel] = 0;
|
|
}
|
|
} else {
|
|
for (iChannel = 0; iChannel < pResampler->config.channels; iChannel += 1) {
|
|
pResampler->x0.s16[iChannel] = 0;
|
|
pResampler->x1.s16[iChannel] = 0;
|
|
}
|
|
}
|
|
|
|
ma_lpf_clear_cache(&pResampler->lpf);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_linear_resampler_config ma_resampling_backend_get_config__linear(const ma_resampler_config* pConfig)
|
|
{
|
|
ma_linear_resampler_config linearConfig;
|
|
|
|
linearConfig = ma_linear_resampler_config_init(pConfig->format, pConfig->channels, pConfig->sampleRateIn, pConfig->sampleRateOut);
|
|
linearConfig.lpfOrder = pConfig->linear.lpfOrder;
|
|
|
|
return linearConfig;
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_get_heap_size__linear(void* pUserData, const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_linear_resampler_config linearConfig;
|
|
|
|
(void)pUserData;
|
|
|
|
linearConfig = ma_resampling_backend_get_config__linear(pConfig);
|
|
|
|
return ma_linear_resampler_get_heap_size(&linearConfig, pHeapSizeInBytes);
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_init__linear(void* pUserData, const ma_resampler_config* pConfig, void* pHeap, ma_resampling_backend** ppBackend)
|
|
{
|
|
ma_resampler* pResampler = (ma_resampler*)pUserData;
|
|
ma_result result;
|
|
ma_linear_resampler_config linearConfig;
|
|
|
|
(void)pUserData;
|
|
|
|
linearConfig = ma_resampling_backend_get_config__linear(pConfig);
|
|
|
|
result = ma_linear_resampler_init_preallocated(&linearConfig, pHeap, &pResampler->state.linear);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = &pResampler->state.linear;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_resampling_backend_uninit__linear(void* pUserData, ma_resampling_backend* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
(void)pUserData;
|
|
|
|
ma_linear_resampler_uninit((ma_linear_resampler*)pBackend, pAllocationCallbacks);
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_process__linear(void* pUserData, ma_resampling_backend* pBackend, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_process_pcm_frames((ma_linear_resampler*)pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_set_rate__linear(void* pUserData, ma_resampling_backend* pBackend, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_set_rate((ma_linear_resampler*)pBackend, sampleRateIn, sampleRateOut);
|
|
}
|
|
|
|
static ma_uint64 ma_resampling_backend_get_input_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_get_input_latency((const ma_linear_resampler*)pBackend);
|
|
}
|
|
|
|
static ma_uint64 ma_resampling_backend_get_output_latency__linear(void* pUserData, const ma_resampling_backend* pBackend)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_get_output_latency((const ma_linear_resampler*)pBackend);
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_get_required_input_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_get_required_input_frame_count((const ma_linear_resampler*)pBackend, outputFrameCount, pInputFrameCount);
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_get_expected_output_frame_count__linear(void* pUserData, const ma_resampling_backend* pBackend, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_get_expected_output_frame_count((const ma_linear_resampler*)pBackend, inputFrameCount, pOutputFrameCount);
|
|
}
|
|
|
|
static ma_result ma_resampling_backend_reset__linear(void* pUserData, ma_resampling_backend* pBackend)
|
|
{
|
|
(void)pUserData;
|
|
|
|
return ma_linear_resampler_reset((ma_linear_resampler*)pBackend);
|
|
}
|
|
|
|
static ma_resampling_backend_vtable g_ma_linear_resampler_vtable =
|
|
{
|
|
ma_resampling_backend_get_heap_size__linear,
|
|
ma_resampling_backend_init__linear,
|
|
ma_resampling_backend_uninit__linear,
|
|
ma_resampling_backend_process__linear,
|
|
ma_resampling_backend_set_rate__linear,
|
|
ma_resampling_backend_get_input_latency__linear,
|
|
ma_resampling_backend_get_output_latency__linear,
|
|
ma_resampling_backend_get_required_input_frame_count__linear,
|
|
ma_resampling_backend_get_expected_output_frame_count__linear,
|
|
ma_resampling_backend_reset__linear
|
|
};
|
|
|
|
MA_API ma_resampler_config ma_resampler_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut, ma_resample_algorithm algorithm)
|
|
{
|
|
ma_resampler_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRateIn = sampleRateIn;
|
|
config.sampleRateOut = sampleRateOut;
|
|
config.algorithm = algorithm;
|
|
|
|
config.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_resampler_get_vtable(const ma_resampler_config* pConfig, ma_resampler* pResampler, ma_resampling_backend_vtable** ppVTable, void** ppUserData)
|
|
{
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(ppVTable != NULL);
|
|
MA_ASSERT(ppUserData != NULL);
|
|
|
|
*ppVTable = NULL;
|
|
*ppUserData = NULL;
|
|
|
|
switch (pConfig->algorithm)
|
|
{
|
|
case ma_resample_algorithm_linear:
|
|
{
|
|
*ppVTable = &g_ma_linear_resampler_vtable;
|
|
*ppUserData = pResampler;
|
|
} break;
|
|
|
|
case ma_resample_algorithm_custom:
|
|
{
|
|
*ppVTable = pConfig->pBackendVTable;
|
|
*ppUserData = pConfig->pBackendUserData;
|
|
} break;
|
|
|
|
default: return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_get_heap_size(const ma_resampler_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_resampling_backend_vtable* pVTable;
|
|
void* pVTableUserData;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_resampler_get_vtable(pConfig, NULL, &pVTable, &pVTableUserData);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pVTable == NULL || pVTable->onGetHeapSize == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
result = pVTable->onGetHeapSize(pVTableUserData, pConfig, pHeapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_init_preallocated(const ma_resampler_config* pConfig, void* pHeap, ma_resampler* pResampler)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pResampler);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pResampler->_pHeap = pHeap;
|
|
pResampler->format = pConfig->format;
|
|
pResampler->channels = pConfig->channels;
|
|
pResampler->sampleRateIn = pConfig->sampleRateIn;
|
|
pResampler->sampleRateOut = pConfig->sampleRateOut;
|
|
|
|
result = ma_resampler_get_vtable(pConfig, pResampler, &pResampler->pBackendVTable, &pResampler->pBackendUserData);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onInit == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
result = pResampler->pBackendVTable->onInit(pResampler->pBackendUserData, pConfig, pHeap, &pResampler->pBackend);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_init(const ma_resampler_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_resampler* pResampler)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_resampler_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_resampler_init_preallocated(pConfig, pHeap, pResampler);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pResampler->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onUninit == NULL) {
|
|
return;
|
|
}
|
|
|
|
pResampler->pBackendVTable->onUninit(pResampler->pBackendUserData, pResampler->pBackend, pAllocationCallbacks);
|
|
|
|
if (pResampler->_ownsHeap) {
|
|
ma_free(pResampler->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_process_pcm_frames(ma_resampler* pResampler, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFrameCountOut == NULL && pFrameCountIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onProcess == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pResampler->pBackendVTable->onProcess(pResampler->pBackendUserData, pResampler->pBackend, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_set_rate(ma_resampler* pResampler, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (sampleRateIn == 0 || sampleRateOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onSetRate == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
result = pResampler->pBackendVTable->onSetRate(pResampler->pBackendUserData, pResampler->pBackend, sampleRateIn, sampleRateOut);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pResampler->sampleRateIn = sampleRateIn;
|
|
pResampler->sampleRateOut = sampleRateOut;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_set_rate_ratio(ma_resampler* pResampler, float ratio)
|
|
{
|
|
ma_uint32 n;
|
|
ma_uint32 d;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ratio <= 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
d = 1000;
|
|
n = (ma_uint32)(ratio * d);
|
|
|
|
if (n == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(n != 0);
|
|
|
|
return ma_resampler_set_rate(pResampler, n, d);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_resampler_get_input_latency(const ma_resampler* pResampler)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetInputLatency == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pResampler->pBackendVTable->onGetInputLatency(pResampler->pBackendUserData, pResampler->pBackend);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_resampler_get_output_latency(const ma_resampler* pResampler)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetOutputLatency == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pResampler->pBackendVTable->onGetOutputLatency(pResampler->pBackendUserData, pResampler->pBackend);
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
|
|
{
|
|
if (pInputFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pInputFrameCount = 0;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetRequiredInputFrameCount == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pResampler->pBackendVTable->onGetRequiredInputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, outputFrameCount, pInputFrameCount);
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
|
|
{
|
|
if (pOutputFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pOutputFrameCount = 0;
|
|
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onGetExpectedOutputFrameCount == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pResampler->pBackendVTable->onGetExpectedOutputFrameCount(pResampler->pBackendUserData, pResampler->pBackend, inputFrameCount, pOutputFrameCount);
|
|
}
|
|
|
|
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler)
|
|
{
|
|
if (pResampler == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResampler->pBackendVTable == NULL || pResampler->pBackendVTable->onReset == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pResampler->pBackendVTable->onReset(pResampler->pBackendUserData, pResampler->pBackend);
|
|
}
|
|
|
|
#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
|
|
#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
|
|
#endif
|
|
|
|
#define MA_PLANE_LEFT 0
|
|
#define MA_PLANE_RIGHT 1
|
|
#define MA_PLANE_FRONT 2
|
|
#define MA_PLANE_BACK 3
|
|
#define MA_PLANE_BOTTOM 4
|
|
#define MA_PLANE_TOP 5
|
|
|
|
static float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f},
|
|
{ 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f},
|
|
{ 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
|
|
{ 0.33f, 0.0f, 0.33f, 0.0f, 0.0f, 0.34f},
|
|
{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.5f},
|
|
{ 0.0f, 0.33f, 0.33f, 0.0f, 0.0f, 0.34f},
|
|
{ 0.33f, 0.0f, 0.0f, 0.33f, 0.0f, 0.34f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.5f},
|
|
{ 0.0f, 0.33f, 0.0f, 0.33f, 0.0f, 0.34f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
|
|
};
|
|
|
|
static float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
|
|
{
|
|
|
|
float contribution =
|
|
g_maChannelPlaneRatios[channelPositionA][0] * g_maChannelPlaneRatios[channelPositionB][0] +
|
|
g_maChannelPlaneRatios[channelPositionA][1] * g_maChannelPlaneRatios[channelPositionB][1] +
|
|
g_maChannelPlaneRatios[channelPositionA][2] * g_maChannelPlaneRatios[channelPositionB][2] +
|
|
g_maChannelPlaneRatios[channelPositionA][3] * g_maChannelPlaneRatios[channelPositionB][3] +
|
|
g_maChannelPlaneRatios[channelPositionA][4] * g_maChannelPlaneRatios[channelPositionB][4] +
|
|
g_maChannelPlaneRatios[channelPositionA][5] * g_maChannelPlaneRatios[channelPositionB][5];
|
|
|
|
return contribution;
|
|
}
|
|
|
|
MA_API ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel* pChannelMapIn, ma_uint32 channelsOut, const ma_channel* pChannelMapOut, ma_channel_mix_mode mixingMode)
|
|
{
|
|
ma_channel_converter_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channelsIn = channelsIn;
|
|
config.channelsOut = channelsOut;
|
|
config.pChannelMapIn = pChannelMapIn;
|
|
config.pChannelMapOut = pChannelMapOut;
|
|
config.mixingMode = mixingMode;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_int32 ma_channel_converter_float_to_fixed(float x)
|
|
{
|
|
return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
|
|
}
|
|
|
|
static ma_uint32 ma_channel_map_get_spatial_channel_count(const ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
ma_uint32 spatialChannelCount = 0;
|
|
ma_uint32 iChannel;
|
|
|
|
MA_ASSERT(pChannelMap != NULL);
|
|
MA_ASSERT(channels > 0);
|
|
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
if (ma_is_spatial_channel_position(ma_channel_map_get_channel(pChannelMap, channels, iChannel))) {
|
|
spatialChannelCount++;
|
|
}
|
|
}
|
|
|
|
return spatialChannelCount;
|
|
}
|
|
|
|
static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
|
|
{
|
|
int i;
|
|
|
|
if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (channelPosition >= MA_CHANNEL_AUX_0) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
for (i = 0; i < 6; ++i) {
|
|
if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
static ma_bool32 ma_channel_map_is_passthrough(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut)
|
|
{
|
|
if (channelsOut == channelsIn) {
|
|
return ma_channel_map_is_equal(pChannelMapOut, pChannelMapIn, channelsOut);
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
static ma_channel_conversion_path ma_channel_map_get_conversion_path(const ma_channel* pChannelMapIn, ma_uint32 channelsIn, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, ma_channel_mix_mode mode)
|
|
{
|
|
if (ma_channel_map_is_passthrough(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut)) {
|
|
return ma_channel_conversion_path_passthrough;
|
|
}
|
|
|
|
if (channelsOut == 1 && (pChannelMapOut == NULL || pChannelMapOut[0] == MA_CHANNEL_MONO)) {
|
|
return ma_channel_conversion_path_mono_out;
|
|
}
|
|
|
|
if (channelsIn == 1 && (pChannelMapIn == NULL || pChannelMapIn[0] == MA_CHANNEL_MONO)) {
|
|
return ma_channel_conversion_path_mono_in;
|
|
}
|
|
|
|
if (mode == ma_channel_mix_mode_custom_weights) {
|
|
return ma_channel_conversion_path_weights;
|
|
}
|
|
|
|
if (channelsIn == channelsOut) {
|
|
ma_uint32 iChannelIn;
|
|
ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; ++iChannelIn) {
|
|
ma_bool32 isInputChannelPositionInOutput = ma_channel_map_contains_channel_position(channelsOut, pChannelMapOut, ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn));
|
|
if (!isInputChannelPositionInOutput) {
|
|
areAllChannelPositionsPresent = MA_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (areAllChannelPositionsPresent) {
|
|
return ma_channel_conversion_path_shuffle;
|
|
}
|
|
}
|
|
|
|
return ma_channel_conversion_path_weights;
|
|
}
|
|
|
|
static ma_result ma_channel_map_build_shuffle_table(const ma_channel* pChannelMapIn, ma_uint32 channelCountIn, const ma_channel* pChannelMapOut, ma_uint32 channelCountOut, ma_uint8* pShuffleTable)
|
|
{
|
|
ma_uint32 iChannelIn;
|
|
ma_uint32 iChannelOut;
|
|
|
|
if (pShuffleTable == NULL || channelCountIn == 0 || channelCountOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iChannelOut = 0; iChannelOut < channelCountOut; iChannelOut += 1) {
|
|
ma_channel channelOut;
|
|
|
|
pShuffleTable[iChannelOut] = MA_CHANNEL_INDEX_NULL;
|
|
|
|
channelOut = ma_channel_map_get_channel(pChannelMapOut, channelCountOut, iChannelOut);
|
|
for (iChannelIn = 0; iChannelIn < channelCountIn; iChannelIn += 1) {
|
|
ma_channel channelIn;
|
|
|
|
channelIn = ma_channel_map_get_channel(pChannelMapIn, channelCountIn, iChannelIn);
|
|
if (channelOut == channelIn) {
|
|
pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
|
|
break;
|
|
}
|
|
|
|
switch (channelOut)
|
|
{
|
|
case MA_CHANNEL_FRONT_LEFT:
|
|
case MA_CHANNEL_SIDE_LEFT:
|
|
{
|
|
switch (channelIn) {
|
|
case MA_CHANNEL_FRONT_LEFT:
|
|
case MA_CHANNEL_SIDE_LEFT:
|
|
{
|
|
pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
|
|
} break;
|
|
}
|
|
} break;
|
|
|
|
case MA_CHANNEL_FRONT_RIGHT:
|
|
case MA_CHANNEL_SIDE_RIGHT:
|
|
{
|
|
switch (channelIn) {
|
|
case MA_CHANNEL_FRONT_RIGHT:
|
|
case MA_CHANNEL_SIDE_RIGHT:
|
|
{
|
|
pShuffleTable[iChannelOut] = (ma_uint8)iChannelIn;
|
|
} break;
|
|
}
|
|
} break;
|
|
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_channel_map_apply_shuffle_table_u8(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
|
|
if (iChannelIn < channelsIn) {
|
|
pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
|
|
} else {
|
|
pFramesOut[iChannelOut] = 0;
|
|
}
|
|
}
|
|
|
|
pFramesOut += channelsOut;
|
|
pFramesIn += channelsIn;
|
|
}
|
|
}
|
|
|
|
static void ma_channel_map_apply_shuffle_table_s16(ma_int16* pFramesOut, ma_uint32 channelsOut, const ma_int16* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
|
|
if (iChannelIn < channelsIn) {
|
|
pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
|
|
} else {
|
|
pFramesOut[iChannelOut] = 0;
|
|
}
|
|
}
|
|
|
|
pFramesOut += channelsOut;
|
|
pFramesIn += channelsIn;
|
|
}
|
|
}
|
|
|
|
static void ma_channel_map_apply_shuffle_table_s24(ma_uint8* pFramesOut, ma_uint32 channelsOut, const ma_uint8* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
|
|
if (iChannelIn < channelsIn) {
|
|
pFramesOut[iChannelOut*3 + 0] = pFramesIn[iChannelIn*3 + 0];
|
|
pFramesOut[iChannelOut*3 + 1] = pFramesIn[iChannelIn*3 + 1];
|
|
pFramesOut[iChannelOut*3 + 2] = pFramesIn[iChannelIn*3 + 2];
|
|
} else {
|
|
pFramesOut[iChannelOut*3 + 0] = 0;
|
|
} pFramesOut[iChannelOut*3 + 1] = 0;
|
|
} pFramesOut[iChannelOut*3 + 2] = 0;
|
|
|
|
pFramesOut += channelsOut*3;
|
|
pFramesIn += channelsIn*3;
|
|
}
|
|
}
|
|
|
|
static void ma_channel_map_apply_shuffle_table_s32(ma_int32* pFramesOut, ma_uint32 channelsOut, const ma_int32* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
|
|
if (iChannelIn < channelsIn) {
|
|
pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
|
|
} else {
|
|
pFramesOut[iChannelOut] = 0;
|
|
}
|
|
}
|
|
|
|
pFramesOut += channelsOut;
|
|
pFramesIn += channelsIn;
|
|
}
|
|
}
|
|
|
|
static void ma_channel_map_apply_shuffle_table_f32(float* pFramesOut, ma_uint32 channelsOut, const float* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_uint8 iChannelIn = pShuffleTable[iChannelOut];
|
|
if (iChannelIn < channelsIn) {
|
|
pFramesOut[iChannelOut] = pFramesIn[iChannelIn];
|
|
} else {
|
|
pFramesOut[iChannelOut] = 0;
|
|
}
|
|
}
|
|
|
|
pFramesOut += channelsOut;
|
|
pFramesIn += channelsIn;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_channel_map_apply_shuffle_table(void* pFramesOut, ma_uint32 channelsOut, const void* pFramesIn, ma_uint32 channelsIn, ma_uint64 frameCount, const ma_uint8* pShuffleTable, ma_format format)
|
|
{
|
|
if (pFramesOut == NULL || pFramesIn == NULL || channelsOut == 0 || pShuffleTable == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
switch (format)
|
|
{
|
|
case ma_format_u8:
|
|
{
|
|
ma_channel_map_apply_shuffle_table_u8((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
ma_channel_map_apply_shuffle_table_s16((ma_int16*)pFramesOut, channelsOut, (const ma_int16*)pFramesIn, channelsIn, frameCount, pShuffleTable);
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
ma_channel_map_apply_shuffle_table_s24((ma_uint8*)pFramesOut, channelsOut, (const ma_uint8*)pFramesIn, channelsIn, frameCount, pShuffleTable);
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
ma_channel_map_apply_shuffle_table_s32((ma_int32*)pFramesOut, channelsOut, (const ma_int32*)pFramesIn, channelsIn, frameCount, pShuffleTable);
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
ma_channel_map_apply_shuffle_table_f32((float*)pFramesOut, channelsOut, (const float*)pFramesIn, channelsIn, frameCount, pShuffleTable);
|
|
} break;
|
|
|
|
default: return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_channel_map_apply_mono_out_f32(float* pFramesOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelIn;
|
|
ma_uint32 accumulationCount;
|
|
|
|
if (pFramesOut == NULL || pFramesIn == NULL || channelsIn == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
accumulationCount = 0;
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
if (ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn) != MA_CHANNEL_NONE) {
|
|
accumulationCount += 1;
|
|
}
|
|
}
|
|
|
|
if (accumulationCount > 0) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float accumulation = 0;
|
|
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
|
|
if (channelIn != MA_CHANNEL_NONE) {
|
|
accumulation += pFramesIn[iChannelIn];
|
|
}
|
|
}
|
|
|
|
pFramesOut[0] = accumulation / accumulationCount;
|
|
pFramesOut += 1;
|
|
pFramesIn += channelsIn;
|
|
}
|
|
} else {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, 1);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* MA_RESTRICT pFramesIn, ma_uint64 frameCount, ma_mono_expansion_mode monoExpansionMode)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
|
|
if (pFramesOut == NULL || channelsOut == 0 || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
switch (monoExpansionMode)
|
|
{
|
|
case ma_mono_expansion_mode_average:
|
|
{
|
|
float weight;
|
|
ma_uint32 validChannelCount = 0;
|
|
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelOut != MA_CHANNEL_NONE) {
|
|
validChannelCount += 1;
|
|
}
|
|
}
|
|
|
|
weight = 1.0f / validChannelCount;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelOut != MA_CHANNEL_NONE) {
|
|
pFramesOut[iChannelOut] = pFramesIn[0] * weight;
|
|
}
|
|
}
|
|
|
|
pFramesOut += channelsOut;
|
|
pFramesIn += 1;
|
|
}
|
|
} break;
|
|
|
|
case ma_mono_expansion_mode_stereo_only:
|
|
{
|
|
if (channelsOut >= 2) {
|
|
ma_uint32 iChannelLeft = (ma_uint32)-1;
|
|
ma_uint32 iChannelRight = (ma_uint32)-1;
|
|
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelOut == MA_CHANNEL_SIDE_LEFT) {
|
|
iChannelLeft = iChannelOut;
|
|
}
|
|
if (channelOut == MA_CHANNEL_SIDE_RIGHT) {
|
|
iChannelRight = iChannelOut;
|
|
}
|
|
}
|
|
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelOut == MA_CHANNEL_FRONT_LEFT) {
|
|
iChannelLeft = iChannelOut;
|
|
}
|
|
if (channelOut == MA_CHANNEL_FRONT_RIGHT) {
|
|
iChannelRight = iChannelOut;
|
|
}
|
|
}
|
|
|
|
if (iChannelLeft != (ma_uint32)-1 && iChannelRight != (ma_uint32)-1) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelOut != MA_CHANNEL_NONE) {
|
|
if (iChannelOut == iChannelLeft || iChannelOut == iChannelRight) {
|
|
pFramesOut[iChannelOut] = pFramesIn[0];
|
|
} else {
|
|
pFramesOut[iChannelOut] = 0.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
pFramesOut += channelsOut;
|
|
pFramesIn += 1;
|
|
}
|
|
|
|
break;
|
|
} else {
|
|
goto default_handler;
|
|
}
|
|
} else {
|
|
goto default_handler;
|
|
}
|
|
};
|
|
|
|
case ma_mono_expansion_mode_duplicate:
|
|
default:
|
|
{
|
|
default_handler:
|
|
{
|
|
if (channelsOut <= MA_MAX_CHANNELS) {
|
|
ma_bool32 hasEmptyChannel = MA_FALSE;
|
|
ma_channel channelPositions[MA_MAX_CHANNELS];
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
channelPositions[iChannelOut] = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelPositions[iChannelOut] == MA_CHANNEL_NONE) {
|
|
hasEmptyChannel = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (hasEmptyChannel == MA_FALSE) {
|
|
if (channelsOut == 2) {
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_uint64 unrolledFrameCount = frameCount >> 1;
|
|
|
|
for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
|
|
__m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
|
|
__m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
|
|
_mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));
|
|
}
|
|
|
|
iFrame = unrolledFrameCount << 1;
|
|
goto generic_on_fastpath;
|
|
} else
|
|
#endif
|
|
{
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < 2; iChannelOut += 1) {
|
|
pFramesOut[iFrame*2 + iChannelOut] = pFramesIn[iFrame];
|
|
}
|
|
}
|
|
}
|
|
} else if (channelsOut == 6) {
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
ma_uint64 unrolledFrameCount = frameCount >> 1;
|
|
|
|
for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) {
|
|
__m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]);
|
|
__m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]);
|
|
|
|
_mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0);
|
|
_mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0)));
|
|
_mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1);
|
|
}
|
|
|
|
iFrame = unrolledFrameCount << 1;
|
|
goto generic_on_fastpath;
|
|
} else
|
|
#endif
|
|
{
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < 6; iChannelOut += 1) {
|
|
pFramesOut[iFrame*6 + iChannelOut] = pFramesIn[iFrame];
|
|
}
|
|
}
|
|
}
|
|
} else if (channelsOut == 8) {
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
if (ma_has_sse2()) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
__m128 in = _mm_set1_ps(pFramesIn[iFrame]);
|
|
_mm_storeu_ps(&pFramesOut[iFrame*8 + 0], in);
|
|
_mm_storeu_ps(&pFramesOut[iFrame*8 + 4], in);
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < 8; iChannelOut += 1) {
|
|
pFramesOut[iFrame*8 + iChannelOut] = pFramesIn[iFrame];
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
iFrame = 0;
|
|
|
|
#if defined(MA_SUPPORT_SSE2)
|
|
generic_on_fastpath:
|
|
#endif
|
|
{
|
|
for (; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
if (channelPositions[iChannelOut] != MA_CHANNEL_NONE) {
|
|
pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
if (channelOut != MA_CHANNEL_NONE) {
|
|
pFramesOut[iFrame*channelsOut + iChannelOut] = pFramesIn[iFrame];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_channel_map_apply_f32(float* pFramesOut, const ma_channel* pChannelMapOut, ma_uint32 channelsOut, const float* pFramesIn, const ma_channel* pChannelMapIn, ma_uint32 channelsIn, ma_uint64 frameCount, ma_channel_mix_mode mode, ma_mono_expansion_mode monoExpansionMode)
|
|
{
|
|
ma_channel_conversion_path conversionPath = ma_channel_map_get_conversion_path(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, mode);
|
|
|
|
if (conversionPath == ma_channel_conversion_path_passthrough) {
|
|
ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, ma_format_f32, channelsOut);
|
|
return;
|
|
}
|
|
|
|
if (conversionPath == ma_channel_conversion_path_mono_out) {
|
|
ma_channel_map_apply_mono_out_f32(pFramesOut, pFramesIn, pChannelMapIn, channelsIn, frameCount);
|
|
return;
|
|
}
|
|
|
|
if (conversionPath == ma_channel_conversion_path_mono_in) {
|
|
ma_channel_map_apply_mono_in_f32(pFramesOut, pChannelMapOut, channelsOut, pFramesIn, frameCount, monoExpansionMode);
|
|
return;
|
|
}
|
|
|
|
if (channelsOut <= MA_MAX_CHANNELS) {
|
|
ma_result result;
|
|
|
|
if (mode == ma_channel_mix_mode_simple) {
|
|
ma_channel shuffleTable[MA_MAX_CHANNELS];
|
|
|
|
result = ma_channel_map_build_shuffle_table(pChannelMapIn, channelsIn, pChannelMapOut, channelsOut, shuffleTable);
|
|
if (result != MA_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
result = ma_channel_map_apply_shuffle_table(pFramesOut, channelsOut, pFramesIn, channelsIn, frameCount, shuffleTable, ma_format_f32);
|
|
if (result != MA_SUCCESS) {
|
|
return;
|
|
}
|
|
} else {
|
|
ma_uint32 iFrame;
|
|
ma_uint32 iChannelOut;
|
|
ma_uint32 iChannelIn;
|
|
float weights[32][32];
|
|
|
|
if (channelsIn <= ma_countof(weights) && channelsOut <= ma_countof(weights)) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
|
|
weights[iChannelOut][iChannelIn] = ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
|
|
}
|
|
}
|
|
|
|
iFrame = 0;
|
|
|
|
if (channelsOut == 8) {
|
|
if (channelsIn == 2) {
|
|
for (; iFrame < frameCount; iFrame += 1) {
|
|
float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
accumulation[0] += pFramesIn[iFrame*2 + 0] * weights[0][0];
|
|
accumulation[1] += pFramesIn[iFrame*2 + 0] * weights[1][0];
|
|
accumulation[2] += pFramesIn[iFrame*2 + 0] * weights[2][0];
|
|
accumulation[3] += pFramesIn[iFrame*2 + 0] * weights[3][0];
|
|
accumulation[4] += pFramesIn[iFrame*2 + 0] * weights[4][0];
|
|
accumulation[5] += pFramesIn[iFrame*2 + 0] * weights[5][0];
|
|
accumulation[6] += pFramesIn[iFrame*2 + 0] * weights[6][0];
|
|
accumulation[7] += pFramesIn[iFrame*2 + 0] * weights[7][0];
|
|
|
|
accumulation[0] += pFramesIn[iFrame*2 + 1] * weights[0][1];
|
|
accumulation[1] += pFramesIn[iFrame*2 + 1] * weights[1][1];
|
|
accumulation[2] += pFramesIn[iFrame*2 + 1] * weights[2][1];
|
|
accumulation[3] += pFramesIn[iFrame*2 + 1] * weights[3][1];
|
|
accumulation[4] += pFramesIn[iFrame*2 + 1] * weights[4][1];
|
|
accumulation[5] += pFramesIn[iFrame*2 + 1] * weights[5][1];
|
|
accumulation[6] += pFramesIn[iFrame*2 + 1] * weights[6][1];
|
|
accumulation[7] += pFramesIn[iFrame*2 + 1] * weights[7][1];
|
|
|
|
pFramesOut[iFrame*8 + 0] = accumulation[0];
|
|
pFramesOut[iFrame*8 + 1] = accumulation[1];
|
|
pFramesOut[iFrame*8 + 2] = accumulation[2];
|
|
pFramesOut[iFrame*8 + 3] = accumulation[3];
|
|
pFramesOut[iFrame*8 + 4] = accumulation[4];
|
|
pFramesOut[iFrame*8 + 5] = accumulation[5];
|
|
pFramesOut[iFrame*8 + 6] = accumulation[6];
|
|
pFramesOut[iFrame*8 + 7] = accumulation[7];
|
|
}
|
|
} else {
|
|
for (; iFrame < frameCount; iFrame += 1) {
|
|
float accumulation[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
|
|
accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
|
|
accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
|
|
accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
|
|
accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
|
|
accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
|
|
accumulation[6] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[6][iChannelIn];
|
|
accumulation[7] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[7][iChannelIn];
|
|
}
|
|
|
|
pFramesOut[iFrame*8 + 0] = accumulation[0];
|
|
pFramesOut[iFrame*8 + 1] = accumulation[1];
|
|
pFramesOut[iFrame*8 + 2] = accumulation[2];
|
|
pFramesOut[iFrame*8 + 3] = accumulation[3];
|
|
pFramesOut[iFrame*8 + 4] = accumulation[4];
|
|
pFramesOut[iFrame*8 + 5] = accumulation[5];
|
|
pFramesOut[iFrame*8 + 6] = accumulation[6];
|
|
pFramesOut[iFrame*8 + 7] = accumulation[7];
|
|
}
|
|
}
|
|
} else if (channelsOut == 6) {
|
|
for (; iFrame < frameCount; iFrame += 1) {
|
|
float accumulation[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
accumulation[0] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[0][iChannelIn];
|
|
accumulation[1] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[1][iChannelIn];
|
|
accumulation[2] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[2][iChannelIn];
|
|
accumulation[3] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[3][iChannelIn];
|
|
accumulation[4] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[4][iChannelIn];
|
|
accumulation[5] += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[5][iChannelIn];
|
|
}
|
|
|
|
pFramesOut[iFrame*6 + 0] = accumulation[0];
|
|
pFramesOut[iFrame*6 + 1] = accumulation[1];
|
|
pFramesOut[iFrame*6 + 2] = accumulation[2];
|
|
pFramesOut[iFrame*6 + 3] = accumulation[3];
|
|
pFramesOut[iFrame*6 + 4] = accumulation[4];
|
|
pFramesOut[iFrame*6 + 5] = accumulation[5];
|
|
}
|
|
}
|
|
|
|
for (; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
float accumulation = 0;
|
|
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * weights[iChannelOut][iChannelIn];
|
|
}
|
|
|
|
pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelOut = 0; iChannelOut < channelsOut; iChannelOut += 1) {
|
|
float accumulation = 0;
|
|
ma_channel channelOut = ma_channel_map_get_channel(pChannelMapOut, channelsOut, iChannelOut);
|
|
|
|
for (iChannelIn = 0; iChannelIn < channelsIn; iChannelIn += 1) {
|
|
ma_channel channelIn = ma_channel_map_get_channel(pChannelMapIn, channelsIn, iChannelIn);
|
|
accumulation += pFramesIn[iFrame*channelsIn + iChannelIn] * ma_calculate_channel_position_rectangular_weight(channelOut, channelIn);
|
|
}
|
|
|
|
pFramesOut[iFrame*channelsOut + iChannelOut] = accumulation;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, channelsOut);
|
|
}
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t channelMapInOffset;
|
|
size_t channelMapOutOffset;
|
|
size_t shuffleTableOffset;
|
|
size_t weightsOffset;
|
|
} ma_channel_converter_heap_layout;
|
|
|
|
static ma_channel_conversion_path ma_channel_converter_config_get_conversion_path(const ma_channel_converter_config* pConfig)
|
|
{
|
|
return ma_channel_map_get_conversion_path(pConfig->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapOut, pConfig->channelsOut, pConfig->mixingMode);
|
|
}
|
|
|
|
static ma_result ma_channel_converter_get_heap_layout(const ma_channel_converter_config* pConfig, ma_channel_converter_heap_layout* pHeapLayout)
|
|
{
|
|
ma_channel_conversion_path conversionPath;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (!ma_channel_map_is_valid(pConfig->pChannelMapIn, pConfig->channelsIn)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (!ma_channel_map_is_valid(pConfig->pChannelMapOut, pConfig->channelsOut)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->channelMapInOffset = pHeapLayout->sizeInBytes;
|
|
if (pConfig->pChannelMapIn != NULL) {
|
|
pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsIn;
|
|
}
|
|
|
|
pHeapLayout->channelMapOutOffset = pHeapLayout->sizeInBytes;
|
|
if (pConfig->pChannelMapOut != NULL) {
|
|
pHeapLayout->sizeInBytes += sizeof(ma_channel) * pConfig->channelsOut;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
|
|
|
|
pHeapLayout->shuffleTableOffset = pHeapLayout->sizeInBytes;
|
|
if (conversionPath == ma_channel_conversion_path_shuffle) {
|
|
pHeapLayout->sizeInBytes += sizeof(ma_uint8) * pConfig->channelsOut;
|
|
}
|
|
|
|
pHeapLayout->weightsOffset = pHeapLayout->sizeInBytes;
|
|
if (conversionPath == ma_channel_conversion_path_weights) {
|
|
pHeapLayout->sizeInBytes += sizeof(float*) * pConfig->channelsIn;
|
|
pHeapLayout->sizeInBytes += sizeof(float ) * pConfig->channelsIn * pConfig->channelsOut;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_channel_converter_get_heap_size(const ma_channel_converter_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_channel_converter_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_channel_converter_init_preallocated(const ma_channel_converter_config* pConfig, void* pHeap, ma_channel_converter* pConverter)
|
|
{
|
|
ma_result result;
|
|
ma_channel_converter_heap_layout heapLayout;
|
|
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pConverter);
|
|
|
|
result = ma_channel_converter_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pConverter->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pConverter->_pHeap, heapLayout.sizeInBytes);
|
|
|
|
pConverter->format = pConfig->format;
|
|
pConverter->channelsIn = pConfig->channelsIn;
|
|
pConverter->channelsOut = pConfig->channelsOut;
|
|
pConverter->mixingMode = pConfig->mixingMode;
|
|
|
|
if (pConfig->pChannelMapIn != NULL) {
|
|
pConverter->pChannelMapIn = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapInOffset);
|
|
ma_channel_map_copy_or_default(pConverter->pChannelMapIn, pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsIn);
|
|
} else {
|
|
pConverter->pChannelMapIn = NULL;
|
|
}
|
|
|
|
if (pConfig->pChannelMapOut != NULL) {
|
|
pConverter->pChannelMapOut = (ma_channel*)ma_offset_ptr(pHeap, heapLayout.channelMapOutOffset);
|
|
ma_channel_map_copy_or_default(pConverter->pChannelMapOut, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelsOut);
|
|
} else {
|
|
pConverter->pChannelMapOut = NULL;
|
|
}
|
|
|
|
pConverter->conversionPath = ma_channel_converter_config_get_conversion_path(pConfig);
|
|
|
|
if (pConverter->conversionPath == ma_channel_conversion_path_shuffle) {
|
|
pConverter->pShuffleTable = (ma_uint8*)ma_offset_ptr(pHeap, heapLayout.shuffleTableOffset);
|
|
ma_channel_map_build_shuffle_table(pConverter->pChannelMapIn, pConverter->channelsIn, pConverter->pChannelMapOut, pConverter->channelsOut, pConverter->pShuffleTable);
|
|
}
|
|
|
|
if (pConverter->conversionPath == ma_channel_conversion_path_weights) {
|
|
ma_uint32 iChannelIn;
|
|
ma_uint32 iChannelOut;
|
|
|
|
if (pConverter->format == ma_format_f32) {
|
|
pConverter->weights.f32 = (float** )ma_offset_ptr(pHeap, heapLayout.weightsOffset);
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
|
|
pConverter->weights.f32[iChannelIn] = (float*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(float*) * pConverter->channelsIn) + (sizeof(float) * pConverter->channelsOut * iChannelIn)));
|
|
}
|
|
} else {
|
|
pConverter->weights.s16 = (ma_int32**)ma_offset_ptr(pHeap, heapLayout.weightsOffset);
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
|
|
pConverter->weights.s16[iChannelIn] = (ma_int32*)ma_offset_ptr(pHeap, heapLayout.weightsOffset + ((sizeof(ma_int32*) * pConverter->channelsIn) + (sizeof(ma_int32) * pConverter->channelsOut * iChannelIn)));
|
|
}
|
|
}
|
|
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
|
|
if (pConverter->format == ma_format_f32) {
|
|
pConverter->weights.f32[iChannelIn][iChannelOut] = 0.0f;
|
|
} else {
|
|
pConverter->weights.s16[iChannelIn][iChannelOut] = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
|
|
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
|
|
|
|
if (channelPosIn == channelPosOut) {
|
|
float weight = 1;
|
|
|
|
if (pConverter->format == ma_format_f32) {
|
|
pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
|
|
} else {
|
|
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (pConverter->mixingMode)
|
|
{
|
|
case ma_channel_mix_mode_custom_weights:
|
|
{
|
|
if (pConfig->ppWeights == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; iChannelOut += 1) {
|
|
float weight = pConfig->ppWeights[iChannelIn][iChannelOut];
|
|
|
|
if (pConverter->format == ma_format_f32) {
|
|
pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
|
|
} else {
|
|
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_channel_mix_mode_simple:
|
|
{
|
|
} break;
|
|
|
|
case ma_channel_mix_mode_rectangular:
|
|
default:
|
|
{
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
|
|
|
|
if (ma_is_spatial_channel_position(channelPosIn)) {
|
|
if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, channelPosIn)) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
|
|
|
|
if (ma_is_spatial_channel_position(channelPosOut)) {
|
|
float weight = 0;
|
|
if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
|
|
weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
|
|
}
|
|
|
|
if (pConverter->format == ma_format_f32) {
|
|
if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
|
|
pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
|
|
}
|
|
} else {
|
|
if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
|
|
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_channel channelPosOut = ma_channel_map_get_channel(pConverter->pChannelMapOut, pConverter->channelsOut, iChannelOut);
|
|
|
|
if (ma_is_spatial_channel_position(channelPosOut)) {
|
|
if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, channelPosOut)) {
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
|
|
|
|
if (ma_is_spatial_channel_position(channelPosIn)) {
|
|
float weight = 0;
|
|
if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
|
|
weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
|
|
}
|
|
|
|
if (pConverter->format == ma_format_f32) {
|
|
if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
|
|
pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
|
|
}
|
|
} else {
|
|
if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
|
|
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fixed(weight);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pConfig->calculateLFEFromSpatialChannels) {
|
|
if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->pChannelMapIn, MA_CHANNEL_LFE)) {
|
|
ma_uint32 spatialChannelCount = ma_channel_map_get_spatial_channel_count(pConverter->pChannelMapIn, pConverter->channelsIn);
|
|
ma_uint32 iChannelOutLFE;
|
|
|
|
if (spatialChannelCount > 0 && ma_channel_map_find_channel_position(pConverter->channelsOut, pConverter->pChannelMapOut, MA_CHANNEL_LFE, &iChannelOutLFE)) {
|
|
const float weightForLFE = 1.0f / spatialChannelCount;
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
const ma_channel channelPosIn = ma_channel_map_get_channel(pConverter->pChannelMapIn, pConverter->channelsIn, iChannelIn);
|
|
if (ma_is_spatial_channel_position(channelPosIn)) {
|
|
if (pConverter->format == ma_format_f32) {
|
|
if (pConverter->weights.f32[iChannelIn][iChannelOutLFE] == 0) {
|
|
pConverter->weights.f32[iChannelIn][iChannelOutLFE] = weightForLFE;
|
|
}
|
|
} else {
|
|
if (pConverter->weights.s16[iChannelIn][iChannelOutLFE] == 0) {
|
|
pConverter->weights.s16[iChannelIn][iChannelOutLFE] = ma_channel_converter_float_to_fixed(weightForLFE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_channel_converter* pConverter)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_channel_converter_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_channel_converter_init_preallocated(pConfig, pHeap, pConverter);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pConverter->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_channel_converter_uninit(ma_channel_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pConverter->_ownsHeap) {
|
|
ma_free(pConverter->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
MA_ASSERT(pFramesIn != NULL);
|
|
|
|
ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_channel_converter_process_pcm_frames__shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
MA_ASSERT(pFramesIn != NULL);
|
|
MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
|
|
|
|
return ma_channel_map_apply_shuffle_table(pFramesOut, pConverter->channelsOut, pFramesIn, pConverter->channelsIn, frameCount, pConverter->pShuffleTable, pConverter->format);
|
|
}
|
|
|
|
static ma_result ma_channel_converter_process_pcm_frames__mono_in(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
MA_ASSERT(pFramesIn != NULL);
|
|
MA_ASSERT(pConverter->channelsIn == 1);
|
|
|
|
switch (pConverter->format)
|
|
{
|
|
case ma_format_u8:
|
|
{
|
|
ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
|
|
const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
|
|
pFramesOutU8[iFrame*pConverter->channelsOut + iChannel] = pFramesInU8[iFrame];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
|
|
if (pConverter->channelsOut == 2) {
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
|
|
pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
|
|
pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
|
|
const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
|
|
ma_uint64 iSampleOut = iFrame*pConverter->channelsOut + iChannel;
|
|
ma_uint64 iSampleIn = iFrame;
|
|
pFramesOutS24[iSampleOut*3 + 0] = pFramesInS24[iSampleIn*3 + 0];
|
|
pFramesOutS24[iSampleOut*3 + 1] = pFramesInS24[iSampleIn*3 + 1];
|
|
pFramesOutS24[iSampleOut*3 + 2] = pFramesInS24[iSampleIn*3 + 2];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
|
|
const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
|
|
pFramesOutS32[iFrame*pConverter->channelsOut + iChannel] = pFramesInS32[iFrame];
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
if (pConverter->channelsOut == 2) {
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
|
|
pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default: return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_channel_converter_process_pcm_frames__mono_out(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannel;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
MA_ASSERT(pFramesIn != NULL);
|
|
MA_ASSERT(pConverter->channelsOut == 1);
|
|
|
|
switch (pConverter->format)
|
|
{
|
|
case ma_format_u8:
|
|
{
|
|
ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
|
|
const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_int32 t = 0;
|
|
for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
|
|
t += ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8[iFrame*pConverter->channelsIn + iChannel]);
|
|
}
|
|
|
|
pFramesOutU8[iFrame] = ma_clip_u8(t / pConverter->channelsOut);
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_int32 t = 0;
|
|
for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
|
|
t += pFramesInS16[iFrame*pConverter->channelsIn + iChannel];
|
|
}
|
|
|
|
pFramesOutS16[iFrame] = (ma_int16)(t / pConverter->channelsIn);
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
|
|
const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_int64 t = 0;
|
|
for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
|
|
t += ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24[(iFrame*pConverter->channelsIn + iChannel)*3]);
|
|
}
|
|
|
|
ma_pcm_sample_s32_to_s24_no_scale(t / pConverter->channelsIn, &pFramesOutS24[iFrame*3]);
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
|
|
const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
ma_int64 t = 0;
|
|
for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
|
|
t += pFramesInS32[iFrame*pConverter->channelsIn + iChannel];
|
|
}
|
|
|
|
pFramesOutS32[iFrame] = (ma_int32)(t / pConverter->channelsIn);
|
|
}
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
|
|
float t = 0;
|
|
for (iChannel = 0; iChannel < pConverter->channelsIn; iChannel += 1) {
|
|
t += pFramesInF32[iFrame*pConverter->channelsIn + iChannel];
|
|
}
|
|
|
|
pFramesOutF32[iFrame] = t / pConverter->channelsIn;
|
|
}
|
|
} break;
|
|
|
|
default: return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
ma_uint32 iFrame;
|
|
ma_uint32 iChannelIn;
|
|
ma_uint32 iChannelOut;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
MA_ASSERT(pFramesIn != NULL);
|
|
|
|
ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
|
|
|
|
switch (pConverter->format)
|
|
{
|
|
case ma_format_u8:
|
|
{
|
|
ma_uint8* pFramesOutU8 = ( ma_uint8*)pFramesOut;
|
|
const ma_uint8* pFramesInU8 = (const ma_uint8*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_int16 u8_O = ma_pcm_sample_u8_to_s16_no_scale(pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut]);
|
|
ma_int16 u8_I = ma_pcm_sample_u8_to_s16_no_scale(pFramesInU8 [iFrame*pConverter->channelsIn + iChannelIn ]);
|
|
ma_int32 s = (ma_int32)ma_clamp(u8_O + ((u8_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -128, 127);
|
|
pFramesOutU8[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_u8((ma_int16)s);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
|
|
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
|
|
s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
|
|
|
|
pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s24:
|
|
{
|
|
ma_uint8* pFramesOutS24 = ( ma_uint8*)pFramesOut;
|
|
const ma_uint8* pFramesInS24 = (const ma_uint8*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_int64 s24_O = ma_pcm_sample_s24_to_s32_no_scale(&pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
|
|
ma_int64 s24_I = ma_pcm_sample_s24_to_s32_no_scale(&pFramesInS24 [(iFrame*pConverter->channelsIn + iChannelIn )*3]);
|
|
ma_int64 s24 = (ma_int32)ma_clamp(s24_O + ((s24_I * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT), -8388608, 8388607);
|
|
ma_pcm_sample_s32_to_s24_no_scale(s24, &pFramesOutS24[(iFrame*pConverter->channelsOut + iChannelOut)*3]);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
ma_int32* pFramesOutS32 = ( ma_int32*)pFramesOut;
|
|
const ma_int32* pFramesInS32 = (const ma_int32*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
ma_int64 s = pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut];
|
|
s += ((ma_int64)pFramesInS32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
|
|
|
|
pFramesOutS32[iFrame*pConverter->channelsOut + iChannelOut] = ma_clip_s32(s);
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case ma_format_f32:
|
|
{
|
|
float* pFramesOutF32 = ( float*)pFramesOut;
|
|
const float* pFramesInF32 = (const float*)pFramesIn;
|
|
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
|
|
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
|
|
pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
|
|
}
|
|
}
|
|
}
|
|
} break;
|
|
|
|
default: return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesIn == NULL) {
|
|
ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
switch (pConverter->conversionPath)
|
|
{
|
|
case ma_channel_conversion_path_passthrough: return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
|
|
case ma_channel_conversion_path_mono_out: return ma_channel_converter_process_pcm_frames__mono_out(pConverter, pFramesOut, pFramesIn, frameCount);
|
|
case ma_channel_conversion_path_mono_in: return ma_channel_converter_process_pcm_frames__mono_in(pConverter, pFramesOut, pFramesIn, frameCount);
|
|
case ma_channel_conversion_path_shuffle: return ma_channel_converter_process_pcm_frames__shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
|
|
case ma_channel_conversion_path_weights:
|
|
default:
|
|
{
|
|
return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_channel_converter_get_input_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pConverter == NULL || pChannelMap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapIn, pConverter->channelsIn);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pConverter == NULL || pChannelMap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_channel_map_copy_or_default(pChannelMap, channelMapCap, pConverter->pChannelMapOut, pConverter->channelsOut);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_data_converter_config ma_data_converter_config_init_default(void)
|
|
{
|
|
ma_data_converter_config config;
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
config.ditherMode = ma_dither_mode_none;
|
|
config.resampling.algorithm = ma_resample_algorithm_linear;
|
|
config.allowDynamicSampleRate = MA_FALSE;
|
|
|
|
config.resampling.linear.lpfOrder = 1;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
|
{
|
|
ma_data_converter_config config = ma_data_converter_config_init_default();
|
|
config.formatIn = formatIn;
|
|
config.formatOut = formatOut;
|
|
config.channelsIn = channelsIn;
|
|
config.channelsOut = channelsOut;
|
|
config.sampleRateIn = sampleRateIn;
|
|
config.sampleRateOut = sampleRateOut;
|
|
|
|
return config;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t channelConverterOffset;
|
|
size_t resamplerOffset;
|
|
} ma_data_converter_heap_layout;
|
|
|
|
static ma_bool32 ma_data_converter_config_is_resampler_required(const ma_data_converter_config* pConfig)
|
|
{
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
return pConfig->allowDynamicSampleRate || pConfig->sampleRateIn != pConfig->sampleRateOut;
|
|
}
|
|
|
|
static ma_format ma_data_converter_config_get_mid_format(const ma_data_converter_config* pConfig)
|
|
{
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
if (ma_data_converter_config_is_resampler_required(pConfig) && pConfig->resampling.algorithm != ma_resample_algorithm_linear) {
|
|
return ma_format_f32;
|
|
} else {
|
|
if (pConfig->formatOut == ma_format_s16 || pConfig->formatOut == ma_format_f32) {
|
|
return pConfig->formatOut;
|
|
} else if (pConfig->formatIn == ma_format_s16 || pConfig->formatIn == ma_format_f32) {
|
|
return pConfig->formatIn;
|
|
} else {
|
|
return ma_format_f32;
|
|
}
|
|
}
|
|
}
|
|
|
|
static ma_channel_converter_config ma_channel_converter_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
|
|
{
|
|
ma_channel_converter_config channelConverterConfig;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
channelConverterConfig = ma_channel_converter_config_init(ma_data_converter_config_get_mid_format(pConfig), pConfig->channelsIn, pConfig->pChannelMapIn, pConfig->channelsOut, pConfig->pChannelMapOut, pConfig->channelMixMode);
|
|
channelConverterConfig.ppWeights = pConfig->ppChannelWeights;
|
|
channelConverterConfig.calculateLFEFromSpatialChannels = pConfig->calculateLFEFromSpatialChannels;
|
|
|
|
return channelConverterConfig;
|
|
}
|
|
|
|
static ma_resampler_config ma_resampler_config_init_from_data_converter_config(const ma_data_converter_config* pConfig)
|
|
{
|
|
ma_resampler_config resamplerConfig;
|
|
ma_uint32 resamplerChannels;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
if (pConfig->channelsIn < pConfig->channelsOut) {
|
|
resamplerChannels = pConfig->channelsIn;
|
|
} else {
|
|
resamplerChannels = pConfig->channelsOut;
|
|
}
|
|
|
|
resamplerConfig = ma_resampler_config_init(ma_data_converter_config_get_mid_format(pConfig), resamplerChannels, pConfig->sampleRateIn, pConfig->sampleRateOut, pConfig->resampling.algorithm);
|
|
resamplerConfig.linear = pConfig->resampling.linear;
|
|
resamplerConfig.pBackendVTable = pConfig->resampling.pBackendVTable;
|
|
resamplerConfig.pBackendUserData = pConfig->resampling.pBackendUserData;
|
|
|
|
return resamplerConfig;
|
|
}
|
|
|
|
static ma_result ma_data_converter_get_heap_layout(const ma_data_converter_config* pConfig, ma_data_converter_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channelsIn == 0 || pConfig->channelsOut == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
pHeapLayout->channelConverterOffset = pHeapLayout->sizeInBytes;
|
|
{
|
|
size_t heapSizeInBytes;
|
|
ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
|
|
|
|
result = ma_channel_converter_get_heap_size(&channelConverterConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += heapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
|
|
if (ma_data_converter_config_is_resampler_required(pConfig)) {
|
|
size_t heapSizeInBytes;
|
|
ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
|
|
|
|
result = ma_resampler_get_heap_size(&resamplerConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes += heapSizeInBytes;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_get_heap_size(const ma_data_converter_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_data_converter_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_init_preallocated(const ma_data_converter_config* pConfig, void* pHeap, ma_data_converter* pConverter)
|
|
{
|
|
ma_result result;
|
|
ma_data_converter_heap_layout heapLayout;
|
|
ma_format midFormat;
|
|
ma_bool32 isResamplingRequired;
|
|
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pConverter);
|
|
|
|
result = ma_data_converter_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pConverter->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pConverter->formatIn = pConfig->formatIn;
|
|
pConverter->formatOut = pConfig->formatOut;
|
|
pConverter->channelsIn = pConfig->channelsIn;
|
|
pConverter->channelsOut = pConfig->channelsOut;
|
|
pConverter->sampleRateIn = pConfig->sampleRateIn;
|
|
pConverter->sampleRateOut = pConfig->sampleRateOut;
|
|
pConverter->ditherMode = pConfig->ditherMode;
|
|
|
|
isResamplingRequired = ma_data_converter_config_is_resampler_required(pConfig);
|
|
midFormat = ma_data_converter_config_get_mid_format(pConfig);
|
|
|
|
{
|
|
ma_channel_converter_config channelConverterConfig = ma_channel_converter_config_init_from_data_converter_config(pConfig);
|
|
|
|
result = ma_channel_converter_init_preallocated(&channelConverterConfig, ma_offset_ptr(pHeap, heapLayout.channelConverterOffset), &pConverter->channelConverter);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConverter->channelConverter.conversionPath != ma_channel_conversion_path_passthrough) {
|
|
pConverter->hasChannelConverter = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isResamplingRequired) {
|
|
ma_resampler_config resamplerConfig = ma_resampler_config_init_from_data_converter_config(pConfig);
|
|
|
|
result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pConverter->resampler);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pConverter->hasResampler = MA_TRUE;
|
|
}
|
|
|
|
if (pConverter->hasChannelConverter == MA_FALSE && pConverter->hasResampler == MA_FALSE) {
|
|
if (pConverter->formatIn == pConverter->formatOut) {
|
|
pConverter->hasPreFormatConversion = MA_FALSE;
|
|
pConverter->hasPostFormatConversion = MA_FALSE;
|
|
} else {
|
|
pConverter->hasPreFormatConversion = MA_FALSE;
|
|
pConverter->hasPostFormatConversion = MA_TRUE;
|
|
}
|
|
} else {
|
|
if (pConverter->formatIn != midFormat) {
|
|
pConverter->hasPreFormatConversion = MA_TRUE;
|
|
}
|
|
if (pConverter->formatOut != midFormat) {
|
|
pConverter->hasPostFormatConversion = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (pConverter->hasPreFormatConversion == MA_FALSE &&
|
|
pConverter->hasPostFormatConversion == MA_FALSE &&
|
|
pConverter->hasChannelConverter == MA_FALSE &&
|
|
pConverter->hasResampler == MA_FALSE) {
|
|
pConverter->isPassthrough = MA_TRUE;
|
|
}
|
|
|
|
if (pConverter->isPassthrough) {
|
|
pConverter->executionPath = ma_data_converter_execution_path_passthrough;
|
|
} else {
|
|
if (pConverter->channelsIn < pConverter->channelsOut) {
|
|
MA_ASSERT(pConverter->hasChannelConverter == MA_TRUE);
|
|
|
|
if (pConverter->hasResampler) {
|
|
pConverter->executionPath = ma_data_converter_execution_path_resample_first;
|
|
} else {
|
|
pConverter->executionPath = ma_data_converter_execution_path_channels_only;
|
|
}
|
|
} else {
|
|
if (pConverter->hasChannelConverter) {
|
|
if (pConverter->hasResampler) {
|
|
pConverter->executionPath = ma_data_converter_execution_path_channels_first;
|
|
} else {
|
|
pConverter->executionPath = ma_data_converter_execution_path_channels_only;
|
|
}
|
|
} else {
|
|
if (pConverter->hasResampler) {
|
|
pConverter->executionPath = ma_data_converter_execution_path_resample_only;
|
|
} else {
|
|
pConverter->executionPath = ma_data_converter_execution_path_format_only;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_converter* pConverter)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_data_converter_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_data_converter_init_preallocated(pConfig, pHeap, pConverter);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pConverter->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_data_converter_uninit(ma_data_converter* pConverter, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pConverter->hasResampler) {
|
|
ma_resampler_uninit(&pConverter->resampler, pAllocationCallbacks);
|
|
}
|
|
|
|
ma_channel_converter_uninit(&pConverter->channelConverter, pAllocationCallbacks);
|
|
|
|
if (pConverter->_ownsHeap) {
|
|
ma_free(pConverter->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__passthrough(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 frameCount;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
|
|
frameCountIn = 0;
|
|
if (pFrameCountIn != NULL) {
|
|
frameCountIn = *pFrameCountIn;
|
|
}
|
|
|
|
frameCountOut = 0;
|
|
if (pFrameCountOut != NULL) {
|
|
frameCountOut = *pFrameCountOut;
|
|
}
|
|
|
|
frameCount = ma_min(frameCountIn, frameCountOut);
|
|
|
|
if (pFramesOut != NULL) {
|
|
if (pFramesIn != NULL) {
|
|
ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
} else {
|
|
ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
}
|
|
}
|
|
|
|
if (pFrameCountIn != NULL) {
|
|
*pFrameCountIn = frameCount;
|
|
}
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 frameCount;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
|
|
frameCountIn = 0;
|
|
if (pFrameCountIn != NULL) {
|
|
frameCountIn = *pFrameCountIn;
|
|
}
|
|
|
|
frameCountOut = 0;
|
|
if (pFrameCountOut != NULL) {
|
|
frameCountOut = *pFrameCountOut;
|
|
}
|
|
|
|
frameCount = ma_min(frameCountIn, frameCountOut);
|
|
|
|
if (pFramesOut != NULL) {
|
|
if (pFramesIn != NULL) {
|
|
ma_convert_pcm_frames_format(pFramesOut, pConverter->formatOut, pFramesIn, pConverter->formatIn, frameCount, pConverter->channelsIn, pConverter->ditherMode);
|
|
} else {
|
|
ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
}
|
|
}
|
|
|
|
if (pFrameCountIn != NULL) {
|
|
*pFrameCountIn = frameCount;
|
|
}
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
|
|
frameCountIn = 0;
|
|
if (pFrameCountIn != NULL) {
|
|
frameCountIn = *pFrameCountIn;
|
|
}
|
|
|
|
frameCountOut = 0;
|
|
if (pFrameCountOut != NULL) {
|
|
frameCountOut = *pFrameCountOut;
|
|
}
|
|
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
|
|
const void* pFramesInThisIteration;
|
|
void* pFramesOutThisIteration;
|
|
ma_uint64 frameCountInThisIteration;
|
|
ma_uint64 frameCountOutThisIteration;
|
|
|
|
if (pFramesIn != NULL) {
|
|
pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
|
|
} else {
|
|
pFramesInThisIteration = NULL;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
} else {
|
|
pFramesOutThisIteration = NULL;
|
|
}
|
|
|
|
if (pConverter->hasPreFormatConversion) {
|
|
ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
|
|
|
|
frameCountInThisIteration = (frameCountIn - framesProcessedIn);
|
|
if (frameCountInThisIteration > tempBufferInCap) {
|
|
frameCountInThisIteration = tempBufferInCap;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (frameCountInThisIteration > tempBufferOutCap) {
|
|
frameCountInThisIteration = tempBufferOutCap;
|
|
}
|
|
}
|
|
|
|
if (pFramesInThisIteration != NULL) {
|
|
ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pFramesInThisIteration, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
|
|
} else {
|
|
MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
|
|
}
|
|
|
|
frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (frameCountOutThisIteration > tempBufferOutCap) {
|
|
frameCountOutThisIteration = tempBufferOutCap;
|
|
}
|
|
|
|
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
|
|
} else {
|
|
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferIn, &frameCountInThisIteration, pFramesOutThisIteration, &frameCountOutThisIteration);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
} else {
|
|
MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
|
|
|
|
frameCountInThisIteration = (frameCountIn - framesProcessedIn);
|
|
frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
|
|
if (frameCountOutThisIteration > tempBufferOutCap) {
|
|
frameCountOutThisIteration = tempBufferOutCap;
|
|
}
|
|
|
|
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (pFramesOutThisIteration != NULL) {
|
|
ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->resampler.channels, pConverter->ditherMode);
|
|
}
|
|
}
|
|
|
|
framesProcessedIn += frameCountInThisIteration;
|
|
framesProcessedOut += frameCountOutThisIteration;
|
|
|
|
MA_ASSERT(framesProcessedIn <= frameCountIn);
|
|
MA_ASSERT(framesProcessedOut <= frameCountOut);
|
|
|
|
if (frameCountOutThisIteration == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pFrameCountIn != NULL) {
|
|
*pFrameCountIn = framesProcessedIn;
|
|
}
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = framesProcessedOut;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
MA_ASSERT(pConverter != NULL);
|
|
|
|
if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
|
|
return ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
} else {
|
|
return ma_data_converter_process_pcm_frames__resample_with_format_conversion(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__channels_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 frameCount;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
|
|
frameCountIn = 0;
|
|
if (pFrameCountIn != NULL) {
|
|
frameCountIn = *pFrameCountIn;
|
|
}
|
|
|
|
frameCountOut = 0;
|
|
if (pFrameCountOut != NULL) {
|
|
frameCountOut = *pFrameCountOut;
|
|
}
|
|
|
|
frameCount = ma_min(frameCountIn, frameCountOut);
|
|
|
|
if (pConverter->hasPreFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE) {
|
|
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOut, pFramesIn, frameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
} else {
|
|
ma_uint64 framesProcessed = 0;
|
|
|
|
while (framesProcessed < frameCount) {
|
|
ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
const ma_uint32 tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
|
|
const void* pFramesInThisIteration;
|
|
void* pFramesOutThisIteration;
|
|
ma_uint64 frameCountThisIteration;
|
|
|
|
if (pFramesIn != NULL) {
|
|
pFramesInThisIteration = ma_offset_ptr(pFramesIn, framesProcessed * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
|
|
} else {
|
|
pFramesInThisIteration = NULL;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
pFramesOutThisIteration = ma_offset_ptr(pFramesOut, framesProcessed * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
} else {
|
|
pFramesOutThisIteration = NULL;
|
|
}
|
|
|
|
if (pConverter->hasPreFormatConversion) {
|
|
ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
const ma_uint32 tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
|
|
|
|
frameCountThisIteration = (frameCount - framesProcessed);
|
|
if (frameCountThisIteration > tempBufferInCap) {
|
|
frameCountThisIteration = tempBufferInCap;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (frameCountThisIteration > tempBufferOutCap) {
|
|
frameCountThisIteration = tempBufferOutCap;
|
|
}
|
|
}
|
|
|
|
if (pFramesInThisIteration != NULL) {
|
|
ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pFramesInThisIteration, pConverter->formatIn, frameCountThisIteration, pConverter->channelsIn, pConverter->ditherMode);
|
|
} else {
|
|
MA_ZERO_MEMORY(pTempBufferIn, sizeof(pTempBufferIn));
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pTempBufferIn, frameCountThisIteration);
|
|
} else {
|
|
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pFramesOutThisIteration, pTempBufferIn, frameCountThisIteration);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
} else {
|
|
MA_ASSERT(pConverter->hasPostFormatConversion == MA_TRUE);
|
|
|
|
frameCountThisIteration = (frameCount - framesProcessed);
|
|
if (frameCountThisIteration > tempBufferOutCap) {
|
|
frameCountThisIteration = tempBufferOutCap;
|
|
}
|
|
|
|
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferOut, pFramesInThisIteration, frameCountThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (pFramesOutThisIteration != NULL) {
|
|
ma_convert_pcm_frames_format(pFramesOutThisIteration, pConverter->formatOut, pTempBufferOut, pConverter->channelConverter.format, frameCountThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
|
|
}
|
|
}
|
|
|
|
framesProcessed += frameCountThisIteration;
|
|
}
|
|
}
|
|
|
|
if (pFrameCountIn != NULL) {
|
|
*pFrameCountIn = frameCount;
|
|
}
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__resample_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 tempBufferInCap;
|
|
ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 tempBufferMidCap;
|
|
ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 tempBufferOutCap;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
|
|
MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsIn);
|
|
MA_ASSERT(pConverter->resampler.channels < pConverter->channelConverter.channelsOut);
|
|
|
|
frameCountIn = 0;
|
|
if (pFrameCountIn != NULL) {
|
|
frameCountIn = *pFrameCountIn;
|
|
}
|
|
|
|
frameCountOut = 0;
|
|
if (pFrameCountOut != NULL) {
|
|
frameCountOut = *pFrameCountOut;
|
|
}
|
|
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
|
|
tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
|
|
tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
ma_uint64 frameCountInThisIteration;
|
|
ma_uint64 frameCountOutThisIteration;
|
|
const void* pRunningFramesIn = NULL;
|
|
void* pRunningFramesOut = NULL;
|
|
const void* pResampleBufferIn;
|
|
void* pChannelsBufferOut;
|
|
|
|
if (pFramesIn != NULL) {
|
|
pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
|
|
}
|
|
if (pFramesOut != NULL) {
|
|
pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
}
|
|
|
|
frameCountInThisIteration = (frameCountIn - framesProcessedIn);
|
|
|
|
if (pConverter->hasPreFormatConversion) {
|
|
if (frameCountInThisIteration > tempBufferInCap) {
|
|
frameCountInThisIteration = tempBufferInCap;
|
|
}
|
|
}
|
|
|
|
frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
|
|
if (frameCountOutThisIteration > tempBufferMidCap) {
|
|
frameCountOutThisIteration = tempBufferMidCap;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (frameCountOutThisIteration > tempBufferOutCap) {
|
|
frameCountOutThisIteration = tempBufferOutCap;
|
|
}
|
|
}
|
|
|
|
#if 1
|
|
{
|
|
ma_uint64 requiredInputFrameCount;
|
|
|
|
result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
|
|
}
|
|
|
|
if (frameCountInThisIteration > requiredInputFrameCount) {
|
|
frameCountInThisIteration = requiredInputFrameCount;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (pConverter->hasPreFormatConversion) {
|
|
if (pFramesIn != NULL) {
|
|
ma_convert_pcm_frames_format(pTempBufferIn, pConverter->resampler.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
|
|
pResampleBufferIn = pTempBufferIn;
|
|
} else {
|
|
pResampleBufferIn = NULL;
|
|
}
|
|
} else {
|
|
pResampleBufferIn = pRunningFramesIn;
|
|
}
|
|
|
|
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pResampleBufferIn, &frameCountInThisIteration, pTempBufferMid, &frameCountOutThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
if (pConverter->hasPostFormatConversion) {
|
|
pChannelsBufferOut = pTempBufferOut;
|
|
} else {
|
|
pChannelsBufferOut = pRunningFramesOut;
|
|
}
|
|
|
|
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pChannelsBufferOut, pTempBufferMid, frameCountOutThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pChannelsBufferOut, pConverter->channelConverter.format, frameCountOutThisIteration, pConverter->channelConverter.channelsOut, pConverter->ditherMode);
|
|
}
|
|
}
|
|
|
|
framesProcessedIn += frameCountInThisIteration;
|
|
framesProcessedOut += frameCountOutThisIteration;
|
|
|
|
MA_ASSERT(framesProcessedIn <= frameCountIn);
|
|
MA_ASSERT(framesProcessedOut <= frameCountOut);
|
|
|
|
if (frameCountOutThisIteration == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pFrameCountIn != NULL) {
|
|
*pFrameCountIn = framesProcessedIn;
|
|
}
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = framesProcessedOut;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_data_converter_process_pcm_frames__channels_first(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 frameCountIn;
|
|
ma_uint64 frameCountOut;
|
|
ma_uint64 framesProcessedIn;
|
|
ma_uint64 framesProcessedOut;
|
|
ma_uint8 pTempBufferIn[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 tempBufferInCap;
|
|
ma_uint8 pTempBufferMid[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 tempBufferMidCap;
|
|
ma_uint8 pTempBufferOut[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 tempBufferOutCap;
|
|
|
|
MA_ASSERT(pConverter != NULL);
|
|
MA_ASSERT(pConverter->resampler.format == pConverter->channelConverter.format);
|
|
MA_ASSERT(pConverter->resampler.channels == pConverter->channelConverter.channelsOut);
|
|
MA_ASSERT(pConverter->resampler.channels <= pConverter->channelConverter.channelsIn);
|
|
|
|
frameCountIn = 0;
|
|
if (pFrameCountIn != NULL) {
|
|
frameCountIn = *pFrameCountIn;
|
|
}
|
|
|
|
frameCountOut = 0;
|
|
if (pFrameCountOut != NULL) {
|
|
frameCountOut = *pFrameCountOut;
|
|
}
|
|
|
|
framesProcessedIn = 0;
|
|
framesProcessedOut = 0;
|
|
|
|
tempBufferInCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsIn);
|
|
tempBufferMidCap = sizeof(pTempBufferIn) / ma_get_bytes_per_frame(pConverter->channelConverter.format, pConverter->channelConverter.channelsOut);
|
|
tempBufferOutCap = sizeof(pTempBufferOut) / ma_get_bytes_per_frame(pConverter->resampler.format, pConverter->resampler.channels);
|
|
|
|
while (framesProcessedOut < frameCountOut) {
|
|
ma_uint64 frameCountInThisIteration;
|
|
ma_uint64 frameCountOutThisIteration;
|
|
const void* pRunningFramesIn = NULL;
|
|
void* pRunningFramesOut = NULL;
|
|
const void* pChannelsBufferIn;
|
|
void* pResampleBufferOut;
|
|
|
|
if (pFramesIn != NULL) {
|
|
pRunningFramesIn = ma_offset_ptr(pFramesIn, framesProcessedIn * ma_get_bytes_per_frame(pConverter->formatIn, pConverter->channelsIn));
|
|
}
|
|
if (pFramesOut != NULL) {
|
|
pRunningFramesOut = ma_offset_ptr(pFramesOut, framesProcessedOut * ma_get_bytes_per_frame(pConverter->formatOut, pConverter->channelsOut));
|
|
}
|
|
|
|
frameCountOutThisIteration = (frameCountOut - framesProcessedOut);
|
|
if (frameCountOutThisIteration > tempBufferMidCap) {
|
|
frameCountOutThisIteration = tempBufferMidCap;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (frameCountOutThisIteration > tempBufferOutCap) {
|
|
frameCountOutThisIteration = tempBufferOutCap;
|
|
}
|
|
}
|
|
|
|
frameCountInThisIteration = (frameCountIn - framesProcessedIn);
|
|
if (pConverter->hasPreFormatConversion) {
|
|
if (frameCountInThisIteration > tempBufferInCap) {
|
|
frameCountInThisIteration = tempBufferInCap;
|
|
}
|
|
}
|
|
|
|
if (frameCountInThisIteration > tempBufferMidCap) {
|
|
frameCountInThisIteration = tempBufferMidCap;
|
|
}
|
|
|
|
#if 1
|
|
{
|
|
ma_uint64 requiredInputFrameCount;
|
|
|
|
result = ma_resampler_get_required_input_frame_count(&pConverter->resampler, frameCountOutThisIteration, &requiredInputFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
requiredInputFrameCount = (frameCountOutThisIteration * pConverter->resampler.sampleRateIn) / pConverter->resampler.sampleRateOut;
|
|
}
|
|
|
|
if (frameCountInThisIteration > requiredInputFrameCount) {
|
|
frameCountInThisIteration = requiredInputFrameCount;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (pConverter->hasPreFormatConversion) {
|
|
if (pRunningFramesIn != NULL) {
|
|
ma_convert_pcm_frames_format(pTempBufferIn, pConverter->channelConverter.format, pRunningFramesIn, pConverter->formatIn, frameCountInThisIteration, pConverter->channelsIn, pConverter->ditherMode);
|
|
pChannelsBufferIn = pTempBufferIn;
|
|
} else {
|
|
pChannelsBufferIn = NULL;
|
|
}
|
|
} else {
|
|
pChannelsBufferIn = pRunningFramesIn;
|
|
}
|
|
|
|
result = ma_channel_converter_process_pcm_frames(&pConverter->channelConverter, pTempBufferMid, pChannelsBufferIn, frameCountInThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
pResampleBufferOut = pTempBufferOut;
|
|
} else {
|
|
pResampleBufferOut = pRunningFramesOut;
|
|
}
|
|
|
|
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pTempBufferMid, &frameCountInThisIteration, pResampleBufferOut, &frameCountOutThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConverter->hasPostFormatConversion) {
|
|
if (pRunningFramesOut != NULL) {
|
|
ma_convert_pcm_frames_format(pRunningFramesOut, pConverter->formatOut, pResampleBufferOut, pConverter->resampler.format, frameCountOutThisIteration, pConverter->channelsOut, pConverter->ditherMode);
|
|
}
|
|
}
|
|
|
|
framesProcessedIn += frameCountInThisIteration;
|
|
framesProcessedOut += frameCountOutThisIteration;
|
|
|
|
MA_ASSERT(framesProcessedIn <= frameCountIn);
|
|
MA_ASSERT(framesProcessedOut <= frameCountOut);
|
|
|
|
if (frameCountOutThisIteration == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pFrameCountIn != NULL) {
|
|
*pFrameCountIn = framesProcessedIn;
|
|
}
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = framesProcessedOut;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_process_pcm_frames(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
switch (pConverter->executionPath)
|
|
{
|
|
case ma_data_converter_execution_path_passthrough: return ma_data_converter_process_pcm_frames__passthrough(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
case ma_data_converter_execution_path_format_only: return ma_data_converter_process_pcm_frames__format_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
case ma_data_converter_execution_path_channels_only: return ma_data_converter_process_pcm_frames__channels_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
case ma_data_converter_execution_path_resample_only: return ma_data_converter_process_pcm_frames__resample_only(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
case ma_data_converter_execution_path_resample_first: return ma_data_converter_process_pcm_frames__resample_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
case ma_data_converter_execution_path_channels_first: return ma_data_converter_process_pcm_frames__channels_first(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
|
|
default: return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_set_rate(ma_data_converter* pConverter, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasResampler == MA_FALSE) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return ma_resampler_set_rate(&pConverter->resampler, sampleRateIn, sampleRateOut);
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_set_rate_ratio(ma_data_converter* pConverter, float ratioInOut)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasResampler == MA_FALSE) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return ma_resampler_set_rate_ratio(&pConverter->resampler, ratioInOut);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_data_converter_get_input_latency(const ma_data_converter* pConverter)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (pConverter->hasResampler) {
|
|
return ma_resampler_get_input_latency(&pConverter->resampler);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MA_API ma_uint64 ma_data_converter_get_output_latency(const ma_data_converter* pConverter)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (pConverter->hasResampler) {
|
|
return ma_resampler_get_output_latency(&pConverter->resampler);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_get_required_input_frame_count(const ma_data_converter* pConverter, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount)
|
|
{
|
|
if (pInputFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pInputFrameCount = 0;
|
|
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasResampler) {
|
|
return ma_resampler_get_required_input_frame_count(&pConverter->resampler, outputFrameCount, pInputFrameCount);
|
|
} else {
|
|
*pInputFrameCount = outputFrameCount;
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_get_expected_output_frame_count(const ma_data_converter* pConverter, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount)
|
|
{
|
|
if (pOutputFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pOutputFrameCount = 0;
|
|
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasResampler) {
|
|
return ma_resampler_get_expected_output_frame_count(&pConverter->resampler, inputFrameCount, pOutputFrameCount);
|
|
} else {
|
|
*pOutputFrameCount = inputFrameCount;
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_get_input_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pConverter == NULL || pChannelMap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasChannelConverter) {
|
|
ma_channel_converter_get_output_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsOut);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_get_output_channel_map(const ma_data_converter* pConverter, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pConverter == NULL || pChannelMap == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasChannelConverter) {
|
|
ma_channel_converter_get_input_channel_map(&pConverter->channelConverter, pChannelMap, channelMapCap);
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pConverter->channelsIn);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_converter_reset(ma_data_converter* pConverter)
|
|
{
|
|
if (pConverter == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConverter->hasResampler == MA_FALSE) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
return ma_resampler_reset(&pConverter->resampler);
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex);
|
|
|
|
MA_API ma_channel ma_channel_map_get_channel(const ma_channel* pChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
if (pChannelMap == NULL) {
|
|
return ma_channel_map_init_standard_channel(ma_standard_channel_map_default, channelCount, channelIndex);
|
|
} else {
|
|
if (channelIndex >= channelCount) {
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
return pChannelMap[channelIndex];
|
|
}
|
|
}
|
|
|
|
MA_API void ma_channel_map_init_blank(ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
if (pChannelMap == NULL) {
|
|
return;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channels);
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_microsoft(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
if (channelCount == 0 || channelIndex >= channelCount) {
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
#ifndef MA_USE_QUAD_MICROSOFT_CHANNEL_MAP
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_BACK_CENTER;
|
|
#else
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
#endif
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_LFE;
|
|
case 4: return MA_CHANNEL_SIDE_LEFT;
|
|
case 5: return MA_CHANNEL_SIDE_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 7:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_LFE;
|
|
case 4: return MA_CHANNEL_BACK_CENTER;
|
|
case 5: return MA_CHANNEL_SIDE_LEFT;
|
|
case 6: return MA_CHANNEL_SIDE_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 8:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_LFE;
|
|
case 4: return MA_CHANNEL_BACK_LEFT;
|
|
case 5: return MA_CHANNEL_BACK_RIGHT;
|
|
case 6: return MA_CHANNEL_SIDE_LEFT;
|
|
case 7: return MA_CHANNEL_SIDE_RIGHT;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 8) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_alsa(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
case 4: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
case 4: return MA_CHANNEL_FRONT_CENTER;
|
|
case 5: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
|
|
case 7:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
case 4: return MA_CHANNEL_FRONT_CENTER;
|
|
case 5: return MA_CHANNEL_LFE;
|
|
case 6: return MA_CHANNEL_BACK_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 8:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
case 4: return MA_CHANNEL_FRONT_CENTER;
|
|
case 5: return MA_CHANNEL_LFE;
|
|
case 6: return MA_CHANNEL_SIDE_LEFT;
|
|
case 7: return MA_CHANNEL_SIDE_RIGHT;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 8) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_rfc3551(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_BACK_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_SIDE_LEFT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 4: return MA_CHANNEL_SIDE_RIGHT;
|
|
case 5: return MA_CHANNEL_BACK_CENTER;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 6) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_flac(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_LFE;
|
|
case 4: return MA_CHANNEL_BACK_LEFT;
|
|
case 5: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 7:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_LFE;
|
|
case 4: return MA_CHANNEL_BACK_CENTER;
|
|
case 5: return MA_CHANNEL_SIDE_LEFT;
|
|
case 6: return MA_CHANNEL_SIDE_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 8:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_LFE;
|
|
case 4: return MA_CHANNEL_BACK_LEFT;
|
|
case 5: return MA_CHANNEL_BACK_RIGHT;
|
|
case 6: return MA_CHANNEL_SIDE_LEFT;
|
|
case 7: return MA_CHANNEL_SIDE_RIGHT;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 8) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_vorbis(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
case 5: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
|
|
case 7:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_SIDE_LEFT;
|
|
case 4: return MA_CHANNEL_SIDE_RIGHT;
|
|
case 5: return MA_CHANNEL_BACK_CENTER;
|
|
case 6: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
|
|
case 8:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_SIDE_LEFT;
|
|
case 4: return MA_CHANNEL_SIDE_RIGHT;
|
|
case 5: return MA_CHANNEL_BACK_LEFT;
|
|
case 6: return MA_CHANNEL_BACK_RIGHT;
|
|
case 7: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 8) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_sound4(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_BACK_LEFT;
|
|
case 4: return MA_CHANNEL_BACK_RIGHT;
|
|
case 5: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
|
|
case 7:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_SIDE_LEFT;
|
|
case 4: return MA_CHANNEL_SIDE_RIGHT;
|
|
case 5: return MA_CHANNEL_BACK_CENTER;
|
|
case 6: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
|
|
case 8:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_CENTER;
|
|
case 2: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 3: return MA_CHANNEL_SIDE_LEFT;
|
|
case 4: return MA_CHANNEL_SIDE_RIGHT;
|
|
case 5: return MA_CHANNEL_BACK_LEFT;
|
|
case 6: return MA_CHANNEL_BACK_RIGHT;
|
|
case 7: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 8) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 8));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel_sndio(ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
switch (channelCount)
|
|
{
|
|
case 0: return MA_CHANNEL_NONE;
|
|
|
|
case 1:
|
|
{
|
|
return MA_CHANNEL_MONO;
|
|
} break;
|
|
|
|
case 2:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 3:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 4:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
}
|
|
} break;
|
|
|
|
case 5:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
case 4: return MA_CHANNEL_FRONT_CENTER;
|
|
}
|
|
} break;
|
|
|
|
case 6:
|
|
default:
|
|
{
|
|
switch (channelIndex) {
|
|
case 0: return MA_CHANNEL_FRONT_LEFT;
|
|
case 1: return MA_CHANNEL_FRONT_RIGHT;
|
|
case 2: return MA_CHANNEL_BACK_LEFT;
|
|
case 3: return MA_CHANNEL_BACK_RIGHT;
|
|
case 4: return MA_CHANNEL_FRONT_CENTER;
|
|
case 5: return MA_CHANNEL_LFE;
|
|
}
|
|
} break;
|
|
}
|
|
|
|
if (channelCount > 6) {
|
|
if (channelIndex < 32) {
|
|
return (ma_channel)(MA_CHANNEL_AUX_0 + (channelIndex - 6));
|
|
}
|
|
}
|
|
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
static ma_channel ma_channel_map_init_standard_channel(ma_standard_channel_map standardChannelMap, ma_uint32 channelCount, ma_uint32 channelIndex)
|
|
{
|
|
if (channelCount == 0 || channelIndex >= channelCount) {
|
|
return MA_CHANNEL_NONE;
|
|
}
|
|
|
|
switch (standardChannelMap)
|
|
{
|
|
case ma_standard_channel_map_alsa:
|
|
{
|
|
return ma_channel_map_init_standard_channel_alsa(channelCount, channelIndex);
|
|
} break;
|
|
|
|
case ma_standard_channel_map_rfc3551:
|
|
{
|
|
return ma_channel_map_init_standard_channel_rfc3551(channelCount, channelIndex);
|
|
} break;
|
|
|
|
case ma_standard_channel_map_flac:
|
|
{
|
|
return ma_channel_map_init_standard_channel_flac(channelCount, channelIndex);
|
|
} break;
|
|
|
|
case ma_standard_channel_map_vorbis:
|
|
{
|
|
return ma_channel_map_init_standard_channel_vorbis(channelCount, channelIndex);
|
|
} break;
|
|
|
|
case ma_standard_channel_map_sound4:
|
|
{
|
|
return ma_channel_map_init_standard_channel_sound4(channelCount, channelIndex);
|
|
} break;
|
|
|
|
case ma_standard_channel_map_sndio:
|
|
{
|
|
return ma_channel_map_init_standard_channel_sndio(channelCount, channelIndex);
|
|
} break;
|
|
|
|
case ma_standard_channel_map_microsoft:
|
|
default:
|
|
{
|
|
return ma_channel_map_init_standard_channel_microsoft(channelCount, channelIndex);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_channel_map_init_standard(ma_standard_channel_map standardChannelMap, ma_channel* pChannelMap, size_t channelMapCap, ma_uint32 channels)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pChannelMap == NULL || channelMapCap == 0 || channels == 0) {
|
|
return;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
if (channelMapCap == 0) {
|
|
break;
|
|
}
|
|
|
|
pChannelMap[0] = ma_channel_map_init_standard_channel(standardChannelMap, channels, iChannel);
|
|
pChannelMap += 1;
|
|
channelMapCap -= 1;
|
|
}
|
|
}
|
|
|
|
MA_API void ma_channel_map_copy(ma_channel* pOut, const ma_channel* pIn, ma_uint32 channels)
|
|
{
|
|
if (pOut != NULL && pIn != NULL && channels > 0) {
|
|
MA_COPY_MEMORY(pOut, pIn, sizeof(*pOut) * channels);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_channel_map_copy_or_default(ma_channel* pOut, size_t channelMapCapOut, const ma_channel* pIn, ma_uint32 channels)
|
|
{
|
|
if (pOut == NULL || channels == 0) {
|
|
return;
|
|
}
|
|
|
|
if (pIn != NULL) {
|
|
ma_channel_map_copy(pOut, pIn, channels);
|
|
} else {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pOut, channelMapCapOut, channels);
|
|
}
|
|
}
|
|
|
|
MA_API ma_bool32 ma_channel_map_is_valid(const ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
if (channels == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (channels > 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == MA_CHANNEL_MONO) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_channel_map_is_equal(const ma_channel* pChannelMapA, const ma_channel* pChannelMapB, ma_uint32 channels)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pChannelMapA == pChannelMapB) {
|
|
return MA_TRUE;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
if (ma_channel_map_get_channel(pChannelMapA, channels, iChannel) != ma_channel_map_get_channel(pChannelMapB, channels, iChannel)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_channel_map_is_blank(const ma_channel* pChannelMap, ma_uint32 channels)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pChannelMap == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
if (pChannelMap[iChannel] != MA_CHANNEL_NONE) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
MA_API ma_bool32 ma_channel_map_contains_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition)
|
|
{
|
|
return ma_channel_map_find_channel_position(channels, pChannelMap, channelPosition, NULL);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_channel_map_find_channel_position(ma_uint32 channels, const ma_channel* pChannelMap, ma_channel channelPosition, ma_uint32* pChannelIndex)
|
|
{
|
|
ma_uint32 iChannel;
|
|
|
|
if (pChannelIndex != NULL) {
|
|
*pChannelIndex = (ma_uint32)-1;
|
|
}
|
|
|
|
for (iChannel = 0; iChannel < channels; ++iChannel) {
|
|
if (ma_channel_map_get_channel(pChannelMap, channels, iChannel) == channelPosition) {
|
|
if (pChannelIndex != NULL) {
|
|
*pChannelIndex = iChannel;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
return MA_FALSE;
|
|
}
|
|
|
|
MA_API size_t ma_channel_map_to_string(const ma_channel* pChannelMap, ma_uint32 channels, char* pBufferOut, size_t bufferCap)
|
|
{
|
|
size_t len;
|
|
ma_uint32 iChannel;
|
|
|
|
len = 0;
|
|
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
const char* pChannelStr = ma_channel_position_to_string(ma_channel_map_get_channel(pChannelMap, channels, iChannel));
|
|
size_t channelStrLen = strlen(pChannelStr);
|
|
|
|
if (pBufferOut != NULL && bufferCap > len + channelStrLen) {
|
|
MA_COPY_MEMORY(pBufferOut + len, pChannelStr, channelStrLen);
|
|
}
|
|
len += channelStrLen;
|
|
|
|
if (iChannel+1 < channels) {
|
|
if (pBufferOut != NULL && bufferCap > len + 1) {
|
|
pBufferOut[len] = ' ';
|
|
}
|
|
len += 1;
|
|
}
|
|
}
|
|
|
|
if (pBufferOut != NULL) {
|
|
if (bufferCap > len) {
|
|
pBufferOut[len] = '\0';
|
|
} else if (bufferCap > 0) {
|
|
pBufferOut[bufferCap - 1] = '\0';
|
|
}
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
MA_API const char* ma_channel_position_to_string(ma_channel channel)
|
|
{
|
|
switch (channel)
|
|
{
|
|
case MA_CHANNEL_NONE : return "CHANNEL_NONE";
|
|
case MA_CHANNEL_MONO : return "CHANNEL_MONO";
|
|
case MA_CHANNEL_FRONT_LEFT : return "CHANNEL_FRONT_LEFT";
|
|
case MA_CHANNEL_FRONT_RIGHT : return "CHANNEL_FRONT_RIGHT";
|
|
case MA_CHANNEL_FRONT_CENTER : return "CHANNEL_FRONT_CENTER";
|
|
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_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";
|
|
case MA_CHANNEL_SIDE_RIGHT : return "CHANNEL_SIDE_RIGHT";
|
|
case MA_CHANNEL_TOP_CENTER : return "CHANNEL_TOP_CENTER";
|
|
case MA_CHANNEL_TOP_FRONT_LEFT : return "CHANNEL_TOP_FRONT_LEFT";
|
|
case MA_CHANNEL_TOP_FRONT_CENTER : return "CHANNEL_TOP_FRONT_CENTER";
|
|
case MA_CHANNEL_TOP_FRONT_RIGHT : return "CHANNEL_TOP_FRONT_RIGHT";
|
|
case MA_CHANNEL_TOP_BACK_LEFT : return "CHANNEL_TOP_BACK_LEFT";
|
|
case MA_CHANNEL_TOP_BACK_CENTER : return "CHANNEL_TOP_BACK_CENTER";
|
|
case MA_CHANNEL_TOP_BACK_RIGHT : return "CHANNEL_TOP_BACK_RIGHT";
|
|
case MA_CHANNEL_AUX_0 : return "CHANNEL_AUX_0";
|
|
case MA_CHANNEL_AUX_1 : return "CHANNEL_AUX_1";
|
|
case MA_CHANNEL_AUX_2 : return "CHANNEL_AUX_2";
|
|
case MA_CHANNEL_AUX_3 : return "CHANNEL_AUX_3";
|
|
case MA_CHANNEL_AUX_4 : return "CHANNEL_AUX_4";
|
|
case MA_CHANNEL_AUX_5 : return "CHANNEL_AUX_5";
|
|
case MA_CHANNEL_AUX_6 : return "CHANNEL_AUX_6";
|
|
case MA_CHANNEL_AUX_7 : return "CHANNEL_AUX_7";
|
|
case MA_CHANNEL_AUX_8 : return "CHANNEL_AUX_8";
|
|
case MA_CHANNEL_AUX_9 : return "CHANNEL_AUX_9";
|
|
case MA_CHANNEL_AUX_10 : return "CHANNEL_AUX_10";
|
|
case MA_CHANNEL_AUX_11 : return "CHANNEL_AUX_11";
|
|
case MA_CHANNEL_AUX_12 : return "CHANNEL_AUX_12";
|
|
case MA_CHANNEL_AUX_13 : return "CHANNEL_AUX_13";
|
|
case MA_CHANNEL_AUX_14 : return "CHANNEL_AUX_14";
|
|
case MA_CHANNEL_AUX_15 : return "CHANNEL_AUX_15";
|
|
case MA_CHANNEL_AUX_16 : return "CHANNEL_AUX_16";
|
|
case MA_CHANNEL_AUX_17 : return "CHANNEL_AUX_17";
|
|
case MA_CHANNEL_AUX_18 : return "CHANNEL_AUX_18";
|
|
case MA_CHANNEL_AUX_19 : return "CHANNEL_AUX_19";
|
|
case MA_CHANNEL_AUX_20 : return "CHANNEL_AUX_20";
|
|
case MA_CHANNEL_AUX_21 : return "CHANNEL_AUX_21";
|
|
case MA_CHANNEL_AUX_22 : return "CHANNEL_AUX_22";
|
|
case MA_CHANNEL_AUX_23 : return "CHANNEL_AUX_23";
|
|
case MA_CHANNEL_AUX_24 : return "CHANNEL_AUX_24";
|
|
case MA_CHANNEL_AUX_25 : return "CHANNEL_AUX_25";
|
|
case MA_CHANNEL_AUX_26 : return "CHANNEL_AUX_26";
|
|
case MA_CHANNEL_AUX_27 : return "CHANNEL_AUX_27";
|
|
case MA_CHANNEL_AUX_28 : return "CHANNEL_AUX_28";
|
|
case MA_CHANNEL_AUX_29 : return "CHANNEL_AUX_29";
|
|
case MA_CHANNEL_AUX_30 : return "CHANNEL_AUX_30";
|
|
case MA_CHANNEL_AUX_31 : return "CHANNEL_AUX_31";
|
|
default: break;
|
|
}
|
|
|
|
return "UNKNOWN";
|
|
}
|
|
|
|
MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_format formatOut, ma_uint32 channelsOut, ma_uint32 sampleRateOut, const void* pIn, ma_uint64 frameCountIn, ma_format formatIn, ma_uint32 channelsIn, ma_uint32 sampleRateIn)
|
|
{
|
|
ma_data_converter_config config;
|
|
|
|
config = ma_data_converter_config_init(formatIn, formatOut, channelsIn, channelsOut, sampleRateIn, sampleRateOut);
|
|
config.resampling.linear.lpfOrder = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
|
|
|
|
return ma_convert_frames_ex(pOut, frameCountOut, pIn, frameCountIn, &config);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig)
|
|
{
|
|
ma_result result;
|
|
ma_data_converter converter;
|
|
|
|
if (frameCountIn == 0 || pConfig == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
result = ma_data_converter_init(pConfig, NULL, &converter);
|
|
if (result != MA_SUCCESS) {
|
|
return 0;
|
|
}
|
|
|
|
if (pOut == NULL) {
|
|
result = ma_data_converter_get_expected_output_frame_count(&converter, frameCountIn, &frameCountOut);
|
|
if (result != MA_SUCCESS) {
|
|
if (result == MA_NOT_IMPLEMENTED) {
|
|
frameCountOut = 0;
|
|
|
|
while (frameCountIn > 0) {
|
|
ma_uint64 framesProcessedIn = frameCountIn;
|
|
ma_uint64 framesProcessedOut = 0xFFFFFFFF;
|
|
|
|
result = ma_data_converter_process_pcm_frames(&converter, pIn, &framesProcessedIn, NULL, &framesProcessedOut);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
frameCountIn -= framesProcessedIn;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
result = ma_data_converter_process_pcm_frames(&converter, pIn, &frameCountIn, pOut, &frameCountOut);
|
|
if (result != MA_SUCCESS) {
|
|
frameCountOut = 0;
|
|
}
|
|
}
|
|
|
|
ma_data_converter_uninit(&converter, NULL);
|
|
return frameCountOut;
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_rb__extract_offset_in_bytes(ma_uint32 encodedOffset)
|
|
{
|
|
return encodedOffset & 0x7FFFFFFF;
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffset)
|
|
{
|
|
return encodedOffset & 0x80000000;
|
|
}
|
|
|
|
static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB)
|
|
{
|
|
MA_ASSERT(pRB != NULL);
|
|
return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset)));
|
|
}
|
|
|
|
static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB)
|
|
{
|
|
MA_ASSERT(pRB != NULL);
|
|
return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset)));
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag)
|
|
{
|
|
return offsetLoopFlag | offsetInBytes;
|
|
}
|
|
|
|
static MA_INLINE void ma_rb__deconstruct_offset(ma_uint32 encodedOffset, ma_uint32* pOffsetInBytes, ma_uint32* pOffsetLoopFlag)
|
|
{
|
|
MA_ASSERT(pOffsetInBytes != NULL);
|
|
MA_ASSERT(pOffsetLoopFlag != NULL);
|
|
|
|
*pOffsetInBytes = ma_rb__extract_offset_in_bytes(encodedOffset);
|
|
*pOffsetLoopFlag = ma_rb__extract_offset_loop_flag(encodedOffset);
|
|
}
|
|
|
|
MA_API ma_result ma_rb_init_ex(size_t subbufferSizeInBytes, size_t subbufferCount, size_t subbufferStrideInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
|
|
{
|
|
ma_result result;
|
|
const ma_uint32 maxSubBufferSize = 0x7FFFFFFF - (MA_SIMD_ALIGNMENT-1);
|
|
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (subbufferSizeInBytes == 0 || subbufferCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (subbufferSizeInBytes > maxSubBufferSize) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pRB);
|
|
|
|
result = ma_allocation_callbacks_init_copy(&pRB->allocationCallbacks, pAllocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pRB->subbufferSizeInBytes = (ma_uint32)subbufferSizeInBytes;
|
|
pRB->subbufferCount = (ma_uint32)subbufferCount;
|
|
|
|
if (pOptionalPreallocatedBuffer != NULL) {
|
|
pRB->subbufferStrideInBytes = (ma_uint32)subbufferStrideInBytes;
|
|
pRB->pBuffer = pOptionalPreallocatedBuffer;
|
|
} else {
|
|
size_t bufferSizeInBytes;
|
|
|
|
pRB->subbufferStrideInBytes = ma_align(pRB->subbufferSizeInBytes, MA_SIMD_ALIGNMENT);
|
|
|
|
bufferSizeInBytes = (size_t)pRB->subbufferCount*pRB->subbufferStrideInBytes;
|
|
pRB->pBuffer = ma_aligned_malloc(bufferSizeInBytes, MA_SIMD_ALIGNMENT, &pRB->allocationCallbacks);
|
|
if (pRB->pBuffer == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(pRB->pBuffer, bufferSizeInBytes);
|
|
pRB->ownsBuffer = MA_TRUE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_rb_init(size_t bufferSizeInBytes, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_rb* pRB)
|
|
{
|
|
return ma_rb_init_ex(bufferSizeInBytes, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
|
|
}
|
|
|
|
MA_API void ma_rb_uninit(ma_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pRB->ownsBuffer) {
|
|
ma_aligned_free(pRB->pBuffer, &pRB->allocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_rb_reset(ma_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pRB->encodedReadOffset, 0);
|
|
ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0);
|
|
}
|
|
|
|
MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
|
|
{
|
|
ma_uint32 writeOffset;
|
|
ma_uint32 writeOffsetInBytes;
|
|
ma_uint32 writeOffsetLoopFlag;
|
|
ma_uint32 readOffset;
|
|
ma_uint32 readOffsetInBytes;
|
|
ma_uint32 readOffsetLoopFlag;
|
|
size_t bytesAvailable;
|
|
size_t bytesRequested;
|
|
|
|
if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
|
|
ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
|
|
|
|
readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
|
|
ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
|
|
|
|
if (readOffsetLoopFlag == writeOffsetLoopFlag) {
|
|
bytesAvailable = writeOffsetInBytes - readOffsetInBytes;
|
|
} else {
|
|
bytesAvailable = pRB->subbufferSizeInBytes - readOffsetInBytes;
|
|
}
|
|
|
|
bytesRequested = *pSizeInBytes;
|
|
if (bytesRequested > bytesAvailable) {
|
|
bytesRequested = bytesAvailable;
|
|
}
|
|
|
|
*pSizeInBytes = bytesRequested;
|
|
(*ppBufferOut) = ma_rb__get_read_ptr(pRB);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes)
|
|
{
|
|
ma_uint32 readOffset;
|
|
ma_uint32 readOffsetInBytes;
|
|
ma_uint32 readOffsetLoopFlag;
|
|
ma_uint32 newReadOffsetInBytes;
|
|
ma_uint32 newReadOffsetLoopFlag;
|
|
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
|
|
ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
|
|
|
|
newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + sizeInBytes);
|
|
if (newReadOffsetInBytes > pRB->subbufferSizeInBytes) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
newReadOffsetLoopFlag = readOffsetLoopFlag;
|
|
if (newReadOffsetInBytes == pRB->subbufferSizeInBytes) {
|
|
newReadOffsetInBytes = 0;
|
|
newReadOffsetLoopFlag ^= 0x80000000;
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut)
|
|
{
|
|
ma_uint32 readOffset;
|
|
ma_uint32 readOffsetInBytes;
|
|
ma_uint32 readOffsetLoopFlag;
|
|
ma_uint32 writeOffset;
|
|
ma_uint32 writeOffsetInBytes;
|
|
ma_uint32 writeOffsetLoopFlag;
|
|
size_t bytesAvailable;
|
|
size_t bytesRequested;
|
|
|
|
if (pRB == NULL || pSizeInBytes == NULL || ppBufferOut == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
|
|
ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
|
|
|
|
writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
|
|
ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
|
|
|
|
if (writeOffsetLoopFlag == readOffsetLoopFlag) {
|
|
bytesAvailable = pRB->subbufferSizeInBytes - writeOffsetInBytes;
|
|
} else {
|
|
bytesAvailable = readOffsetInBytes - writeOffsetInBytes;
|
|
}
|
|
|
|
bytesRequested = *pSizeInBytes;
|
|
if (bytesRequested > bytesAvailable) {
|
|
bytesRequested = bytesAvailable;
|
|
}
|
|
|
|
*pSizeInBytes = bytesRequested;
|
|
*ppBufferOut = ma_rb__get_write_ptr(pRB);
|
|
|
|
if (pRB->clearOnWriteAcquire) {
|
|
MA_ZERO_MEMORY(*ppBufferOut, *pSizeInBytes);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes)
|
|
{
|
|
ma_uint32 writeOffset;
|
|
ma_uint32 writeOffsetInBytes;
|
|
ma_uint32 writeOffsetLoopFlag;
|
|
ma_uint32 newWriteOffsetInBytes;
|
|
ma_uint32 newWriteOffsetLoopFlag;
|
|
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
|
|
ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
|
|
|
|
newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + sizeInBytes);
|
|
if (newWriteOffsetInBytes > pRB->subbufferSizeInBytes) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
newWriteOffsetLoopFlag = writeOffsetLoopFlag;
|
|
if (newWriteOffsetInBytes == pRB->subbufferSizeInBytes) {
|
|
newWriteOffsetInBytes = 0;
|
|
newWriteOffsetLoopFlag ^= 0x80000000;
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes)
|
|
{
|
|
ma_uint32 readOffset;
|
|
ma_uint32 readOffsetInBytes;
|
|
ma_uint32 readOffsetLoopFlag;
|
|
ma_uint32 writeOffset;
|
|
ma_uint32 writeOffsetInBytes;
|
|
ma_uint32 writeOffsetLoopFlag;
|
|
ma_uint32 newReadOffsetInBytes;
|
|
ma_uint32 newReadOffsetLoopFlag;
|
|
|
|
if (pRB == NULL || offsetInBytes > pRB->subbufferSizeInBytes) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
|
|
ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
|
|
|
|
writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
|
|
ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
|
|
|
|
newReadOffsetLoopFlag = readOffsetLoopFlag;
|
|
|
|
if (readOffsetLoopFlag == writeOffsetLoopFlag) {
|
|
if ((readOffsetInBytes + offsetInBytes) > writeOffsetInBytes) {
|
|
newReadOffsetInBytes = writeOffsetInBytes;
|
|
} else {
|
|
newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
|
|
}
|
|
} else {
|
|
if ((readOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
|
|
newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
|
|
newReadOffsetLoopFlag ^= 0x80000000;
|
|
} else {
|
|
newReadOffsetInBytes = (ma_uint32)(readOffsetInBytes + offsetInBytes);
|
|
}
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag));
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes)
|
|
{
|
|
ma_uint32 readOffset;
|
|
ma_uint32 readOffsetInBytes;
|
|
ma_uint32 readOffsetLoopFlag;
|
|
ma_uint32 writeOffset;
|
|
ma_uint32 writeOffsetInBytes;
|
|
ma_uint32 writeOffsetLoopFlag;
|
|
ma_uint32 newWriteOffsetInBytes;
|
|
ma_uint32 newWriteOffsetLoopFlag;
|
|
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
|
|
ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
|
|
|
|
writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
|
|
ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
|
|
|
|
newWriteOffsetLoopFlag = writeOffsetLoopFlag;
|
|
|
|
if (readOffsetLoopFlag == writeOffsetLoopFlag) {
|
|
if ((writeOffsetInBytes + offsetInBytes) >= pRB->subbufferSizeInBytes) {
|
|
newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes) - pRB->subbufferSizeInBytes;
|
|
newWriteOffsetLoopFlag ^= 0x80000000;
|
|
} else {
|
|
newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
|
|
}
|
|
} else {
|
|
if ((writeOffsetInBytes + offsetInBytes) > readOffsetInBytes) {
|
|
newWriteOffsetInBytes = readOffsetInBytes;
|
|
} else {
|
|
newWriteOffsetInBytes = (ma_uint32)(writeOffsetInBytes + offsetInBytes);
|
|
}
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag));
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB)
|
|
{
|
|
ma_uint32 readOffset;
|
|
ma_uint32 readOffsetInBytes;
|
|
ma_uint32 readOffsetLoopFlag;
|
|
ma_uint32 writeOffset;
|
|
ma_uint32 writeOffsetInBytes;
|
|
ma_uint32 writeOffsetLoopFlag;
|
|
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
readOffset = ma_atomic_load_32(&pRB->encodedReadOffset);
|
|
ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag);
|
|
|
|
writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset);
|
|
ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag);
|
|
|
|
if (readOffsetLoopFlag == writeOffsetLoopFlag) {
|
|
return writeOffsetInBytes - readOffsetInBytes;
|
|
} else {
|
|
return writeOffsetInBytes + (pRB->subbufferSizeInBytes - readOffsetInBytes);
|
|
}
|
|
}
|
|
|
|
MA_API ma_uint32 ma_rb_available_read(ma_rb* pRB)
|
|
{
|
|
ma_int32 dist;
|
|
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
dist = ma_rb_pointer_distance(pRB);
|
|
if (dist < 0) {
|
|
return 0;
|
|
}
|
|
|
|
return dist;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_rb_available_write(ma_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (ma_uint32)(ma_rb_get_subbuffer_size(pRB) - ma_rb_pointer_distance(pRB));
|
|
}
|
|
|
|
MA_API size_t ma_rb_get_subbuffer_size(ma_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pRB->subbufferSizeInBytes;
|
|
}
|
|
|
|
MA_API size_t ma_rb_get_subbuffer_stride(ma_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (pRB->subbufferStrideInBytes == 0) {
|
|
return (size_t)pRB->subbufferSizeInBytes;
|
|
}
|
|
|
|
return (size_t)pRB->subbufferStrideInBytes;
|
|
}
|
|
|
|
MA_API size_t ma_rb_get_subbuffer_offset(ma_rb* pRB, size_t subbufferIndex)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return subbufferIndex * ma_rb_get_subbuffer_stride(pRB);
|
|
}
|
|
|
|
MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pBuffer)
|
|
{
|
|
if (pRB == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return ma_offset_ptr(pBuffer, ma_rb_get_subbuffer_offset(pRB, subbufferIndex));
|
|
}
|
|
|
|
static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
|
|
ma_result result;
|
|
ma_uint64 totalFramesRead;
|
|
|
|
MA_ASSERT(pRB != NULL);
|
|
|
|
totalFramesRead = 0;
|
|
while (totalFramesRead < frameCount) {
|
|
void* pMappedBuffer;
|
|
ma_uint32 mappedFrameCount;
|
|
ma_uint64 framesToRead = frameCount - totalFramesRead;
|
|
if (framesToRead > 0xFFFFFFFF) {
|
|
framesToRead = 0xFFFFFFFF;
|
|
}
|
|
|
|
mappedFrameCount = (ma_uint32)framesToRead;
|
|
result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (mappedFrameCount == 0) {
|
|
break;
|
|
}
|
|
|
|
ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels);
|
|
|
|
result = ma_pcm_rb_commit_read(pRB, mappedFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
totalFramesRead += mappedFrameCount;
|
|
}
|
|
|
|
if (totalFramesRead < frameCount) {
|
|
ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), (frameCount - totalFramesRead), pRB->format, pRB->channels);
|
|
totalFramesRead = frameCount;
|
|
}
|
|
|
|
*pFramesRead = totalFramesRead;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource;
|
|
MA_ASSERT(pRB != NULL);
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pRB->format;
|
|
}
|
|
|
|
if (pChannels != NULL) {
|
|
*pChannels = pRB->channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pRB->sampleRate;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_data_source_vtable ma_gRBDataSourceVTable =
|
|
{
|
|
ma_pcm_rb_data_source__on_read,
|
|
NULL,
|
|
ma_pcm_rb_data_source__on_get_data_format,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB)
|
|
{
|
|
MA_ASSERT(pRB != NULL);
|
|
|
|
return ma_get_bytes_per_frame(pRB->format, pRB->channels);
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)
|
|
{
|
|
ma_uint32 bpf;
|
|
ma_result result;
|
|
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pRB);
|
|
|
|
bpf = ma_get_bytes_per_frame(format, channels);
|
|
if (bpf == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_rb_init_ex(subbufferSizeInFrames*bpf, subbufferCount, subbufferStrideInFrames*bpf, pOptionalPreallocatedBuffer, pAllocationCallbacks, &pRB->rb);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pRB->format = format;
|
|
pRB->channels = channels;
|
|
pRB->sampleRate = 0;
|
|
|
|
{
|
|
ma_data_source_config dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &ma_gRBDataSourceVTable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pRB->ds);
|
|
if (result != MA_SUCCESS) {
|
|
ma_rb_uninit(&pRB->rb);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_init(ma_format format, ma_uint32 channels, ma_uint32 bufferSizeInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB)
|
|
{
|
|
return ma_pcm_rb_init_ex(format, channels, bufferSizeInFrames, 1, 0, pOptionalPreallocatedBuffer, pAllocationCallbacks, pRB);
|
|
}
|
|
|
|
MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_data_source_uninit(&pRB->ds);
|
|
ma_rb_uninit(&pRB->rb);
|
|
}
|
|
|
|
MA_API void ma_pcm_rb_reset(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_rb_reset(&pRB->rb);
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_acquire_read(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
|
|
{
|
|
size_t sizeInBytes;
|
|
ma_result result;
|
|
|
|
if (pRB == NULL || pSizeInFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
|
|
|
|
result = ma_rb_acquire_read(&pRB->rb, &sizeInBytes, ppBufferOut);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pSizeInFrames = (ma_uint32)(sizeInBytes / (size_t)ma_pcm_rb_get_bpf(pRB));
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_commit_read(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
|
|
{
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_rb_commit_read(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_acquire_write(ma_pcm_rb* pRB, ma_uint32* pSizeInFrames, void** ppBufferOut)
|
|
{
|
|
size_t sizeInBytes;
|
|
ma_result result;
|
|
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
sizeInBytes = *pSizeInFrames * ma_pcm_rb_get_bpf(pRB);
|
|
|
|
result = ma_rb_acquire_write(&pRB->rb, &sizeInBytes, ppBufferOut);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pSizeInFrames = (ma_uint32)(sizeInBytes / ma_pcm_rb_get_bpf(pRB));
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_commit_write(ma_pcm_rb* pRB, ma_uint32 sizeInFrames)
|
|
{
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_rb_commit_write(&pRB->rb, sizeInFrames * ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_seek_read(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
|
|
{
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_rb_seek_read(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API ma_result ma_pcm_rb_seek_write(ma_pcm_rb* pRB, ma_uint32 offsetInFrames)
|
|
{
|
|
if (pRB == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_rb_seek_write(&pRB->rb, offsetInFrames * ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API ma_int32 ma_pcm_rb_pointer_distance(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_rb_pointer_distance(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_available_read(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_rb_available_read(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_available_write(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_rb_available_write(&pRB->rb) / ma_pcm_rb_get_bpf(pRB);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (ma_uint32)(ma_rb_get_subbuffer_size(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (ma_uint32)(ma_rb_get_subbuffer_stride(&pRB->rb) / ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return (ma_uint32)(ma_rb_get_subbuffer_offset(&pRB->rb, subbufferIndex) / ma_pcm_rb_get_bpf(pRB));
|
|
}
|
|
|
|
MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer)
|
|
{
|
|
if (pRB == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer);
|
|
}
|
|
|
|
MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return ma_format_unknown;
|
|
}
|
|
|
|
return pRB->format;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pRB->channels;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB)
|
|
{
|
|
if (pRB == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pRB->sampleRate;
|
|
}
|
|
|
|
MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate)
|
|
{
|
|
if (pRB == NULL) {
|
|
return;
|
|
}
|
|
|
|
pRB->sampleRate = sampleRate;
|
|
}
|
|
|
|
MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 sizeInFrames;
|
|
|
|
sizeInFrames = (ma_uint32)ma_calculate_frame_count_after_resampling(sampleRate, captureInternalSampleRate, captureInternalPeriodSizeInFrames * 5);
|
|
if (sizeInFrames == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_pcm_rb_init(captureFormat, captureChannels, sizeInFrames, NULL, pAllocationCallbacks, &pRB->rb);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
ma_pcm_rb_seek_write((ma_pcm_rb*)pRB, captureInternalPeriodSizeInFrames * 2);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_duplex_rb_uninit(ma_duplex_rb* pRB)
|
|
{
|
|
ma_pcm_rb_uninit((ma_pcm_rb*)pRB);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API const char* ma_result_description(ma_result result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case MA_SUCCESS: return "No error";
|
|
case MA_ERROR: return "Unknown error";
|
|
case MA_INVALID_ARGS: return "Invalid argument";
|
|
case MA_INVALID_OPERATION: return "Invalid operation";
|
|
case MA_OUT_OF_MEMORY: return "Out of memory";
|
|
case MA_OUT_OF_RANGE: return "Out of range";
|
|
case MA_ACCESS_DENIED: return "Permission denied";
|
|
case MA_DOES_NOT_EXIST: return "Resource does not exist";
|
|
case MA_ALREADY_EXISTS: return "Resource already exists";
|
|
case MA_TOO_MANY_OPEN_FILES: return "Too many open files";
|
|
case MA_INVALID_FILE: return "Invalid file";
|
|
case MA_TOO_BIG: return "Too large";
|
|
case MA_PATH_TOO_LONG: return "Path too long";
|
|
case MA_NAME_TOO_LONG: return "Name too long";
|
|
case MA_NOT_DIRECTORY: return "Not a directory";
|
|
case MA_IS_DIRECTORY: return "Is a directory";
|
|
case MA_DIRECTORY_NOT_EMPTY: return "Directory not empty";
|
|
case MA_AT_END: return "At end";
|
|
case MA_NO_SPACE: return "No space available";
|
|
case MA_BUSY: return "Device or resource busy";
|
|
case MA_IO_ERROR: return "Input/output error";
|
|
case MA_INTERRUPT: return "Interrupted";
|
|
case MA_UNAVAILABLE: return "Resource unavailable";
|
|
case MA_ALREADY_IN_USE: return "Resource already in use";
|
|
case MA_BAD_ADDRESS: return "Bad address";
|
|
case MA_BAD_SEEK: return "Illegal seek";
|
|
case MA_BAD_PIPE: return "Broken pipe";
|
|
case MA_DEADLOCK: return "Deadlock";
|
|
case MA_TOO_MANY_LINKS: return "Too many links";
|
|
case MA_NOT_IMPLEMENTED: return "Not implemented";
|
|
case MA_NO_MESSAGE: return "No message of desired type";
|
|
case MA_BAD_MESSAGE: return "Invalid message";
|
|
case MA_NO_DATA_AVAILABLE: return "No data available";
|
|
case MA_INVALID_DATA: return "Invalid data";
|
|
case MA_TIMEOUT: return "Timeout";
|
|
case MA_NO_NETWORK: return "Network unavailable";
|
|
case MA_NOT_UNIQUE: return "Not unique";
|
|
case MA_NOT_SOCKET: return "Socket operation on non-socket";
|
|
case MA_NO_ADDRESS: return "Destination address required";
|
|
case MA_BAD_PROTOCOL: return "Protocol wrong type for socket";
|
|
case MA_PROTOCOL_UNAVAILABLE: return "Protocol not available";
|
|
case MA_PROTOCOL_NOT_SUPPORTED: return "Protocol not supported";
|
|
case MA_PROTOCOL_FAMILY_NOT_SUPPORTED: return "Protocol family not supported";
|
|
case MA_ADDRESS_FAMILY_NOT_SUPPORTED: return "Address family not supported";
|
|
case MA_SOCKET_NOT_SUPPORTED: return "Socket type not supported";
|
|
case MA_CONNECTION_RESET: return "Connection reset";
|
|
case MA_ALREADY_CONNECTED: return "Already connected";
|
|
case MA_NOT_CONNECTED: return "Not connected";
|
|
case MA_CONNECTION_REFUSED: return "Connection refused";
|
|
case MA_NO_HOST: return "No host";
|
|
case MA_IN_PROGRESS: return "Operation in progress";
|
|
case MA_CANCELLED: return "Operation cancelled";
|
|
case MA_MEMORY_ALREADY_MAPPED: return "Memory already mapped";
|
|
|
|
case MA_FORMAT_NOT_SUPPORTED: return "Format not supported";
|
|
case MA_DEVICE_TYPE_NOT_SUPPORTED: return "Device type not supported";
|
|
case MA_SHARE_MODE_NOT_SUPPORTED: return "Share mode not supported";
|
|
case MA_NO_BACKEND: return "No backend";
|
|
case MA_NO_DEVICE: return "No device";
|
|
case MA_API_NOT_FOUND: return "API not found";
|
|
case MA_INVALID_DEVICE_CONFIG: return "Invalid device config";
|
|
|
|
case MA_DEVICE_NOT_INITIALIZED: return "Device not initialized";
|
|
case MA_DEVICE_NOT_STARTED: return "Device not started";
|
|
|
|
case MA_FAILED_TO_INIT_BACKEND: return "Failed to initialize backend";
|
|
case MA_FAILED_TO_OPEN_BACKEND_DEVICE: return "Failed to open backend device";
|
|
case MA_FAILED_TO_START_BACKEND_DEVICE: return "Failed to start backend device";
|
|
case MA_FAILED_TO_STOP_BACKEND_DEVICE: return "Failed to stop backend device";
|
|
|
|
default: return "Unknown error";
|
|
}
|
|
}
|
|
|
|
MA_API void* ma_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
if (pAllocationCallbacks->onMalloc != NULL) {
|
|
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
return ma__malloc_default(sz, NULL);
|
|
}
|
|
}
|
|
|
|
MA_API void* ma_calloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
void* p = ma_malloc(sz, pAllocationCallbacks);
|
|
if (p != NULL) {
|
|
MA_ZERO_MEMORY(p, sz);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
MA_API void* ma_realloc(void* p, size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(p, sz, pAllocationCallbacks->pUserData);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
return ma__realloc_default(p, sz, NULL);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (p == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pAllocationCallbacks != NULL) {
|
|
if (pAllocationCallbacks->onFree != NULL) {
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
ma__free_default(p, NULL);
|
|
}
|
|
}
|
|
|
|
MA_API void* ma_aligned_malloc(size_t sz, size_t alignment, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
size_t extraBytes;
|
|
void* pUnaligned;
|
|
void* pAligned;
|
|
|
|
if (alignment == 0) {
|
|
return 0;
|
|
}
|
|
|
|
extraBytes = alignment-1 + sizeof(void*);
|
|
|
|
pUnaligned = ma_malloc(sz + extraBytes, pAllocationCallbacks);
|
|
if (pUnaligned == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pAligned = (void*)(((ma_uintptr)pUnaligned + extraBytes) & ~((ma_uintptr)(alignment-1)));
|
|
((void**)pAligned)[-1] = pUnaligned;
|
|
|
|
return pAligned;
|
|
}
|
|
|
|
MA_API void ma_aligned_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_free(((void**)p)[-1], pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API const char* ma_get_format_name(ma_format format)
|
|
{
|
|
switch (format)
|
|
{
|
|
case ma_format_unknown: return "Unknown";
|
|
case ma_format_u8: return "8-bit Unsigned Integer";
|
|
case ma_format_s16: return "16-bit Signed Integer";
|
|
case ma_format_s24: return "24-bit Signed Integer (Tightly Packed)";
|
|
case ma_format_s32: return "32-bit Signed Integer";
|
|
case ma_format_f32: return "32-bit IEEE Floating Point";
|
|
default: return "Invalid";
|
|
}
|
|
}
|
|
|
|
MA_API void ma_blend_f32(float* pOut, float* pInA, float* pInB, float factor, ma_uint32 channels)
|
|
{
|
|
ma_uint32 i;
|
|
for (i = 0; i < channels; ++i) {
|
|
pOut[i] = ma_mix_f32(pInA[i], pInB[i], factor);
|
|
}
|
|
}
|
|
|
|
MA_API ma_uint32 ma_get_bytes_per_sample(ma_format format)
|
|
{
|
|
ma_uint32 sizes[] = {
|
|
0,
|
|
1,
|
|
2,
|
|
3,
|
|
4,
|
|
4,
|
|
};
|
|
return sizes[format];
|
|
}
|
|
|
|
#define MA_DATA_SOURCE_DEFAULT_RANGE_BEG 0
|
|
#define MA_DATA_SOURCE_DEFAULT_RANGE_END ~((ma_uint64)0)
|
|
#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG 0
|
|
#define MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END ~((ma_uint64)0)
|
|
|
|
MA_API ma_data_source_config ma_data_source_config_init(void)
|
|
{
|
|
ma_data_source_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDataSourceBase);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->vtable == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDataSourceBase->vtable = pConfig->vtable;
|
|
pDataSourceBase->rangeBegInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
|
|
pDataSourceBase->rangeEndInFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
|
|
pDataSourceBase->loopBegInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
|
|
pDataSourceBase->loopEndInFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
|
|
pDataSourceBase->pCurrent = pDataSource;
|
|
pDataSourceBase->pNext = NULL;
|
|
pDataSourceBase->onGetNext = NULL;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_data_source_uninit(ma_data_source* pDataSource)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
static ma_result ma_data_source_resolve_current(ma_data_source* pDataSource, ma_data_source** ppCurrentDataSource)
|
|
{
|
|
ma_data_source_base* pCurrentDataSource = (ma_data_source_base*)pDataSource;
|
|
|
|
MA_ASSERT(pDataSource != NULL);
|
|
MA_ASSERT(ppCurrentDataSource != NULL);
|
|
|
|
if (pCurrentDataSource->pCurrent == NULL) {
|
|
if (pCurrentDataSource->pNext != NULL || pCurrentDataSource->onGetNext != NULL) {
|
|
pCurrentDataSource = NULL;
|
|
} else {
|
|
pCurrentDataSource = (ma_data_source_base*)pDataSource;
|
|
}
|
|
} else {
|
|
pCurrentDataSource = (ma_data_source_base*)pCurrentDataSource->pCurrent;
|
|
}
|
|
|
|
*ppCurrentDataSource = pCurrentDataSource;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_data_source_read_pcm_frames_from_backend(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
MA_ASSERT(pDataSourceBase != NULL);
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
MA_ASSERT(pDataSourceBase->vtable->onRead != NULL);
|
|
MA_ASSERT(pFramesRead != NULL);
|
|
|
|
if (pFramesOut != NULL) {
|
|
return pDataSourceBase->vtable->onRead(pDataSourceBase, pFramesOut, frameCount, pFramesRead);
|
|
} else {
|
|
ma_result result;
|
|
ma_uint64 framesRead;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint64 discardBufferCapInFrames;
|
|
ma_uint8 pDiscardBuffer[4096];
|
|
|
|
result = ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
discardBufferCapInFrames = sizeof(pDiscardBuffer) / ma_get_bytes_per_frame(format, channels);
|
|
|
|
framesRead = 0;
|
|
while (framesRead < frameCount) {
|
|
ma_uint64 framesReadThisIteration = 0;
|
|
ma_uint64 framesToRead = frameCount - framesRead;
|
|
if (framesToRead > discardBufferCapInFrames) {
|
|
framesToRead = discardBufferCapInFrames;
|
|
}
|
|
|
|
result = pDataSourceBase->vtable->onRead(pDataSourceBase, pDiscardBuffer, framesToRead, &framesReadThisIteration);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
framesRead += framesReadThisIteration;
|
|
}
|
|
|
|
*pFramesRead = framesRead;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_data_source_read_pcm_frames_within_range(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
ma_result result;
|
|
ma_uint64 framesRead = 0;
|
|
ma_bool32 loop = ma_data_source_is_looping(pDataSource);
|
|
|
|
if (pDataSourceBase == NULL) {
|
|
return MA_AT_END;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
|
|
if ((pDataSourceBase->vtable->flags & MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT) != 0 || (pDataSourceBase->rangeEndInFrames == ~((ma_uint64)0) && (pDataSourceBase->loopEndInFrames == ~((ma_uint64)0) || loop == MA_FALSE))) {
|
|
result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
|
|
} else {
|
|
ma_uint64 relativeCursor;
|
|
ma_uint64 absoluteCursor;
|
|
|
|
result = ma_data_source_get_cursor_in_pcm_frames(pDataSourceBase, &relativeCursor);
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
|
|
} else {
|
|
ma_uint64 rangeBeg;
|
|
ma_uint64 rangeEnd;
|
|
|
|
rangeBeg = pDataSourceBase->rangeBegInFrames;
|
|
rangeEnd = pDataSourceBase->rangeEndInFrames;
|
|
|
|
absoluteCursor = rangeBeg + relativeCursor;
|
|
|
|
if (loop) {
|
|
if (pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
|
|
rangeEnd = ma_min(rangeEnd, pDataSourceBase->rangeBegInFrames + pDataSourceBase->loopEndInFrames);
|
|
}
|
|
}
|
|
|
|
if (frameCount > (rangeEnd - absoluteCursor) && rangeEnd != ~((ma_uint64)0)) {
|
|
frameCount = (rangeEnd - absoluteCursor);
|
|
}
|
|
|
|
if (frameCount > 0) {
|
|
result = ma_data_source_read_pcm_frames_from_backend(pDataSource, pFramesOut, frameCount, &framesRead);
|
|
} else {
|
|
result = MA_AT_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = framesRead;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && framesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
ma_data_source_base* pCurrentDataSource;
|
|
void* pRunningFramesOut = pFramesOut;
|
|
ma_uint64 totalFramesProcessed = 0;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 emptyLoopCounter = 0;
|
|
ma_bool32 loop;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDataSourceBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
loop = ma_data_source_is_looping(pDataSource);
|
|
|
|
if (ma_data_source_get_data_format(pDataSource, &format, &channels, NULL, NULL, 0) != MA_SUCCESS) {
|
|
result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
while (totalFramesProcessed < frameCount) {
|
|
ma_uint64 framesProcessed;
|
|
ma_uint64 framesRemaining = frameCount - totalFramesProcessed;
|
|
|
|
result = ma_data_source_resolve_current(pDataSource, (ma_data_source**)&pCurrentDataSource);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (pCurrentDataSource == NULL) {
|
|
break;
|
|
}
|
|
|
|
result = ma_data_source_read_pcm_frames_within_range(pCurrentDataSource, pRunningFramesOut, framesRemaining, &framesProcessed);
|
|
totalFramesProcessed += framesProcessed;
|
|
|
|
if (result != MA_SUCCESS && result != MA_AT_END) {
|
|
break;
|
|
}
|
|
|
|
if (result == MA_AT_END) {
|
|
result = MA_SUCCESS;
|
|
|
|
if (loop) {
|
|
if (framesProcessed == 0) {
|
|
emptyLoopCounter += 1;
|
|
if (emptyLoopCounter > 1) {
|
|
break;
|
|
}
|
|
} else {
|
|
emptyLoopCounter = 0;
|
|
}
|
|
|
|
result = ma_data_source_seek_to_pcm_frame(pCurrentDataSource, pCurrentDataSource->loopBegInFrames);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
result = MA_SUCCESS;
|
|
} else {
|
|
if (pCurrentDataSource->pNext != NULL) {
|
|
pDataSourceBase->pCurrent = pCurrentDataSource->pNext;
|
|
} else if (pCurrentDataSource->onGetNext != NULL) {
|
|
pDataSourceBase->pCurrent = pCurrentDataSource->onGetNext(pCurrentDataSource);
|
|
if (pDataSourceBase->pCurrent == NULL) {
|
|
break;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
|
|
result = ma_data_source_seek_to_pcm_frame(pDataSourceBase->pCurrent, 0);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pRunningFramesOut != NULL) {
|
|
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesProcessed * ma_get_bytes_per_frame(format, channels));
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesProcessed;
|
|
}
|
|
|
|
MA_ASSERT(!(result == MA_AT_END && totalFramesProcessed > 0));
|
|
|
|
if (result == MA_SUCCESS && totalFramesProcessed == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked)
|
|
{
|
|
return ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, pFramesSeeked);
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSourceBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDataSourceBase->vtable->onSeek == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (frameIndex > pDataSourceBase->rangeEndInFrames) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
|
|
return pDataSourceBase->vtable->onSeek(pDataSource, pDataSourceBase->rangeBegInFrames + frameIndex);
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_seek_seconds(ma_data_source* pDataSource, float secondCount, float* pSecondsSeeked)
|
|
{
|
|
ma_uint64 frameCount;
|
|
ma_uint64 framesSeeked = 0;
|
|
ma_uint32 sampleRate;
|
|
ma_result result;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
frameCount = (ma_uint64)(secondCount * sampleRate);
|
|
|
|
result = ma_data_source_seek_pcm_frames(pDataSource, frameCount, &framesSeeked);
|
|
|
|
*pSecondsSeeked = (ma_int64)framesSeeked / (float)sampleRate;
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_seek_to_second(ma_data_source* pDataSource, float seekPointInSeconds)
|
|
{
|
|
ma_uint64 frameIndex;
|
|
ma_uint32 sampleRate;
|
|
ma_result result;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate);
|
|
|
|
return ma_data_source_seek_to_pcm_frame(pDataSource, frameIndex);
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
ma_result result;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_unknown;
|
|
}
|
|
if (pChannels != NULL) {
|
|
*pChannels = 0;
|
|
}
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = 0;
|
|
}
|
|
if (pChannelMap != NULL) {
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
|
|
}
|
|
|
|
if (pDataSourceBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
|
|
if (pDataSourceBase->vtable->onGetDataFormat == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
result = pDataSourceBase->vtable->onGetDataFormat(pDataSource, &format, &channels, &sampleRate, pChannelMap, channelMapCap);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = format;
|
|
}
|
|
if (pChannels != NULL) {
|
|
*pChannels = channels;
|
|
}
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = sampleRate;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
ma_result result;
|
|
ma_uint64 cursor;
|
|
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pDataSourceBase == NULL) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
|
|
if (pDataSourceBase->vtable->onGetCursor == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
result = pDataSourceBase->vtable->onGetCursor(pDataSourceBase, &cursor);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (cursor < pDataSourceBase->rangeBegInFrames) {
|
|
*pCursor = 0;
|
|
} else {
|
|
*pCursor = cursor - pDataSourceBase->rangeBegInFrames;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pDataSourceBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
|
|
if (pDataSourceBase->rangeEndInFrames != ~((ma_uint64)0)) {
|
|
*pLength = pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
if (pDataSourceBase->vtable->onGetLength == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pDataSourceBase->vtable->onGetLength(pDataSource, pLength);
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 cursorInPCMFrames;
|
|
ma_uint32 sampleRate;
|
|
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &cursorInPCMFrames);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 lengthInPCMFrames;
|
|
ma_uint32 sampleRate;
|
|
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
result = ma_data_source_get_length_in_pcm_frames(pDataSource, &lengthInPCMFrames);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_data_source_get_data_format(pDataSource, NULL, NULL, &sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pLength = (ma_int64)lengthInPCMFrames / (float)sampleRate;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping);
|
|
|
|
MA_ASSERT(pDataSourceBase->vtable != NULL);
|
|
|
|
if (pDataSourceBase->vtable->onSetLooping == NULL) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
return pDataSourceBase->vtable->onSetLooping(pDataSource, isLooping);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource)
|
|
{
|
|
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_atomic_load_32(&pDataSourceBase->isLooping);
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
ma_result result;
|
|
ma_uint64 relativeCursor;
|
|
ma_uint64 absoluteCursor;
|
|
ma_bool32 doSeekAdjustment = MA_FALSE;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (rangeEndInFrames < rangeBegInFrames) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_data_source_get_cursor_in_pcm_frames(pDataSource, &relativeCursor);
|
|
if (result == MA_SUCCESS) {
|
|
doSeekAdjustment = MA_TRUE;
|
|
absoluteCursor = relativeCursor + pDataSourceBase->rangeBegInFrames;
|
|
} else {
|
|
doSeekAdjustment = MA_FALSE;
|
|
relativeCursor = 0;
|
|
absoluteCursor = 0;
|
|
}
|
|
|
|
pDataSourceBase->rangeBegInFrames = rangeBegInFrames;
|
|
pDataSourceBase->rangeEndInFrames = rangeEndInFrames;
|
|
|
|
pDataSourceBase->loopBegInFrames = 0;
|
|
pDataSourceBase->loopEndInFrames = ~((ma_uint64)0);
|
|
|
|
if (doSeekAdjustment) {
|
|
if (absoluteCursor < rangeBegInFrames) {
|
|
ma_data_source_seek_to_pcm_frame(pDataSource, 0);
|
|
} else if (absoluteCursor > rangeEndInFrames) {
|
|
ma_data_source_seek_to_pcm_frame(pDataSource, rangeEndInFrames - rangeBegInFrames);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames)
|
|
{
|
|
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
|
|
|
|
if (pRangeBegInFrames != NULL) {
|
|
*pRangeBegInFrames = 0;
|
|
}
|
|
if (pRangeEndInFrames != NULL) {
|
|
*pRangeEndInFrames = 0;
|
|
}
|
|
|
|
if (pDataSource == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pRangeBegInFrames != NULL) {
|
|
*pRangeBegInFrames = pDataSourceBase->rangeBegInFrames;
|
|
}
|
|
|
|
if (pRangeEndInFrames != NULL) {
|
|
*pRangeEndInFrames = pDataSourceBase->rangeEndInFrames;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (loopEndInFrames < loopBegInFrames) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (loopEndInFrames > pDataSourceBase->rangeEndInFrames && loopEndInFrames != ~((ma_uint64)0)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDataSourceBase->loopBegInFrames = loopBegInFrames;
|
|
pDataSourceBase->loopEndInFrames = loopEndInFrames;
|
|
|
|
if (pDataSourceBase->loopEndInFrames > (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames) && pDataSourceBase->loopEndInFrames != ~((ma_uint64)0)) {
|
|
pDataSourceBase->loopEndInFrames = (pDataSourceBase->rangeEndInFrames - pDataSourceBase->rangeBegInFrames);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames)
|
|
{
|
|
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
|
|
|
|
if (pLoopBegInFrames != NULL) {
|
|
*pLoopBegInFrames = 0;
|
|
}
|
|
if (pLoopEndInFrames != NULL) {
|
|
*pLoopEndInFrames = 0;
|
|
}
|
|
|
|
if (pDataSource == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pLoopBegInFrames != NULL) {
|
|
*pLoopBegInFrames = pDataSourceBase->loopBegInFrames;
|
|
}
|
|
|
|
if (pLoopEndInFrames != NULL) {
|
|
*pLoopEndInFrames = pDataSourceBase->loopEndInFrames;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDataSourceBase->pCurrent = pCurrentDataSource;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource)
|
|
{
|
|
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pDataSourceBase->pCurrent;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDataSourceBase->pNext = pNextDataSource;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource)
|
|
{
|
|
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pDataSourceBase->pNext;
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext)
|
|
{
|
|
ma_data_source_base* pDataSourceBase = (ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDataSourceBase->onGetNext = onGetNext;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource)
|
|
{
|
|
const ma_data_source_base* pDataSourceBase = (const ma_data_source_base*)pDataSource;
|
|
|
|
if (pDataSource == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pDataSourceBase->onGetNext;
|
|
}
|
|
|
|
static ma_result ma_audio_buffer_ref__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
|
|
ma_uint64 framesRead = ma_audio_buffer_ref_read_pcm_frames(pAudioBufferRef, pFramesOut, frameCount, MA_FALSE);
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = framesRead;
|
|
}
|
|
|
|
if (framesRead < frameCount || framesRead == 0) {
|
|
return MA_AT_END;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_audio_buffer_ref__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_audio_buffer_ref_seek_to_pcm_frame((ma_audio_buffer_ref*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_audio_buffer_ref__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
|
|
|
|
*pFormat = pAudioBufferRef->format;
|
|
*pChannels = pAudioBufferRef->channels;
|
|
*pSampleRate = pAudioBufferRef->sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pAudioBufferRef->channels);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_audio_buffer_ref__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
|
|
|
|
*pCursor = pAudioBufferRef->cursor;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_audio_buffer_ref__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
ma_audio_buffer_ref* pAudioBufferRef = (ma_audio_buffer_ref*)pDataSource;
|
|
|
|
*pLength = pAudioBufferRef->sizeInFrames;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_audio_buffer_ref_data_source_vtable =
|
|
{
|
|
ma_audio_buffer_ref__data_source_on_read,
|
|
ma_audio_buffer_ref__data_source_on_seek,
|
|
ma_audio_buffer_ref__data_source_on_get_data_format,
|
|
ma_audio_buffer_ref__data_source_on_get_cursor,
|
|
ma_audio_buffer_ref__data_source_on_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pAudioBufferRef);
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_audio_buffer_ref_data_source_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pAudioBufferRef->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pAudioBufferRef->format = format;
|
|
pAudioBufferRef->channels = channels;
|
|
pAudioBufferRef->sampleRate = 0;
|
|
pAudioBufferRef->cursor = 0;
|
|
pAudioBufferRef->sizeInFrames = sizeInFrames;
|
|
pAudioBufferRef->pData = pData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef)
|
|
{
|
|
if (pAudioBufferRef == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_data_source_uninit(&pAudioBufferRef->ds);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames)
|
|
{
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pAudioBufferRef->cursor = 0;
|
|
pAudioBufferRef->sizeInFrames = sizeInFrames;
|
|
pAudioBufferRef->pData = pData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
|
|
if (pAudioBufferRef == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return 0;
|
|
}
|
|
|
|
while (totalFramesRead < frameCount) {
|
|
ma_uint64 framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
|
|
ma_uint64 framesRemaining = frameCount - totalFramesRead;
|
|
ma_uint64 framesToRead;
|
|
|
|
framesToRead = framesRemaining;
|
|
if (framesToRead > framesAvailable) {
|
|
framesToRead = framesAvailable;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
ma_copy_pcm_frames(ma_offset_ptr(pFramesOut, totalFramesRead * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels)), framesToRead, pAudioBufferRef->format, pAudioBufferRef->channels);
|
|
}
|
|
|
|
totalFramesRead += framesToRead;
|
|
|
|
pAudioBufferRef->cursor += framesToRead;
|
|
if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
|
|
if (loop) {
|
|
pAudioBufferRef->cursor = 0;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(pAudioBufferRef->cursor < pAudioBufferRef->sizeInFrames);
|
|
}
|
|
|
|
return totalFramesRead;
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex)
|
|
{
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (frameIndex > pAudioBufferRef->sizeInFrames) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pAudioBufferRef->cursor = (size_t)frameIndex;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount)
|
|
{
|
|
ma_uint64 framesAvailable;
|
|
ma_uint64 frameCount = 0;
|
|
|
|
if (ppFramesOut != NULL) {
|
|
*ppFramesOut = NULL;
|
|
}
|
|
|
|
if (pFrameCount != NULL) {
|
|
frameCount = *pFrameCount;
|
|
*pFrameCount = 0;
|
|
}
|
|
|
|
if (pAudioBufferRef == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
|
|
if (frameCount > framesAvailable) {
|
|
frameCount = framesAvailable;
|
|
}
|
|
|
|
*ppFramesOut = ma_offset_ptr(pAudioBufferRef->pData, pAudioBufferRef->cursor * ma_get_bytes_per_frame(pAudioBufferRef->format, pAudioBufferRef->channels));
|
|
*pFrameCount = frameCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 framesAvailable;
|
|
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
framesAvailable = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
|
|
if (frameCount > framesAvailable) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pAudioBufferRef->cursor += frameCount;
|
|
|
|
if (pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames) {
|
|
return MA_AT_END;
|
|
} else {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef)
|
|
{
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return pAudioBufferRef->cursor == pAudioBufferRef->sizeInFrames;
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = pAudioBufferRef->cursor;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = pAudioBufferRef->sizeInFrames;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames)
|
|
{
|
|
if (pAvailableFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pAvailableFrames = 0;
|
|
|
|
if (pAudioBufferRef == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pAudioBufferRef->sizeInFrames <= pAudioBufferRef->cursor) {
|
|
*pAvailableFrames = 0;
|
|
} else {
|
|
*pAvailableFrames = pAudioBufferRef->sizeInFrames - pAudioBufferRef->cursor;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_audio_buffer_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = 0;
|
|
config.sizeInFrames = sizeInFrames;
|
|
config.pData = pData;
|
|
ma_allocation_callbacks_init_copy(&config.allocationCallbacks, pAllocationCallbacks);
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_audio_buffer_init_ex(const ma_audio_buffer_config* pConfig, ma_bool32 doCopy, ma_audio_buffer* pAudioBuffer)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_MEMORY(pAudioBuffer, sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData));
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->sizeInFrames == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_audio_buffer_ref_init(pConfig->format, pConfig->channels, NULL, 0, &pAudioBuffer->ref);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pAudioBuffer->ref.sampleRate = pConfig->sampleRate;
|
|
|
|
ma_allocation_callbacks_init_copy(&pAudioBuffer->allocationCallbacks, &pConfig->allocationCallbacks);
|
|
|
|
if (doCopy) {
|
|
ma_uint64 allocationSizeInBytes;
|
|
void* pData;
|
|
|
|
allocationSizeInBytes = pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels);
|
|
if (allocationSizeInBytes > MA_SIZE_MAX) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pData = ma_malloc((size_t)allocationSizeInBytes, &pAudioBuffer->allocationCallbacks);
|
|
if (pData == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (pConfig->pData != NULL) {
|
|
ma_copy_pcm_frames(pData, pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
|
|
} else {
|
|
ma_silence_pcm_frames(pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
|
|
}
|
|
|
|
ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pData, pConfig->sizeInFrames);
|
|
pAudioBuffer->ownsData = MA_TRUE;
|
|
} else {
|
|
ma_audio_buffer_ref_set_data(&pAudioBuffer->ref, pConfig->pData, pConfig->sizeInFrames);
|
|
pAudioBuffer->ownsData = MA_FALSE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_audio_buffer_uninit_ex(ma_audio_buffer* pAudioBuffer, ma_bool32 doFree)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pAudioBuffer->ownsData && pAudioBuffer->ref.pData != &pAudioBuffer->_pExtraData[0]) {
|
|
ma_free((void*)pAudioBuffer->ref.pData, &pAudioBuffer->allocationCallbacks);
|
|
}
|
|
|
|
if (doFree) {
|
|
ma_free(pAudioBuffer, &pAudioBuffer->allocationCallbacks);
|
|
}
|
|
|
|
ma_audio_buffer_ref_uninit(&pAudioBuffer->ref);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
|
|
{
|
|
return ma_audio_buffer_init_ex(pConfig, MA_FALSE, pAudioBuffer);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer)
|
|
{
|
|
return ma_audio_buffer_init_ex(pConfig, MA_TRUE, pAudioBuffer);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer)
|
|
{
|
|
ma_result result;
|
|
ma_audio_buffer* pAudioBuffer;
|
|
ma_audio_buffer_config innerConfig;
|
|
ma_uint64 allocationSizeInBytes;
|
|
|
|
if (ppAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*ppAudioBuffer = NULL;
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
innerConfig = *pConfig;
|
|
ma_allocation_callbacks_init_copy(&innerConfig.allocationCallbacks, &pConfig->allocationCallbacks);
|
|
|
|
allocationSizeInBytes = sizeof(*pAudioBuffer) - sizeof(pAudioBuffer->_pExtraData) + (pConfig->sizeInFrames * ma_get_bytes_per_frame(pConfig->format, pConfig->channels));
|
|
if (allocationSizeInBytes > MA_SIZE_MAX) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pAudioBuffer = (ma_audio_buffer*)ma_malloc((size_t)allocationSizeInBytes, &innerConfig.allocationCallbacks);
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if (pConfig->pData != NULL) {
|
|
ma_copy_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->pData, pConfig->sizeInFrames, pConfig->format, pConfig->channels);
|
|
} else {
|
|
ma_silence_pcm_frames(&pAudioBuffer->_pExtraData[0], pConfig->sizeInFrames, pConfig->format, pConfig->channels);
|
|
}
|
|
|
|
innerConfig.pData = &pAudioBuffer->_pExtraData[0];
|
|
|
|
result = ma_audio_buffer_init_ex(&innerConfig, MA_FALSE, pAudioBuffer);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pAudioBuffer, &innerConfig.allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppAudioBuffer = pAudioBuffer;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer)
|
|
{
|
|
ma_audio_buffer_uninit_ex(pAudioBuffer, MA_FALSE);
|
|
}
|
|
|
|
MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer)
|
|
{
|
|
ma_audio_buffer_uninit_ex(pAudioBuffer, MA_TRUE);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_read_pcm_frames(&pAudioBuffer->ref, pFramesOut, frameCount, loop);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_seek_to_pcm_frame(&pAudioBuffer->ref, frameIndex);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount)
|
|
{
|
|
if (ppFramesOut != NULL) {
|
|
*ppFramesOut = NULL;
|
|
}
|
|
|
|
if (pAudioBuffer == NULL) {
|
|
if (pFrameCount != NULL) {
|
|
*pFrameCount = 0;
|
|
}
|
|
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_map(&pAudioBuffer->ref, ppFramesOut, pFrameCount);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_unmap(&pAudioBuffer->ref, frameCount);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_at_end(&pAudioBuffer->ref);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_get_cursor_in_pcm_frames(&pAudioBuffer->ref, pCursor);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength)
|
|
{
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_get_length_in_pcm_frames(&pAudioBuffer->ref, pLength);
|
|
}
|
|
|
|
MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames)
|
|
{
|
|
if (pAvailableFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pAvailableFrames = 0;
|
|
|
|
if (pAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_audio_buffer_ref_get_available_frames(&pAudioBuffer->ref, pAvailableFrames);
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData)
|
|
{
|
|
if (pData == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pData);
|
|
|
|
pData->format = format;
|
|
pData->channels = channels;
|
|
pData->pTail = &pData->head;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_paged_audio_buffer_page* pPage;
|
|
|
|
if (pData == NULL) {
|
|
return;
|
|
}
|
|
|
|
pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext);
|
|
while (pPage != NULL) {
|
|
ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext);
|
|
|
|
ma_free(pPage, pAllocationCallbacks);
|
|
pPage = pNext;
|
|
}
|
|
}
|
|
|
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData)
|
|
{
|
|
if (pData == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return &pData->head;
|
|
}
|
|
|
|
MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData)
|
|
{
|
|
if (pData == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pData->pTail;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength)
|
|
{
|
|
ma_paged_audio_buffer_page* pPage;
|
|
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pData == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) {
|
|
*pLength += pPage->sizeInFrames;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage)
|
|
{
|
|
ma_paged_audio_buffer_page* pPage;
|
|
ma_uint64 allocationSize;
|
|
|
|
if (ppPage == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*ppPage = NULL;
|
|
|
|
if (pData == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
allocationSize = sizeof(*pPage) + (pageSizeInFrames * ma_get_bytes_per_frame(pData->format, pData->channels));
|
|
if (allocationSize > MA_SIZE_MAX) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pPage = (ma_paged_audio_buffer_page*)ma_malloc((size_t)allocationSize, pAllocationCallbacks);
|
|
if (pPage == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pPage->pNext = NULL;
|
|
pPage->sizeInFrames = pageSizeInFrames;
|
|
|
|
if (pInitialData != NULL) {
|
|
ma_copy_pcm_frames(pPage->pAudioData, pInitialData, pageSizeInFrames, pData->format, pData->channels);
|
|
}
|
|
|
|
*ppPage = pPage;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pData == NULL || pPage == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_free(pPage, pAllocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage)
|
|
{
|
|
if (pData == NULL || pPage == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (;;) {
|
|
ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail);
|
|
ma_paged_audio_buffer_page* pNewTail = pPage;
|
|
|
|
if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) {
|
|
ma_atomic_exchange_ptr(&pOldTail->pNext, pPage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_result result;
|
|
ma_paged_audio_buffer_page* pPage;
|
|
|
|
result = ma_paged_audio_buffer_data_allocate_page(pData, pageSizeInFrames, pInitialData, pAllocationCallbacks, &pPage);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_paged_audio_buffer_data_append_page(pData, pPage);
|
|
}
|
|
|
|
MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData)
|
|
{
|
|
ma_paged_audio_buffer_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.pData = pData;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_paged_audio_buffer__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_paged_audio_buffer_read_pcm_frames((ma_paged_audio_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_paged_audio_buffer__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_paged_audio_buffer_seek_to_pcm_frame((ma_paged_audio_buffer*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_paged_audio_buffer__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_paged_audio_buffer* pPagedAudioBuffer = (ma_paged_audio_buffer*)pDataSource;
|
|
|
|
*pFormat = pPagedAudioBuffer->pData->format;
|
|
*pChannels = pPagedAudioBuffer->pData->channels;
|
|
*pSampleRate = 0;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pPagedAudioBuffer->pData->channels);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_paged_audio_buffer__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_paged_audio_buffer_get_cursor_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_paged_audio_buffer__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_paged_audio_buffer_get_length_in_pcm_frames((ma_paged_audio_buffer*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_paged_audio_buffer_data_source_vtable =
|
|
{
|
|
ma_paged_audio_buffer__data_source_on_read,
|
|
ma_paged_audio_buffer__data_source_on_seek,
|
|
ma_paged_audio_buffer__data_source_on_get_data_format,
|
|
ma_paged_audio_buffer__data_source_on_get_cursor,
|
|
ma_paged_audio_buffer__data_source_on_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
if (pPagedAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pPagedAudioBuffer);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->pData == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_paged_audio_buffer_data_source_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pPagedAudioBuffer->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pPagedAudioBuffer->pData = pConfig->pData;
|
|
pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pConfig->pData);
|
|
pPagedAudioBuffer->relativeCursor = 0;
|
|
pPagedAudioBuffer->absoluteCursor = 0;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer)
|
|
{
|
|
if (pPagedAudioBuffer == NULL) {
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
|
|
if (pPagedAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
format = pPagedAudioBuffer->pData->format;
|
|
channels = pPagedAudioBuffer->pData->channels;
|
|
|
|
while (totalFramesRead < frameCount) {
|
|
ma_uint64 framesRemainingInCurrentPage;
|
|
ma_uint64 framesRemainingToRead = frameCount - totalFramesRead;
|
|
ma_uint64 framesToReadThisIteration;
|
|
|
|
MA_ASSERT(pPagedAudioBuffer->pCurrent != NULL);
|
|
|
|
framesRemainingInCurrentPage = pPagedAudioBuffer->pCurrent->sizeInFrames - pPagedAudioBuffer->relativeCursor;
|
|
|
|
framesToReadThisIteration = ma_min(framesRemainingInCurrentPage, framesRemainingToRead);
|
|
ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), ma_offset_pcm_frames_ptr(pPagedAudioBuffer->pCurrent->pAudioData, pPagedAudioBuffer->relativeCursor, format, channels), framesToReadThisIteration, format, channels);
|
|
totalFramesRead += framesToReadThisIteration;
|
|
|
|
pPagedAudioBuffer->absoluteCursor += framesToReadThisIteration;
|
|
pPagedAudioBuffer->relativeCursor += framesToReadThisIteration;
|
|
|
|
MA_ASSERT(pPagedAudioBuffer->relativeCursor <= pPagedAudioBuffer->pCurrent->sizeInFrames);
|
|
|
|
if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) {
|
|
ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext);
|
|
if (pNext == NULL) {
|
|
result = MA_AT_END;
|
|
break;
|
|
} else {
|
|
pPagedAudioBuffer->pCurrent = pNext;
|
|
pPagedAudioBuffer->relativeCursor = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex)
|
|
{
|
|
if (pPagedAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (frameIndex == pPagedAudioBuffer->absoluteCursor) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
if (frameIndex < pPagedAudioBuffer->absoluteCursor) {
|
|
pPagedAudioBuffer->pCurrent = ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData);
|
|
pPagedAudioBuffer->absoluteCursor = 0;
|
|
pPagedAudioBuffer->relativeCursor = 0;
|
|
|
|
}
|
|
|
|
if (frameIndex > pPagedAudioBuffer->absoluteCursor) {
|
|
ma_paged_audio_buffer_page* pPage;
|
|
ma_uint64 runningCursor = 0;
|
|
|
|
for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) {
|
|
ma_uint64 pageRangeBeg = runningCursor;
|
|
ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames;
|
|
|
|
if (frameIndex >= pageRangeBeg) {
|
|
if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) {
|
|
pPagedAudioBuffer->pCurrent = pPage;
|
|
pPagedAudioBuffer->absoluteCursor = frameIndex;
|
|
pPagedAudioBuffer->relativeCursor = frameIndex - pageRangeBeg;
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
runningCursor = pageRangeEnd;
|
|
}
|
|
|
|
return MA_BAD_SEEK;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pPagedAudioBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = pPagedAudioBuffer->absoluteCursor;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength)
|
|
{
|
|
return ma_paged_audio_buffer_data_get_length_in_pcm_frames(pPagedAudioBuffer->pData, pLength);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pFile == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pFile = NULL;
|
|
|
|
if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onOpen == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onOpen(pVFS, pFilePath, openMode, pFile);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pFile == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pFile = NULL;
|
|
|
|
if (pVFS == NULL || pFilePath == NULL || openMode == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onOpenW == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onOpenW(pVFS, pFilePath, openMode, pFile);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pVFS == NULL || file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onClose == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onClose(pVFS, file);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
ma_result result;
|
|
size_t bytesRead = 0;
|
|
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = 0;
|
|
}
|
|
|
|
if (pVFS == NULL || file == NULL || pDst == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onRead == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
result = pCallbacks->onRead(pVFS, file, pDst, sizeInBytes, &bytesRead);
|
|
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = bytesRead;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && bytesRead == 0 && sizeInBytes > 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pBytesWritten != NULL) {
|
|
*pBytesWritten = 0;
|
|
}
|
|
|
|
if (pVFS == NULL || file == NULL || pSrc == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onWrite == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onWrite(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pVFS == NULL || file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onSeek == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onSeek(pVFS, file, offset, origin);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pVFS == NULL || file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onTell == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onTell(pVFS, file, pCursor);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
|
|
{
|
|
ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS;
|
|
|
|
if (pInfo == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pInfo);
|
|
|
|
if (pVFS == NULL || file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pCallbacks->onInfo == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pCallbacks->onInfo(pVFS, file, pInfo);
|
|
}
|
|
|
|
#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && (defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_NXDK)) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX))
|
|
#define MA_USE_WIN32_FILEIO
|
|
#endif
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
|
|
typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod);
|
|
typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod);
|
|
|
|
static ma_handle hKernel32DLL = NULL;
|
|
static ma_SetFilePointer_proc ma_SetFilePointer = NULL;
|
|
static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL;
|
|
|
|
static void ma_win32_fileio_init(void)
|
|
{
|
|
if (hKernel32DLL == NULL) {
|
|
hKernel32DLL = ma_dlopen(NULL, "kernel32.dll");
|
|
if (hKernel32DLL != NULL) {
|
|
ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer");
|
|
ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition)
|
|
{
|
|
*pDesiredAccess = 0;
|
|
if ((openMode & MA_OPEN_MODE_READ) != 0) {
|
|
*pDesiredAccess |= GENERIC_READ;
|
|
}
|
|
if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
|
|
*pDesiredAccess |= GENERIC_WRITE;
|
|
}
|
|
|
|
*pShareMode = 0;
|
|
if ((openMode & MA_OPEN_MODE_READ) != 0) {
|
|
*pShareMode |= FILE_SHARE_READ;
|
|
}
|
|
|
|
if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
|
|
*pCreationDisposition = CREATE_ALWAYS;
|
|
} else {
|
|
*pCreationDisposition = OPEN_EXISTING;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
HANDLE hFile;
|
|
DWORD dwDesiredAccess;
|
|
DWORD dwShareMode;
|
|
DWORD dwCreationDisposition;
|
|
|
|
(void)pVFS;
|
|
|
|
ma_win32_fileio_init();
|
|
|
|
ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
|
|
|
|
hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
*pFile = hFile;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
#if !defined(MA_XBOX_NXDK)
|
|
{
|
|
HANDLE hFile;
|
|
DWORD dwDesiredAccess;
|
|
DWORD dwShareMode;
|
|
DWORD dwCreationDisposition;
|
|
|
|
(void)pVFS;
|
|
|
|
ma_win32_fileio_init();
|
|
|
|
ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition);
|
|
|
|
hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
*pFile = hFile;
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_close__win32(ma_vfs* pVFS, ma_vfs_file file)
|
|
{
|
|
(void)pVFS;
|
|
|
|
if (CloseHandle((HANDLE)file) == 0) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_read__win32(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
size_t totalBytesRead;
|
|
|
|
(void)pVFS;
|
|
|
|
totalBytesRead = 0;
|
|
while (totalBytesRead < sizeInBytes) {
|
|
size_t bytesRemaining;
|
|
DWORD bytesToRead;
|
|
DWORD bytesRead;
|
|
BOOL readResult;
|
|
|
|
bytesRemaining = sizeInBytes - totalBytesRead;
|
|
if (bytesRemaining >= 0xFFFFFFFF) {
|
|
bytesToRead = 0xFFFFFFFF;
|
|
} else {
|
|
bytesToRead = (DWORD)bytesRemaining;
|
|
}
|
|
|
|
readResult = ReadFile((HANDLE)file, ma_offset_ptr(pDst, totalBytesRead), bytesToRead, &bytesRead, NULL);
|
|
if (readResult == 1 && bytesRead == 0) {
|
|
result = MA_AT_END;
|
|
break;
|
|
}
|
|
|
|
totalBytesRead += bytesRead;
|
|
|
|
if (bytesRead < bytesToRead) {
|
|
break;
|
|
}
|
|
|
|
if (readResult == 0) {
|
|
result = ma_result_from_GetLastError(GetLastError());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = totalBytesRead;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_write__win32(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
size_t totalBytesWritten;
|
|
|
|
(void)pVFS;
|
|
|
|
totalBytesWritten = 0;
|
|
while (totalBytesWritten < sizeInBytes) {
|
|
size_t bytesRemaining;
|
|
DWORD bytesToWrite;
|
|
DWORD bytesWritten;
|
|
BOOL writeResult;
|
|
|
|
bytesRemaining = sizeInBytes - totalBytesWritten;
|
|
if (bytesRemaining >= 0xFFFFFFFF) {
|
|
bytesToWrite = 0xFFFFFFFF;
|
|
} else {
|
|
bytesToWrite = (DWORD)bytesRemaining;
|
|
}
|
|
|
|
writeResult = WriteFile((HANDLE)file, ma_offset_ptr(pSrc, totalBytesWritten), bytesToWrite, &bytesWritten, NULL);
|
|
totalBytesWritten += bytesWritten;
|
|
|
|
if (writeResult == 0) {
|
|
result = ma_result_from_GetLastError(GetLastError());
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pBytesWritten != NULL) {
|
|
*pBytesWritten = totalBytesWritten;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
LARGE_INTEGER liDistanceToMove;
|
|
DWORD dwMoveMethod;
|
|
BOOL result;
|
|
|
|
(void)pVFS;
|
|
|
|
liDistanceToMove.QuadPart = offset;
|
|
|
|
if (origin == ma_seek_origin_current) {
|
|
dwMoveMethod = FILE_CURRENT;
|
|
} else if (origin == ma_seek_origin_end) {
|
|
dwMoveMethod = FILE_END;
|
|
} else {
|
|
dwMoveMethod = FILE_BEGIN;
|
|
}
|
|
|
|
if (ma_SetFilePointerEx != NULL) {
|
|
result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod);
|
|
} else if (ma_SetFilePointer != NULL) {
|
|
if (offset > 0x7FFFFFFF) {
|
|
return MA_OUT_OF_RANGE;
|
|
}
|
|
|
|
result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod);
|
|
} else {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (result == 0) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
|
|
{
|
|
LARGE_INTEGER liZero;
|
|
LARGE_INTEGER liTell;
|
|
BOOL result;
|
|
|
|
(void)pVFS;
|
|
|
|
liZero.QuadPart = 0;
|
|
|
|
if (ma_SetFilePointerEx != NULL) {
|
|
result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT);
|
|
} else if (ma_SetFilePointer != NULL) {
|
|
LONG tell;
|
|
|
|
result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT);
|
|
liTell.QuadPart = tell;
|
|
} else {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (result == 0) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
if (pCursor != NULL) {
|
|
*pCursor = liTell.QuadPart;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_info__win32(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
|
|
{
|
|
(void)pVFS;
|
|
|
|
#if !defined(MA_XBOX_NXDK)
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION fi;
|
|
BOOL result;
|
|
|
|
result = GetFileInformationByHandle((HANDLE)file, &fi);
|
|
if (result == 0) {
|
|
return ma_result_from_GetLastError(GetLastError());
|
|
}
|
|
|
|
pInfo->sizeInBytes = ((ma_uint64)fi.nFileSizeHigh << 32) | ((ma_uint64)fi.nFileSizeLow);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
#else
|
|
static ma_result ma_default_vfs_open__stdio(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
ma_result result;
|
|
FILE* pFileStd;
|
|
const char* pOpenModeStr;
|
|
|
|
MA_ASSERT(pFilePath != NULL);
|
|
MA_ASSERT(openMode != 0);
|
|
MA_ASSERT(pFile != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
if ((openMode & MA_OPEN_MODE_READ) != 0) {
|
|
if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
|
|
pOpenModeStr = "r+";
|
|
} else {
|
|
pOpenModeStr = "rb";
|
|
}
|
|
} else {
|
|
pOpenModeStr = "wb";
|
|
}
|
|
|
|
result = ma_fopen(&pFileStd, pFilePath, pOpenModeStr);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pFile = pFileStd;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_open_w__stdio(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
ma_result result;
|
|
FILE* pFileStd;
|
|
const wchar_t* pOpenModeStr;
|
|
|
|
MA_ASSERT(pFilePath != NULL);
|
|
MA_ASSERT(openMode != 0);
|
|
MA_ASSERT(pFile != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
if ((openMode & MA_OPEN_MODE_READ) != 0) {
|
|
if ((openMode & MA_OPEN_MODE_WRITE) != 0) {
|
|
pOpenModeStr = L"r+";
|
|
} else {
|
|
pOpenModeStr = L"rb";
|
|
}
|
|
} else {
|
|
pOpenModeStr = L"wb";
|
|
}
|
|
|
|
result = ma_wfopen(&pFileStd, pFilePath, pOpenModeStr, (pVFS != NULL) ? &((ma_default_vfs*)pVFS)->allocationCallbacks : NULL);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pFile = pFileStd;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_close__stdio(ma_vfs* pVFS, ma_vfs_file file)
|
|
{
|
|
MA_ASSERT(file != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
fclose((FILE*)file);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_read__stdio(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
|
|
{
|
|
size_t result;
|
|
|
|
MA_ASSERT(file != NULL);
|
|
MA_ASSERT(pDst != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
result = fread(pDst, 1, sizeInBytes, (FILE*)file);
|
|
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = result;
|
|
}
|
|
|
|
if (result != sizeInBytes) {
|
|
if (result == 0 && feof((FILE*)file)) {
|
|
return MA_AT_END;
|
|
} else {
|
|
return ma_result_from_errno(ferror((FILE*)file));
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_write__stdio(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
|
|
{
|
|
size_t result;
|
|
|
|
MA_ASSERT(file != NULL);
|
|
MA_ASSERT(pSrc != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
result = fwrite(pSrc, 1, sizeInBytes, (FILE*)file);
|
|
|
|
if (pBytesWritten != NULL) {
|
|
*pBytesWritten = result;
|
|
}
|
|
|
|
if (result != sizeInBytes) {
|
|
return ma_result_from_errno(ferror((FILE*)file));
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_seek__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
int result;
|
|
int whence;
|
|
|
|
MA_ASSERT(file != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
if (origin == ma_seek_origin_start) {
|
|
whence = SEEK_SET;
|
|
} else if (origin == ma_seek_origin_end) {
|
|
whence = SEEK_END;
|
|
} else {
|
|
whence = SEEK_CUR;
|
|
}
|
|
|
|
#if defined(_WIN32)
|
|
#if defined(_MSC_VER) && _MSC_VER > 1200
|
|
result = _fseeki64((FILE*)file, offset, whence);
|
|
#else
|
|
if (offset > 0x7FFFFFFF) {
|
|
return MA_OUT_OF_RANGE;
|
|
}
|
|
|
|
result = fseek((FILE*)file, (int)offset, whence);
|
|
#endif
|
|
#else
|
|
result = fseek((FILE*)file, (long int)offset, whence);
|
|
#endif
|
|
if (result != 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_default_vfs_tell__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
|
|
{
|
|
ma_int64 result;
|
|
|
|
MA_ASSERT(file != NULL);
|
|
MA_ASSERT(pCursor != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
#if defined(_WIN32)
|
|
#if defined(_MSC_VER) && _MSC_VER > 1200
|
|
result = _ftelli64((FILE*)file);
|
|
#else
|
|
result = ftell((FILE*)file);
|
|
#endif
|
|
#else
|
|
result = ftell((FILE*)file);
|
|
#endif
|
|
|
|
*pCursor = result;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if !defined(_MSC_VER) && !((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 1) || defined(_XOPEN_SOURCE) || defined(_POSIX_SOURCE)) && !defined(MA_BSD)
|
|
int fileno(FILE *stream);
|
|
#endif
|
|
|
|
static ma_result ma_default_vfs_info__stdio(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
|
|
{
|
|
int fd;
|
|
struct stat info;
|
|
|
|
MA_ASSERT(file != NULL);
|
|
MA_ASSERT(pInfo != NULL);
|
|
|
|
(void)pVFS;
|
|
|
|
#if defined(_MSC_VER)
|
|
fd = _fileno((FILE*)file);
|
|
#else
|
|
fd = fileno((FILE*)file);
|
|
#endif
|
|
|
|
if (fstat(fd, &info) != 0) {
|
|
return ma_result_from_errno(errno);
|
|
}
|
|
|
|
pInfo->sizeInBytes = info.st_size;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
if (pFile == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pFile = NULL;
|
|
|
|
if (pFilePath == NULL || openMode == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile);
|
|
#else
|
|
return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
if (pFile == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pFile = NULL;
|
|
|
|
if (pFilePath == NULL || openMode == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile);
|
|
#else
|
|
return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file)
|
|
{
|
|
if (file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_close__win32(pVFS, file);
|
|
#else
|
|
return ma_default_vfs_close__stdio(pVFS, file);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
|
|
{
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = 0;
|
|
}
|
|
|
|
if (file == NULL || pDst == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead);
|
|
#else
|
|
return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
|
|
{
|
|
if (pBytesWritten != NULL) {
|
|
*pBytesWritten = 0;
|
|
}
|
|
|
|
if (file == NULL || pSrc == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
|
|
#else
|
|
return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
if (file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_seek__win32(pVFS, file, offset, origin);
|
|
#else
|
|
return ma_default_vfs_seek__stdio(pVFS, file, offset, origin);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
return ma_default_vfs_tell__win32(pVFS, file, pCursor);
|
|
#else
|
|
return ma_default_vfs_tell__stdio(pVFS, file, pCursor);
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pInfo == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pInfo);
|
|
|
|
if (file == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if defined(MA_USE_WIN32_FILEIO)
|
|
result = ma_default_vfs_info__win32(pVFS, file, pInfo);
|
|
#else
|
|
result = ma_default_vfs_info__stdio(pVFS, file, pInfo);
|
|
#endif
|
|
|
|
if (result == MA_NOT_IMPLEMENTED) {
|
|
ma_int64 cursor;
|
|
ma_int64 sizeInBytes;
|
|
|
|
result = ma_default_vfs_tell(pVFS, file, &cursor);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_default_vfs_seek(pVFS, file, 0, ma_seek_origin_end);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_default_vfs_tell(pVFS, file, &sizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pInfo->sizeInBytes = sizeInBytes;
|
|
|
|
result = ma_default_vfs_seek(pVFS, file, cursor, ma_seek_origin_start);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
MA_ASSERT(result == MA_SUCCESS);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_default_vfs_init(ma_default_vfs* pVFS, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pVFS == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pVFS->cb.onOpen = ma_default_vfs_open;
|
|
pVFS->cb.onOpenW = ma_default_vfs_open_w;
|
|
pVFS->cb.onClose = ma_default_vfs_close;
|
|
pVFS->cb.onRead = ma_default_vfs_read;
|
|
pVFS->cb.onWrite = ma_default_vfs_write;
|
|
pVFS->cb.onSeek = ma_default_vfs_seek;
|
|
pVFS->cb.onTell = ma_default_vfs_tell;
|
|
pVFS->cb.onInfo = ma_default_vfs_info;
|
|
ma_allocation_callbacks_init_copy(&pVFS->allocationCallbacks, pAllocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_open(ma_vfs* pVFS, const char* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_open(pVFS, pFilePath, openMode, pFile);
|
|
} else {
|
|
return ma_default_vfs_open(pVFS, pFilePath, openMode, pFile);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, ma_uint32 openMode, ma_vfs_file* pFile)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_open_w(pVFS, pFilePath, openMode, pFile);
|
|
} else {
|
|
return ma_default_vfs_open_w(pVFS, pFilePath, openMode, pFile);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_close(ma_vfs* pVFS, ma_vfs_file file)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_close(pVFS, file);
|
|
} else {
|
|
return ma_default_vfs_close(pVFS, file);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t sizeInBytes, size_t* pBytesRead)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
|
|
} else {
|
|
return ma_default_vfs_read(pVFS, file, pDst, sizeInBytes, pBytesRead);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_write(ma_vfs* pVFS, ma_vfs_file file, const void* pSrc, size_t sizeInBytes, size_t* pBytesWritten)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
|
|
} else {
|
|
return ma_default_vfs_write(pVFS, file, pSrc, sizeInBytes, pBytesWritten);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_seek(pVFS, file, offset, origin);
|
|
} else {
|
|
return ma_default_vfs_seek(pVFS, file, offset, origin);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* pCursor)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_tell(pVFS, file, pCursor);
|
|
} else {
|
|
return ma_default_vfs_tell(pVFS, file, pCursor);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo)
|
|
{
|
|
if (pVFS != NULL) {
|
|
return ma_vfs_info(pVFS, file, pInfo);
|
|
} else {
|
|
return ma_default_vfs_info(pVFS, file, pInfo);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_result result;
|
|
ma_vfs_file file;
|
|
ma_file_info info;
|
|
void* pData;
|
|
size_t bytesRead;
|
|
|
|
if (ppData != NULL) {
|
|
*ppData = NULL;
|
|
}
|
|
if (pSize != NULL) {
|
|
*pSize = 0;
|
|
}
|
|
|
|
if (ppData == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFilePath != NULL) {
|
|
result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
|
|
} else {
|
|
result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file);
|
|
}
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_vfs_or_default_info(pVFS, file, &info);
|
|
if (result != MA_SUCCESS) {
|
|
ma_vfs_or_default_close(pVFS, file);
|
|
return result;
|
|
}
|
|
|
|
if (info.sizeInBytes > MA_SIZE_MAX) {
|
|
ma_vfs_or_default_close(pVFS, file);
|
|
return MA_TOO_BIG;
|
|
}
|
|
|
|
pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks);
|
|
if (pData == NULL) {
|
|
ma_vfs_or_default_close(pVFS, file);
|
|
return result;
|
|
}
|
|
|
|
result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead);
|
|
ma_vfs_or_default_close(pVFS, file);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pData, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if (pSize != NULL) {
|
|
*pSize = bytesRead;
|
|
}
|
|
|
|
MA_ASSERT(ppData != NULL);
|
|
*ppData = pData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks);
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
|
|
#define MA_HAS_WAV
|
|
|
|
#ifndef ma_dr_wav_h
|
|
#define ma_dr_wav_h
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#define MA_DR_WAV_STRINGIFY(x) #x
|
|
#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x)
|
|
#define MA_DR_WAV_VERSION_MAJOR 0
|
|
#define MA_DR_WAV_VERSION_MINOR 14
|
|
#define MA_DR_WAV_VERSION_REVISION 4
|
|
#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION)
|
|
#include <stddef.h>
|
|
#define MA_DR_WAVE_FORMAT_PCM 0x1
|
|
#define MA_DR_WAVE_FORMAT_ADPCM 0x2
|
|
#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3
|
|
#define MA_DR_WAVE_FORMAT_ALAW 0x6
|
|
#define MA_DR_WAVE_FORMAT_MULAW 0x7
|
|
#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11
|
|
#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE
|
|
#define MA_DR_WAV_SEQUENTIAL 0x00000001
|
|
#define MA_DR_WAV_WITH_METADATA 0x00000002
|
|
MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
|
|
MA_API const char* ma_dr_wav_version_string(void);
|
|
typedef enum
|
|
{
|
|
MA_DR_WAV_SEEK_SET,
|
|
MA_DR_WAV_SEEK_CUR,
|
|
MA_DR_WAV_SEEK_END
|
|
} ma_dr_wav_seek_origin;
|
|
typedef enum
|
|
{
|
|
ma_dr_wav_container_riff,
|
|
ma_dr_wav_container_rifx,
|
|
ma_dr_wav_container_w64,
|
|
ma_dr_wav_container_rf64,
|
|
ma_dr_wav_container_aiff
|
|
} ma_dr_wav_container;
|
|
typedef struct
|
|
{
|
|
union
|
|
{
|
|
ma_uint8 fourcc[4];
|
|
ma_uint8 guid[16];
|
|
} id;
|
|
ma_uint64 sizeInBytes;
|
|
unsigned int paddingSize;
|
|
} ma_dr_wav_chunk_header;
|
|
typedef struct
|
|
{
|
|
ma_uint16 formatTag;
|
|
ma_uint16 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 avgBytesPerSec;
|
|
ma_uint16 blockAlign;
|
|
ma_uint16 bitsPerSample;
|
|
ma_uint16 extendedSize;
|
|
ma_uint16 validBitsPerSample;
|
|
ma_uint32 channelMask;
|
|
ma_uint8 subFormat[16];
|
|
} ma_dr_wav_fmt;
|
|
MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT);
|
|
typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
|
|
typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite);
|
|
typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin);
|
|
typedef ma_bool32 (* ma_dr_wav_tell_proc)(void* pUserData, ma_int64* pCursor);
|
|
typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT);
|
|
typedef struct
|
|
{
|
|
const ma_uint8* data;
|
|
size_t dataSize;
|
|
size_t currentReadPos;
|
|
} ma_dr_wav__memory_stream;
|
|
typedef struct
|
|
{
|
|
void** ppData;
|
|
size_t* pDataSize;
|
|
size_t dataSize;
|
|
size_t dataCapacity;
|
|
size_t currentWritePos;
|
|
} ma_dr_wav__memory_stream_write;
|
|
typedef struct
|
|
{
|
|
ma_dr_wav_container container;
|
|
ma_uint32 format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 bitsPerSample;
|
|
} ma_dr_wav_data_format;
|
|
typedef enum
|
|
{
|
|
ma_dr_wav_metadata_type_none = 0,
|
|
ma_dr_wav_metadata_type_unknown = 1 << 0,
|
|
ma_dr_wav_metadata_type_smpl = 1 << 1,
|
|
ma_dr_wav_metadata_type_inst = 1 << 2,
|
|
ma_dr_wav_metadata_type_cue = 1 << 3,
|
|
ma_dr_wav_metadata_type_acid = 1 << 4,
|
|
ma_dr_wav_metadata_type_bext = 1 << 5,
|
|
ma_dr_wav_metadata_type_list_label = 1 << 6,
|
|
ma_dr_wav_metadata_type_list_note = 1 << 7,
|
|
ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8,
|
|
ma_dr_wav_metadata_type_list_info_software = 1 << 9,
|
|
ma_dr_wav_metadata_type_list_info_copyright = 1 << 10,
|
|
ma_dr_wav_metadata_type_list_info_title = 1 << 11,
|
|
ma_dr_wav_metadata_type_list_info_artist = 1 << 12,
|
|
ma_dr_wav_metadata_type_list_info_comment = 1 << 13,
|
|
ma_dr_wav_metadata_type_list_info_date = 1 << 14,
|
|
ma_dr_wav_metadata_type_list_info_genre = 1 << 15,
|
|
ma_dr_wav_metadata_type_list_info_album = 1 << 16,
|
|
ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17,
|
|
ma_dr_wav_metadata_type_list_info_location = 1 << 18,
|
|
ma_dr_wav_metadata_type_list_info_organization = 1 << 19,
|
|
ma_dr_wav_metadata_type_list_info_keywords = 1 << 20,
|
|
ma_dr_wav_metadata_type_list_info_medium = 1 << 21,
|
|
ma_dr_wav_metadata_type_list_info_description = 1 << 22,
|
|
ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software
|
|
| ma_dr_wav_metadata_type_list_info_copyright
|
|
| ma_dr_wav_metadata_type_list_info_title
|
|
| ma_dr_wav_metadata_type_list_info_artist
|
|
| ma_dr_wav_metadata_type_list_info_comment
|
|
| ma_dr_wav_metadata_type_list_info_date
|
|
| ma_dr_wav_metadata_type_list_info_genre
|
|
| ma_dr_wav_metadata_type_list_info_album
|
|
| ma_dr_wav_metadata_type_list_info_tracknumber
|
|
| ma_dr_wav_metadata_type_list_info_location
|
|
| ma_dr_wav_metadata_type_list_info_organization
|
|
| ma_dr_wav_metadata_type_list_info_keywords
|
|
| ma_dr_wav_metadata_type_list_info_medium
|
|
| ma_dr_wav_metadata_type_list_info_description,
|
|
ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label
|
|
| ma_dr_wav_metadata_type_list_note
|
|
| ma_dr_wav_metadata_type_list_labelled_cue_region,
|
|
ma_dr_wav_metadata_type_all = -2,
|
|
ma_dr_wav_metadata_type_all_including_unknown = -1
|
|
} ma_dr_wav_metadata_type;
|
|
typedef enum
|
|
{
|
|
ma_dr_wav_smpl_loop_type_forward = 0,
|
|
ma_dr_wav_smpl_loop_type_pingpong = 1,
|
|
ma_dr_wav_smpl_loop_type_backward = 2
|
|
} ma_dr_wav_smpl_loop_type;
|
|
typedef struct
|
|
{
|
|
ma_uint32 cuePointId;
|
|
ma_uint32 type;
|
|
ma_uint32 firstSampleOffset;
|
|
ma_uint32 lastSampleOffset;
|
|
ma_uint32 sampleFraction;
|
|
ma_uint32 playCount;
|
|
} ma_dr_wav_smpl_loop;
|
|
typedef struct
|
|
{
|
|
ma_uint32 manufacturerId;
|
|
ma_uint32 productId;
|
|
ma_uint32 samplePeriodNanoseconds;
|
|
ma_uint32 midiUnityNote;
|
|
ma_uint32 midiPitchFraction;
|
|
ma_uint32 smpteFormat;
|
|
ma_uint32 smpteOffset;
|
|
ma_uint32 sampleLoopCount;
|
|
ma_uint32 samplerSpecificDataSizeInBytes;
|
|
ma_dr_wav_smpl_loop* pLoops;
|
|
ma_uint8* pSamplerSpecificData;
|
|
} ma_dr_wav_smpl;
|
|
typedef struct
|
|
{
|
|
ma_int8 midiUnityNote;
|
|
ma_int8 fineTuneCents;
|
|
ma_int8 gainDecibels;
|
|
ma_int8 lowNote;
|
|
ma_int8 highNote;
|
|
ma_int8 lowVelocity;
|
|
ma_int8 highVelocity;
|
|
} ma_dr_wav_inst;
|
|
typedef struct
|
|
{
|
|
ma_uint32 id;
|
|
ma_uint32 playOrderPosition;
|
|
ma_uint8 dataChunkId[4];
|
|
ma_uint32 chunkStart;
|
|
ma_uint32 blockStart;
|
|
ma_uint32 sampleOffset;
|
|
} ma_dr_wav_cue_point;
|
|
typedef struct
|
|
{
|
|
ma_uint32 cuePointCount;
|
|
ma_dr_wav_cue_point *pCuePoints;
|
|
} ma_dr_wav_cue;
|
|
typedef enum
|
|
{
|
|
ma_dr_wav_acid_flag_one_shot = 1,
|
|
ma_dr_wav_acid_flag_root_note_set = 2,
|
|
ma_dr_wav_acid_flag_stretch = 4,
|
|
ma_dr_wav_acid_flag_disk_based = 8,
|
|
ma_dr_wav_acid_flag_acidizer = 16
|
|
} ma_dr_wav_acid_flag;
|
|
typedef struct
|
|
{
|
|
ma_uint32 flags;
|
|
ma_uint16 midiUnityNote;
|
|
ma_uint16 reserved1;
|
|
float reserved2;
|
|
ma_uint32 numBeats;
|
|
ma_uint16 meterDenominator;
|
|
ma_uint16 meterNumerator;
|
|
float tempo;
|
|
} ma_dr_wav_acid;
|
|
typedef struct
|
|
{
|
|
ma_uint32 cuePointId;
|
|
ma_uint32 stringLength;
|
|
char* pString;
|
|
} ma_dr_wav_list_label_or_note;
|
|
typedef struct
|
|
{
|
|
char* pDescription;
|
|
char* pOriginatorName;
|
|
char* pOriginatorReference;
|
|
char pOriginationDate[10];
|
|
char pOriginationTime[8];
|
|
ma_uint64 timeReference;
|
|
ma_uint16 version;
|
|
char* pCodingHistory;
|
|
ma_uint32 codingHistorySize;
|
|
ma_uint8* pUMID;
|
|
ma_uint16 loudnessValue;
|
|
ma_uint16 loudnessRange;
|
|
ma_uint16 maxTruePeakLevel;
|
|
ma_uint16 maxMomentaryLoudness;
|
|
ma_uint16 maxShortTermLoudness;
|
|
} ma_dr_wav_bext;
|
|
typedef struct
|
|
{
|
|
ma_uint32 stringLength;
|
|
char* pString;
|
|
} ma_dr_wav_list_info_text;
|
|
typedef struct
|
|
{
|
|
ma_uint32 cuePointId;
|
|
ma_uint32 sampleLength;
|
|
ma_uint8 purposeId[4];
|
|
ma_uint16 country;
|
|
ma_uint16 language;
|
|
ma_uint16 dialect;
|
|
ma_uint16 codePage;
|
|
ma_uint32 stringLength;
|
|
char* pString;
|
|
} ma_dr_wav_list_labelled_cue_region;
|
|
typedef enum
|
|
{
|
|
ma_dr_wav_metadata_location_invalid,
|
|
ma_dr_wav_metadata_location_top_level,
|
|
ma_dr_wav_metadata_location_inside_info_list,
|
|
ma_dr_wav_metadata_location_inside_adtl_list
|
|
} ma_dr_wav_metadata_location;
|
|
typedef struct
|
|
{
|
|
ma_uint8 id[4];
|
|
ma_dr_wav_metadata_location chunkLocation;
|
|
ma_uint32 dataSizeInBytes;
|
|
ma_uint8* pData;
|
|
} ma_dr_wav_unknown_metadata;
|
|
typedef struct
|
|
{
|
|
ma_dr_wav_metadata_type type;
|
|
union
|
|
{
|
|
ma_dr_wav_cue cue;
|
|
ma_dr_wav_smpl smpl;
|
|
ma_dr_wav_acid acid;
|
|
ma_dr_wav_inst inst;
|
|
ma_dr_wav_bext bext;
|
|
ma_dr_wav_list_label_or_note labelOrNote;
|
|
ma_dr_wav_list_labelled_cue_region labelledCueRegion;
|
|
ma_dr_wav_list_info_text infoText;
|
|
ma_dr_wav_unknown_metadata unknown;
|
|
} data;
|
|
} ma_dr_wav_metadata;
|
|
typedef struct
|
|
{
|
|
ma_dr_wav_read_proc onRead;
|
|
ma_dr_wav_write_proc onWrite;
|
|
ma_dr_wav_seek_proc onSeek;
|
|
ma_dr_wav_tell_proc onTell;
|
|
void* pUserData;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_dr_wav_container container;
|
|
ma_dr_wav_fmt fmt;
|
|
ma_uint32 sampleRate;
|
|
ma_uint16 channels;
|
|
ma_uint16 bitsPerSample;
|
|
ma_uint16 translatedFormatTag;
|
|
ma_uint64 totalPCMFrameCount;
|
|
ma_uint64 dataChunkDataSize;
|
|
ma_uint64 dataChunkDataPos;
|
|
ma_uint64 bytesRemaining;
|
|
ma_uint64 readCursorInPCMFrames;
|
|
ma_uint64 dataChunkDataSizeTargetWrite;
|
|
ma_bool32 isSequentialWrite;
|
|
ma_dr_wav_metadata* pMetadata;
|
|
ma_uint32 metadataCount;
|
|
ma_dr_wav__memory_stream memoryStream;
|
|
ma_dr_wav__memory_stream_write memoryStreamWrite;
|
|
struct
|
|
{
|
|
ma_uint32 bytesRemainingInBlock;
|
|
ma_uint16 predictor[2];
|
|
ma_int32 delta[2];
|
|
ma_int32 cachedFrames[4];
|
|
ma_uint32 cachedFrameCount;
|
|
ma_int32 prevFrames[2][2];
|
|
} msadpcm;
|
|
struct
|
|
{
|
|
ma_uint32 bytesRemainingInBlock;
|
|
ma_int32 predictor[2];
|
|
ma_int32 stepIndex[2];
|
|
ma_int32 cachedFrames[16];
|
|
ma_uint32 cachedFrameCount;
|
|
} ima;
|
|
struct
|
|
{
|
|
ma_bool8 isLE;
|
|
ma_bool8 isUnsigned;
|
|
} aiff;
|
|
} ma_dr_wav;
|
|
MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount);
|
|
MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount);
|
|
MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav);
|
|
MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav);
|
|
MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut);
|
|
MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex);
|
|
MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor);
|
|
MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength);
|
|
MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData);
|
|
MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
|
|
MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
|
|
MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData);
|
|
#ifndef MA_DR_WAV_NO_CONVERSION_API
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut);
|
|
MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut);
|
|
MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut);
|
|
MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount);
|
|
#endif
|
|
#ifndef MA_DR_WAV_NO_STDIO
|
|
MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#ifndef MA_DR_WAV_NO_CONVERSION_API
|
|
MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#ifndef MA_DR_WAV_NO_STDIO
|
|
MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data);
|
|
MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data);
|
|
MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data);
|
|
MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data);
|
|
MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data);
|
|
MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data);
|
|
MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data);
|
|
MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]);
|
|
MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
|
|
#define MA_HAS_FLAC
|
|
|
|
#ifndef ma_dr_flac_h
|
|
#define ma_dr_flac_h
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#define MA_DR_FLAC_STRINGIFY(x) #x
|
|
#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x)
|
|
#define MA_DR_FLAC_VERSION_MAJOR 0
|
|
#define MA_DR_FLAC_VERSION_MINOR 13
|
|
#define MA_DR_FLAC_VERSION_REVISION 3
|
|
#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION)
|
|
#include <stddef.h>
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1700
|
|
#define MA_DR_FLAC_DEPRECATED __declspec(deprecated)
|
|
#elif (defined(__GNUC__) && __GNUC__ >= 4)
|
|
#define MA_DR_FLAC_DEPRECATED __attribute__((deprecated))
|
|
#elif defined(__has_feature)
|
|
#if __has_feature(attribute_deprecated)
|
|
#define MA_DR_FLAC_DEPRECATED __attribute__((deprecated))
|
|
#else
|
|
#define MA_DR_FLAC_DEPRECATED
|
|
#endif
|
|
#else
|
|
#define MA_DR_FLAC_DEPRECATED
|
|
#endif
|
|
MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
|
|
MA_API const char* ma_dr_flac_version_string(void);
|
|
#ifndef MA_DR_FLAC_BUFFER_SIZE
|
|
#define MA_DR_FLAC_BUFFER_SIZE 4096
|
|
#endif
|
|
#ifdef MA_64BIT
|
|
typedef ma_uint64 ma_dr_flac_cache_t;
|
|
#else
|
|
typedef ma_uint32 ma_dr_flac_cache_t;
|
|
#endif
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6
|
|
#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127
|
|
#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0
|
|
#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1
|
|
#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2
|
|
#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3
|
|
#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4
|
|
#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5
|
|
#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6
|
|
#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7
|
|
#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8
|
|
#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9
|
|
#define MA_DR_FLAC_PICTURE_TYPE_BAND 10
|
|
#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11
|
|
#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12
|
|
#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13
|
|
#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14
|
|
#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15
|
|
#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16
|
|
#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17
|
|
#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18
|
|
#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19
|
|
#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20
|
|
typedef enum
|
|
{
|
|
ma_dr_flac_container_native,
|
|
ma_dr_flac_container_ogg,
|
|
ma_dr_flac_container_unknown
|
|
} ma_dr_flac_container;
|
|
typedef enum
|
|
{
|
|
MA_DR_FLAC_SEEK_SET,
|
|
MA_DR_FLAC_SEEK_CUR,
|
|
MA_DR_FLAC_SEEK_END
|
|
} ma_dr_flac_seek_origin;
|
|
typedef struct
|
|
{
|
|
ma_uint64 firstPCMFrame;
|
|
ma_uint64 flacFrameOffset;
|
|
ma_uint16 pcmFrameCount;
|
|
} ma_dr_flac_seekpoint;
|
|
typedef struct
|
|
{
|
|
ma_uint16 minBlockSizeInPCMFrames;
|
|
ma_uint16 maxBlockSizeInPCMFrames;
|
|
ma_uint32 minFrameSizeInPCMFrames;
|
|
ma_uint32 maxFrameSizeInPCMFrames;
|
|
ma_uint32 sampleRate;
|
|
ma_uint8 channels;
|
|
ma_uint8 bitsPerSample;
|
|
ma_uint64 totalPCMFrameCount;
|
|
ma_uint8 md5[16];
|
|
} ma_dr_flac_streaminfo;
|
|
typedef struct
|
|
{
|
|
ma_uint32 type;
|
|
ma_uint32 rawDataSize;
|
|
ma_uint64 rawDataOffset;
|
|
const void* pRawData;
|
|
union
|
|
{
|
|
ma_dr_flac_streaminfo streaminfo;
|
|
struct
|
|
{
|
|
int unused;
|
|
} padding;
|
|
struct
|
|
{
|
|
ma_uint32 id;
|
|
const void* pData;
|
|
ma_uint32 dataSize;
|
|
} application;
|
|
struct
|
|
{
|
|
ma_uint32 seekpointCount;
|
|
const ma_dr_flac_seekpoint* pSeekpoints;
|
|
} seektable;
|
|
struct
|
|
{
|
|
ma_uint32 vendorLength;
|
|
const char* vendor;
|
|
ma_uint32 commentCount;
|
|
const void* pComments;
|
|
} vorbis_comment;
|
|
struct
|
|
{
|
|
char catalog[128];
|
|
ma_uint64 leadInSampleCount;
|
|
ma_bool32 isCD;
|
|
ma_uint8 trackCount;
|
|
const void* pTrackData;
|
|
} cuesheet;
|
|
struct
|
|
{
|
|
ma_uint32 type;
|
|
ma_uint32 mimeLength;
|
|
const char* mime;
|
|
ma_uint32 descriptionLength;
|
|
const char* description;
|
|
ma_uint32 width;
|
|
ma_uint32 height;
|
|
ma_uint32 colorDepth;
|
|
ma_uint32 indexColorCount;
|
|
ma_uint32 pictureDataSize;
|
|
ma_uint64 pictureDataOffset;
|
|
const ma_uint8* pPictureData;
|
|
} picture;
|
|
} data;
|
|
} ma_dr_flac_metadata;
|
|
typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
|
|
typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin);
|
|
typedef ma_bool32 (* ma_dr_flac_tell_proc)(void* pUserData, ma_int64* pCursor);
|
|
typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata);
|
|
typedef struct
|
|
{
|
|
const ma_uint8* data;
|
|
size_t dataSize;
|
|
size_t currentReadPos;
|
|
} ma_dr_flac__memory_stream;
|
|
typedef struct
|
|
{
|
|
ma_dr_flac_read_proc onRead;
|
|
ma_dr_flac_seek_proc onSeek;
|
|
ma_dr_flac_tell_proc onTell;
|
|
void* pUserData;
|
|
size_t unalignedByteCount;
|
|
ma_dr_flac_cache_t unalignedCache;
|
|
ma_uint32 nextL2Line;
|
|
ma_uint32 consumedBits;
|
|
ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)];
|
|
ma_dr_flac_cache_t cache;
|
|
ma_uint16 crc16;
|
|
ma_dr_flac_cache_t crc16Cache;
|
|
ma_uint32 crc16CacheIgnoredBytes;
|
|
} ma_dr_flac_bs;
|
|
typedef struct
|
|
{
|
|
ma_uint8 subframeType;
|
|
ma_uint8 wastedBitsPerSample;
|
|
ma_uint8 lpcOrder;
|
|
ma_int32* pSamplesS32;
|
|
} ma_dr_flac_subframe;
|
|
typedef struct
|
|
{
|
|
ma_uint64 pcmFrameNumber;
|
|
ma_uint32 flacFrameNumber;
|
|
ma_uint32 sampleRate;
|
|
ma_uint16 blockSizeInPCMFrames;
|
|
ma_uint8 channelAssignment;
|
|
ma_uint8 bitsPerSample;
|
|
ma_uint8 crc8;
|
|
} ma_dr_flac_frame_header;
|
|
typedef struct
|
|
{
|
|
ma_dr_flac_frame_header header;
|
|
ma_uint32 pcmFramesRemaining;
|
|
ma_dr_flac_subframe subframes[8];
|
|
} ma_dr_flac_frame;
|
|
typedef struct
|
|
{
|
|
ma_dr_flac_meta_proc onMeta;
|
|
void* pUserDataMD;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_uint32 sampleRate;
|
|
ma_uint8 channels;
|
|
ma_uint8 bitsPerSample;
|
|
ma_uint16 maxBlockSizeInPCMFrames;
|
|
ma_uint64 totalPCMFrameCount;
|
|
ma_dr_flac_container container;
|
|
ma_uint32 seekpointCount;
|
|
ma_dr_flac_frame currentFLACFrame;
|
|
ma_uint64 currentPCMFrame;
|
|
ma_uint64 firstFLACFramePosInBytes;
|
|
ma_dr_flac__memory_stream memoryStream;
|
|
ma_int32* pDecodedSamples;
|
|
ma_dr_flac_seekpoint* pSeekpoints;
|
|
void* _oggbs;
|
|
ma_bool32 _noSeekTableSeek : 1;
|
|
ma_bool32 _noBinarySearchSeek : 1;
|
|
ma_bool32 _noBruteForceSeek : 1;
|
|
ma_dr_flac_bs bs;
|
|
ma_uint8 pExtraData[1];
|
|
} ma_dr_flac;
|
|
MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API void ma_dr_flac_close(ma_dr_flac* pFlac);
|
|
MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut);
|
|
MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex);
|
|
#ifndef MA_DR_FLAC_NO_STDIO
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#ifndef MA_DR_FLAC_NO_STDIO
|
|
MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
typedef struct
|
|
{
|
|
ma_uint32 countRemaining;
|
|
const char* pRunningData;
|
|
} ma_dr_flac_vorbis_comment_iterator;
|
|
MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments);
|
|
MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut);
|
|
typedef struct
|
|
{
|
|
ma_uint32 countRemaining;
|
|
const char* pRunningData;
|
|
} ma_dr_flac_cuesheet_track_iterator;
|
|
typedef struct
|
|
{
|
|
ma_uint64 offset;
|
|
ma_uint8 index;
|
|
ma_uint8 reserved[3];
|
|
} ma_dr_flac_cuesheet_track_index;
|
|
typedef struct
|
|
{
|
|
ma_uint64 offset;
|
|
ma_uint8 trackNumber;
|
|
char ISRC[12];
|
|
ma_bool8 isAudio;
|
|
ma_bool8 preEmphasis;
|
|
ma_uint8 indexCount;
|
|
const ma_dr_flac_cuesheet_track_index* pIndexPoints;
|
|
} ma_dr_flac_cuesheet_track;
|
|
MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData);
|
|
MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
|
|
#define MA_HAS_MP3
|
|
|
|
#ifndef MA_DR_MP3_NO_SIMD
|
|
#if (defined(MA_NO_NEON) && defined(MA_ARM)) || (defined(MA_NO_SSE2) && (defined(MA_X86) || defined(MA_X64)))
|
|
#define MA_DR_MP3_NO_SIMD
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef ma_dr_mp3_h
|
|
#define ma_dr_mp3_h
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
#define MA_DR_MP3_STRINGIFY(x) #x
|
|
#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x)
|
|
#define MA_DR_MP3_VERSION_MAJOR 0
|
|
#define MA_DR_MP3_VERSION_MINOR 7
|
|
#define MA_DR_MP3_VERSION_REVISION 3
|
|
#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION)
|
|
#include <stddef.h>
|
|
#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
|
|
#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
|
|
MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision);
|
|
MA_API const char* ma_dr_mp3_version_string(void);
|
|
#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511
|
|
#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304
|
|
#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE
|
|
typedef struct
|
|
{
|
|
int frame_bytes, channels, sample_rate, layer, bitrate_kbps;
|
|
} ma_dr_mp3dec_frame_info;
|
|
typedef struct
|
|
{
|
|
const ma_uint8 *buf;
|
|
int pos, limit;
|
|
} ma_dr_mp3_bs;
|
|
typedef struct
|
|
{
|
|
const ma_uint8 *sfbtab;
|
|
ma_uint16 part_23_length, big_values, scalefac_compress;
|
|
ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb;
|
|
ma_uint8 table_select[3], region_count[3], subblock_gain[3];
|
|
ma_uint8 preflag, scalefac_scale, count1_table, scfsi;
|
|
} ma_dr_mp3_L3_gr_info;
|
|
typedef struct
|
|
{
|
|
ma_dr_mp3_bs bs;
|
|
ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES];
|
|
ma_dr_mp3_L3_gr_info gr_info[4];
|
|
float grbuf[2][576], scf[40], syn[18 + 15][2*32];
|
|
ma_uint8 ist_pos[2][39];
|
|
} ma_dr_mp3dec_scratch;
|
|
typedef struct
|
|
{
|
|
float mdct_overlap[2][9*32], qmf_state[15*2*32];
|
|
int reserv, free_format_bytes;
|
|
ma_uint8 header[4], reserv_buf[511];
|
|
ma_dr_mp3dec_scratch scratch;
|
|
} ma_dr_mp3dec;
|
|
MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec);
|
|
MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info);
|
|
MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples);
|
|
typedef enum
|
|
{
|
|
MA_DR_MP3_SEEK_SET,
|
|
MA_DR_MP3_SEEK_CUR,
|
|
MA_DR_MP3_SEEK_END
|
|
} ma_dr_mp3_seek_origin;
|
|
typedef struct
|
|
{
|
|
ma_uint64 seekPosInBytes;
|
|
ma_uint64 pcmFrameIndex;
|
|
ma_uint16 mp3FramesToDiscard;
|
|
ma_uint16 pcmFramesToDiscard;
|
|
} ma_dr_mp3_seek_point;
|
|
typedef enum
|
|
{
|
|
MA_DR_MP3_METADATA_TYPE_ID3V1,
|
|
MA_DR_MP3_METADATA_TYPE_ID3V2,
|
|
MA_DR_MP3_METADATA_TYPE_APE,
|
|
MA_DR_MP3_METADATA_TYPE_XING,
|
|
MA_DR_MP3_METADATA_TYPE_VBRI
|
|
} ma_dr_mp3_metadata_type;
|
|
typedef struct
|
|
{
|
|
ma_dr_mp3_metadata_type type;
|
|
const void* pRawData;
|
|
size_t rawDataSize;
|
|
} ma_dr_mp3_metadata;
|
|
typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
|
|
typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin);
|
|
typedef ma_bool32 (* ma_dr_mp3_tell_proc)(void* pUserData, ma_int64* pCursor);
|
|
typedef void (* ma_dr_mp3_meta_proc)(void* pUserData, const ma_dr_mp3_metadata* pMetadata);
|
|
typedef struct
|
|
{
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
} ma_dr_mp3_config;
|
|
typedef struct
|
|
{
|
|
ma_dr_mp3dec decoder;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_dr_mp3_read_proc onRead;
|
|
ma_dr_mp3_seek_proc onSeek;
|
|
ma_dr_mp3_meta_proc onMeta;
|
|
void* pUserData;
|
|
void* pUserDataMeta;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_uint32 mp3FrameChannels;
|
|
ma_uint32 mp3FrameSampleRate;
|
|
ma_uint32 pcmFramesConsumedInMP3Frame;
|
|
ma_uint32 pcmFramesRemainingInMP3Frame;
|
|
ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME];
|
|
ma_uint64 currentPCMFrame;
|
|
ma_uint64 streamCursor;
|
|
ma_uint64 streamLength;
|
|
ma_uint64 streamStartOffset;
|
|
ma_dr_mp3_seek_point* pSeekPoints;
|
|
ma_uint32 seekPointCount;
|
|
ma_uint32 delayInPCMFrames;
|
|
ma_uint32 paddingInPCMFrames;
|
|
ma_uint64 totalPCMFrameCount;
|
|
ma_bool32 isVBR;
|
|
ma_bool32 isCBR;
|
|
size_t dataSize;
|
|
size_t dataCapacity;
|
|
size_t dataConsumed;
|
|
ma_uint8* pData;
|
|
ma_bool32 atEnd;
|
|
struct
|
|
{
|
|
const ma_uint8* pData;
|
|
size_t dataSize;
|
|
size_t currentReadPos;
|
|
} memory;
|
|
} ma_dr_mp3;
|
|
MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#ifndef MA_DR_MP3_NO_STDIO
|
|
MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3);
|
|
MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut);
|
|
MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut);
|
|
MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex);
|
|
MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3);
|
|
MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3);
|
|
MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount);
|
|
MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints);
|
|
MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints);
|
|
MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#ifndef MA_DR_MP3_NO_STDIO
|
|
MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#endif
|
|
MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef MA_NO_DECODING
|
|
|
|
static ma_result ma_decoder_read_bytes(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return pDecoder->onRead(pDecoder, pBufferOut, bytesToRead, pBytesRead);
|
|
}
|
|
|
|
static ma_result ma_decoder_seek_bytes(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return pDecoder->onSeek(pDecoder, byteOffset, origin);
|
|
}
|
|
|
|
static ma_result ma_decoder_tell_bytes(ma_decoder* pDecoder, ma_int64* pCursor)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pDecoder->onTell == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return pDecoder->onTell(pDecoder, pCursor);
|
|
}
|
|
|
|
MA_API ma_decoding_backend_config ma_decoding_backend_config_init(ma_format preferredFormat, ma_uint32 seekPointCount)
|
|
{
|
|
ma_decoding_backend_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.preferredFormat = preferredFormat;
|
|
config.seekPointCount = seekPointCount;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_decoder_config ma_decoder_config_init(ma_format outputFormat, ma_uint32 outputChannels, ma_uint32 outputSampleRate)
|
|
{
|
|
ma_decoder_config config;
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = outputFormat;
|
|
config.channels = outputChannels;
|
|
config.sampleRate = outputSampleRate;
|
|
config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear);
|
|
config.encodingFormat = ma_encoding_format_unknown;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_decoder_config ma_decoder_config_init_default(void)
|
|
{
|
|
return ma_decoder_config_init(ma_format_unknown, 0, 0);
|
|
}
|
|
|
|
MA_API ma_decoder_config ma_decoder_config_init_copy(const ma_decoder_config* pConfig)
|
|
{
|
|
ma_decoder_config config;
|
|
if (pConfig != NULL) {
|
|
config = *pConfig;
|
|
} else {
|
|
MA_ZERO_OBJECT(&config);
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_decoder__init_data_converter(ma_decoder* pDecoder, const ma_decoder_config* pConfig)
|
|
{
|
|
ma_result result;
|
|
ma_data_converter_config converterConfig;
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
ma_uint32 internalSampleRate;
|
|
ma_channel internalChannelMap[MA_MAX_CHANNELS];
|
|
|
|
MA_ASSERT(pDecoder != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, &internalSampleRate, internalChannelMap, ma_countof(internalChannelMap));
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->channels > MA_MAX_CHANNELS) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (internalChannels > MA_MAX_CHANNELS) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format == ma_format_unknown) {
|
|
pDecoder->outputFormat = internalFormat;
|
|
} else {
|
|
pDecoder->outputFormat = pConfig->format;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
pDecoder->outputChannels = internalChannels;
|
|
} else {
|
|
pDecoder->outputChannels = pConfig->channels;
|
|
}
|
|
|
|
if (pConfig->sampleRate == 0) {
|
|
pDecoder->outputSampleRate = internalSampleRate;
|
|
} else {
|
|
pDecoder->outputSampleRate = pConfig->sampleRate;
|
|
}
|
|
|
|
converterConfig = ma_data_converter_config_init(
|
|
internalFormat, pDecoder->outputFormat,
|
|
internalChannels, pDecoder->outputChannels,
|
|
internalSampleRate, pDecoder->outputSampleRate
|
|
);
|
|
converterConfig.pChannelMapIn = internalChannelMap;
|
|
converterConfig.pChannelMapOut = pConfig->pChannelMap;
|
|
converterConfig.channelMixMode = pConfig->channelMixMode;
|
|
converterConfig.ditherMode = pConfig->ditherMode;
|
|
converterConfig.allowDynamicSampleRate = MA_FALSE;
|
|
converterConfig.resampling = pConfig->resampling;
|
|
|
|
result = ma_data_converter_init(&converterConfig, &pDecoder->allocationCallbacks, &pDecoder->converter);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
{
|
|
ma_uint64 unused;
|
|
|
|
result = ma_data_converter_get_required_input_frame_count(&pDecoder->converter, 1, &unused);
|
|
if (result != MA_SUCCESS) {
|
|
ma_uint64 inputCacheCapSizeInBytes;
|
|
|
|
pDecoder->inputCacheCap = MA_DATA_CONVERTER_STACK_BUFFER_SIZE / ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
|
|
inputCacheCapSizeInBytes = pDecoder->inputCacheCap * ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
if (inputCacheCapSizeInBytes > MA_SIZE_MAX) {
|
|
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pDecoder->pInputCache = ma_malloc((size_t)inputCacheCapSizeInBytes, &pDecoder->allocationCallbacks);
|
|
if (pDecoder->pInputCache == NULL) {
|
|
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder_internal_on_read__custom(void* pUserData, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
|
|
{
|
|
ma_decoder* pDecoder = (ma_decoder*)pUserData;
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return ma_decoder_read_bytes(pDecoder, pBufferOut, bytesToRead, pBytesRead);
|
|
}
|
|
|
|
static ma_result ma_decoder_internal_on_seek__custom(void* pUserData, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
ma_decoder* pDecoder = (ma_decoder*)pUserData;
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return ma_decoder_seek_bytes(pDecoder, offset, origin);
|
|
}
|
|
|
|
static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_decoder* pDecoder = (ma_decoder*)pUserData;
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return ma_decoder_tell_bytes(pDecoder, pCursor);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoding_backend_config backendConfig;
|
|
ma_data_source* pBackend;
|
|
|
|
MA_ASSERT(pVTable != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pVTable->onInit == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
|
|
|
|
result = pVTable->onInit(pVTableUserData, ma_decoder_internal_on_read__custom, ma_decoder_internal_on_seek__custom, ma_decoder_internal_on_tell__custom, pDecoder, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->pBackend = pBackend;
|
|
pDecoder->pBackendVTable = pVTable;
|
|
pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoding_backend_config backendConfig;
|
|
ma_data_source* pBackend;
|
|
|
|
MA_ASSERT(pVTable != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pVTable->onInitFile == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
|
|
|
|
result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->pBackend = pBackend;
|
|
pDecoder->pBackendVTable = pVTable;
|
|
pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoding_backend_config backendConfig;
|
|
ma_data_source* pBackend;
|
|
|
|
MA_ASSERT(pVTable != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pVTable->onInitFileW == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
|
|
|
|
result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->pBackend = pBackend;
|
|
pDecoder->pBackendVTable = pVTable;
|
|
pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoding_backend_config backendConfig;
|
|
ma_data_source* pBackend;
|
|
|
|
MA_ASSERT(pVTable != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pVTable->onInitMemory == NULL) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount);
|
|
|
|
result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->pBackend = pBackend;
|
|
pDecoder->pBackendVTable = pVTable;
|
|
pDecoder->pBackendUserData = pConfig->pCustomBackendUserData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = MA_NO_BACKEND;
|
|
size_t ivtable;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pConfig->ppCustomBackendVTables == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
|
|
const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
|
|
if (pVTable != NULL) {
|
|
result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder);
|
|
if (result == MA_SUCCESS) {
|
|
return MA_SUCCESS;
|
|
} else {
|
|
result = ma_decoder_seek_bytes(pDecoder, 0, ma_seek_origin_start);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = MA_NO_BACKEND;
|
|
size_t ivtable;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pConfig->ppCustomBackendVTables == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
|
|
const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
|
|
if (pVTable != NULL) {
|
|
result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);
|
|
if (result == MA_SUCCESS) {
|
|
return MA_SUCCESS;
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = MA_NO_BACKEND;
|
|
size_t ivtable;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pConfig->ppCustomBackendVTables == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
|
|
const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
|
|
if (pVTable != NULL) {
|
|
result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder);
|
|
if (result == MA_SUCCESS) {
|
|
return MA_SUCCESS;
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = MA_NO_BACKEND;
|
|
size_t ivtable;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pConfig->ppCustomBackendVTables == NULL) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) {
|
|
const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable];
|
|
if (pVTable != NULL) {
|
|
result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder);
|
|
if (result == MA_SUCCESS) {
|
|
return MA_SUCCESS;
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
#ifdef ma_dr_wav_h
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_read_proc onRead;
|
|
ma_seek_proc onSeek;
|
|
ma_tell_proc onTell;
|
|
void* pReadSeekTellUserData;
|
|
ma_format format;
|
|
#if !defined(MA_NO_WAV)
|
|
ma_dr_wav dr;
|
|
#endif
|
|
} ma_wav;
|
|
|
|
MA_API ma_result ma_wav_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_wav* pWav);
|
|
MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);
|
|
MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);
|
|
MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav);
|
|
MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor);
|
|
MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength);
|
|
|
|
static ma_result ma_wav_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_wav_read_pcm_frames((ma_wav*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_wav_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_wav_seek_to_pcm_frame((ma_wav*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_wav_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_wav_get_data_format((ma_wav*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_wav_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_wav_get_cursor_in_pcm_frames((ma_wav*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_wav_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_wav_get_length_in_pcm_frames((ma_wav*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_wav_ds_vtable =
|
|
{
|
|
ma_wav_ds_read,
|
|
ma_wav_ds_seek,
|
|
ma_wav_ds_get_data_format,
|
|
ma_wav_ds_get_cursor,
|
|
ma_wav_ds_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
ma_wav* pWav = (ma_wav*)pUserData;
|
|
ma_result result;
|
|
size_t bytesRead;
|
|
|
|
MA_ASSERT(pWav != NULL);
|
|
|
|
result = pWav->onRead(pWav->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
|
|
(void)result;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
|
|
{
|
|
ma_wav* pWav = (ma_wav*)pUserData;
|
|
ma_result result;
|
|
ma_seek_origin maSeekOrigin;
|
|
|
|
MA_ASSERT(pWav != NULL);
|
|
|
|
maSeekOrigin = ma_seek_origin_start;
|
|
if (origin == MA_DR_WAV_SEEK_CUR) {
|
|
maSeekOrigin = ma_seek_origin_current;
|
|
} else if (origin == MA_DR_WAV_SEEK_END) {
|
|
maSeekOrigin = ma_seek_origin_end;
|
|
}
|
|
|
|
result = pWav->onSeek(pWav->pReadSeekTellUserData, offset, maSeekOrigin);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static ma_bool32 ma_wav_dr_callback__tell(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_wav* pWav = (ma_wav*)pUserData;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pWav != NULL);
|
|
MA_ASSERT(pCursor != NULL);
|
|
|
|
if (pWav->onTell == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
result = pWav->onTell(pWav->pReadSeekTellUserData, pCursor);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, ma_wav* pWav)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pWav);
|
|
pWav->format = ma_format_unknown;
|
|
|
|
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
|
|
pWav->format = pConfig->preferredFormat;
|
|
} else {
|
|
}
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_wav_ds_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pWav->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_wav_post_init(ma_wav* pWav)
|
|
{
|
|
if (pWav->format == ma_format_unknown) {
|
|
switch (pWav->dr.translatedFormatTag)
|
|
{
|
|
case MA_DR_WAVE_FORMAT_PCM:
|
|
{
|
|
if (pWav->dr.bitsPerSample == 8) {
|
|
pWav->format = ma_format_u8;
|
|
} else if (pWav->dr.bitsPerSample == 16) {
|
|
pWav->format = ma_format_s16;
|
|
} else if (pWav->dr.bitsPerSample == 24) {
|
|
pWav->format = ma_format_s24;
|
|
} else if (pWav->dr.bitsPerSample == 32) {
|
|
pWav->format = ma_format_s32;
|
|
}
|
|
} break;
|
|
|
|
case MA_DR_WAVE_FORMAT_IEEE_FLOAT:
|
|
{
|
|
if (pWav->dr.bitsPerSample == 32) {
|
|
pWav->format = ma_format_f32;
|
|
}
|
|
} break;
|
|
|
|
default: break;
|
|
}
|
|
|
|
if (pWav->format == ma_format_unknown) {
|
|
pWav->format = ma_format_f32;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_wav_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_wav* pWav)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_wav_init_internal(pConfig, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (onRead == NULL || onSeek == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWav->onRead = onRead;
|
|
pWav->onSeek = onSeek;
|
|
pWav->onTell = onTell;
|
|
pWav->pReadSeekTellUserData = pReadSeekTellUserData;
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_bool32 wavResult;
|
|
|
|
wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, ma_wav_dr_callback__tell, pWav, pAllocationCallbacks);
|
|
if (wavResult != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_wav_post_init(pWav);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_wav_init_internal(pConfig, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_bool32 wavResult;
|
|
|
|
wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks);
|
|
if (wavResult != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_wav_post_init(pWav);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_wav_init_internal(pConfig, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_bool32 wavResult;
|
|
|
|
wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks);
|
|
if (wavResult != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_wav_post_init(pWav);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_wav_init_internal(pConfig, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_bool32 wavResult;
|
|
|
|
wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks);
|
|
if (wavResult != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_wav_post_init(pWav);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pData;
|
|
(void)dataSize;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pWav == NULL) {
|
|
return;
|
|
}
|
|
|
|
(void)pAllocationCallbacks;
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_dr_wav_uninit(&pWav->dr);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
|
|
ma_data_source_uninit(&pWav->ds);
|
|
}
|
|
|
|
MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_format format;
|
|
|
|
ma_wav_get_data_format(pWav, &format, NULL, NULL, NULL, 0);
|
|
|
|
switch (format)
|
|
{
|
|
case ma_format_f32:
|
|
{
|
|
totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_unknown: return MA_INVALID_OPERATION;
|
|
default:
|
|
{
|
|
totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut);
|
|
} break;
|
|
}
|
|
|
|
if (totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)pFramesOut;
|
|
(void)frameCount;
|
|
(void)pFramesRead;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex)
|
|
{
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_bool32 wavResult;
|
|
|
|
wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex);
|
|
if (wavResult != MA_TRUE) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)frameIndex;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_get_data_format(ma_wav* pWav, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_unknown;
|
|
}
|
|
if (pChannels != NULL) {
|
|
*pChannels = 0;
|
|
}
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = 0;
|
|
}
|
|
if (pChannelMap != NULL) {
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
|
|
}
|
|
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pWav->format;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
if (pChannels != NULL) {
|
|
*pChannels = pWav->dr.channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pWav->dr.sampleRate;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pWav->dr.channels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor);
|
|
if (wavResult != MA_SUCCESS) {
|
|
return (ma_result)wavResult;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_WAV)
|
|
{
|
|
ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength);
|
|
if (wavResult != MA_SUCCESS) {
|
|
return (ma_result)wavResult;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init__wav(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_wav* pWav;
|
|
|
|
(void)pUserData;
|
|
|
|
pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
|
|
if (pWav == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_wav_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pWav, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pWav;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_file__wav(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_wav* pWav;
|
|
|
|
(void)pUserData;
|
|
|
|
pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
|
|
if (pWav == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_wav_init_file(pFilePath, pConfig, pAllocationCallbacks, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pWav, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pWav;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_file_w__wav(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_wav* pWav;
|
|
|
|
(void)pUserData;
|
|
|
|
pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
|
|
if (pWav == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_wav_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pWav, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pWav;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_memory__wav(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_wav* pWav;
|
|
|
|
(void)pUserData;
|
|
|
|
pWav = (ma_wav*)ma_malloc(sizeof(*pWav), pAllocationCallbacks);
|
|
if (pWav == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_wav_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pWav);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pWav, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pWav;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_decoding_backend_uninit__wav(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_wav* pWav = (ma_wav*)pBackend;
|
|
|
|
(void)pUserData;
|
|
|
|
ma_wav_uninit(pWav, pAllocationCallbacks);
|
|
ma_free(pWav, pAllocationCallbacks);
|
|
}
|
|
|
|
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav =
|
|
{
|
|
ma_decoding_backend_init__wav,
|
|
ma_decoding_backend_init_file__wav,
|
|
ma_decoding_backend_init_file_w__wav,
|
|
ma_decoding_backend_init_memory__wav,
|
|
ma_decoding_backend_uninit__wav
|
|
};
|
|
|
|
static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ma_dr_flac_h
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_read_proc onRead;
|
|
ma_seek_proc onSeek;
|
|
ma_tell_proc onTell;
|
|
void* pReadSeekTellUserData;
|
|
ma_format format;
|
|
#if !defined(MA_NO_FLAC)
|
|
ma_dr_flac* dr;
|
|
#endif
|
|
} ma_flac;
|
|
|
|
MA_API ma_result ma_flac_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_flac* pFlac);
|
|
MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);
|
|
MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);
|
|
MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac);
|
|
MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor);
|
|
MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength);
|
|
|
|
static ma_result ma_flac_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_flac_read_pcm_frames((ma_flac*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_flac_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_flac_seek_to_pcm_frame((ma_flac*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_flac_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_flac_get_data_format((ma_flac*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_flac_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_flac_get_cursor_in_pcm_frames((ma_flac*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_flac_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_flac_get_length_in_pcm_frames((ma_flac*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_flac_ds_vtable =
|
|
{
|
|
ma_flac_ds_read,
|
|
ma_flac_ds_seek,
|
|
ma_flac_ds_get_data_format,
|
|
ma_flac_ds_get_cursor,
|
|
ma_flac_ds_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
ma_flac* pFlac = (ma_flac*)pUserData;
|
|
ma_result result;
|
|
size_t bytesRead;
|
|
|
|
MA_ASSERT(pFlac != NULL);
|
|
|
|
result = pFlac->onRead(pFlac->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
|
|
(void)result;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
|
|
{
|
|
ma_flac* pFlac = (ma_flac*)pUserData;
|
|
ma_result result;
|
|
ma_seek_origin maSeekOrigin;
|
|
|
|
MA_ASSERT(pFlac != NULL);
|
|
|
|
maSeekOrigin = ma_seek_origin_start;
|
|
if (origin == MA_DR_FLAC_SEEK_CUR) {
|
|
maSeekOrigin = ma_seek_origin_current;
|
|
} else if (origin == MA_DR_FLAC_SEEK_END) {
|
|
maSeekOrigin = ma_seek_origin_end;
|
|
}
|
|
|
|
result = pFlac->onSeek(pFlac->pReadSeekTellUserData, offset, maSeekOrigin);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static ma_bool32 ma_flac_dr_callback__tell(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_flac* pFlac = (ma_flac*)pUserData;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pFlac != NULL);
|
|
MA_ASSERT(pCursor != NULL);
|
|
|
|
if (pFlac->onTell == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
result = pFlac->onTell(pFlac->pReadSeekTellUserData, pCursor);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_flac_init_internal(const ma_decoding_backend_config* pConfig, ma_flac* pFlac)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
if (pFlac == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pFlac);
|
|
pFlac->format = ma_format_f32;
|
|
|
|
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16 || pConfig->preferredFormat == ma_format_s32)) {
|
|
pFlac->format = pConfig->preferredFormat;
|
|
} else {
|
|
}
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_flac_ds_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pFlac->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_flac_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_flac* pFlac)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_flac_init_internal(pConfig, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (onRead == NULL || onSeek == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pFlac->onRead = onRead;
|
|
pFlac->onSeek = onSeek;
|
|
pFlac->onTell = onTell;
|
|
pFlac->pReadSeekTellUserData = pReadSeekTellUserData;
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, ma_flac_dr_callback__tell, pFlac, pAllocationCallbacks);
|
|
if (pFlac->dr == NULL) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_flac_init_internal(pConfig, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks);
|
|
if (pFlac->dr == NULL) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_flac_init_internal(pConfig, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks);
|
|
if (pFlac->dr == NULL) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_flac* pFlac)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_flac_init_internal(pConfig, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks);
|
|
if (pFlac->dr == NULL) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pData;
|
|
(void)dataSize;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFlac == NULL) {
|
|
return;
|
|
}
|
|
|
|
(void)pAllocationCallbacks;
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
ma_dr_flac_close(pFlac->dr);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
|
|
ma_data_source_uninit(&pFlac->ds);
|
|
}
|
|
|
|
MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFlac == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_format format;
|
|
|
|
ma_flac_get_data_format(pFlac, &format, NULL, NULL, NULL, 0);
|
|
|
|
switch (format)
|
|
{
|
|
case ma_format_f32:
|
|
{
|
|
totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_s32:
|
|
{
|
|
totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_u8:
|
|
case ma_format_s24:
|
|
case ma_format_unknown:
|
|
default:
|
|
{
|
|
return MA_INVALID_OPERATION;
|
|
};
|
|
}
|
|
|
|
if (totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)pFramesOut;
|
|
(void)frameCount;
|
|
(void)pFramesRead;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex)
|
|
{
|
|
if (pFlac == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
ma_bool32 flacResult;
|
|
|
|
flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex);
|
|
if (flacResult != MA_TRUE) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)frameIndex;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_get_data_format(ma_flac* pFlac, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_unknown;
|
|
}
|
|
if (pChannels != NULL) {
|
|
*pChannels = 0;
|
|
}
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = 0;
|
|
}
|
|
if (pChannelMap != NULL) {
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
|
|
}
|
|
|
|
if (pFlac == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pFlac->format;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
if (pChannels != NULL) {
|
|
*pChannels = pFlac->dr->channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pFlac->dr->sampleRate;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pChannelMap, channelMapCap, pFlac->dr->channels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_get_cursor_in_pcm_frames(ma_flac* pFlac, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pFlac == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
*pCursor = pFlac->dr->currentPCMFrame;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_flac_get_length_in_pcm_frames(ma_flac* pFlac, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pFlac == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_FLAC)
|
|
{
|
|
*pLength = pFlac->dr->totalPCMFrameCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init__flac(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_flac* pFlac;
|
|
|
|
(void)pUserData;
|
|
|
|
pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_flac_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pFlac, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pFlac;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_file__flac(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_flac* pFlac;
|
|
|
|
(void)pUserData;
|
|
|
|
pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_flac_init_file(pFilePath, pConfig, pAllocationCallbacks, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pFlac, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pFlac;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_file_w__flac(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_flac* pFlac;
|
|
|
|
(void)pUserData;
|
|
|
|
pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_flac_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pFlac, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pFlac;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_memory__flac(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_flac* pFlac;
|
|
|
|
(void)pUserData;
|
|
|
|
pFlac = (ma_flac*)ma_malloc(sizeof(*pFlac), pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_flac_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pFlac, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pFlac;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_decoding_backend_uninit__flac(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_flac* pFlac = (ma_flac*)pBackend;
|
|
|
|
(void)pUserData;
|
|
|
|
ma_flac_uninit(pFlac, pAllocationCallbacks);
|
|
ma_free(pFlac, pAllocationCallbacks);
|
|
}
|
|
|
|
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac =
|
|
{
|
|
ma_decoding_backend_init__flac,
|
|
ma_decoding_backend_init_file__flac,
|
|
ma_decoding_backend_init_file_w__flac,
|
|
ma_decoding_backend_init_memory__flac,
|
|
ma_decoding_backend_uninit__flac
|
|
};
|
|
|
|
static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ma_dr_mp3_h
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_read_proc onRead;
|
|
ma_seek_proc onSeek;
|
|
ma_tell_proc onTell;
|
|
void* pReadSeekTellUserData;
|
|
ma_format format;
|
|
#if !defined(MA_NO_MP3)
|
|
ma_dr_mp3 dr;
|
|
ma_uint32 seekPointCount;
|
|
ma_dr_mp3_seek_point* pSeekPoints;
|
|
#endif
|
|
} ma_mp3;
|
|
|
|
MA_API ma_result ma_mp3_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_mp3* pMP3);
|
|
MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);
|
|
MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);
|
|
MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3);
|
|
MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor);
|
|
MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength);
|
|
|
|
static ma_result ma_mp3_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_mp3_read_pcm_frames((ma_mp3*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_mp3_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_mp3_seek_to_pcm_frame((ma_mp3*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_mp3_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_mp3_get_data_format((ma_mp3*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_mp3_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_mp3_get_cursor_in_pcm_frames((ma_mp3*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_mp3_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_mp3_get_length_in_pcm_frames((ma_mp3*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_mp3_ds_vtable =
|
|
{
|
|
ma_mp3_ds_read,
|
|
ma_mp3_ds_seek,
|
|
ma_mp3_ds_get_data_format,
|
|
ma_mp3_ds_get_cursor,
|
|
ma_mp3_ds_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
ma_mp3* pMP3 = (ma_mp3*)pUserData;
|
|
ma_result result;
|
|
size_t bytesRead;
|
|
|
|
MA_ASSERT(pMP3 != NULL);
|
|
|
|
result = pMP3->onRead(pMP3->pReadSeekTellUserData, pBufferOut, bytesToRead, &bytesRead);
|
|
(void)result;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)
|
|
{
|
|
ma_mp3* pMP3 = (ma_mp3*)pUserData;
|
|
ma_result result;
|
|
ma_seek_origin maSeekOrigin;
|
|
|
|
MA_ASSERT(pMP3 != NULL);
|
|
|
|
if (origin == MA_DR_MP3_SEEK_SET) {
|
|
maSeekOrigin = ma_seek_origin_start;
|
|
} else if (origin == MA_DR_MP3_SEEK_END) {
|
|
maSeekOrigin = ma_seek_origin_end;
|
|
} else {
|
|
maSeekOrigin = ma_seek_origin_current;
|
|
}
|
|
|
|
result = pMP3->onSeek(pMP3->pReadSeekTellUserData, offset, maSeekOrigin);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
|
|
static ma_bool32 ma_mp3_dr_callback__tell(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_mp3* pMP3 = (ma_mp3*)pUserData;
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pMP3 != NULL);
|
|
|
|
result = pMP3->onTell(pMP3->pReadSeekTellUserData, pCursor);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, ma_mp3* pMP3)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
if (pMP3 == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pMP3);
|
|
pMP3->format = ma_format_f32;
|
|
|
|
if (pConfig != NULL && (pConfig->preferredFormat == ma_format_f32 || pConfig->preferredFormat == ma_format_s16)) {
|
|
pMP3->format = pConfig->preferredFormat;
|
|
} else {
|
|
}
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_mp3_ds_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pMP3->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bool32 mp3Result;
|
|
ma_uint32 seekPointCount = 0;
|
|
ma_dr_mp3_seek_point* pSeekPoints = NULL;
|
|
|
|
MA_ASSERT(pMP3 != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
seekPointCount = pConfig->seekPointCount;
|
|
if (seekPointCount > 0) {
|
|
pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks);
|
|
if (pSeekPoints == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints);
|
|
if (mp3Result != MA_TRUE) {
|
|
ma_free(pSeekPoints, pAllocationCallbacks);
|
|
return MA_ERROR;
|
|
}
|
|
|
|
mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints);
|
|
if (mp3Result != MA_TRUE) {
|
|
ma_free(pSeekPoints, pAllocationCallbacks);
|
|
return MA_ERROR;
|
|
}
|
|
|
|
pMP3->seekPointCount = seekPointCount;
|
|
pMP3->pSeekPoints = pSeekPoints;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_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_mp3* pMP3)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_mp3_init_internal(pConfig, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (onRead == NULL || onSeek == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pMP3->onRead = onRead;
|
|
pMP3->onSeek = onSeek;
|
|
pMP3->onTell = onTell;
|
|
pMP3->pReadSeekTellUserData = pReadSeekTellUserData;
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_bool32 mp3Result;
|
|
|
|
mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, ma_mp3_dr_callback__tell, NULL, pMP3, pAllocationCallbacks);
|
|
if (mp3Result != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_mp3_init_internal(pConfig, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_bool32 mp3Result;
|
|
|
|
mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks);
|
|
if (mp3Result != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_mp3_init_internal(pConfig, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_bool32 mp3Result;
|
|
|
|
mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks);
|
|
if (mp3Result != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_mp3_init_internal(pConfig, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_bool32 mp3Result;
|
|
|
|
mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks);
|
|
if (mp3Result != MA_TRUE) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pData;
|
|
(void)dataSize;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pMP3 == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_dr_mp3_uninit(&pMP3->dr);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
|
|
ma_free(pMP3->pSeekPoints, pAllocationCallbacks);
|
|
|
|
ma_data_source_uninit(&pMP3->ds);
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pMP3 == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_format format;
|
|
|
|
ma_mp3_get_data_format(pMP3, &format, NULL, NULL, NULL, 0);
|
|
|
|
switch (format)
|
|
{
|
|
case ma_format_f32:
|
|
{
|
|
totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_s16:
|
|
{
|
|
totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut);
|
|
} break;
|
|
|
|
case ma_format_u8:
|
|
case ma_format_s24:
|
|
case ma_format_s32:
|
|
case ma_format_unknown:
|
|
default:
|
|
{
|
|
return MA_INVALID_OPERATION;
|
|
};
|
|
}
|
|
|
|
if (totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)pFramesOut;
|
|
(void)frameCount;
|
|
(void)pFramesRead;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex)
|
|
{
|
|
if (pMP3 == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
ma_bool32 mp3Result;
|
|
|
|
mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex);
|
|
if (mp3Result != MA_TRUE) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)frameIndex;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_get_data_format(ma_mp3* pMP3, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_unknown;
|
|
}
|
|
if (pChannels != NULL) {
|
|
*pChannels = 0;
|
|
}
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = 0;
|
|
}
|
|
if (pChannelMap != NULL) {
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
|
|
}
|
|
|
|
if (pMP3 == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pMP3->format;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
if (pChannels != NULL) {
|
|
*pChannels = pMP3->dr.channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pMP3->dr.sampleRate;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pMP3->dr.channels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_get_cursor_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pMP3 == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
*pCursor = pMP3->dr.currentPCMFrame;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pMP3 == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_MP3)
|
|
{
|
|
*pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init__mp3(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_mp3* pMP3;
|
|
|
|
(void)pUserData;
|
|
|
|
pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
|
|
if (pMP3 == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_mp3_init(onRead, onSeek, onTell, pReadSeekTellUserData, pConfig, pAllocationCallbacks, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pMP3, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pMP3;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_file__mp3(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_mp3* pMP3;
|
|
|
|
(void)pUserData;
|
|
|
|
pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
|
|
if (pMP3 == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_mp3_init_file(pFilePath, pConfig, pAllocationCallbacks, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pMP3, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pMP3;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_file_w__mp3(void* pUserData, const wchar_t* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_mp3* pMP3;
|
|
|
|
(void)pUserData;
|
|
|
|
pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
|
|
if (pMP3 == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_mp3_init_file_w(pFilePath, pConfig, pAllocationCallbacks, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pMP3, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pMP3;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init_memory__mp3(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_mp3* pMP3;
|
|
|
|
(void)pUserData;
|
|
|
|
pMP3 = (ma_mp3*)ma_malloc(sizeof(*pMP3), pAllocationCallbacks);
|
|
if (pMP3 == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_mp3_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pMP3);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pMP3, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pMP3;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_decoding_backend_uninit__mp3(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_mp3* pMP3 = (ma_mp3*)pBackend;
|
|
|
|
(void)pUserData;
|
|
|
|
ma_mp3_uninit(pMP3, pAllocationCallbacks);
|
|
ma_free(pMP3, pAllocationCallbacks);
|
|
}
|
|
|
|
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 =
|
|
{
|
|
ma_decoding_backend_init__mp3,
|
|
ma_decoding_backend_init_file__mp3,
|
|
ma_decoding_backend_init_file_w__mp3,
|
|
ma_decoding_backend_init_memory__mp3,
|
|
ma_decoding_backend_uninit__mp3
|
|
};
|
|
|
|
static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
#ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H
|
|
#define MA_HAS_VORBIS
|
|
|
|
#define MA_VORBIS_DATA_CHUNK_SIZE 4096
|
|
|
|
typedef struct
|
|
{
|
|
ma_data_source_base ds;
|
|
ma_read_proc onRead;
|
|
ma_seek_proc onSeek;
|
|
ma_tell_proc onTell;
|
|
void* pReadSeekTellUserData;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 sampleRate;
|
|
ma_uint64 cursor;
|
|
#if !defined(MA_NO_VORBIS)
|
|
stb_vorbis* stb;
|
|
ma_bool32 usingPushMode;
|
|
struct
|
|
{
|
|
ma_uint8* pData;
|
|
size_t dataSize;
|
|
size_t dataCapacity;
|
|
size_t audioStartOffsetInBytes;
|
|
ma_uint32 framesConsumed;
|
|
ma_uint32 framesRemaining;
|
|
float** ppPacketData;
|
|
} push;
|
|
#endif
|
|
} ma_stbvorbis;
|
|
|
|
MA_API ma_result ma_stbvorbis_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_stbvorbis* pVorbis);
|
|
MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
|
|
MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis);
|
|
MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks);
|
|
MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* pVorbis, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead);
|
|
MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex);
|
|
MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap);
|
|
MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor);
|
|
MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength);
|
|
|
|
static ma_result ma_stbvorbis_ds_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_stbvorbis_read_pcm_frames((ma_stbvorbis*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_stbvorbis_ds_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_stbvorbis_seek_to_pcm_frame((ma_stbvorbis*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_stbvorbis_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_stbvorbis_get_data_format((ma_stbvorbis*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_stbvorbis_ds_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_stbvorbis_get_cursor_in_pcm_frames((ma_stbvorbis*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_stbvorbis_ds_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_stbvorbis_get_length_in_pcm_frames((ma_stbvorbis*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_stbvorbis_ds_vtable =
|
|
{
|
|
ma_stbvorbis_ds_read,
|
|
ma_stbvorbis_ds_seek,
|
|
ma_stbvorbis_ds_get_data_format,
|
|
ma_stbvorbis_ds_get_cursor,
|
|
ma_stbvorbis_ds_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
static ma_result ma_stbvorbis_init_internal(const ma_decoding_backend_config* pConfig, ma_stbvorbis* pVorbis)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
(void)pConfig;
|
|
|
|
if (pVorbis == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pVorbis);
|
|
pVorbis->format = ma_format_f32;
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_stbvorbis_ds_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pVorbis->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis)
|
|
{
|
|
stb_vorbis_info info;
|
|
|
|
MA_ASSERT(pVorbis != NULL);
|
|
|
|
info = stb_vorbis_get_info(pVorbis->stb);
|
|
|
|
pVorbis->channels = info.channels;
|
|
pVorbis->sampleRate = info.sample_rate;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis)
|
|
{
|
|
ma_result result;
|
|
stb_vorbis* stb;
|
|
size_t dataSize = 0;
|
|
size_t dataCapacity = 0;
|
|
ma_uint8* pData = NULL;
|
|
|
|
for (;;) {
|
|
int vorbisError;
|
|
int consumedDataSize;
|
|
size_t bytesRead;
|
|
ma_uint8* pNewData;
|
|
|
|
dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE;
|
|
pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks);
|
|
if (pNewData == NULL) {
|
|
ma_free(pData, &pVorbis->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
pData = pNewData;
|
|
|
|
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead);
|
|
dataSize += bytesRead;
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pData, &pVorbis->allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if (dataSize > INT_MAX) {
|
|
ma_free(pData, &pVorbis->allocationCallbacks);
|
|
return MA_TOO_BIG;
|
|
}
|
|
|
|
stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL);
|
|
if (stb != NULL) {
|
|
dataSize -= (size_t)consumedDataSize;
|
|
MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize);
|
|
|
|
pVorbis->push.audioStartOffsetInBytes = consumedDataSize;
|
|
|
|
break;
|
|
} else {
|
|
if (vorbisError == VORBIS_need_more_data) {
|
|
continue;
|
|
} else {
|
|
ma_free(pData, &pVorbis->allocationCallbacks);
|
|
return MA_ERROR;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(stb != NULL);
|
|
pVorbis->stb = stb;
|
|
pVorbis->push.pData = pData;
|
|
pVorbis->push.dataSize = dataSize;
|
|
pVorbis->push.dataCapacity = dataCapacity;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_result ma_stbvorbis_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_stbvorbis* pVorbis)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_stbvorbis_init_internal(pConfig, pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (onRead == NULL || onSeek == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pVorbis->onRead = onRead;
|
|
pVorbis->onSeek = onSeek;
|
|
pVorbis->onTell = onTell;
|
|
pVorbis->pReadSeekTellUserData = pReadSeekTellUserData;
|
|
ma_allocation_callbacks_init_copy(&pVorbis->allocationCallbacks, pAllocationCallbacks);
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pVorbis->usingPushMode = MA_TRUE;
|
|
|
|
result = ma_stbvorbis_post_init(pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
stb_vorbis_close(pVorbis->stb);
|
|
ma_free(pVorbis->push.pData, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_init_file(const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_stbvorbis_init_internal(pConfig, pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
(void)pAllocationCallbacks;
|
|
|
|
pVorbis->stb = stb_vorbis_open_filename(pFilePath, NULL, NULL);
|
|
if (pVorbis->stb == NULL) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
pVorbis->usingPushMode = MA_FALSE;
|
|
|
|
result = ma_stbvorbis_post_init(pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
stb_vorbis_close(pVorbis->stb);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pFilePath;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_init_memory(const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_stbvorbis_init_internal(pConfig, pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
(void)pAllocationCallbacks;
|
|
|
|
if (dataSize > INT_MAX) {
|
|
return MA_TOO_BIG;
|
|
}
|
|
|
|
pVorbis->stb = stb_vorbis_open_memory((const unsigned char*)pData, (int)dataSize, NULL, NULL);
|
|
if (pVorbis->stb == NULL) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
|
|
pVorbis->usingPushMode = MA_FALSE;
|
|
|
|
result = ma_stbvorbis_post_init(pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
stb_vorbis_close(pVorbis->stb);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
(void)pData;
|
|
(void)dataSize;
|
|
(void)pAllocationCallbacks;
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API void ma_stbvorbis_uninit(ma_stbvorbis* pVorbis, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pVorbis == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
stb_vorbis_close(pVorbis->stb);
|
|
|
|
if (pVorbis->usingPushMode) {
|
|
ma_free(pVorbis->push.pData, pAllocationCallbacks);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
|
|
ma_data_source_uninit(&pVorbis->ds);
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_read_pcm_frames(ma_stbvorbis* 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_VORBIS)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
|
|
ma_stbvorbis_get_data_format(pVorbis, &format, &channels, NULL, NULL, 0);
|
|
|
|
if (format == ma_format_f32) {
|
|
if (pVorbis->usingPushMode) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
|
|
while (totalFramesRead < frameCount) {
|
|
ma_uint32 framesToReadFromCache = (ma_uint32)ma_min(pVorbis->push.framesRemaining, (frameCount - totalFramesRead));
|
|
|
|
if (pFramesOut != NULL) {
|
|
ma_uint64 iFrame;
|
|
for (iFrame = 0; iFrame < framesToReadFromCache; iFrame += 1) {
|
|
ma_uint32 iChannel;
|
|
for (iChannel = 0; iChannel < pVorbis->channels; iChannel += 1) {
|
|
pFramesOutF32[iChannel] = pVorbis->push.ppPacketData[iChannel][pVorbis->push.framesConsumed + iFrame];
|
|
}
|
|
|
|
pFramesOutF32 += pVorbis->channels;
|
|
}
|
|
}
|
|
|
|
pVorbis->push.framesConsumed += framesToReadFromCache;
|
|
pVorbis->push.framesRemaining -= framesToReadFromCache;
|
|
totalFramesRead += framesToReadFromCache;
|
|
|
|
if (totalFramesRead == frameCount) {
|
|
break;
|
|
}
|
|
|
|
MA_ASSERT(pVorbis->push.framesRemaining == 0);
|
|
|
|
for (;;) {
|
|
int samplesRead = 0;
|
|
int consumedDataSize;
|
|
|
|
if (pVorbis->push.dataSize > INT_MAX) {
|
|
break;
|
|
}
|
|
|
|
consumedDataSize = stb_vorbis_decode_frame_pushdata(pVorbis->stb, pVorbis->push.pData, (int)pVorbis->push.dataSize, NULL, &pVorbis->push.ppPacketData, &samplesRead);
|
|
if (consumedDataSize != 0) {
|
|
pVorbis->push.dataSize -= (size_t)consumedDataSize;
|
|
MA_MOVE_MEMORY(pVorbis->push.pData, ma_offset_ptr(pVorbis->push.pData, consumedDataSize), pVorbis->push.dataSize);
|
|
|
|
pVorbis->push.framesConsumed = 0;
|
|
pVorbis->push.framesRemaining = samplesRead;
|
|
|
|
break;
|
|
} else {
|
|
size_t bytesRead;
|
|
|
|
if (pVorbis->push.dataCapacity == pVorbis->push.dataSize) {
|
|
size_t newCap = pVorbis->push.dataCapacity + MA_VORBIS_DATA_CHUNK_SIZE;
|
|
ma_uint8* pNewData;
|
|
|
|
pNewData = (ma_uint8*)ma_realloc(pVorbis->push.pData, newCap, &pVorbis->allocationCallbacks);
|
|
if (pNewData == NULL) {
|
|
result = MA_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
|
|
pVorbis->push.pData = pNewData;
|
|
pVorbis->push.dataCapacity = newCap;
|
|
}
|
|
|
|
result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pVorbis->push.pData, pVorbis->push.dataSize), (pVorbis->push.dataCapacity - pVorbis->push.dataSize), &bytesRead);
|
|
pVorbis->push.dataSize += bytesRead;
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
while (totalFramesRead < frameCount) {
|
|
ma_uint64 framesRemaining = (frameCount - totalFramesRead);
|
|
int framesRead;
|
|
|
|
if (framesRemaining > INT_MAX) {
|
|
framesRemaining = INT_MAX;
|
|
}
|
|
|
|
framesRead = stb_vorbis_get_samples_float_interleaved(pVorbis->stb, channels, (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, format, channels), (int)framesRemaining * channels);
|
|
totalFramesRead += framesRead;
|
|
|
|
if (framesRead < (int)framesRemaining) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
result = MA_INVALID_ARGS;
|
|
}
|
|
|
|
pVorbis->cursor += totalFramesRead;
|
|
|
|
if (totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && totalFramesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)pFramesOut;
|
|
(void)frameCount;
|
|
(void)pFramesRead;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 frameIndex)
|
|
{
|
|
if (pVorbis == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
if (pVorbis->usingPushMode) {
|
|
ma_result result;
|
|
float buffer[4096];
|
|
|
|
if (frameIndex < pVorbis->cursor) {
|
|
if (frameIndex > 0x7FFFFFFF) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
stb_vorbis_close(pVorbis->stb);
|
|
ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks);
|
|
|
|
MA_ZERO_OBJECT(&pVorbis->push);
|
|
|
|
result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_stbvorbis_init_internal_decoder_push(pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pVorbis->cursor = 0;
|
|
}
|
|
|
|
while (pVorbis->cursor < frameIndex) {
|
|
ma_uint64 framesRead;
|
|
ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels;
|
|
if (framesToRead > (frameIndex - pVorbis->cursor)) {
|
|
framesToRead = (frameIndex - pVorbis->cursor);
|
|
}
|
|
|
|
result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
} else {
|
|
int vorbisResult;
|
|
|
|
if (frameIndex > UINT_MAX) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
vorbisResult = stb_vorbis_seek(pVorbis->stb, (unsigned int)frameIndex);
|
|
if (vorbisResult == 0) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
pVorbis->cursor = frameIndex;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
|
|
(void)frameIndex;
|
|
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_get_data_format(ma_stbvorbis* pVorbis, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_unknown;
|
|
}
|
|
if (pChannels != NULL) {
|
|
*pChannels = 0;
|
|
}
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = 0;
|
|
}
|
|
if (pChannelMap != NULL) {
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
|
|
}
|
|
|
|
if (pVorbis == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pVorbis->format;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
if (pChannels != NULL) {
|
|
*pChannels = pVorbis->channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pVorbis->sampleRate;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_vorbis, pChannelMap, channelMapCap, pVorbis->channels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_get_cursor_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pVorbis == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
*pCursor = pVorbis->cursor;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_stbvorbis_get_length_in_pcm_frames(ma_stbvorbis* pVorbis, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pVorbis == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_VORBIS)
|
|
{
|
|
if (pVorbis->usingPushMode) {
|
|
*pLength = 0;
|
|
} else {
|
|
*pLength = stb_vorbis_stream_length_in_samples(pVorbis->stb);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_result ma_decoding_backend_init__stbvorbis(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_stbvorbis* pVorbis;
|
|
|
|
(void)pUserData;
|
|
|
|
pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
|
if (pVorbis == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_stbvorbis_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__stbvorbis(void* pUserData, const char* pFilePath, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_stbvorbis* pVorbis;
|
|
|
|
(void)pUserData;
|
|
|
|
pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
|
if (pVorbis == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_stbvorbis_init_file(pFilePath, 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_memory__stbvorbis(void* pUserData, const void* pData, size_t dataSize, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source** ppBackend)
|
|
{
|
|
ma_result result;
|
|
ma_stbvorbis* pVorbis;
|
|
|
|
(void)pUserData;
|
|
|
|
pVorbis = (ma_stbvorbis*)ma_malloc(sizeof(*pVorbis), pAllocationCallbacks);
|
|
if (pVorbis == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_stbvorbis_init_memory(pData, dataSize, pConfig, pAllocationCallbacks, pVorbis);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pVorbis, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
*ppBackend = pVorbis;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_decoding_backend_uninit__stbvorbis(void* pUserData, ma_data_source* pBackend, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_stbvorbis* pVorbis = (ma_stbvorbis*)pBackend;
|
|
|
|
(void)pUserData;
|
|
|
|
ma_stbvorbis_uninit(pVorbis, pAllocationCallbacks);
|
|
ma_free(pVorbis, pAllocationCallbacks);
|
|
}
|
|
|
|
static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis =
|
|
{
|
|
ma_decoding_backend_init__stbvorbis,
|
|
ma_decoding_backend_init_file__stbvorbis,
|
|
NULL,
|
|
ma_decoding_backend_init_memory__stbvorbis,
|
|
ma_decoding_backend_uninit__stbvorbis
|
|
};
|
|
|
|
static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_decoder__init_allocation_callbacks(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
if (pConfig != NULL) {
|
|
return ma_allocation_callbacks_init_copy(&pDecoder->allocationCallbacks, &pConfig->allocationCallbacks);
|
|
} else {
|
|
pDecoder->allocationCallbacks = ma_allocation_callbacks_init_default();
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_decoder__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_decoder_read_pcm_frames((ma_decoder*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_decoder__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_decoder_seek_to_pcm_frame((ma_decoder*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_decoder__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
return ma_decoder_get_data_format((ma_decoder*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_decoder__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_decoder_get_cursor_in_pcm_frames((ma_decoder*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_decoder__data_source_on_get_length(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_decoder_get_length_in_pcm_frames((ma_decoder*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_decoder_data_source_vtable =
|
|
{
|
|
ma_decoder__data_source_on_read,
|
|
ma_decoder__data_source_on_seek,
|
|
ma_decoder__data_source_on_get_data_format,
|
|
ma_decoder__data_source_on_get_cursor,
|
|
ma_decoder__data_source_on_get_length,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, ma_decoder_tell_proc onTell, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDecoder);
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pDecoder->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->onRead = onRead;
|
|
pDecoder->onSeek = onSeek;
|
|
pDecoder->onTell = onTell;
|
|
pDecoder->pUserData = pUserData;
|
|
|
|
result = ma_decoder__init_allocation_callbacks(pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_data_source_uninit(&pDecoder->ds);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__postinit(const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_decoder__init_data_converter(pDecoder, pConfig);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder_uninit(pDecoder);
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_decoder_init__internal(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = MA_NO_BACKEND;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
(void)onRead;
|
|
(void)onSeek;
|
|
(void)pUserData;
|
|
|
|
if (pConfig->encodingFormat != ma_encoding_format_unknown) {
|
|
#ifdef MA_HAS_WAV
|
|
if (pConfig->encodingFormat == ma_encoding_format_wav) {
|
|
result = ma_decoder_init_wav__internal(pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (pConfig->encodingFormat == ma_encoding_format_flac) {
|
|
result = ma_decoder_init_flac__internal(pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (pConfig->encodingFormat == ma_encoding_format_mp3) {
|
|
result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (pConfig->encodingFormat == ma_encoding_format_vorbis) {
|
|
result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
onSeek(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
|
|
result = ma_decoder_init_custom__internal(pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
onSeek(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
|
|
if (pConfig->encodingFormat != ma_encoding_format_unknown) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_wav__internal(pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
onSeek(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_flac__internal(pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
onSeek(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_mp3__internal(pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
onSeek(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_vorbis__internal(pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
onSeek(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_decoder__postinit(pConfig, pDecoder);
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_init(ma_decoder_read_proc onRead, ma_decoder_seek_proc onSeek, void* pUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_decoder_config config;
|
|
ma_result result;
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
|
|
result = ma_decoder__preinit(onRead, onSeek, NULL, pUserData, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_decoder_init__internal(onRead, onSeek, pUserData, &config, pDecoder);
|
|
}
|
|
|
|
static ma_result ma_decoder__on_read_memory(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
|
|
{
|
|
size_t bytesRemaining;
|
|
|
|
MA_ASSERT(pDecoder->data.memory.dataSize >= pDecoder->data.memory.currentReadPos);
|
|
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = 0;
|
|
}
|
|
|
|
bytesRemaining = pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos;
|
|
if (bytesToRead > bytesRemaining) {
|
|
bytesToRead = bytesRemaining;
|
|
}
|
|
|
|
if (bytesRemaining == 0) {
|
|
return MA_AT_END;
|
|
}
|
|
|
|
if (bytesToRead > 0) {
|
|
MA_COPY_MEMORY(pBufferOut, pDecoder->data.memory.pData + pDecoder->data.memory.currentReadPos, bytesToRead);
|
|
pDecoder->data.memory.currentReadPos += bytesToRead;
|
|
}
|
|
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = bytesToRead;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__on_seek_memory(ma_decoder* pDecoder, ma_int64 byteOffset, ma_seek_origin origin)
|
|
{
|
|
if (byteOffset > 0 && (ma_uint64)byteOffset > MA_SIZE_MAX) {
|
|
return MA_BAD_SEEK;
|
|
}
|
|
|
|
if (origin == ma_seek_origin_current) {
|
|
if (byteOffset > 0) {
|
|
if (pDecoder->data.memory.currentReadPos + byteOffset > pDecoder->data.memory.dataSize) {
|
|
byteOffset = (ma_int64)(pDecoder->data.memory.dataSize - pDecoder->data.memory.currentReadPos);
|
|
}
|
|
|
|
pDecoder->data.memory.currentReadPos += (size_t)byteOffset;
|
|
} else {
|
|
if (pDecoder->data.memory.currentReadPos < (size_t)-byteOffset) {
|
|
byteOffset = -(ma_int64)pDecoder->data.memory.currentReadPos;
|
|
}
|
|
|
|
pDecoder->data.memory.currentReadPos -= (size_t)-byteOffset;
|
|
}
|
|
} else {
|
|
if (origin == ma_seek_origin_end) {
|
|
if (byteOffset < 0) {
|
|
byteOffset = -byteOffset;
|
|
}
|
|
|
|
if (byteOffset > (ma_int64)pDecoder->data.memory.dataSize) {
|
|
pDecoder->data.memory.currentReadPos = 0;
|
|
} else {
|
|
pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize - (size_t)byteOffset;
|
|
}
|
|
} else {
|
|
if ((size_t)byteOffset <= pDecoder->data.memory.dataSize) {
|
|
pDecoder->data.memory.currentReadPos = (size_t)byteOffset;
|
|
} else {
|
|
pDecoder->data.memory.currentReadPos = pDecoder->data.memory.dataSize;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCursor)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
MA_ASSERT(pCursor != NULL);
|
|
|
|
*pCursor = (ma_int64)pDecoder->data.memory.currentReadPos;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pData == NULL || dataSize == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDecoder->data.memory.pData = (const ma_uint8*)pData;
|
|
pDecoder->data.memory.dataSize = dataSize;
|
|
pDecoder->data.memory.currentReadPos = 0;
|
|
|
|
(void)pConfig;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
|
|
result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pData == NULL || dataSize == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = MA_NO_BACKEND;
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
#ifdef MA_HAS_WAV
|
|
if (config.encodingFormat == ma_encoding_format_wav) {
|
|
result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (config.encodingFormat == ma_encoding_format_flac) {
|
|
result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (config.encodingFormat == ma_encoding_format_mp3) {
|
|
result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (config.encodingFormat == ma_encoding_format_vorbis) {
|
|
result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
|
|
result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
|
|
if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_decoder__postinit(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
} else {
|
|
result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if defined(MA_HAS_WAV) || \
|
|
defined(MA_HAS_MP3) || \
|
|
defined(MA_HAS_FLAC) || \
|
|
defined(MA_HAS_VORBIS)
|
|
#define MA_HAS_PATH_API
|
|
#endif
|
|
|
|
#if defined(MA_HAS_PATH_API)
|
|
static const char* ma_path_file_name(const char* path)
|
|
{
|
|
const char* fileName;
|
|
|
|
if (path == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
fileName = path;
|
|
|
|
while (path[0] != '\0') {
|
|
if (path[0] == '/' || path[0] == '\\') {
|
|
fileName = path;
|
|
}
|
|
|
|
path += 1;
|
|
}
|
|
|
|
while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
|
|
fileName += 1;
|
|
}
|
|
|
|
return fileName;
|
|
}
|
|
|
|
static const wchar_t* ma_path_file_name_w(const wchar_t* path)
|
|
{
|
|
const wchar_t* fileName;
|
|
|
|
if (path == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
fileName = path;
|
|
|
|
while (path[0] != '\0') {
|
|
if (path[0] == '/' || path[0] == '\\') {
|
|
fileName = path;
|
|
}
|
|
|
|
path += 1;
|
|
}
|
|
|
|
while (fileName[0] != '\0' && (fileName[0] == '/' || fileName[0] == '\\')) {
|
|
fileName += 1;
|
|
}
|
|
|
|
return fileName;
|
|
}
|
|
|
|
static const char* ma_path_extension(const char* path)
|
|
{
|
|
const char* extension;
|
|
const char* lastOccurance;
|
|
|
|
if (path == NULL) {
|
|
path = "";
|
|
}
|
|
|
|
extension = ma_path_file_name(path);
|
|
lastOccurance = NULL;
|
|
|
|
while (extension[0] != '\0') {
|
|
if (extension[0] == '.') {
|
|
extension += 1;
|
|
lastOccurance = extension;
|
|
}
|
|
|
|
extension += 1;
|
|
}
|
|
|
|
return (lastOccurance != NULL) ? lastOccurance : extension;
|
|
}
|
|
|
|
static const wchar_t* ma_path_extension_w(const wchar_t* path)
|
|
{
|
|
const wchar_t* extension;
|
|
const wchar_t* lastOccurance;
|
|
|
|
if (path == NULL) {
|
|
path = L"";
|
|
}
|
|
|
|
extension = ma_path_file_name_w(path);
|
|
lastOccurance = NULL;
|
|
|
|
while (extension[0] != '\0') {
|
|
if (extension[0] == '.') {
|
|
extension += 1;
|
|
lastOccurance = extension;
|
|
}
|
|
|
|
extension += 1;
|
|
}
|
|
|
|
return (lastOccurance != NULL) ? lastOccurance : extension;
|
|
}
|
|
|
|
static ma_bool32 ma_path_extension_equal(const char* path, const char* extension)
|
|
{
|
|
const char* ext1;
|
|
const char* ext2;
|
|
|
|
if (path == NULL || extension == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
ext1 = extension;
|
|
ext2 = ma_path_extension(path);
|
|
|
|
#if defined(_MSC_VER) || defined(__DMC__)
|
|
return _stricmp(ext1, ext2) == 0;
|
|
#else
|
|
return strcasecmp(ext1, ext2) == 0;
|
|
#endif
|
|
}
|
|
|
|
static ma_bool32 ma_path_extension_equal_w(const wchar_t* path, const wchar_t* extension)
|
|
{
|
|
const wchar_t* ext1;
|
|
const wchar_t* ext2;
|
|
|
|
if (path == NULL || extension == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
ext1 = extension;
|
|
ext2 = ma_path_extension_w(path);
|
|
|
|
#if (defined(_MSC_VER) || defined(__WATCOMC__) || defined(__DMC__)) && !defined(MA_XBOX_NXDK)
|
|
{
|
|
return _wcsicmp(ext1, ext2) == 0;
|
|
}
|
|
#elif !defined(MA_XBOX_NXDK) && !defined(MA_DOS)
|
|
{
|
|
char ext1MB[4096];
|
|
char ext2MB[4096];
|
|
const wchar_t* pext1 = ext1;
|
|
const wchar_t* pext2 = ext2;
|
|
mbstate_t mbs1;
|
|
mbstate_t mbs2;
|
|
|
|
MA_ZERO_OBJECT(&mbs1);
|
|
MA_ZERO_OBJECT(&mbs2);
|
|
|
|
if (wcsrtombs(ext1MB, &pext1, sizeof(ext1MB), &mbs1) == (size_t)-1) {
|
|
return MA_FALSE;
|
|
}
|
|
if (wcsrtombs(ext2MB, &pext2, sizeof(ext2MB), &mbs2) == (size_t)-1) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return strcasecmp(ext1MB, ext2MB) == 0;
|
|
}
|
|
#else
|
|
{
|
|
return ma_wcscmp(ext1, ext2) == 0;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_decoder__on_read_vfs(ma_decoder* pDecoder, void* pBufferOut, size_t bytesToRead, size_t* pBytesRead)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
MA_ASSERT(pBufferOut != NULL);
|
|
|
|
return ma_vfs_or_default_read(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pBufferOut, bytesToRead, pBytesRead);
|
|
}
|
|
|
|
static ma_result ma_decoder__on_seek_vfs(ma_decoder* pDecoder, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return ma_vfs_or_default_seek(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, offset, origin);
|
|
}
|
|
|
|
static ma_result ma_decoder__on_tell_vfs(ma_decoder* pDecoder, ma_int64* pCursor)
|
|
{
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
return ma_vfs_or_default_tell(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file, pCursor);
|
|
}
|
|
|
|
static ma_result ma_decoder__preinit_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_vfs_file file;
|
|
|
|
result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFilePath == NULL || pFilePath[0] == '\0') {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->data.vfs.pVFS = pVFS;
|
|
pDecoder->data.vfs.file = file;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
result = ma_decoder__preinit_vfs(pVFS, pFilePath, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = MA_NO_BACKEND;
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
#ifdef MA_HAS_WAV
|
|
if (config.encodingFormat == ma_encoding_format_wav) {
|
|
result = ma_decoder_init_wav__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (config.encodingFormat == ma_encoding_format_flac) {
|
|
result = ma_decoder_init_flac__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (config.encodingFormat == ma_encoding_format_mp3) {
|
|
result = ma_decoder_init_mp3__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (config.encodingFormat == ma_encoding_format_vorbis) {
|
|
result = ma_decoder_init_vorbis__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
|
|
result = ma_decoder_init_custom__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
|
|
result = ma_decoder_init_wav__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
|
|
result = ma_decoder_init_flac__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
|
|
result = ma_decoder_init_mp3__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
|
|
} else {
|
|
result = ma_decoder__postinit(&config, pDecoder);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
if (pDecoder->data.vfs.file != NULL) {
|
|
ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__preinit_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_vfs_file file;
|
|
|
|
result = ma_decoder__preinit(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, ma_decoder__on_tell_vfs, NULL, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFilePath == NULL || pFilePath[0] == '\0') {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_READ, &file);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDecoder->data.vfs.pVFS = pVFS;
|
|
pDecoder->data.vfs.file = file;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
result = ma_decoder__preinit_vfs_w(pVFS, pFilePath, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = MA_NO_BACKEND;
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
#ifdef MA_HAS_WAV
|
|
if (config.encodingFormat == ma_encoding_format_wav) {
|
|
result = ma_decoder_init_wav__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (config.encodingFormat == ma_encoding_format_flac) {
|
|
result = ma_decoder_init_flac__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (config.encodingFormat == ma_encoding_format_mp3) {
|
|
result = ma_decoder_init_mp3__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (config.encodingFormat == ma_encoding_format_vorbis) {
|
|
result = ma_decoder_init_vorbis__internal(&config, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
|
|
result = ma_decoder_init_custom__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
|
|
result = ma_decoder_init_wav__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
|
|
result = ma_decoder_init_flac__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
|
|
result = ma_decoder_init_mp3__internal(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder__on_seek_vfs(pDecoder, 0, ma_seek_origin_start);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init__internal(ma_decoder__on_read_vfs, ma_decoder__on_seek_vfs, NULL, &config, pDecoder);
|
|
} else {
|
|
result = ma_decoder__postinit(&config, pDecoder);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_vfs_or_default_close(pVFS, pDecoder->data.vfs.file);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFilePath == NULL || pFilePath[0] == '\0') {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
result = ma_decoder__preinit_file(pFilePath, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = MA_NO_BACKEND;
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
#ifdef MA_HAS_WAV
|
|
if (config.encodingFormat == ma_encoding_format_wav) {
|
|
result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (config.encodingFormat == ma_encoding_format_flac) {
|
|
result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (config.encodingFormat == ma_encoding_format_mp3) {
|
|
result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (config.encodingFormat == ma_encoding_format_vorbis) {
|
|
result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
|
|
result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder);
|
|
|
|
if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) {
|
|
result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) {
|
|
result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) {
|
|
result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) {
|
|
result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_decoder__postinit(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
} else {
|
|
result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFilePath == NULL || pFilePath[0] == '\0') {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = MA_NO_BACKEND;
|
|
|
|
if (config.encodingFormat != ma_encoding_format_unknown) {
|
|
#ifdef MA_HAS_WAV
|
|
if (config.encodingFormat == ma_encoding_format_wav) {
|
|
result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (config.encodingFormat == ma_encoding_format_flac) {
|
|
result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (config.encodingFormat == ma_encoding_format_mp3) {
|
|
result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (config.encodingFormat == ma_encoding_format_vorbis) {
|
|
result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
|
|
result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
|
|
if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) {
|
|
result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) {
|
|
result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) {
|
|
result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) {
|
|
result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
#ifdef MA_HAS_WAV
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_FLAC
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_MP3
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
#ifdef MA_HAS_VORBIS
|
|
if (result != MA_SUCCESS) {
|
|
result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_decoder__postinit(&config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
} else {
|
|
result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder)
|
|
{
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDecoder->pBackend != NULL) {
|
|
if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) {
|
|
pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, pDecoder->pBackend, &pDecoder->allocationCallbacks);
|
|
}
|
|
}
|
|
|
|
if (pDecoder->onRead == ma_decoder__on_read_vfs) {
|
|
ma_vfs_or_default_close(pDecoder->data.vfs.pVFS, pDecoder->data.vfs.file);
|
|
pDecoder->data.vfs.file = NULL;
|
|
}
|
|
|
|
ma_data_converter_uninit(&pDecoder->converter, &pDecoder->allocationCallbacks);
|
|
ma_data_source_uninit(&pDecoder->ds);
|
|
|
|
if (pDecoder->pInputCache != NULL) {
|
|
ma_free(pDecoder->pInputCache, &pDecoder->allocationCallbacks);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_read_pcm_frames(ma_decoder* pDecoder, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesReadOut;
|
|
void* pRunningFramesOut;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDecoder->pBackend == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (pDecoder->converter.isPassthrough) {
|
|
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pFramesOut, frameCount, &totalFramesReadOut);
|
|
} else {
|
|
if (pFramesOut == NULL && pDecoder->converter.hasResampler == MA_FALSE) {
|
|
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, NULL, frameCount, &totalFramesReadOut);
|
|
} else {
|
|
ma_format internalFormat;
|
|
ma_uint32 internalChannels;
|
|
|
|
totalFramesReadOut = 0;
|
|
pRunningFramesOut = pFramesOut;
|
|
|
|
result = ma_data_source_get_data_format(pDecoder->pBackend, &internalFormat, &internalChannels, NULL, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pDecoder->pInputCache != NULL) {
|
|
while (totalFramesReadOut < frameCount) {
|
|
ma_uint64 framesToReadThisIterationIn;
|
|
ma_uint64 framesToReadThisIterationOut;
|
|
|
|
if (pDecoder->inputCacheRemaining > 0) {
|
|
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
|
|
framesToReadThisIterationIn = framesToReadThisIterationOut;
|
|
if (framesToReadThisIterationIn > pDecoder->inputCacheRemaining) {
|
|
framesToReadThisIterationIn = pDecoder->inputCacheRemaining;
|
|
}
|
|
|
|
result = ma_data_converter_process_pcm_frames(&pDecoder->converter, ma_offset_pcm_frames_ptr(pDecoder->pInputCache, pDecoder->inputCacheConsumed, internalFormat, internalChannels), &framesToReadThisIterationIn, pRunningFramesOut, &framesToReadThisIterationOut);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
pDecoder->inputCacheConsumed += framesToReadThisIterationIn;
|
|
pDecoder->inputCacheRemaining -= framesToReadThisIterationIn;
|
|
|
|
totalFramesReadOut += framesToReadThisIterationOut;
|
|
|
|
if (pRunningFramesOut != NULL) {
|
|
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesToReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
|
|
}
|
|
|
|
if (framesToReadThisIterationIn == 0 && framesToReadThisIterationOut == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pDecoder->inputCacheRemaining == 0) {
|
|
pDecoder->inputCacheConsumed = 0;
|
|
|
|
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pDecoder->pInputCache, pDecoder->inputCacheCap, &pDecoder->inputCacheRemaining);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
while (totalFramesReadOut < frameCount) {
|
|
ma_uint8 pIntermediaryBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint64 intermediaryBufferCap = sizeof(pIntermediaryBuffer) / ma_get_bytes_per_frame(internalFormat, internalChannels);
|
|
ma_uint64 framesToReadThisIterationIn;
|
|
ma_uint64 framesReadThisIterationIn;
|
|
ma_uint64 framesToReadThisIterationOut;
|
|
ma_uint64 framesReadThisIterationOut;
|
|
ma_uint64 requiredInputFrameCount;
|
|
|
|
framesToReadThisIterationOut = (frameCount - totalFramesReadOut);
|
|
framesToReadThisIterationIn = framesToReadThisIterationOut;
|
|
if (framesToReadThisIterationIn > intermediaryBufferCap) {
|
|
framesToReadThisIterationIn = intermediaryBufferCap;
|
|
}
|
|
|
|
ma_data_converter_get_required_input_frame_count(&pDecoder->converter, framesToReadThisIterationOut, &requiredInputFrameCount);
|
|
if (framesToReadThisIterationIn > requiredInputFrameCount) {
|
|
framesToReadThisIterationIn = requiredInputFrameCount;
|
|
}
|
|
|
|
if (requiredInputFrameCount > 0) {
|
|
result = ma_data_source_read_pcm_frames(pDecoder->pBackend, pIntermediaryBuffer, framesToReadThisIterationIn, &framesReadThisIterationIn);
|
|
|
|
if (result != MA_SUCCESS && result != MA_AT_END) {
|
|
break;
|
|
}
|
|
} else {
|
|
framesReadThisIterationIn = 0;
|
|
pIntermediaryBuffer[0] = 0;
|
|
}
|
|
|
|
framesReadThisIterationOut = framesToReadThisIterationOut;
|
|
result = ma_data_converter_process_pcm_frames(&pDecoder->converter, pIntermediaryBuffer, &framesReadThisIterationIn, pRunningFramesOut, &framesReadThisIterationOut);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
totalFramesReadOut += framesReadThisIterationOut;
|
|
|
|
if (pRunningFramesOut != NULL) {
|
|
pRunningFramesOut = ma_offset_ptr(pRunningFramesOut, framesReadThisIterationOut * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels));
|
|
}
|
|
|
|
if (framesReadThisIterationIn == 0 && framesReadThisIterationOut == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pDecoder->readPointerInPCMFrames += totalFramesReadOut;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesReadOut;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && totalFramesReadOut == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_seek_to_pcm_frame(ma_decoder* pDecoder, ma_uint64 frameIndex)
|
|
{
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDecoder->pBackend != NULL) {
|
|
ma_result result;
|
|
ma_uint64 internalFrameIndex;
|
|
ma_uint32 internalSampleRate;
|
|
ma_uint64 currentFrameIndex;
|
|
|
|
result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (internalSampleRate == pDecoder->outputSampleRate) {
|
|
internalFrameIndex = frameIndex;
|
|
} else {
|
|
internalFrameIndex = ma_calculate_frame_count_after_resampling(internalSampleRate, pDecoder->outputSampleRate, frameIndex);
|
|
}
|
|
|
|
ma_data_source_get_cursor_in_pcm_frames(pDecoder->pBackend, ¤tFrameIndex);
|
|
if (currentFrameIndex != internalFrameIndex) {
|
|
result = ma_data_source_seek_to_pcm_frame(pDecoder->pBackend, internalFrameIndex);
|
|
if (result == MA_SUCCESS) {
|
|
pDecoder->readPointerInPCMFrames = frameIndex;
|
|
}
|
|
|
|
ma_data_converter_reset(&pDecoder->converter);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_get_data_format(ma_decoder* pDecoder, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = pDecoder->outputFormat;
|
|
}
|
|
|
|
if (pChannels != NULL) {
|
|
*pChannels = pDecoder->outputChannels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pDecoder->outputSampleRate;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_data_converter_get_output_channel_map(&pDecoder->converter, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_get_cursor_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = pDecoder->readPointerInPCMFrames;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_get_length_in_pcm_frames(ma_decoder* pDecoder, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDecoder->pBackend != NULL) {
|
|
ma_result result;
|
|
ma_uint64 internalLengthInPCMFrames;
|
|
ma_uint32 internalSampleRate;
|
|
|
|
result = ma_data_source_get_length_in_pcm_frames(pDecoder->pBackend, &internalLengthInPCMFrames);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_data_source_get_data_format(pDecoder->pBackend, NULL, NULL, &internalSampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (internalSampleRate == pDecoder->outputSampleRate) {
|
|
*pLength = internalLengthInPCMFrames;
|
|
} else {
|
|
*pLength = ma_calculate_frame_count_after_resampling(pDecoder->outputSampleRate, internalSampleRate, internalLengthInPCMFrames);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
} else {
|
|
return MA_NO_BACKEND;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_decoder_get_available_frames(ma_decoder* pDecoder, ma_uint64* pAvailableFrames)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 totalFrameCount;
|
|
|
|
if (pAvailableFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pAvailableFrames = 0;
|
|
|
|
if (pDecoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (totalFrameCount <= pDecoder->readPointerInPCMFrames) {
|
|
*pAvailableFrames = 0;
|
|
} else {
|
|
*pAvailableFrames = totalFrameCount - pDecoder->readPointerInPCMFrames;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_decoder__full_decode_and_uninit(ma_decoder* pDecoder, ma_decoder_config* pConfigOut, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 totalFrameCount;
|
|
ma_uint64 bpf;
|
|
ma_uint64 dataCapInFrames;
|
|
void* pPCMFramesOut;
|
|
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
totalFrameCount = 0;
|
|
bpf = ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
|
|
|
|
dataCapInFrames = 0;
|
|
pPCMFramesOut = NULL;
|
|
for (;;) {
|
|
ma_uint64 frameCountToTryReading;
|
|
ma_uint64 framesJustRead;
|
|
|
|
if (totalFrameCount == dataCapInFrames) {
|
|
void* pNewPCMFramesOut;
|
|
ma_uint64 newDataCapInFrames = dataCapInFrames*2;
|
|
if (newDataCapInFrames == 0) {
|
|
newDataCapInFrames = 4096;
|
|
}
|
|
|
|
if ((newDataCapInFrames * bpf) > MA_SIZE_MAX) {
|
|
ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
|
|
return MA_TOO_BIG;
|
|
}
|
|
|
|
pNewPCMFramesOut = (void*)ma_realloc(pPCMFramesOut, (size_t)(newDataCapInFrames * bpf), &pDecoder->allocationCallbacks);
|
|
if (pNewPCMFramesOut == NULL) {
|
|
ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
dataCapInFrames = newDataCapInFrames;
|
|
pPCMFramesOut = pNewPCMFramesOut;
|
|
}
|
|
|
|
frameCountToTryReading = dataCapInFrames - totalFrameCount;
|
|
MA_ASSERT(frameCountToTryReading > 0);
|
|
|
|
result = ma_decoder_read_pcm_frames(pDecoder, (ma_uint8*)pPCMFramesOut + (totalFrameCount * bpf), frameCountToTryReading, &framesJustRead);
|
|
totalFrameCount += framesJustRead;
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (framesJustRead < frameCountToTryReading) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pConfigOut != NULL) {
|
|
pConfigOut->format = pDecoder->outputFormat;
|
|
pConfigOut->channels = pDecoder->outputChannels;
|
|
pConfigOut->sampleRate = pDecoder->outputSampleRate;
|
|
}
|
|
|
|
if (ppPCMFramesOut != NULL) {
|
|
*ppPCMFramesOut = pPCMFramesOut;
|
|
} else {
|
|
ma_free(pPCMFramesOut, &pDecoder->allocationCallbacks);
|
|
}
|
|
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = totalFrameCount;
|
|
}
|
|
|
|
ma_decoder_uninit(pDecoder);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_decode_from_vfs(ma_vfs* pVFS, const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
ma_decoder decoder;
|
|
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = 0;
|
|
}
|
|
if (ppPCMFramesOut != NULL) {
|
|
*ppPCMFramesOut = NULL;
|
|
}
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
|
|
result = ma_decoder_init_vfs(pVFS, pFilePath, &config, &decoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_decode_file(const char* pFilePath, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
|
|
{
|
|
return ma_decode_from_vfs(NULL, pFilePath, pConfig, pFrameCountOut, ppPCMFramesOut);
|
|
}
|
|
|
|
MA_API ma_result ma_decode_memory(const void* pData, size_t dataSize, ma_decoder_config* pConfig, ma_uint64* pFrameCountOut, void** ppPCMFramesOut)
|
|
{
|
|
ma_decoder_config config;
|
|
ma_decoder decoder;
|
|
ma_result result;
|
|
|
|
if (pFrameCountOut != NULL) {
|
|
*pFrameCountOut = 0;
|
|
}
|
|
if (ppPCMFramesOut != NULL) {
|
|
*ppPCMFramesOut = NULL;
|
|
}
|
|
|
|
if (pData == NULL || dataSize == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
config = ma_decoder_config_init_copy(pConfig);
|
|
|
|
result = ma_decoder_init_memory(pData, dataSize, &config, &decoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_decoder__full_decode_and_uninit(&decoder, pConfig, pFrameCountOut, ppPCMFramesOut);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MA_NO_ENCODING
|
|
|
|
#if defined(MA_HAS_WAV)
|
|
static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pData, size_t bytesToWrite)
|
|
{
|
|
ma_encoder* pEncoder = (ma_encoder*)pUserData;
|
|
size_t bytesWritten = 0;
|
|
|
|
MA_ASSERT(pEncoder != NULL);
|
|
|
|
pEncoder->onWrite(pEncoder, pData, bytesToWrite, &bytesWritten);
|
|
return bytesWritten;
|
|
}
|
|
|
|
static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
|
|
{
|
|
ma_encoder* pEncoder = (ma_encoder*)pUserData;
|
|
ma_result result;
|
|
ma_seek_origin maSeekOrigin;
|
|
|
|
MA_ASSERT(pEncoder != NULL);
|
|
|
|
maSeekOrigin = ma_seek_origin_start;
|
|
if (origin == MA_DR_WAV_SEEK_CUR) {
|
|
maSeekOrigin = ma_seek_origin_current;
|
|
} else if (origin == MA_DR_WAV_SEEK_END) {
|
|
maSeekOrigin = ma_seek_origin_end;
|
|
}
|
|
|
|
result = pEncoder->onSeek(pEncoder, offset, maSeekOrigin);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
} else {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder)
|
|
{
|
|
ma_dr_wav_data_format wavFormat;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_dr_wav* pWav;
|
|
|
|
MA_ASSERT(pEncoder != NULL);
|
|
|
|
pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks);
|
|
if (pWav == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
wavFormat.container = ma_dr_wav_container_riff;
|
|
wavFormat.channels = pEncoder->config.channels;
|
|
wavFormat.sampleRate = pEncoder->config.sampleRate;
|
|
wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8;
|
|
if (pEncoder->config.format == ma_format_f32) {
|
|
wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT;
|
|
} else {
|
|
wavFormat.format = MA_DR_WAVE_FORMAT_PCM;
|
|
}
|
|
|
|
allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData;
|
|
allocationCallbacks.onMalloc = pEncoder->config.allocationCallbacks.onMalloc;
|
|
allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc;
|
|
allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree;
|
|
|
|
if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) {
|
|
return MA_ERROR;
|
|
}
|
|
|
|
pEncoder->pInternalEncoder = pWav;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder)
|
|
{
|
|
ma_dr_wav* pWav;
|
|
|
|
MA_ASSERT(pEncoder != NULL);
|
|
|
|
pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;
|
|
MA_ASSERT(pWav != NULL);
|
|
|
|
ma_dr_wav_uninit(pWav);
|
|
ma_free(pWav, &pEncoder->config.allocationCallbacks);
|
|
}
|
|
|
|
static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
|
|
{
|
|
ma_dr_wav* pWav;
|
|
ma_uint64 framesWritten;
|
|
|
|
MA_ASSERT(pEncoder != NULL);
|
|
|
|
pWav = (ma_dr_wav*)pEncoder->pInternalEncoder;
|
|
MA_ASSERT(pWav != NULL);
|
|
|
|
framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn);
|
|
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = framesWritten;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_encoder_config ma_encoder_config_init(ma_encoding_format encodingFormat, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
|
|
{
|
|
ma_encoder_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.encodingFormat = encodingFormat;
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_preinit(const ma_encoder_config* pConfig, ma_encoder* pEncoder)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pEncoder == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pEncoder);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->format == ma_format_unknown || pConfig->channels == 0 || pConfig->sampleRate == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pEncoder->config = *pConfig;
|
|
|
|
result = ma_allocation_callbacks_init_copy(&pEncoder->config.allocationCallbacks, &pConfig->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_init__internal(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, ma_encoder* pEncoder)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
|
|
MA_ASSERT(pEncoder != NULL);
|
|
|
|
if (onWrite == NULL || onSeek == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pEncoder->onWrite = onWrite;
|
|
pEncoder->onSeek = onSeek;
|
|
pEncoder->pUserData = pUserData;
|
|
|
|
switch (pEncoder->config.encodingFormat)
|
|
{
|
|
case ma_encoding_format_wav:
|
|
{
|
|
#if defined(MA_HAS_WAV)
|
|
pEncoder->onInit = ma_encoder__on_init_wav;
|
|
pEncoder->onUninit = ma_encoder__on_uninit_wav;
|
|
pEncoder->onWritePCMFrames = ma_encoder__on_write_pcm_frames_wav;
|
|
#else
|
|
result = MA_NO_BACKEND;
|
|
#endif
|
|
} break;
|
|
|
|
default:
|
|
{
|
|
result = MA_INVALID_ARGS;
|
|
} break;
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
result = pEncoder->onInit(pEncoder);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_encoder__on_write_vfs(ma_encoder* pEncoder, const void* pBufferIn, size_t bytesToWrite, size_t* pBytesWritten)
|
|
{
|
|
return ma_vfs_or_default_write(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, pBufferIn, bytesToWrite, pBytesWritten);
|
|
}
|
|
|
|
static ma_result ma_encoder__on_seek_vfs(ma_encoder* pEncoder, ma_int64 offset, ma_seek_origin origin)
|
|
{
|
|
return ma_vfs_or_default_seek(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file, offset, origin);
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_init_vfs(ma_vfs* pVFS, const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
|
|
{
|
|
ma_result result;
|
|
ma_vfs_file file;
|
|
|
|
result = ma_encoder_preinit(pConfig, pEncoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pEncoder->data.vfs.pVFS = pVFS;
|
|
pEncoder->data.vfs.file = file;
|
|
|
|
result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_vfs_or_default_close(pVFS, file);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
|
|
{
|
|
ma_result result;
|
|
ma_vfs_file file;
|
|
|
|
result = ma_encoder_preinit(pConfig, pEncoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_vfs_or_default_open_w(pVFS, pFilePath, MA_OPEN_MODE_WRITE, &file);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pEncoder->data.vfs.pVFS = pVFS;
|
|
pEncoder->data.vfs.file = file;
|
|
|
|
result = ma_encoder_init__internal(ma_encoder__on_write_vfs, ma_encoder__on_seek_vfs, NULL, pEncoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_vfs_or_default_close(pVFS, file);
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_init_file(const char* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
|
|
{
|
|
return ma_encoder_init_vfs(NULL, pFilePath, pConfig, pEncoder);
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_init_file_w(const wchar_t* pFilePath, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
|
|
{
|
|
return ma_encoder_init_vfs_w(NULL, pFilePath, pConfig, pEncoder);
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_init(ma_encoder_write_proc onWrite, ma_encoder_seek_proc onSeek, void* pUserData, const ma_encoder_config* pConfig, ma_encoder* pEncoder)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_encoder_preinit(pConfig, pEncoder);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_encoder_init__internal(onWrite, onSeek, pUserData, pEncoder);
|
|
}
|
|
|
|
MA_API void ma_encoder_uninit(ma_encoder* pEncoder)
|
|
{
|
|
if (pEncoder == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pEncoder->onUninit) {
|
|
pEncoder->onUninit(pEncoder);
|
|
}
|
|
|
|
if (pEncoder->onWrite == ma_encoder__on_write_vfs) {
|
|
ma_vfs_or_default_close(pEncoder->data.vfs.pVFS, pEncoder->data.vfs.file);
|
|
pEncoder->data.vfs.file = NULL;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_encoder_write_pcm_frames(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten)
|
|
{
|
|
if (pFramesWritten != NULL) {
|
|
*pFramesWritten = 0;
|
|
}
|
|
|
|
if (pEncoder == NULL || pFramesIn == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return pEncoder->onWritePCMFrames(pEncoder, pFramesIn, frameCount, pFramesWritten);
|
|
}
|
|
#endif
|
|
|
|
#ifndef MA_NO_GENERATION
|
|
MA_API ma_waveform_config ma_waveform_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, ma_waveform_type type, double amplitude, double frequency)
|
|
{
|
|
ma_waveform_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.type = type;
|
|
config.amplitude = amplitude;
|
|
config.frequency = frequency;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_waveform__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_waveform_read_pcm_frames((ma_waveform*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_waveform__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_waveform_seek_to_pcm_frame((ma_waveform*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_waveform__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_waveform* pWaveform = (ma_waveform*)pDataSource;
|
|
|
|
*pFormat = pWaveform->config.format;
|
|
*pChannels = pWaveform->config.channels;
|
|
*pSampleRate = pWaveform->config.sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pWaveform->config.channels);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_waveform__data_source_on_get_cursor(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
ma_waveform* pWaveform = (ma_waveform*)pDataSource;
|
|
|
|
*pCursor = (ma_uint64)(pWaveform->time / pWaveform->advance);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static double ma_waveform__calculate_advance(ma_uint32 sampleRate, double frequency)
|
|
{
|
|
return (1.0 / (sampleRate / frequency));
|
|
}
|
|
|
|
static void ma_waveform__update_advance(ma_waveform* pWaveform)
|
|
{
|
|
pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_waveform_data_source_vtable =
|
|
{
|
|
ma_waveform__data_source_on_read,
|
|
ma_waveform__data_source_on_seek,
|
|
ma_waveform__data_source_on_get_data_format,
|
|
ma_waveform__data_source_on_get_cursor,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_waveform_init(const ma_waveform_config* pConfig, ma_waveform* pWaveform)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pWaveform);
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_waveform_data_source_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pWaveform->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pWaveform->config = *pConfig;
|
|
pWaveform->advance = ma_waveform__calculate_advance(pWaveform->config.sampleRate, pWaveform->config.frequency);
|
|
pWaveform->time = 0;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_waveform_uninit(ma_waveform* pWaveform)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_data_source_uninit(&pWaveform->ds);
|
|
}
|
|
|
|
MA_API ma_result ma_waveform_set_amplitude(ma_waveform* pWaveform, double amplitude)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.amplitude = amplitude;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double frequency)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.frequency = frequency;
|
|
ma_waveform__update_advance(pWaveform);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.type = type;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.sampleRate = sampleRate;
|
|
ma_waveform__update_advance(pWaveform);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static float ma_waveform_sine_f32(double time, double amplitude)
|
|
{
|
|
return (float)(ma_sind(MA_TAU_D * time) * amplitude);
|
|
}
|
|
|
|
static ma_int16 ma_waveform_sine_s16(double time, double amplitude)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude));
|
|
}
|
|
|
|
static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude)
|
|
{
|
|
double f = time - (ma_int64)time;
|
|
double r;
|
|
|
|
if (f < dutyCycle) {
|
|
r = amplitude;
|
|
} else {
|
|
r = -amplitude;
|
|
}
|
|
|
|
return (float)r;
|
|
}
|
|
|
|
static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude));
|
|
}
|
|
|
|
static float ma_waveform_triangle_f32(double time, double amplitude)
|
|
{
|
|
double f = time - (ma_int64)time;
|
|
double r;
|
|
|
|
r = 2 * ma_abs(2 * (f - 0.5)) - 1;
|
|
|
|
return (float)(r * amplitude);
|
|
}
|
|
|
|
static ma_int16 ma_waveform_triangle_s16(double time, double amplitude)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_waveform_triangle_f32(time, amplitude));
|
|
}
|
|
|
|
static float ma_waveform_sawtooth_f32(double time, double amplitude)
|
|
{
|
|
double f = time - (ma_int64)time;
|
|
double r;
|
|
|
|
r = 2 * (f - 0.5);
|
|
|
|
return (float)(r * amplitude);
|
|
}
|
|
|
|
static ma_int16 ma_waveform_sawtooth_s16(double time, double amplitude)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_waveform_sawtooth_f32(time, amplitude));
|
|
}
|
|
|
|
static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint64 iChannel;
|
|
ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
|
|
ma_uint32 bpf = bps * pWaveform->config.channels;
|
|
|
|
MA_ASSERT(pWaveform != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
|
|
if (pWaveform->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else if (pWaveform->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_waveform_sine_s16(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_sine_f32(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint64 iChannel;
|
|
ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
|
|
ma_uint32 bpf = bps * pWaveform->config.channels;
|
|
|
|
MA_ASSERT(pWaveform != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
|
|
if (pWaveform->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else if (pWaveform->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_waveform_read_pcm_frames__triangle(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint64 iChannel;
|
|
ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
|
|
ma_uint32 bpf = bps * pWaveform->config.channels;
|
|
|
|
MA_ASSERT(pWaveform != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
|
|
if (pWaveform->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else if (pWaveform->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_waveform_triangle_s16(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_triangle_f32(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_waveform_read_pcm_frames__sawtooth(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint64 iChannel;
|
|
ma_uint32 bps = ma_get_bytes_per_sample(pWaveform->config.format);
|
|
ma_uint32 bpf = bps * pWaveform->config.channels;
|
|
|
|
MA_ASSERT(pWaveform != NULL);
|
|
MA_ASSERT(pFramesOut != NULL);
|
|
|
|
if (pWaveform->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else if (pWaveform->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_waveform_sawtooth_s16(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*pWaveform->config.channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_waveform_sawtooth_f32(pWaveform->time, pWaveform->config.amplitude);
|
|
pWaveform->time += pWaveform->advance;
|
|
|
|
for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pWaveform->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
switch (pWaveform->config.type)
|
|
{
|
|
case ma_waveform_type_sine:
|
|
{
|
|
ma_waveform_read_pcm_frames__sine(pWaveform, pFramesOut, frameCount);
|
|
} break;
|
|
|
|
case ma_waveform_type_square:
|
|
{
|
|
ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount);
|
|
} break;
|
|
|
|
case ma_waveform_type_triangle:
|
|
{
|
|
ma_waveform_read_pcm_frames__triangle(pWaveform, pFramesOut, frameCount);
|
|
} break;
|
|
|
|
case ma_waveform_type_sawtooth:
|
|
{
|
|
ma_waveform_read_pcm_frames__sawtooth(pWaveform, pFramesOut, frameCount);
|
|
} break;
|
|
|
|
default: return MA_INVALID_OPERATION;
|
|
}
|
|
} else {
|
|
pWaveform->time += pWaveform->advance * (ma_int64)frameCount;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 frameIndex)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->time = pWaveform->advance * (ma_int64)frameIndex;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency)
|
|
{
|
|
ma_pulsewave_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.sampleRate = sampleRate;
|
|
config.dutyCycle = dutyCycle;
|
|
config.amplitude = amplitude;
|
|
config.frequency = frequency;
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform)
|
|
{
|
|
ma_result result;
|
|
ma_waveform_config config;
|
|
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pWaveform);
|
|
|
|
config = ma_waveform_config_init(
|
|
pConfig->format,
|
|
pConfig->channels,
|
|
pConfig->sampleRate,
|
|
ma_waveform_type_square,
|
|
pConfig->amplitude,
|
|
pConfig->frequency
|
|
);
|
|
|
|
result = ma_waveform_init(&config, &pWaveform->waveform);
|
|
ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_waveform_uninit(&pWaveform->waveform);
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount);
|
|
} else {
|
|
pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = frameCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.amplitude = amplitude;
|
|
ma_waveform_set_amplitude(&pWaveform->waveform, amplitude);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.frequency = frequency;
|
|
ma_waveform_set_frequency(&pWaveform->waveform, frequency);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.sampleRate = sampleRate;
|
|
ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle)
|
|
{
|
|
if (pWaveform == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pWaveform->config.dutyCycle = dutyCycle;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude)
|
|
{
|
|
ma_noise_config config;
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
config.format = format;
|
|
config.channels = channels;
|
|
config.type = type;
|
|
config.seed = seed;
|
|
config.amplitude = amplitude;
|
|
|
|
if (config.seed == 0) {
|
|
config.seed = MA_DEFAULT_LCG_SEED;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_noise__data_source_on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_noise_read_pcm_frames((ma_noise*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_noise__data_source_on_seek(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
(void)pDataSource;
|
|
(void)frameIndex;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_noise__data_source_on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
ma_noise* pNoise = (ma_noise*)pDataSource;
|
|
|
|
*pFormat = pNoise->config.format;
|
|
*pChannels = pNoise->config.channels;
|
|
*pSampleRate = 0;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pNoise->config.channels);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_noise_data_source_vtable =
|
|
{
|
|
ma_noise__data_source_on_read,
|
|
ma_noise__data_source_on_seek,
|
|
ma_noise__data_source_on_get_data_format,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
0
|
|
};
|
|
|
|
#ifndef MA_PINK_NOISE_BIN_SIZE
|
|
#define MA_PINK_NOISE_BIN_SIZE 16
|
|
#endif
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
struct
|
|
{
|
|
size_t binOffset;
|
|
size_t accumulationOffset;
|
|
size_t counterOffset;
|
|
} pink;
|
|
struct
|
|
{
|
|
size_t accumulationOffset;
|
|
} brownian;
|
|
} ma_noise_heap_layout;
|
|
|
|
static ma_result ma_noise_get_heap_layout(const ma_noise_config* pConfig, ma_noise_heap_layout* pHeapLayout)
|
|
{
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
if (pConfig->type == ma_noise_type_pink) {
|
|
pHeapLayout->pink.binOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(double*) * pConfig->channels;
|
|
pHeapLayout->sizeInBytes += sizeof(double ) * pConfig->channels * MA_PINK_NOISE_BIN_SIZE;
|
|
|
|
pHeapLayout->pink.accumulationOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
|
|
|
|
pHeapLayout->pink.counterOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(ma_uint32) * pConfig->channels;
|
|
}
|
|
|
|
if (pConfig->type == ma_noise_type_brownian) {
|
|
pHeapLayout->brownian.accumulationOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += sizeof(double) * pConfig->channels;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_noise_get_heap_size(const ma_noise_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_noise_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_noise_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_noise_init_preallocated(const ma_noise_config* pConfig, void* pHeap, ma_noise* pNoise)
|
|
{
|
|
ma_result result;
|
|
ma_noise_heap_layout heapLayout;
|
|
ma_data_source_config dataSourceConfig;
|
|
ma_uint32 iChannel;
|
|
|
|
if (pNoise == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNoise);
|
|
|
|
result = ma_noise_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pNoise->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pNoise->_pHeap, heapLayout.sizeInBytes);
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_noise_data_source_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pNoise->ds);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pNoise->config = *pConfig;
|
|
ma_lcg_seed(&pNoise->lcg, pConfig->seed);
|
|
|
|
if (pNoise->config.type == ma_noise_type_pink) {
|
|
pNoise->state.pink.bin = (double** )ma_offset_ptr(pHeap, heapLayout.pink.binOffset);
|
|
pNoise->state.pink.accumulation = (double* )ma_offset_ptr(pHeap, heapLayout.pink.accumulationOffset);
|
|
pNoise->state.pink.counter = (ma_uint32*)ma_offset_ptr(pHeap, heapLayout.pink.counterOffset);
|
|
|
|
for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
|
|
pNoise->state.pink.bin[iChannel] = (double*)ma_offset_ptr(pHeap, heapLayout.pink.binOffset + (sizeof(double*) * pConfig->channels) + (sizeof(double) * MA_PINK_NOISE_BIN_SIZE * iChannel));
|
|
pNoise->state.pink.accumulation[iChannel] = 0;
|
|
pNoise->state.pink.counter[iChannel] = 1;
|
|
}
|
|
}
|
|
|
|
if (pNoise->config.type == ma_noise_type_brownian) {
|
|
pNoise->state.brownian.accumulation = (double*)ma_offset_ptr(pHeap, heapLayout.brownian.accumulationOffset);
|
|
|
|
for (iChannel = 0; iChannel < pConfig->channels; iChannel += 1) {
|
|
pNoise->state.brownian.accumulation[iChannel] = 0;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_noise_init(const ma_noise_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_noise* pNoise)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_noise_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_noise_init_preallocated(pConfig, pHeap, pNoise);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pNoise->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_noise_uninit(ma_noise* pNoise, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pNoise == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_data_source_uninit(&pNoise->ds);
|
|
|
|
if (pNoise->_ownsHeap) {
|
|
ma_free(pNoise->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_noise_set_amplitude(ma_noise* pNoise, double amplitude)
|
|
{
|
|
if (pNoise == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pNoise->config.amplitude = amplitude;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_noise_set_seed(ma_noise* pNoise, ma_int32 seed)
|
|
{
|
|
if (pNoise == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pNoise->lcg.state = seed;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_noise_set_type(ma_noise* pNoise, ma_noise_type type)
|
|
{
|
|
if (pNoise == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(MA_FALSE);
|
|
(void)type;
|
|
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
static MA_INLINE float ma_noise_f32_white(ma_noise* pNoise)
|
|
{
|
|
return (float)(ma_lcg_rand_f64(&pNoise->lcg) * pNoise->config.amplitude);
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_noise_s16_white(ma_noise* pNoise)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_noise_f32_white(pNoise));
|
|
}
|
|
|
|
static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__white(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannel;
|
|
const ma_uint32 channels = pNoise->config.channels;
|
|
MA_ASSUME(channels > 0);
|
|
|
|
if (pNoise->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_noise_f32_white(pNoise);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_white(pNoise);
|
|
}
|
|
}
|
|
}
|
|
} else if (pNoise->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_noise_s16_white(pNoise);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_white(pNoise);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
|
|
const ma_uint32 bpf = bps * channels;
|
|
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_noise_f32_white(pNoise);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
float s = ma_noise_f32_white(pNoise);
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return frameCount;
|
|
}
|
|
|
|
static MA_INLINE unsigned int ma_tzcnt32(unsigned int x)
|
|
{
|
|
unsigned int n;
|
|
|
|
if (x & 0x1) {
|
|
return 0;
|
|
}
|
|
|
|
if (x == 0) {
|
|
return sizeof(x) << 3;
|
|
}
|
|
|
|
n = 1;
|
|
if ((x & 0x0000FFFF) == 0) { x >>= 16; n += 16; }
|
|
if ((x & 0x000000FF) == 0) { x >>= 8; n += 8; }
|
|
if ((x & 0x0000000F) == 0) { x >>= 4; n += 4; }
|
|
if ((x & 0x00000003) == 0) { x >>= 2; n += 2; }
|
|
n -= x & 0x00000001;
|
|
|
|
return n;
|
|
}
|
|
|
|
static MA_INLINE float ma_noise_f32_pink(ma_noise* pNoise, ma_uint32 iChannel)
|
|
{
|
|
double result;
|
|
double binPrev;
|
|
double binNext;
|
|
unsigned int ibin;
|
|
|
|
ibin = ma_tzcnt32(pNoise->state.pink.counter[iChannel]) & (MA_PINK_NOISE_BIN_SIZE - 1);
|
|
|
|
binPrev = pNoise->state.pink.bin[iChannel][ibin];
|
|
binNext = ma_lcg_rand_f64(&pNoise->lcg);
|
|
pNoise->state.pink.bin[iChannel][ibin] = binNext;
|
|
|
|
pNoise->state.pink.accumulation[iChannel] += (binNext - binPrev);
|
|
pNoise->state.pink.counter[iChannel] += 1;
|
|
|
|
result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.pink.accumulation[iChannel]);
|
|
result /= 10;
|
|
|
|
return (float)(result * pNoise->config.amplitude);
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_noise_s16_pink(ma_noise* pNoise, ma_uint32 iChannel)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_noise_f32_pink(pNoise, iChannel));
|
|
}
|
|
|
|
static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__pink(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannel;
|
|
const ma_uint32 channels = pNoise->config.channels;
|
|
MA_ASSUME(channels > 0);
|
|
|
|
if (pNoise->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_noise_f32_pink(pNoise, 0);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_pink(pNoise, iChannel);
|
|
}
|
|
}
|
|
}
|
|
} else if (pNoise->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_noise_s16_pink(pNoise, 0);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_pink(pNoise, iChannel);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
|
|
const ma_uint32 bpf = bps * channels;
|
|
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_noise_f32_pink(pNoise, 0);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
float s = ma_noise_f32_pink(pNoise, iChannel);
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return frameCount;
|
|
}
|
|
|
|
static MA_INLINE float ma_noise_f32_brownian(ma_noise* pNoise, ma_uint32 iChannel)
|
|
{
|
|
double result;
|
|
|
|
result = (ma_lcg_rand_f64(&pNoise->lcg) + pNoise->state.brownian.accumulation[iChannel]);
|
|
result /= 1.005;
|
|
|
|
pNoise->state.brownian.accumulation[iChannel] = result;
|
|
result /= 20;
|
|
|
|
return (float)(result * pNoise->config.amplitude);
|
|
}
|
|
|
|
static MA_INLINE ma_int16 ma_noise_s16_brownian(ma_noise* pNoise, ma_uint32 iChannel)
|
|
{
|
|
return ma_pcm_sample_f32_to_s16(ma_noise_f32_brownian(pNoise, iChannel));
|
|
}
|
|
|
|
static MA_INLINE ma_uint64 ma_noise_read_pcm_frames__brownian(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount)
|
|
{
|
|
ma_uint64 iFrame;
|
|
ma_uint32 iChannel;
|
|
const ma_uint32 channels = pNoise->config.channels;
|
|
MA_ASSUME(channels > 0);
|
|
|
|
if (pNoise->config.format == ma_format_f32) {
|
|
float* pFramesOutF32 = (float*)pFramesOut;
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_noise_f32_brownian(pNoise, 0);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutF32[iFrame*channels + iChannel] = ma_noise_f32_brownian(pNoise, iChannel);
|
|
}
|
|
}
|
|
}
|
|
} else if (pNoise->config.format == ma_format_s16) {
|
|
ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut;
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
ma_int16 s = ma_noise_s16_brownian(pNoise, 0);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*channels + iChannel] = s;
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
pFramesOutS16[iFrame*channels + iChannel] = ma_noise_s16_brownian(pNoise, iChannel);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
const ma_uint32 bps = ma_get_bytes_per_sample(pNoise->config.format);
|
|
const ma_uint32 bpf = bps * channels;
|
|
|
|
if (pNoise->config.duplicateChannels) {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
float s = ma_noise_f32_brownian(pNoise, 0);
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
} else {
|
|
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
|
|
for (iChannel = 0; iChannel < channels; iChannel += 1) {
|
|
float s = ma_noise_f32_brownian(pNoise, iChannel);
|
|
ma_pcm_convert(ma_offset_ptr(pFramesOut, iFrame*bpf + iChannel*bps), pNoise->config.format, &s, ma_format_f32, 1, ma_dither_mode_none);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return frameCount;
|
|
}
|
|
|
|
MA_API ma_result ma_noise_read_pcm_frames(ma_noise* pNoise, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_uint64 framesRead = 0;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pNoise == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pFramesOut == NULL) {
|
|
framesRead = frameCount;
|
|
} else {
|
|
switch (pNoise->config.type) {
|
|
case ma_noise_type_white: framesRead = ma_noise_read_pcm_frames__white (pNoise, pFramesOut, frameCount); break;
|
|
case ma_noise_type_pink: framesRead = ma_noise_read_pcm_frames__pink (pNoise, pFramesOut, frameCount); break;
|
|
case ma_noise_type_brownian: framesRead = ma_noise_read_pcm_frames__brownian(pNoise, pFramesOut, frameCount); break;
|
|
default: return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = framesRead;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
#ifndef MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS
|
|
#define MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS 1000
|
|
#endif
|
|
|
|
#ifndef MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY
|
|
#define MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY 1024
|
|
#endif
|
|
|
|
MA_API ma_resource_manager_pipeline_notifications ma_resource_manager_pipeline_notifications_init(void)
|
|
{
|
|
ma_resource_manager_pipeline_notifications notifications;
|
|
|
|
MA_ZERO_OBJECT(¬ifications);
|
|
|
|
return notifications;
|
|
}
|
|
|
|
static void ma_resource_manager_pipeline_notifications_signal_all_notifications(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
|
|
{
|
|
if (pPipelineNotifications == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pPipelineNotifications->init.pNotification) { ma_async_notification_signal(pPipelineNotifications->init.pNotification); }
|
|
if (pPipelineNotifications->done.pNotification) { ma_async_notification_signal(pPipelineNotifications->done.pNotification); }
|
|
}
|
|
|
|
static void ma_resource_manager_pipeline_notifications_acquire_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
|
|
{
|
|
if (pPipelineNotifications == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pPipelineNotifications->init.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->init.pFence); }
|
|
if (pPipelineNotifications->done.pFence != NULL) { ma_fence_acquire(pPipelineNotifications->done.pFence); }
|
|
}
|
|
|
|
static void ma_resource_manager_pipeline_notifications_release_all_fences(const ma_resource_manager_pipeline_notifications* pPipelineNotifications)
|
|
{
|
|
if (pPipelineNotifications == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pPipelineNotifications->init.pFence != NULL) { ma_fence_release(pPipelineNotifications->init.pFence); }
|
|
if (pPipelineNotifications->done.pFence != NULL) { ma_fence_release(pPipelineNotifications->done.pFence); }
|
|
}
|
|
|
|
#ifndef MA_DEFAULT_HASH_SEED
|
|
#define MA_DEFAULT_HASH_SEED 42
|
|
#endif
|
|
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic push
|
|
#if __GNUC__ >= 7
|
|
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
|
#endif
|
|
#endif
|
|
|
|
static MA_INLINE ma_uint32 ma_rotl32(ma_uint32 x, ma_int8 r)
|
|
{
|
|
return (x << r) | (x >> (32 - r));
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_hash_getblock(const ma_uint32* blocks, int i)
|
|
{
|
|
ma_uint32 block;
|
|
|
|
MA_COPY_MEMORY(&block, ma_offset_ptr(blocks, i * (int) sizeof(block)), sizeof(block));
|
|
|
|
if (ma_is_little_endian()) {
|
|
return block;
|
|
} else {
|
|
return ma_swap_endian_uint32(block);
|
|
}
|
|
}
|
|
|
|
static MA_INLINE ma_uint32 ma_hash_fmix32(ma_uint32 h)
|
|
{
|
|
h ^= h >> 16;
|
|
h *= 0x85ebca6b;
|
|
h ^= h >> 13;
|
|
h *= 0xc2b2ae35;
|
|
h ^= h >> 16;
|
|
|
|
return h;
|
|
}
|
|
|
|
static ma_uint32 ma_hash_32(const void* key, int len, ma_uint32 seed)
|
|
{
|
|
const ma_uint8* data = (const ma_uint8*)key;
|
|
const ma_uint32* blocks;
|
|
const ma_uint8* tail;
|
|
const int nblocks = len / 4;
|
|
ma_uint32 h1 = seed;
|
|
ma_uint32 c1 = 0xcc9e2d51;
|
|
ma_uint32 c2 = 0x1b873593;
|
|
ma_uint32 k1;
|
|
int i;
|
|
|
|
blocks = (const ma_uint32 *)(data + nblocks*4);
|
|
|
|
for(i = -nblocks; i; i++) {
|
|
k1 = ma_hash_getblock(blocks,i);
|
|
|
|
k1 *= c1;
|
|
k1 = ma_rotl32(k1, 15);
|
|
k1 *= c2;
|
|
|
|
h1 ^= k1;
|
|
h1 = ma_rotl32(h1, 13);
|
|
h1 = h1*5 + 0xe6546b64;
|
|
}
|
|
|
|
tail = (const ma_uint8*)(data + nblocks*4);
|
|
|
|
k1 = 0;
|
|
switch(len & 3) {
|
|
case 3: k1 ^= tail[2] << 16;
|
|
case 2: k1 ^= tail[1] << 8;
|
|
case 1: k1 ^= tail[0];
|
|
k1 *= c1; k1 = ma_rotl32(k1, 15); k1 *= c2; h1 ^= k1;
|
|
};
|
|
|
|
h1 ^= len;
|
|
h1 = ma_hash_fmix32(h1);
|
|
|
|
return h1;
|
|
}
|
|
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic push
|
|
#endif
|
|
|
|
static ma_uint32 ma_hash_string_32(const char* str)
|
|
{
|
|
return ma_hash_32(str, (int)strlen(str), MA_DEFAULT_HASH_SEED);
|
|
}
|
|
|
|
static ma_uint32 ma_hash_string_w_32(const wchar_t* str)
|
|
{
|
|
return ma_hash_32(str, (int)ma_wcslen(str) * sizeof(*str), MA_DEFAULT_HASH_SEED);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_search(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppDataBufferNode)
|
|
{
|
|
ma_resource_manager_data_buffer_node* pCurrentNode;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(ppDataBufferNode != NULL);
|
|
|
|
pCurrentNode = pResourceManager->pRootDataBufferNode;
|
|
while (pCurrentNode != NULL) {
|
|
if (hashedName32 == pCurrentNode->hashedName32) {
|
|
break;
|
|
} else if (hashedName32 < pCurrentNode->hashedName32) {
|
|
pCurrentNode = pCurrentNode->pChildLo;
|
|
} else {
|
|
pCurrentNode = pCurrentNode->pChildHi;
|
|
}
|
|
}
|
|
|
|
*ppDataBufferNode = pCurrentNode;
|
|
|
|
if (pCurrentNode == NULL) {
|
|
return MA_DOES_NOT_EXIST;
|
|
} else {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_insert_point(ma_resource_manager* pResourceManager, ma_uint32 hashedName32, ma_resource_manager_data_buffer_node** ppInsertPoint)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager_data_buffer_node* pCurrentNode;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(ppInsertPoint != NULL);
|
|
|
|
*ppInsertPoint = NULL;
|
|
|
|
if (pResourceManager->pRootDataBufferNode == NULL) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
pCurrentNode = pResourceManager->pRootDataBufferNode;
|
|
while (pCurrentNode != NULL) {
|
|
if (hashedName32 == pCurrentNode->hashedName32) {
|
|
result = MA_ALREADY_EXISTS;
|
|
break;
|
|
} else {
|
|
if (hashedName32 < pCurrentNode->hashedName32) {
|
|
if (pCurrentNode->pChildLo == NULL) {
|
|
result = MA_SUCCESS;
|
|
break;
|
|
} else {
|
|
pCurrentNode = pCurrentNode->pChildLo;
|
|
}
|
|
} else {
|
|
if (pCurrentNode->pChildHi == NULL) {
|
|
result = MA_SUCCESS;
|
|
break;
|
|
} else {
|
|
pCurrentNode = pCurrentNode->pChildHi;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*ppInsertPoint = pCurrentNode;
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_insert_at(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_buffer_node* pInsertPoint)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
MA_ASSERT(pDataBufferNode->hashedName32 != 0);
|
|
|
|
if (pInsertPoint == NULL) {
|
|
pResourceManager->pRootDataBufferNode = pDataBufferNode;
|
|
} else {
|
|
if (pDataBufferNode->hashedName32 < pInsertPoint->hashedName32) {
|
|
MA_ASSERT(pInsertPoint->pChildLo == NULL);
|
|
pInsertPoint->pChildLo = pDataBufferNode;
|
|
} else {
|
|
MA_ASSERT(pInsertPoint->pChildHi == NULL);
|
|
pInsertPoint->pChildHi = pDataBufferNode;
|
|
}
|
|
}
|
|
|
|
pDataBufferNode->pParent = pInsertPoint;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if 0
|
|
static ma_result ma_resource_manager_data_buffer_node_insert(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
ma_result result;
|
|
ma_resource_manager_data_buffer_node* pInsertPoint;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, pDataBufferNode->hashedName32, &pInsertPoint);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
|
|
}
|
|
#endif
|
|
|
|
static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_min(ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
ma_resource_manager_data_buffer_node* pCurrentNode;
|
|
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
pCurrentNode = pDataBufferNode;
|
|
while (pCurrentNode->pChildLo != NULL) {
|
|
pCurrentNode = pCurrentNode->pChildLo;
|
|
}
|
|
|
|
return pCurrentNode;
|
|
}
|
|
|
|
static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_max(ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
ma_resource_manager_data_buffer_node* pCurrentNode;
|
|
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
pCurrentNode = pDataBufferNode;
|
|
while (pCurrentNode->pChildHi != NULL) {
|
|
pCurrentNode = pCurrentNode->pChildHi;
|
|
}
|
|
|
|
return pCurrentNode;
|
|
}
|
|
|
|
static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_successor(ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
MA_ASSERT(pDataBufferNode->pChildHi != NULL);
|
|
|
|
return ma_resource_manager_data_buffer_node_find_min(pDataBufferNode->pChildHi);
|
|
}
|
|
|
|
#if 0
|
|
static MA_INLINE ma_resource_manager_data_buffer_node* ma_resource_manager_data_buffer_node_find_inorder_predecessor(ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
MA_ASSERT(pDataBufferNode->pChildLo != NULL);
|
|
|
|
return ma_resource_manager_data_buffer_node_find_max(pDataBufferNode->pChildLo);
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_remove(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
if (pDataBufferNode->pChildLo == NULL) {
|
|
if (pDataBufferNode->pChildHi == NULL) {
|
|
if (pDataBufferNode->pParent == NULL) {
|
|
MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
|
|
pResourceManager->pRootDataBufferNode = NULL;
|
|
} else {
|
|
if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
|
|
pDataBufferNode->pParent->pChildLo = NULL;
|
|
} else {
|
|
pDataBufferNode->pParent->pChildHi = NULL;
|
|
}
|
|
}
|
|
} else {
|
|
pDataBufferNode->pChildHi->pParent = pDataBufferNode->pParent;
|
|
|
|
if (pDataBufferNode->pParent == NULL) {
|
|
MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
|
|
pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildHi;
|
|
} else {
|
|
if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
|
|
pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildHi;
|
|
} else {
|
|
pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildHi;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (pDataBufferNode->pChildHi == NULL) {
|
|
pDataBufferNode->pChildLo->pParent = pDataBufferNode->pParent;
|
|
|
|
if (pDataBufferNode->pParent == NULL) {
|
|
MA_ASSERT(pResourceManager->pRootDataBufferNode == pDataBufferNode);
|
|
pResourceManager->pRootDataBufferNode = pDataBufferNode->pChildLo;
|
|
} else {
|
|
if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
|
|
pDataBufferNode->pParent->pChildLo = pDataBufferNode->pChildLo;
|
|
} else {
|
|
pDataBufferNode->pParent->pChildHi = pDataBufferNode->pChildLo;
|
|
}
|
|
}
|
|
} else {
|
|
ma_resource_manager_data_buffer_node* pReplacementDataBufferNode;
|
|
|
|
pReplacementDataBufferNode = ma_resource_manager_data_buffer_node_find_inorder_successor(pDataBufferNode);
|
|
MA_ASSERT(pReplacementDataBufferNode != NULL);
|
|
|
|
MA_ASSERT(pReplacementDataBufferNode->pParent != NULL);
|
|
MA_ASSERT(pReplacementDataBufferNode->pChildLo == NULL);
|
|
|
|
if (pReplacementDataBufferNode->pChildHi == NULL) {
|
|
if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
|
|
pReplacementDataBufferNode->pParent->pChildLo = NULL;
|
|
} else {
|
|
pReplacementDataBufferNode->pParent->pChildHi = NULL;
|
|
}
|
|
} else {
|
|
pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode->pParent;
|
|
if (pReplacementDataBufferNode->pParent->pChildLo == pReplacementDataBufferNode) {
|
|
pReplacementDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode->pChildHi;
|
|
} else {
|
|
pReplacementDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode->pChildHi;
|
|
}
|
|
}
|
|
|
|
if (pDataBufferNode->pParent != NULL) {
|
|
if (pDataBufferNode->pParent->pChildLo == pDataBufferNode) {
|
|
pDataBufferNode->pParent->pChildLo = pReplacementDataBufferNode;
|
|
} else {
|
|
pDataBufferNode->pParent->pChildHi = pReplacementDataBufferNode;
|
|
}
|
|
}
|
|
|
|
pReplacementDataBufferNode->pParent = pDataBufferNode->pParent;
|
|
pReplacementDataBufferNode->pChildLo = pDataBufferNode->pChildLo;
|
|
pReplacementDataBufferNode->pChildHi = pDataBufferNode->pChildHi;
|
|
|
|
if (pReplacementDataBufferNode->pChildLo != NULL) {
|
|
pReplacementDataBufferNode->pChildLo->pParent = pReplacementDataBufferNode;
|
|
}
|
|
if (pReplacementDataBufferNode->pChildHi != NULL) {
|
|
pReplacementDataBufferNode->pChildHi->pParent = pReplacementDataBufferNode;
|
|
}
|
|
|
|
if (pResourceManager->pRootDataBufferNode == pDataBufferNode) {
|
|
pResourceManager->pRootDataBufferNode = pReplacementDataBufferNode;
|
|
}
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#if 0
|
|
static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_manager* pResourceManager, ma_uint32 hashedName32)
|
|
{
|
|
ma_result result;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode;
|
|
|
|
result = ma_resource_manager_data_buffer_search(pResourceManager, hashedName32, &pDataBufferNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_resource_manager_data_buffer_remove(pResourceManager, pDataBufferNode);
|
|
}
|
|
#endif
|
|
|
|
static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type);
|
|
}
|
|
|
|
static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType)
|
|
{
|
|
ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
|
|
{
|
|
ma_uint32 refCount;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
(void)pResourceManager;
|
|
|
|
refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1;
|
|
|
|
if (pNewRefCount != NULL) {
|
|
*pNewRefCount = refCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount)
|
|
{
|
|
ma_uint32 refCount;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
(void)pResourceManager;
|
|
|
|
refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1;
|
|
|
|
if (pNewRefCount != NULL) {
|
|
*pNewRefCount = refCount;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_resource_manager_data_buffer_node_free(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
if (pDataBufferNode->isDataOwnedByResourceManager) {
|
|
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_encoded) {
|
|
ma_free((void*)pDataBufferNode->data.backend.encoded.pData, &pResourceManager->config.allocationCallbacks);
|
|
pDataBufferNode->data.backend.encoded.pData = NULL;
|
|
pDataBufferNode->data.backend.encoded.sizeInBytes = 0;
|
|
} else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded) {
|
|
ma_free((void*)pDataBufferNode->data.backend.decoded.pData, &pResourceManager->config.allocationCallbacks);
|
|
pDataBufferNode->data.backend.decoded.pData = NULL;
|
|
pDataBufferNode->data.backend.decoded.totalFrameCount = 0;
|
|
} else if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode) == ma_resource_manager_data_supply_type_decoded_paged) {
|
|
ma_paged_audio_buffer_data_uninit(&pDataBufferNode->data.backend.decodedPaged.data, &pResourceManager->config.allocationCallbacks);
|
|
} else {
|
|
MA_ASSERT(pDataBufferNode->result != MA_SUCCESS);
|
|
}
|
|
}
|
|
|
|
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result);
|
|
}
|
|
|
|
static ma_bool32 ma_resource_manager_is_threading_enabled(const ma_resource_manager* pResourceManager)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
return (pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) == 0;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
union
|
|
{
|
|
ma_async_notification_event e;
|
|
ma_async_notification_poll p;
|
|
} backend;
|
|
ma_resource_manager* pResourceManager;
|
|
} ma_resource_manager_inline_notification;
|
|
|
|
static ma_result ma_resource_manager_inline_notification_init(ma_resource_manager* pResourceManager, ma_resource_manager_inline_notification* pNotification)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pNotification != NULL);
|
|
|
|
pNotification->pResourceManager = pResourceManager;
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
|
|
return ma_async_notification_event_init(&pNotification->backend.e);
|
|
} else {
|
|
return ma_async_notification_poll_init(&pNotification->backend.p);
|
|
}
|
|
}
|
|
|
|
static void ma_resource_manager_inline_notification_uninit(ma_resource_manager_inline_notification* pNotification)
|
|
{
|
|
MA_ASSERT(pNotification != NULL);
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
|
|
ma_async_notification_event_uninit(&pNotification->backend.e);
|
|
} else {
|
|
}
|
|
}
|
|
|
|
static void ma_resource_manager_inline_notification_wait(ma_resource_manager_inline_notification* pNotification)
|
|
{
|
|
MA_ASSERT(pNotification != NULL);
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pNotification->pResourceManager)) {
|
|
ma_async_notification_event_wait(&pNotification->backend.e);
|
|
} else {
|
|
while (ma_async_notification_poll_is_signalled(&pNotification->backend.p) == MA_FALSE) {
|
|
ma_result result = ma_resource_manager_process_next_job(pNotification->pResourceManager);
|
|
if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ma_resource_manager_inline_notification_wait_and_uninit(ma_resource_manager_inline_notification* pNotification)
|
|
{
|
|
ma_resource_manager_inline_notification_wait(pNotification);
|
|
ma_resource_manager_inline_notification_uninit(pNotification);
|
|
}
|
|
|
|
static void ma_resource_manager_data_buffer_bst_lock(ma_resource_manager* pResourceManager)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_mutex_lock(&pResourceManager->dataBufferBSTLock);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
} else {
|
|
}
|
|
}
|
|
|
|
static void ma_resource_manager_data_buffer_bst_unlock(ma_resource_manager* pResourceManager)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_mutex_unlock(&pResourceManager->dataBufferBSTLock);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
} else {
|
|
}
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
static ma_thread_result MA_THREADCALL ma_resource_manager_job_thread(void* pUserData)
|
|
{
|
|
ma_resource_manager* pResourceManager = (ma_resource_manager*)pUserData;
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
for (;;) {
|
|
ma_result result;
|
|
ma_job job;
|
|
|
|
result = ma_resource_manager_next_job(pResourceManager, &job);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (job.toc.breakup.code == MA_JOB_TYPE_QUIT) {
|
|
break;
|
|
}
|
|
|
|
ma_job_process(&job);
|
|
}
|
|
|
|
return (ma_thread_result)0;
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_resource_manager_config ma_resource_manager_config_init(void)
|
|
{
|
|
ma_resource_manager_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.decodedFormat = ma_format_unknown;
|
|
config.decodedChannels = 0;
|
|
config.decodedSampleRate = 0;
|
|
config.jobThreadCount = 1;
|
|
config.jobQueueCapacity = MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY;
|
|
config.resampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear);
|
|
|
|
config.flags = 0;
|
|
#ifdef MA_NO_THREADING
|
|
{
|
|
config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
|
|
config.jobThreadCount = 0;
|
|
}
|
|
#endif
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pConfig, ma_resource_manager* pResourceManager)
|
|
{
|
|
ma_result result;
|
|
ma_job_queue_config jobQueueConfig;
|
|
|
|
if (pResourceManager == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pResourceManager);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
if (pConfig->jobThreadCount > ma_countof(pResourceManager->jobThreads)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pResourceManager->config = *pConfig;
|
|
ma_allocation_callbacks_init_copy(&pResourceManager->config.allocationCallbacks, &pConfig->allocationCallbacks);
|
|
|
|
if (pResourceManager->config.pLog == NULL) {
|
|
result = ma_log_init(&pResourceManager->config.allocationCallbacks, &pResourceManager->log);
|
|
if (result == MA_SUCCESS) {
|
|
pResourceManager->config.pLog = &pResourceManager->log;
|
|
} else {
|
|
pResourceManager->config.pLog = NULL;
|
|
}
|
|
}
|
|
|
|
if (pResourceManager->config.pVFS == NULL) {
|
|
result = ma_default_vfs_init(&pResourceManager->defaultVFS, &pResourceManager->config.allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pResourceManager->config.pVFS = &pResourceManager->defaultVFS;
|
|
}
|
|
|
|
#ifdef MA_NO_THREADING
|
|
{
|
|
pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
|
|
}
|
|
#endif
|
|
|
|
if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
|
|
pResourceManager->config.flags |= MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING;
|
|
|
|
if (pResourceManager->config.jobThreadCount > 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
jobQueueConfig.capacity = pResourceManager->config.jobQueueCapacity;
|
|
jobQueueConfig.flags = 0;
|
|
if ((pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NON_BLOCKING) != 0) {
|
|
if (pResourceManager->config.jobThreadCount > 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
jobQueueConfig.flags |= MA_JOB_QUEUE_FLAG_NON_BLOCKING;
|
|
}
|
|
|
|
result = ma_job_queue_init(&jobQueueConfig, &pResourceManager->config.allocationCallbacks, &pResourceManager->jobQueue);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->ppCustomDecodingBackendVTables != NULL && pConfig->customDecodingBackendCount > 0) {
|
|
size_t sizeInBytes = sizeof(*pResourceManager->config.ppCustomDecodingBackendVTables) * pConfig->customDecodingBackendCount;
|
|
ma_decoding_backend_vtable** ppCustomDecodingBackendVTables;
|
|
|
|
ppCustomDecodingBackendVTables = (ma_decoding_backend_vtable**)ma_malloc(sizeInBytes, &pResourceManager->config.allocationCallbacks);
|
|
if (pResourceManager->config.ppCustomDecodingBackendVTables == NULL) {
|
|
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
MA_COPY_MEMORY(ppCustomDecodingBackendVTables, pConfig->ppCustomDecodingBackendVTables, sizeInBytes);
|
|
|
|
pResourceManager->config.ppCustomDecodingBackendVTables = ppCustomDecodingBackendVTables;
|
|
pResourceManager->config.customDecodingBackendCount = pConfig->customDecodingBackendCount;
|
|
pResourceManager->config.pCustomDecodingBackendUserData = pConfig->pCustomDecodingBackendUserData;
|
|
}
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_uint32 iJobThread;
|
|
|
|
result = ma_mutex_init(&pResourceManager->dataBufferBSTLock);
|
|
if (result != MA_SUCCESS) {
|
|
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
|
|
result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
|
|
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_resource_manager_delete_all_data_buffer_nodes(ma_resource_manager* pResourceManager)
|
|
{
|
|
MA_ASSERT(pResourceManager);
|
|
|
|
while (pResourceManager->pRootDataBufferNode != NULL) {
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode = pResourceManager->pRootDataBufferNode;
|
|
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
|
|
|
|
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_resource_manager_uninit(ma_resource_manager* pResourceManager)
|
|
{
|
|
if (pResourceManager == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_resource_manager_post_job_quit(pResourceManager);
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_uint32 iJobThread;
|
|
|
|
for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) {
|
|
ma_thread_wait(&pResourceManager->jobThreads[iJobThread]);
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ma_resource_manager_delete_all_data_buffer_nodes(pResourceManager);
|
|
|
|
ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks);
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager)) {
|
|
#ifndef MA_NO_THREADING
|
|
{
|
|
ma_mutex_uninit(&pResourceManager->dataBufferBSTLock);
|
|
}
|
|
#else
|
|
{
|
|
MA_ASSERT(MA_FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ma_free((ma_decoding_backend_vtable**)pResourceManager->config.ppCustomDecodingBackendVTables, &pResourceManager->config.allocationCallbacks);
|
|
|
|
if (pResourceManager->config.pLog == &pResourceManager->log) {
|
|
ma_log_uninit(&pResourceManager->log);
|
|
}
|
|
}
|
|
|
|
MA_API ma_log* ma_resource_manager_get_log(ma_resource_manager* pResourceManager)
|
|
{
|
|
if (pResourceManager == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pResourceManager->config.pLog;
|
|
}
|
|
|
|
MA_API ma_resource_manager_data_source_config ma_resource_manager_data_source_config_init(void)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.rangeBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_BEG;
|
|
config.rangeEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_RANGE_END;
|
|
config.loopPointBegInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG;
|
|
config.loopPointEndInPCMFrames = MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END;
|
|
config.isLooping = MA_FALSE;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_decoder_config ma_resource_manager__init_decoder_config(ma_resource_manager* pResourceManager)
|
|
{
|
|
ma_decoder_config config;
|
|
|
|
config = ma_decoder_config_init(pResourceManager->config.decodedFormat, pResourceManager->config.decodedChannels, pResourceManager->config.decodedSampleRate);
|
|
config.allocationCallbacks = pResourceManager->config.allocationCallbacks;
|
|
config.ppCustomBackendVTables = pResourceManager->config.ppCustomDecodingBackendVTables;
|
|
config.customBackendCount = pResourceManager->config.customDecodingBackendCount;
|
|
config.pCustomBackendUserData = pResourceManager->config.pCustomDecodingBackendUserData;
|
|
config.resampling = pResourceManager->config.resampling;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_result ma_resource_manager__init_decoder(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result;
|
|
ma_decoder_config config;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
config = ma_resource_manager__init_decoder_config(pResourceManager);
|
|
|
|
if (pFilePath != NULL) {
|
|
result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pFilePath, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result));
|
|
return result;
|
|
}
|
|
} else {
|
|
result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pFilePathW, &config, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result));
|
|
#endif
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_bool32 ma_resource_manager_data_buffer_has_connector(ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
return ma_atomic_bool32_get(&pDataBuffer->isConnectorInitialized);
|
|
}
|
|
|
|
static ma_data_source* ma_resource_manager_data_buffer_get_connector(ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
|
|
return NULL;
|
|
}
|
|
|
|
switch (pDataBuffer->pNode->data.type)
|
|
{
|
|
case ma_resource_manager_data_supply_type_encoded: return &pDataBuffer->connector.decoder;
|
|
case ma_resource_manager_data_supply_type_decoded: return &pDataBuffer->connector.buffer;
|
|
case ma_resource_manager_data_supply_type_decoded_paged: return &pDataBuffer->connector.pagedBuffer;
|
|
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
default:
|
|
{
|
|
ma_log_postf(ma_resource_manager_get_log(pDataBuffer->pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to retrieve data buffer connector. Unknown data supply type.\n");
|
|
return NULL;
|
|
};
|
|
};
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_init_connector(ma_resource_manager_data_buffer* pDataBuffer, const ma_resource_manager_data_source_config* pConfig, ma_async_notification* pInitNotification, ma_fence* pInitFence)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE);
|
|
|
|
result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
|
|
if (result != MA_SUCCESS && result != MA_BUSY) {
|
|
return result;
|
|
}
|
|
|
|
switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
|
|
{
|
|
case ma_resource_manager_data_supply_type_encoded:
|
|
{
|
|
ma_decoder_config config;
|
|
config = ma_resource_manager__init_decoder_config(pDataBuffer->pResourceManager);
|
|
result = ma_decoder_init_memory(pDataBuffer->pNode->data.backend.encoded.pData, pDataBuffer->pNode->data.backend.encoded.sizeInBytes, &config, &pDataBuffer->connector.decoder);
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_decoded:
|
|
{
|
|
ma_audio_buffer_config config;
|
|
config = ma_audio_buffer_config_init(pDataBuffer->pNode->data.backend.decoded.format, pDataBuffer->pNode->data.backend.decoded.channels, pDataBuffer->pNode->data.backend.decoded.totalFrameCount, pDataBuffer->pNode->data.backend.decoded.pData, NULL);
|
|
result = ma_audio_buffer_init(&config, &pDataBuffer->connector.buffer);
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_decoded_paged:
|
|
{
|
|
ma_paged_audio_buffer_config config;
|
|
config = ma_paged_audio_buffer_config_init(&pDataBuffer->pNode->data.backend.decodedPaged.data);
|
|
result = ma_paged_audio_buffer_init(&config, &pDataBuffer->connector.pagedBuffer);
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
default:
|
|
{
|
|
return MA_INVALID_ARGS;
|
|
};
|
|
}
|
|
|
|
if (result == MA_SUCCESS) {
|
|
|
|
if (pConfig->rangeBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_BEG || pConfig->rangeEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_RANGE_END) {
|
|
ma_data_source_set_range_in_pcm_frames(pDataBuffer, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
|
|
}
|
|
|
|
if (pConfig->loopPointBegInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_BEG || pConfig->loopPointEndInPCMFrames != MA_DATA_SOURCE_DEFAULT_LOOP_POINT_END) {
|
|
ma_data_source_set_loop_point_in_pcm_frames(pDataBuffer, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
|
|
}
|
|
|
|
if (pConfig->isLooping != MA_FALSE) {
|
|
ma_data_source_set_looping(pDataBuffer, pConfig->isLooping);
|
|
}
|
|
|
|
ma_atomic_bool32_set(&pDataBuffer->isConnectorInitialized, MA_TRUE);
|
|
|
|
if (pInitNotification != NULL) {
|
|
ma_async_notification_signal(pInitNotification);
|
|
}
|
|
|
|
if (pInitFence != NULL) {
|
|
ma_fence_release(pInitFence);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
|
|
(void)pResourceManager;
|
|
|
|
switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
|
|
{
|
|
case ma_resource_manager_data_supply_type_encoded:
|
|
{
|
|
ma_decoder_uninit(&pDataBuffer->connector.decoder);
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_decoded:
|
|
{
|
|
ma_audio_buffer_uninit(&pDataBuffer->connector.buffer);
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_decoded_paged:
|
|
{
|
|
ma_paged_audio_buffer_uninit(&pDataBuffer->connector.pagedBuffer);
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
default:
|
|
{
|
|
return MA_INVALID_ARGS;
|
|
};
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode)
|
|
{
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW)
|
|
{
|
|
ma_result result;
|
|
size_t dataSizeInBytes;
|
|
void* pData;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
|
|
|
|
result = ma_vfs_open_and_read_file_ex(pResourceManager->config.pVFS, pFilePath, pFilePathW, &pData, &dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
if (pFilePath != NULL) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%s\". %s.\n", pFilePath, ma_result_description(result));
|
|
} else {
|
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to load file \"%ls\". %s.\n", pFilePathW, ma_result_description(result));
|
|
#endif
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
pDataBufferNode->data.backend.encoded.pData = pData;
|
|
pDataBufferNode->data.backend.encoded.sizeInBytes = dataSizeInBytes;
|
|
ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_encoded);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_init_supply_decoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 flags, ma_decoder** ppDecoder)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_decoder* pDecoder;
|
|
ma_uint64 totalFrameCount;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
MA_ASSERT(ppDecoder != NULL);
|
|
MA_ASSERT(pFilePath != NULL || pFilePathW != NULL);
|
|
|
|
*ppDecoder = NULL;
|
|
|
|
pDecoder = (ma_decoder*)ma_malloc(sizeof(*pDecoder), &pResourceManager->config.allocationCallbacks);
|
|
if (pDecoder == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_resource_manager__init_decoder(pResourceManager, pFilePath, pFilePathW, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
|
|
result = ma_decoder_get_length_in_pcm_frames(pDecoder, &totalFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
} else {
|
|
totalFrameCount = 0;
|
|
}
|
|
|
|
if (totalFrameCount > 0) {
|
|
ma_uint64 dataSizeInBytes;
|
|
void* pData;
|
|
|
|
dataSizeInBytes = totalFrameCount * ma_get_bytes_per_frame(pDecoder->outputFormat, pDecoder->outputChannels);
|
|
if (dataSizeInBytes > MA_SIZE_MAX) {
|
|
ma_decoder_uninit(pDecoder);
|
|
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
return MA_TOO_BIG;
|
|
}
|
|
|
|
pData = ma_malloc((size_t)dataSizeInBytes, &pResourceManager->config.allocationCallbacks);
|
|
if (pData == NULL) {
|
|
ma_decoder_uninit(pDecoder);
|
|
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
ma_silence_pcm_frames(pData, totalFrameCount, pDecoder->outputFormat, pDecoder->outputChannels);
|
|
|
|
pDataBufferNode->data.backend.decoded.pData = pData;
|
|
pDataBufferNode->data.backend.decoded.totalFrameCount = totalFrameCount;
|
|
pDataBufferNode->data.backend.decoded.format = pDecoder->outputFormat;
|
|
pDataBufferNode->data.backend.decoded.channels = pDecoder->outputChannels;
|
|
pDataBufferNode->data.backend.decoded.sampleRate = pDecoder->outputSampleRate;
|
|
pDataBufferNode->data.backend.decoded.decodedFrameCount = 0;
|
|
ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded);
|
|
} else {
|
|
result = ma_paged_audio_buffer_data_init(pDecoder->outputFormat, pDecoder->outputChannels, &pDataBufferNode->data.backend.decodedPaged.data);
|
|
if (result != MA_SUCCESS) {
|
|
ma_decoder_uninit(pDecoder);
|
|
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pDataBufferNode->data.backend.decodedPaged.sampleRate = pDecoder->outputSampleRate;
|
|
pDataBufferNode->data.backend.decodedPaged.decodedFrameCount = 0;
|
|
ma_resource_manager_data_buffer_node_set_data_supply_type(pDataBufferNode, ma_resource_manager_data_supply_type_decoded_paged);
|
|
}
|
|
|
|
*ppDecoder = pDecoder;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_decode_next_page(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_decoder* pDecoder)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 pageSizeInFrames;
|
|
ma_uint64 framesToTryReading;
|
|
ma_uint64 framesRead;
|
|
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
MA_ASSERT(pDecoder != NULL);
|
|
|
|
pageSizeInFrames = MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDecoder->outputSampleRate/1000);
|
|
framesToTryReading = pageSizeInFrames;
|
|
|
|
switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode))
|
|
{
|
|
case ma_resource_manager_data_supply_type_decoded:
|
|
{
|
|
void* pDst;
|
|
ma_uint64 framesRemaining = pDataBufferNode->data.backend.decoded.totalFrameCount - pDataBufferNode->data.backend.decoded.decodedFrameCount;
|
|
if (framesToTryReading > framesRemaining) {
|
|
framesToTryReading = framesRemaining;
|
|
}
|
|
|
|
if (framesToTryReading > 0) {
|
|
pDst = ma_offset_ptr(
|
|
pDataBufferNode->data.backend.decoded.pData,
|
|
pDataBufferNode->data.backend.decoded.decodedFrameCount * ma_get_bytes_per_frame(pDataBufferNode->data.backend.decoded.format, pDataBufferNode->data.backend.decoded.channels)
|
|
);
|
|
MA_ASSERT(pDst != NULL);
|
|
|
|
result = ma_decoder_read_pcm_frames(pDecoder, pDst, framesToTryReading, &framesRead);
|
|
if (framesRead > 0) {
|
|
pDataBufferNode->data.backend.decoded.decodedFrameCount += framesRead;
|
|
}
|
|
} else {
|
|
framesRead = 0;
|
|
}
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_decoded_paged:
|
|
{
|
|
ma_paged_audio_buffer_page* pPage;
|
|
|
|
result = ma_paged_audio_buffer_data_allocate_page(&pDataBufferNode->data.backend.decodedPaged.data, framesToTryReading, NULL, &pResourceManager->config.allocationCallbacks, &pPage);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_decoder_read_pcm_frames(pDecoder, pPage->pAudioData, framesToTryReading, &framesRead);
|
|
if (result == MA_SUCCESS && framesRead > 0) {
|
|
pPage->sizeInFrames = framesRead;
|
|
|
|
result = ma_paged_audio_buffer_data_append_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage);
|
|
if (result == MA_SUCCESS) {
|
|
pDataBufferNode->data.backend.decodedPaged.decodedFrameCount += framesRead;
|
|
} else {
|
|
ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
|
|
result = MA_AT_END;
|
|
}
|
|
} else {
|
|
ma_paged_audio_buffer_data_free_page(&pDataBufferNode->data.backend.decodedPaged.data, pPage, &pResourceManager->config.allocationCallbacks);
|
|
result = MA_AT_END;
|
|
}
|
|
} break;
|
|
|
|
case ma_resource_manager_data_supply_type_encoded:
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
default:
|
|
{
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Unexpected data supply type (%d) when decoding page.", ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBufferNode));
|
|
return MA_ERROR;
|
|
};
|
|
}
|
|
|
|
if (result == MA_SUCCESS && framesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_inline_notification* pInitNotification, ma_resource_manager_data_buffer_node** ppDataBufferNode)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
|
|
ma_resource_manager_data_buffer_node* pInsertPoint;
|
|
|
|
if (ppDataBufferNode != NULL) {
|
|
*ppDataBufferNode = NULL;
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_node_insert_point(pResourceManager, hashedName32, &pInsertPoint);
|
|
if (result == MA_ALREADY_EXISTS) {
|
|
pDataBufferNode = pInsertPoint;
|
|
|
|
result = ma_resource_manager_data_buffer_node_increment_ref(pResourceManager, pDataBufferNode, NULL);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = MA_ALREADY_EXISTS;
|
|
goto done;
|
|
} else {
|
|
pDataBufferNode = (ma_resource_manager_data_buffer_node*)ma_malloc(sizeof(*pDataBufferNode), &pResourceManager->config.allocationCallbacks);
|
|
if (pDataBufferNode == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDataBufferNode);
|
|
pDataBufferNode->hashedName32 = hashedName32;
|
|
pDataBufferNode->refCount = 1;
|
|
|
|
if (pExistingData == NULL) {
|
|
pDataBufferNode->data.type = ma_resource_manager_data_supply_type_unknown;
|
|
pDataBufferNode->result = MA_BUSY;
|
|
pDataBufferNode->isDataOwnedByResourceManager = MA_TRUE;
|
|
} else {
|
|
pDataBufferNode->data = *pExistingData;
|
|
pDataBufferNode->result = MA_SUCCESS;
|
|
pDataBufferNode->isDataOwnedByResourceManager = MA_FALSE;
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_node_insert_at(pResourceManager, pDataBufferNode, pInsertPoint);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if (pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
|
|
ma_job job;
|
|
char* pFilePathCopy = NULL;
|
|
wchar_t* pFilePathWCopy = NULL;
|
|
|
|
if (pFilePath != NULL) {
|
|
pFilePathCopy = ma_copy_string(pFilePath, &pResourceManager->config.allocationCallbacks);
|
|
} else {
|
|
pFilePathWCopy = ma_copy_string_w(pFilePathW, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
|
|
if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
|
|
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
|
|
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification);
|
|
}
|
|
|
|
if (pInitFence != NULL) { ma_fence_acquire(pInitFence); }
|
|
if (pDoneFence != NULL) { ma_fence_acquire(pDoneFence); }
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE);
|
|
job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
|
|
job.data.resourceManager.loadDataBufferNode.pResourceManager = pResourceManager;
|
|
job.data.resourceManager.loadDataBufferNode.pDataBufferNode = pDataBufferNode;
|
|
job.data.resourceManager.loadDataBufferNode.pFilePath = pFilePathCopy;
|
|
job.data.resourceManager.loadDataBufferNode.pFilePathW = pFilePathWCopy;
|
|
job.data.resourceManager.loadDataBufferNode.flags = flags;
|
|
job.data.resourceManager.loadDataBufferNode.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? pInitNotification : NULL;
|
|
job.data.resourceManager.loadDataBufferNode.pDoneNotification = NULL;
|
|
job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence;
|
|
job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence;
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
result = ma_job_process(&job);
|
|
} else {
|
|
result = ma_resource_manager_post_job(pResourceManager, &job);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result));
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_uninit(pInitNotification);
|
|
} else {
|
|
|
|
if (pInitFence != NULL) { ma_fence_release(pInitFence); }
|
|
if (pDoneFence != NULL) { ma_fence_release(pDoneFence); }
|
|
|
|
ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
|
|
ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
|
|
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
|
|
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
|
|
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (ppDataBufferNode != NULL) {
|
|
*ppDataBufferNode = pDataBufferNode;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manager* pResourceManager, const char* pFilePath, const wchar_t* pFilePathW, ma_uint32 hashedName32, ma_uint32 flags, const ma_resource_manager_data_supply* pExistingData, ma_fence* pInitFence, ma_fence* pDoneFence, ma_resource_manager_data_buffer_node** ppDataBufferNode)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_bool32 nodeAlreadyExists = MA_FALSE;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode = NULL;
|
|
ma_resource_manager_inline_notification initNotification;
|
|
|
|
if (ppDataBufferNode != NULL) {
|
|
*ppDataBufferNode = NULL;
|
|
}
|
|
|
|
if (pResourceManager == NULL || (pFilePath == NULL && pFilePathW == NULL && hashedName32 == 0)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pExistingData != NULL && pExistingData->type == ma_resource_manager_data_supply_type_unknown) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
|
|
flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
|
|
}
|
|
|
|
if (hashedName32 == 0) {
|
|
if (pFilePath != NULL) {
|
|
hashedName32 = ma_hash_string_32(pFilePath);
|
|
} else {
|
|
hashedName32 = ma_hash_string_w_32(pFilePathW);
|
|
}
|
|
}
|
|
|
|
ma_resource_manager_data_buffer_bst_lock(pResourceManager);
|
|
{
|
|
result = ma_resource_manager_data_buffer_node_acquire_critical_section(pResourceManager, pFilePath, pFilePathW, hashedName32, flags, pExistingData, pInitFence, pDoneFence, &initNotification, &pDataBufferNode);
|
|
}
|
|
ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
|
|
|
|
if (result == MA_ALREADY_EXISTS) {
|
|
nodeAlreadyExists = MA_TRUE;
|
|
result = MA_SUCCESS;
|
|
} else {
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (nodeAlreadyExists == MA_FALSE) {
|
|
if (pFilePath == NULL && pFilePathW == NULL) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Cloning data buffer node failed because the source node was released. The source node must remain valid until the cloning has completed.\n");
|
|
result = MA_INVALID_OPERATION;
|
|
goto done;
|
|
}
|
|
|
|
if (pDataBufferNode->isDataOwnedByResourceManager) {
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0) {
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) == 0) {
|
|
result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW);
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
} else {
|
|
ma_decoder* pDecoder;
|
|
result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pFilePath, pFilePathW, flags, &pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
|
|
for (;;) {
|
|
result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, pDecoder);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result == MA_AT_END) {
|
|
result = MA_SUCCESS;
|
|
}
|
|
|
|
ma_decoder_uninit(pDecoder);
|
|
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
|
|
ma_atomic_exchange_i32(&pDataBufferNode->result, result);
|
|
} else {
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_wait(&initNotification);
|
|
}
|
|
}
|
|
} else {
|
|
MA_ASSERT(pExistingData != NULL);
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (result != MA_SUCCESS) {
|
|
if (nodeAlreadyExists == MA_FALSE) {
|
|
ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
|
|
ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
}
|
|
|
|
if (nodeAlreadyExists == MA_FALSE && pDataBufferNode->isDataOwnedByResourceManager && (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0) {
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_uninit(&initNotification);
|
|
}
|
|
}
|
|
|
|
if (ppDataBufferNode != NULL) {
|
|
*ppDataBufferNode = pDataBufferNode;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_node_unacquire(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pName, const wchar_t* pNameW)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 refCount = 0xFFFFFFFF;
|
|
ma_uint32 hashedName32 = 0;
|
|
|
|
if (pResourceManager == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pDataBufferNode == NULL) {
|
|
if (pName == NULL && pNameW == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pName != NULL) {
|
|
hashedName32 = ma_hash_string_32(pName);
|
|
} else {
|
|
hashedName32 = ma_hash_string_w_32(pNameW);
|
|
}
|
|
}
|
|
|
|
ma_resource_manager_data_buffer_bst_lock(pResourceManager);
|
|
{
|
|
if (pDataBufferNode == NULL) {
|
|
result = ma_resource_manager_data_buffer_node_search(pResourceManager, hashedName32, &pDataBufferNode);
|
|
if (result != MA_SUCCESS) {
|
|
goto stage2;
|
|
}
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_node_decrement_ref(pResourceManager, pDataBufferNode, &refCount);
|
|
if (result != MA_SUCCESS) {
|
|
goto stage2;
|
|
}
|
|
|
|
if (refCount == 0) {
|
|
result = ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode);
|
|
if (result != MA_SUCCESS) {
|
|
goto stage2;
|
|
}
|
|
}
|
|
}
|
|
ma_resource_manager_data_buffer_bst_unlock(pResourceManager);
|
|
|
|
stage2:
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (refCount == 0) {
|
|
if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
|
|
ma_job job;
|
|
|
|
ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE);
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE);
|
|
job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
|
|
job.data.resourceManager.freeDataBufferNode.pResourceManager = pResourceManager;
|
|
job.data.resourceManager.freeDataBufferNode.pDataBufferNode = pDataBufferNode;
|
|
|
|
result = ma_resource_manager_post_job(pResourceManager, &job);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result));
|
|
return result;
|
|
}
|
|
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
|
|
while (ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_BUSY) {
|
|
result = ma_resource_manager_process_next_job(pResourceManager);
|
|
if (result == MA_NO_DATA_AVAILABLE || result == MA_CANCELLED) {
|
|
result = MA_SUCCESS;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else {
|
|
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_resource_manager_data_buffer_read_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_resource_manager_data_buffer_seek_to_pcm_frame((ma_resource_manager_data_buffer*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
return ma_resource_manager_data_buffer_get_data_format((ma_resource_manager_data_buffer*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_resource_manager_data_buffer_get_length_in_pcm_frames((ma_resource_manager_data_buffer*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
|
|
{
|
|
ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource;
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
|
|
ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping);
|
|
|
|
ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_resource_manager_data_buffer_vtable =
|
|
{
|
|
ma_resource_manager_data_buffer_cb__read_pcm_frames,
|
|
ma_resource_manager_data_buffer_cb__seek_to_pcm_frame,
|
|
ma_resource_manager_data_buffer_cb__get_data_format,
|
|
ma_resource_manager_data_buffer_cb__get_cursor_in_pcm_frames,
|
|
ma_resource_manager_data_buffer_cb__get_length_in_pcm_frames,
|
|
ma_resource_manager_data_buffer_cb__set_looping,
|
|
0
|
|
};
|
|
|
|
static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_uint32 hashedName32, ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode;
|
|
ma_data_source_config dataSourceConfig;
|
|
ma_bool32 async;
|
|
ma_uint32 flags;
|
|
ma_resource_manager_pipeline_notifications notifications;
|
|
|
|
if (pDataBuffer == NULL) {
|
|
if (pConfig != NULL && pConfig->pNotifications != NULL) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
|
|
}
|
|
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDataBuffer);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->pNotifications != NULL) {
|
|
notifications = *pConfig->pNotifications;
|
|
} else {
|
|
MA_ZERO_OBJECT(¬ifications);
|
|
}
|
|
|
|
flags = pConfig->flags;
|
|
if (ma_resource_manager_is_threading_enabled(pResourceManager) == MA_FALSE) {
|
|
flags &= ~MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC;
|
|
}
|
|
|
|
if (pConfig->isLooping) {
|
|
flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
|
|
}
|
|
|
|
async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0;
|
|
|
|
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
|
|
{
|
|
result = ma_resource_manager_data_buffer_node_acquire(pResourceManager, pConfig->pFilePath, pConfig->pFilePathW, hashedName32, flags, NULL, notifications.init.pFence, notifications.done.pFence, &pDataBufferNode);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
goto done;
|
|
}
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_resource_manager_data_buffer_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pDataBuffer->ds);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
goto done;
|
|
}
|
|
|
|
pDataBuffer->pResourceManager = pResourceManager;
|
|
pDataBuffer->pNode = pDataBufferNode;
|
|
pDataBuffer->flags = flags;
|
|
pDataBuffer->result = MA_BUSY;
|
|
|
|
if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) {
|
|
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL);
|
|
ma_atomic_exchange_i32(&pDataBuffer->result, result);
|
|
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
goto done;
|
|
} else {
|
|
ma_job job;
|
|
ma_resource_manager_inline_notification initNotification;
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_init(pResourceManager, &initNotification);
|
|
}
|
|
|
|
ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY);
|
|
|
|
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER);
|
|
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
|
|
job.data.resourceManager.loadDataBuffer.pDataBuffer = pDataBuffer;
|
|
job.data.resourceManager.loadDataBuffer.pInitNotification = ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) ? &initNotification : notifications.init.pNotification;
|
|
job.data.resourceManager.loadDataBuffer.pDoneNotification = notifications.done.pNotification;
|
|
job.data.resourceManager.loadDataBuffer.pInitFence = notifications.init.pFence;
|
|
job.data.resourceManager.loadDataBuffer.pDoneFence = notifications.done.pFence;
|
|
job.data.resourceManager.loadDataBuffer.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
|
|
job.data.resourceManager.loadDataBuffer.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
|
|
job.data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
|
|
job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
|
|
job.data.resourceManager.loadDataBuffer.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0;
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
result = ma_job_process(&job);
|
|
} else {
|
|
result = ma_resource_manager_post_job(pResourceManager, &job);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result));
|
|
ma_atomic_exchange_i32(&pDataBuffer->result, result);
|
|
|
|
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
|
|
} else {
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_wait(&initNotification);
|
|
|
|
if (notifications.init.pNotification != NULL) {
|
|
ma_async_notification_signal(notifications.init.pNotification);
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_result(pDataBuffer);
|
|
if (result == MA_BUSY) {
|
|
result = MA_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
ma_resource_manager_inline_notification_uninit(&initNotification);
|
|
}
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_data_buffer_node_unacquire(pResourceManager, pDataBufferNode, NULL, NULL);
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
if (result == MA_SUCCESS) {
|
|
if (pConfig->initialSeekPointInPCMFrames > 0) {
|
|
ma_resource_manager_data_buffer_seek_to_pcm_frame(pDataBuffer, pConfig->initialSeekPointInPCMFrames);
|
|
}
|
|
}
|
|
|
|
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, pConfig, 0, pDataBuffer);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.pFilePath = pFilePath;
|
|
config.flags = flags;
|
|
config.pNotifications = pNotifications;
|
|
|
|
return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.pFilePathW = pFilePath;
|
|
config.flags = flags;
|
|
config.pNotifications = pNotifications;
|
|
|
|
return ma_resource_manager_data_buffer_init_ex(pResourceManager, &config, pDataBuffer);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_buffer* pExistingDataBuffer, ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
if (pExistingDataBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(pExistingDataBuffer->pNode != NULL);
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.flags = pExistingDataBuffer->flags;
|
|
|
|
return ma_resource_manager_data_buffer_init_ex_internal(pResourceManager, &config, pExistingDataBuffer->pNode->hashedName32, pDataBuffer);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_buffer_uninit_internal(ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
|
|
ma_resource_manager_data_buffer_uninit_connector(pDataBuffer->pResourceManager, pDataBuffer);
|
|
|
|
ma_resource_manager_data_buffer_node_unacquire(pDataBuffer->pResourceManager, pDataBuffer->pNode, NULL, NULL);
|
|
|
|
ma_data_source_uninit(&pDataBuffer->ds);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pDataBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_data_buffer_result(pDataBuffer) == MA_SUCCESS) {
|
|
return ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
|
|
} else {
|
|
ma_resource_manager_inline_notification notification;
|
|
ma_job job;
|
|
|
|
ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE);
|
|
|
|
result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER);
|
|
job.order = ma_resource_manager_data_buffer_next_execution_order(pDataBuffer);
|
|
job.data.resourceManager.freeDataBuffer.pDataBuffer = pDataBuffer;
|
|
job.data.resourceManager.freeDataBuffer.pDoneNotification = ¬ification;
|
|
job.data.resourceManager.freeDataBuffer.pDoneFence = NULL;
|
|
|
|
result = ma_resource_manager_post_job(pDataBuffer->pResourceManager, &job);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_inline_notification_uninit(¬ification);
|
|
return result;
|
|
}
|
|
|
|
ma_resource_manager_inline_notification_wait_and_uninit(¬ification);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_read_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 framesRead = 0;
|
|
ma_bool32 isDecodedBufferBusy = MA_FALSE;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
|
|
|
|
if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
|
|
return MA_BUSY;
|
|
}
|
|
|
|
if (pDataBuffer->seekToCursorOnNextRead) {
|
|
pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
|
|
|
|
result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pDataBuffer->seekTargetInPCMFrames);
|
|
if (result != MA_SUCCESS) {
|
|
if (result == MA_BAD_SEEK && ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded_paged) {
|
|
pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
|
|
return MA_BUSY;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_decoded) {
|
|
ma_uint64 availableFrames;
|
|
|
|
isDecodedBufferBusy = (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY);
|
|
|
|
if (ma_resource_manager_data_buffer_get_available_frames(pDataBuffer, &availableFrames) == MA_SUCCESS) {
|
|
if (isDecodedBufferBusy) {
|
|
if (frameCount > availableFrames) {
|
|
frameCount = availableFrames;
|
|
|
|
if (frameCount == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
} else {
|
|
isDecodedBufferBusy = MA_FALSE;
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
|
|
if (frameCount > 0) {
|
|
result = ma_data_source_read_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pFramesOut, frameCount, &framesRead);
|
|
}
|
|
|
|
if (result == MA_AT_END) {
|
|
if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
|
|
result = MA_BUSY;
|
|
}
|
|
}
|
|
|
|
if (isDecodedBufferBusy) {
|
|
result = MA_BUSY;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = framesRead;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && framesRead == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_seek_to_pcm_frame(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64 frameIndex)
|
|
{
|
|
ma_result result;
|
|
|
|
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
|
|
|
|
if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE) {
|
|
pDataBuffer->seekTargetInPCMFrames = frameIndex;
|
|
pDataBuffer->seekToCursorOnNextRead = MA_TRUE;
|
|
return MA_BUSY;
|
|
}
|
|
|
|
result = ma_data_source_seek_to_pcm_frame(ma_resource_manager_data_buffer_get_connector(pDataBuffer), frameIndex);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDataBuffer->seekTargetInPCMFrames = ~(ma_uint64)0;
|
|
pDataBuffer->seekToCursorOnNextRead = MA_FALSE;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_data_format(ma_resource_manager_data_buffer* pDataBuffer, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
|
|
|
|
switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
|
|
{
|
|
case ma_resource_manager_data_supply_type_encoded:
|
|
{
|
|
return ma_data_source_get_data_format(&pDataBuffer->connector.decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_decoded:
|
|
{
|
|
*pFormat = pDataBuffer->pNode->data.backend.decoded.format;
|
|
*pChannels = pDataBuffer->pNode->data.backend.decoded.channels;
|
|
*pSampleRate = pDataBuffer->pNode->data.backend.decoded.sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);
|
|
return MA_SUCCESS;
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_decoded_paged:
|
|
{
|
|
*pFormat = pDataBuffer->pNode->data.backend.decodedPaged.data.format;
|
|
*pChannels = pDataBuffer->pNode->data.backend.decodedPaged.data.channels;
|
|
*pSampleRate = pDataBuffer->pNode->data.backend.decodedPaged.sampleRate;
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pDataBuffer->pNode->data.backend.decoded.channels);
|
|
return MA_SUCCESS;
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
{
|
|
return MA_BUSY;
|
|
};
|
|
|
|
default:
|
|
{
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pCursor)
|
|
{
|
|
if (pDataBuffer == NULL || pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
|
|
|
|
*pCursor = 0;
|
|
|
|
switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
|
|
{
|
|
case ma_resource_manager_data_supply_type_encoded:
|
|
{
|
|
return ma_decoder_get_cursor_in_pcm_frames(&pDataBuffer->connector.decoder, pCursor);
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_decoded:
|
|
{
|
|
return ma_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.buffer, pCursor);
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_decoded_paged:
|
|
{
|
|
return ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, pCursor);
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
{
|
|
return MA_BUSY;
|
|
};
|
|
|
|
default:
|
|
{
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_length_in_pcm_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pLength)
|
|
{
|
|
if (pDataBuffer == NULL || pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) != MA_UNAVAILABLE);
|
|
|
|
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
|
|
return MA_BUSY;
|
|
}
|
|
|
|
return ma_data_source_get_length_in_pcm_frames(ma_resource_manager_data_buffer_get_connector(pDataBuffer), pLength);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
if (pDataBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping)
|
|
{
|
|
return ma_data_source_set_looping(pDataBuffer, isLooping);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_resource_manager_data_buffer_is_looping(const ma_resource_manager_data_buffer* pDataBuffer)
|
|
{
|
|
return ma_data_source_is_looping(pDataBuffer);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_buffer_get_available_frames(ma_resource_manager_data_buffer* pDataBuffer, ma_uint64* pAvailableFrames)
|
|
{
|
|
if (pAvailableFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pAvailableFrames = 0;
|
|
|
|
if (pDataBuffer == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode) == ma_resource_manager_data_supply_type_unknown) {
|
|
if (ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode) == MA_BUSY) {
|
|
return MA_BUSY;
|
|
} else {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
|
|
switch (ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode))
|
|
{
|
|
case ma_resource_manager_data_supply_type_encoded:
|
|
{
|
|
return ma_decoder_get_available_frames(&pDataBuffer->connector.decoder, pAvailableFrames);
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_decoded:
|
|
{
|
|
return ma_audio_buffer_get_available_frames(&pDataBuffer->connector.buffer, pAvailableFrames);
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_decoded_paged:
|
|
{
|
|
ma_uint64 cursor;
|
|
ma_paged_audio_buffer_get_cursor_in_pcm_frames(&pDataBuffer->connector.pagedBuffer, &cursor);
|
|
|
|
if (pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount > cursor) {
|
|
*pAvailableFrames = pDataBuffer->pNode->data.backend.decodedPaged.decodedFrameCount - cursor;
|
|
} else {
|
|
*pAvailableFrames = 0;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
};
|
|
|
|
case ma_resource_manager_data_supply_type_unknown:
|
|
default:
|
|
{
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_register_file(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags)
|
|
{
|
|
return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pFilePath, NULL, 0, flags, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_register_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags)
|
|
{
|
|
return ma_resource_manager_data_buffer_node_acquire(pResourceManager, NULL, pFilePath, 0, flags, NULL, NULL, NULL, NULL);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_register_data(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, ma_resource_manager_data_supply* pExistingData)
|
|
{
|
|
return ma_resource_manager_data_buffer_node_acquire(pResourceManager, pName, pNameW, 0, 0, pExistingData, NULL, NULL, NULL);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_register_decoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
|
|
{
|
|
ma_resource_manager_data_supply data;
|
|
data.type = ma_resource_manager_data_supply_type_decoded;
|
|
data.backend.decoded.pData = pData;
|
|
data.backend.decoded.totalFrameCount = frameCount;
|
|
data.backend.decoded.format = format;
|
|
data.backend.decoded.channels = channels;
|
|
data.backend.decoded.sampleRate = sampleRate;
|
|
|
|
return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_register_decoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
|
|
{
|
|
return ma_resource_manager_register_decoded_data_internal(pResourceManager, pName, NULL, pData, frameCount, format, channels, sampleRate);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_register_decoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, ma_uint64 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
|
|
{
|
|
return ma_resource_manager_register_decoded_data_internal(pResourceManager, NULL, pName, pData, frameCount, format, channels, sampleRate);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_register_encoded_data_internal(ma_resource_manager* pResourceManager, const char* pName, const wchar_t* pNameW, const void* pData, size_t sizeInBytes)
|
|
{
|
|
ma_resource_manager_data_supply data;
|
|
data.type = ma_resource_manager_data_supply_type_encoded;
|
|
data.backend.encoded.pData = pData;
|
|
data.backend.encoded.sizeInBytes = sizeInBytes;
|
|
|
|
return ma_resource_manager_register_data(pResourceManager, pName, pNameW, &data);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_register_encoded_data(ma_resource_manager* pResourceManager, const char* pName, const void* pData, size_t sizeInBytes)
|
|
{
|
|
return ma_resource_manager_register_encoded_data_internal(pResourceManager, pName, NULL, pData, sizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_register_encoded_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName, const void* pData, size_t sizeInBytes)
|
|
{
|
|
return ma_resource_manager_register_encoded_data_internal(pResourceManager, NULL, pName, pData, sizeInBytes);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_unregister_file(ma_resource_manager* pResourceManager, const char* pFilePath)
|
|
{
|
|
return ma_resource_manager_unregister_data(pResourceManager, pFilePath);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_unregister_file_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath)
|
|
{
|
|
return ma_resource_manager_unregister_data_w(pResourceManager, pFilePath);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_unregister_data(ma_resource_manager* pResourceManager, const char* pName)
|
|
{
|
|
return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, pName, NULL);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pResourceManager, const wchar_t* pName)
|
|
{
|
|
return ma_resource_manager_data_buffer_node_unacquire(pResourceManager, NULL, NULL, pName);
|
|
}
|
|
|
|
static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
MA_ASSERT(pDataStream != NULL);
|
|
return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1);
|
|
}
|
|
|
|
static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
MA_ASSERT(pDataStream != NULL);
|
|
return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd);
|
|
}
|
|
|
|
static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
MA_ASSERT(pDataStream != NULL);
|
|
return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
return ma_resource_manager_data_stream_read_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_cb__seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
return ma_resource_manager_data_stream_seek_to_pcm_frame((ma_resource_manager_data_stream*)pDataSource, frameIndex);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_cb__get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
return ma_resource_manager_data_stream_get_data_format((ma_resource_manager_data_stream*)pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
return ma_resource_manager_data_stream_get_cursor_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pCursor);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_cb__get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
return ma_resource_manager_data_stream_get_length_in_pcm_frames((ma_resource_manager_data_stream*)pDataSource, pLength);
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* pDataSource, ma_bool32 isLooping)
|
|
{
|
|
ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource;
|
|
MA_ASSERT(pDataStream != NULL);
|
|
|
|
ma_atomic_exchange_32(&pDataStream->isLooping, isLooping);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable =
|
|
{
|
|
ma_resource_manager_data_stream_cb__read_pcm_frames,
|
|
ma_resource_manager_data_stream_cb__seek_to_pcm_frame,
|
|
ma_resource_manager_data_stream_cb__get_data_format,
|
|
ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames,
|
|
ma_resource_manager_data_stream_cb__get_length_in_pcm_frames,
|
|
ma_resource_manager_data_stream_cb__set_looping,
|
|
0
|
|
};
|
|
|
|
static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor)
|
|
{
|
|
if (absoluteCursor > pDataStream->totalLengthInPCMFrames && pDataStream->totalLengthInPCMFrames > 0) {
|
|
absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames;
|
|
}
|
|
|
|
ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
ma_result result;
|
|
ma_data_source_config dataSourceConfig;
|
|
char* pFilePathCopy = NULL;
|
|
wchar_t* pFilePathWCopy = NULL;
|
|
ma_job job;
|
|
ma_bool32 waitBeforeReturning = MA_FALSE;
|
|
ma_resource_manager_inline_notification waitNotification;
|
|
ma_resource_manager_pipeline_notifications notifications;
|
|
ma_uint32 flags;
|
|
|
|
if (pDataStream == NULL) {
|
|
if (pConfig != NULL && pConfig->pNotifications != NULL) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(pConfig->pNotifications);
|
|
}
|
|
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDataStream);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->pNotifications != NULL) {
|
|
notifications = *pConfig->pNotifications;
|
|
} else {
|
|
MA_ZERO_OBJECT(¬ifications);
|
|
}
|
|
|
|
dataSourceConfig = ma_data_source_config_init();
|
|
dataSourceConfig.vtable = &g_ma_resource_manager_data_stream_vtable;
|
|
|
|
result = ma_data_source_init(&dataSourceConfig, &pDataStream->ds);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
return result;
|
|
}
|
|
|
|
flags = pConfig->flags;
|
|
if (pConfig->isLooping) {
|
|
flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
|
|
}
|
|
|
|
pDataStream->pResourceManager = pResourceManager;
|
|
pDataStream->flags = pConfig->flags;
|
|
pDataStream->result = MA_BUSY;
|
|
|
|
ma_data_source_set_range_in_pcm_frames(pDataStream, pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
|
|
ma_data_source_set_loop_point_in_pcm_frames(pDataStream, pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
|
|
ma_data_source_set_looping(pDataStream, (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0);
|
|
|
|
if (pResourceManager == NULL || (pConfig->pFilePath == NULL && pConfig->pFilePathW == NULL)) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->pFilePath != NULL) {
|
|
pFilePathCopy = ma_copy_string(pConfig->pFilePath, &pResourceManager->config.allocationCallbacks);
|
|
} else {
|
|
pFilePathWCopy = ma_copy_string_w(pConfig->pFilePathW, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
|
|
if (pFilePathCopy == NULL && pFilePathWCopy == NULL) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) == 0 || (pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) {
|
|
waitBeforeReturning = MA_TRUE;
|
|
ma_resource_manager_inline_notification_init(pResourceManager, &waitNotification);
|
|
}
|
|
|
|
ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications);
|
|
|
|
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, pConfig->initialSeekPointInPCMFrames);
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_STREAM);
|
|
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
|
|
job.data.resourceManager.loadDataStream.pDataStream = pDataStream;
|
|
job.data.resourceManager.loadDataStream.pFilePath = pFilePathCopy;
|
|
job.data.resourceManager.loadDataStream.pFilePathW = pFilePathWCopy;
|
|
job.data.resourceManager.loadDataStream.initialSeekPoint = pConfig->initialSeekPointInPCMFrames;
|
|
job.data.resourceManager.loadDataStream.pInitNotification = (waitBeforeReturning == MA_TRUE) ? &waitNotification : notifications.init.pNotification;
|
|
job.data.resourceManager.loadDataStream.pInitFence = notifications.init.pFence;
|
|
result = ma_resource_manager_post_job(pResourceManager, &job);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications);
|
|
ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications);
|
|
|
|
if (waitBeforeReturning) {
|
|
ma_resource_manager_inline_notification_uninit(&waitNotification);
|
|
}
|
|
|
|
ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks);
|
|
ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if (waitBeforeReturning) {
|
|
ma_resource_manager_inline_notification_wait_and_uninit(&waitNotification);
|
|
|
|
if (notifications.init.pNotification != NULL) {
|
|
ma_async_notification_signal(notifications.init.pNotification);
|
|
}
|
|
|
|
if (pDataStream->result != MA_SUCCESS) {
|
|
return pDataStream->result;
|
|
}
|
|
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_init(ma_resource_manager* pResourceManager, const char* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.pFilePath = pFilePath;
|
|
config.flags = flags;
|
|
config.pNotifications = pNotifications;
|
|
|
|
return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_init_w(ma_resource_manager* pResourceManager, const wchar_t* pFilePath, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.pFilePathW = pFilePath;
|
|
config.flags = flags;
|
|
config.pNotifications = pNotifications;
|
|
|
|
return ma_resource_manager_data_stream_init_ex(pResourceManager, &config, pDataStream);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
ma_resource_manager_inline_notification freeEvent;
|
|
ma_job job;
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE);
|
|
|
|
ma_resource_manager_inline_notification_init(pDataStream->pResourceManager, &freeEvent);
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_STREAM);
|
|
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
|
|
job.data.resourceManager.freeDataStream.pDataStream = pDataStream;
|
|
job.data.resourceManager.freeDataStream.pDoneNotification = &freeEvent;
|
|
job.data.resourceManager.freeDataStream.pDoneFence = NULL;
|
|
ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
|
|
|
|
ma_resource_manager_inline_notification_wait_and_uninit(&freeEvent);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_uint32 ma_resource_manager_data_stream_get_page_size_in_frames(ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
MA_ASSERT(pDataStream != NULL);
|
|
MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
|
|
|
|
return MA_RESOURCE_MANAGER_PAGE_SIZE_IN_MILLISECONDS * (pDataStream->decoder.outputSampleRate/1000);
|
|
}
|
|
|
|
static void* ma_resource_manager_data_stream_get_page_data_pointer(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex, ma_uint32 relativeCursor)
|
|
{
|
|
MA_ASSERT(pDataStream != NULL);
|
|
MA_ASSERT(pDataStream->isDecoderInitialized == MA_TRUE);
|
|
MA_ASSERT(pageIndex == 0 || pageIndex == 1);
|
|
|
|
return ma_offset_ptr(pDataStream->pPageData, ((ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * pageIndex) + relativeCursor) * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels));
|
|
}
|
|
|
|
static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_stream* pDataStream, ma_uint32 pageIndex)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 pageSizeInFrames;
|
|
ma_uint64 totalFramesReadForThisPage = 0;
|
|
void* pPageData = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pageIndex, 0);
|
|
|
|
pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
|
|
|
|
{
|
|
ma_uint64 rangeBeg;
|
|
ma_uint64 rangeEnd;
|
|
ma_uint64 loopPointBeg;
|
|
ma_uint64 loopPointEnd;
|
|
|
|
ma_data_source_set_looping(&pDataStream->decoder, ma_resource_manager_data_stream_is_looping(pDataStream));
|
|
|
|
ma_data_source_get_range_in_pcm_frames(pDataStream, &rangeBeg, &rangeEnd);
|
|
ma_data_source_set_range_in_pcm_frames(&pDataStream->decoder, rangeBeg, rangeEnd);
|
|
|
|
ma_data_source_get_loop_point_in_pcm_frames(pDataStream, &loopPointBeg, &loopPointEnd);
|
|
ma_data_source_set_loop_point_in_pcm_frames(&pDataStream->decoder, loopPointBeg, loopPointEnd);
|
|
}
|
|
|
|
result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage);
|
|
if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) {
|
|
ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE);
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage);
|
|
ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE);
|
|
}
|
|
|
|
static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
ma_uint32 iPage;
|
|
|
|
MA_ASSERT(pDataStream != NULL);
|
|
|
|
for (iPage = 0; iPage < 2; iPage += 1) {
|
|
ma_resource_manager_data_stream_fill_page(pDataStream, iPage);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_stream* pDataStream, void** ppFramesOut, ma_uint64* pFrameCount)
|
|
{
|
|
ma_uint64 framesAvailable;
|
|
ma_uint64 frameCount = 0;
|
|
|
|
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
|
|
|
|
if (pFrameCount != NULL) {
|
|
frameCount = *pFrameCount;
|
|
*pFrameCount = 0;
|
|
}
|
|
if (ppFramesOut != NULL) {
|
|
*ppFramesOut = NULL;
|
|
}
|
|
|
|
if (pDataStream == NULL || ppFramesOut == NULL || pFrameCount == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
|
|
return MA_BUSY;
|
|
}
|
|
|
|
if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) {
|
|
framesAvailable = 0;
|
|
} else {
|
|
ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]);
|
|
MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor);
|
|
|
|
framesAvailable = currentPageFrameCount - pDataStream->relativeCursor;
|
|
}
|
|
|
|
if (framesAvailable == 0) {
|
|
if (ma_resource_manager_data_stream_is_decoder_at_end(pDataStream)) {
|
|
return MA_AT_END;
|
|
} else {
|
|
return MA_BUSY;
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(framesAvailable > 0);
|
|
|
|
if (frameCount > framesAvailable) {
|
|
frameCount = framesAvailable;
|
|
}
|
|
|
|
*ppFramesOut = ma_resource_manager_data_stream_get_page_data_pointer(pDataStream, pDataStream->currentPageIndex, pDataStream->relativeCursor);
|
|
*pFrameCount = frameCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameCount)
|
|
{
|
|
ma_uint32 newRelativeCursor;
|
|
ma_uint32 pageSizeInFrames;
|
|
ma_job job;
|
|
|
|
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (frameCount > 0xFFFFFFFF) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream);
|
|
|
|
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount);
|
|
|
|
newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount;
|
|
|
|
if (newRelativeCursor >= pageSizeInFrames) {
|
|
newRelativeCursor -= pageSizeInFrames;
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_STREAM);
|
|
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
|
|
job.data.resourceManager.pageDataStream.pDataStream = pDataStream;
|
|
job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex;
|
|
|
|
ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE);
|
|
|
|
pDataStream->relativeCursor = newRelativeCursor;
|
|
pDataStream->currentPageIndex = (pDataStream->currentPageIndex + 1) & 0x01;
|
|
return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
|
|
} else {
|
|
pDataStream->relativeCursor = newRelativeCursor;
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_read_pcm_frames(ma_resource_manager_data_stream* pDataStream, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesProcessed;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (frameCount == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_seek_counter(pDataStream) > 0) {
|
|
return MA_BUSY;
|
|
}
|
|
|
|
ma_resource_manager_data_stream_get_data_format(pDataStream, &format, &channels, NULL, NULL, 0);
|
|
|
|
totalFramesProcessed = 0;
|
|
while (totalFramesProcessed < frameCount) {
|
|
void* pMappedFrames;
|
|
ma_uint64 mappedFrameCount;
|
|
|
|
mappedFrameCount = frameCount - totalFramesProcessed;
|
|
result = ma_resource_manager_data_stream_map(pDataStream, &pMappedFrames, &mappedFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesProcessed, format, channels), pMappedFrames, mappedFrameCount, format, channels);
|
|
}
|
|
|
|
totalFramesProcessed += mappedFrameCount;
|
|
|
|
result = ma_resource_manager_data_stream_unmap(pDataStream, mappedFrameCount);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesProcessed;
|
|
}
|
|
|
|
if (result == MA_SUCCESS && totalFramesProcessed == 0) {
|
|
result = MA_AT_END;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_manager_data_stream* pDataStream, ma_uint64 frameIndex)
|
|
{
|
|
ma_job job;
|
|
ma_result streamResult;
|
|
|
|
streamResult = ma_resource_manager_data_stream_result(pDataStream);
|
|
|
|
MA_ASSERT(streamResult != MA_UNAVAILABLE);
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (streamResult != MA_SUCCESS && streamResult != MA_BUSY) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) {
|
|
if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) {
|
|
return MA_SUCCESS;
|
|
}
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1);
|
|
|
|
ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex);
|
|
|
|
pDataStream->relativeCursor = 0;
|
|
pDataStream->currentPageIndex = 0;
|
|
ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE);
|
|
ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE);
|
|
|
|
ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE);
|
|
|
|
job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_SEEK_DATA_STREAM);
|
|
job.order = ma_resource_manager_data_stream_next_execution_order(pDataStream);
|
|
job.data.resourceManager.seekDataStream.pDataStream = pDataStream;
|
|
job.data.resourceManager.seekDataStream.frameIndex = frameIndex;
|
|
return ma_resource_manager_post_job(pDataStream->pResourceManager, &job);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_get_data_format(ma_resource_manager_data_stream* pDataStream, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_unknown;
|
|
}
|
|
|
|
if (pChannels != NULL) {
|
|
*pChannels = 0;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = 0;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
MA_ZERO_MEMORY(pChannelMap, sizeof(*pChannelMap) * channelMapCap);
|
|
}
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return ma_data_source_get_data_format(&pDataStream->decoder, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pCursor)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pCursor = 0;
|
|
|
|
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) != MA_UNAVAILABLE);
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_resource_manager_data_stream_result(pDataStream);
|
|
if (result != MA_SUCCESS && result != MA_BUSY) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
*pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_get_length_in_pcm_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pLength)
|
|
{
|
|
ma_result streamResult;
|
|
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pLength = 0;
|
|
|
|
streamResult = ma_resource_manager_data_stream_result(pDataStream);
|
|
|
|
MA_ASSERT(streamResult != MA_UNAVAILABLE);
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (streamResult != MA_SUCCESS) {
|
|
return streamResult;
|
|
}
|
|
|
|
*pLength = pDataStream->totalLengthInPCMFrames;
|
|
if (*pLength == 0) {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return (ma_result)ma_atomic_load_i32(&pDataStream->result);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping)
|
|
{
|
|
return ma_data_source_set_looping(pDataStream, isLooping);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_manager_data_stream* pDataStream)
|
|
{
|
|
if (pDataStream == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames)
|
|
{
|
|
ma_uint32 pageIndex0;
|
|
ma_uint32 pageIndex1;
|
|
ma_uint32 relativeCursor;
|
|
ma_uint64 availableFrames;
|
|
|
|
if (pAvailableFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pAvailableFrames = 0;
|
|
|
|
if (pDataStream == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pageIndex0 = pDataStream->currentPageIndex;
|
|
pageIndex1 = (pDataStream->currentPageIndex + 1) & 0x01;
|
|
relativeCursor = pDataStream->relativeCursor;
|
|
|
|
availableFrames = 0;
|
|
if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) {
|
|
availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor;
|
|
if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) {
|
|
availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]);
|
|
}
|
|
}
|
|
|
|
*pAvailableFrames = availableFrames;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_resource_manager_data_source_preinit(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDataSource);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pResourceManager == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pDataSource->flags = pConfig->flags;
|
|
if (pConfig->isLooping) {
|
|
pDataSource->flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_resource_manager_data_source_preinit(pResourceManager, pConfig, pDataSource);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if ((pConfig->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_init_ex(pResourceManager, pConfig, &pDataSource->backend.stream);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_init_ex(pResourceManager, pConfig, &pDataSource->backend.buffer);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_init(ma_resource_manager* pResourceManager, const char* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.pFilePath = pName;
|
|
config.flags = flags;
|
|
config.pNotifications = pNotifications;
|
|
|
|
return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_init_w(ma_resource_manager* pResourceManager, const wchar_t* pName, ma_uint32 flags, const ma_resource_manager_pipeline_notifications* pNotifications, ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.pFilePathW = pName;
|
|
config.flags = flags;
|
|
config.pNotifications = pNotifications;
|
|
|
|
return ma_resource_manager_data_source_init_ex(pResourceManager, &config, pDataSource);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_init_copy(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source* pExistingDataSource, ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
ma_result result;
|
|
ma_resource_manager_data_source_config config;
|
|
|
|
if (pExistingDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
config = ma_resource_manager_data_source_config_init();
|
|
config.flags = pExistingDataSource->flags;
|
|
|
|
result = ma_resource_manager_data_source_preinit(pResourceManager, &config, pDataSource);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if ((pExistingDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return ma_resource_manager_data_buffer_init_copy(pResourceManager, &pExistingDataSource->backend.buffer, &pDataSource->backend.buffer);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_uninit(ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_uninit(&pDataSource->backend.stream);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_uninit(&pDataSource->backend.buffer);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_read_pcm_frames(ma_resource_manager_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_read_pcm_frames(&pDataSource->backend.stream, pFramesOut, frameCount, pFramesRead);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_read_pcm_frames(&pDataSource->backend.buffer, pFramesOut, frameCount, pFramesRead);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_seek_to_pcm_frame(ma_resource_manager_data_source* pDataSource, ma_uint64 frameIndex)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_seek_to_pcm_frame(&pDataSource->backend.stream, frameIndex);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_seek_to_pcm_frame(&pDataSource->backend.buffer, frameIndex);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_map(ma_resource_manager_data_source* pDataSource, void** ppFramesOut, ma_uint64* pFrameCount)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_map(&pDataSource->backend.stream, ppFramesOut, pFrameCount);
|
|
} else {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_unmap(ma_resource_manager_data_source* pDataSource, ma_uint64 frameCount)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_unmap(&pDataSource->backend.stream, frameCount);
|
|
} else {
|
|
return MA_NOT_IMPLEMENTED;
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_get_data_format(ma_resource_manager_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_get_data_format(&pDataSource->backend.stream, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_get_data_format(&pDataSource->backend.buffer, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_get_cursor_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pCursor)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_get_cursor_in_pcm_frames(&pDataSource->backend.stream, pCursor);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_get_cursor_in_pcm_frames(&pDataSource->backend.buffer, pCursor);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_get_length_in_pcm_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pLength)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_get_length_in_pcm_frames(&pDataSource->backend.stream, pLength);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_get_length_in_pcm_frames(&pDataSource->backend.buffer, pLength);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_result(const ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_result(&pDataSource->backend.stream);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_result(&pDataSource->backend.buffer);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_set_looping(ma_resource_manager_data_source* pDataSource, ma_bool32 isLooping)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_set_looping(&pDataSource->backend.stream, isLooping);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_set_looping(&pDataSource->backend.buffer, isLooping);
|
|
}
|
|
}
|
|
|
|
MA_API ma_bool32 ma_resource_manager_data_source_is_looping(const ma_resource_manager_data_source* pDataSource)
|
|
{
|
|
if (pDataSource == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_is_looping(&pDataSource->backend.stream);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_is_looping(&pDataSource->backend.buffer);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_data_source_get_available_frames(ma_resource_manager_data_source* pDataSource, ma_uint64* pAvailableFrames)
|
|
{
|
|
if (pAvailableFrames == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pAvailableFrames = 0;
|
|
|
|
if (pDataSource == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pDataSource->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM) != 0) {
|
|
return ma_resource_manager_data_stream_get_available_frames(&pDataSource->backend.stream, pAvailableFrames);
|
|
} else {
|
|
return ma_resource_manager_data_buffer_get_available_frames(&pDataSource->backend.buffer, pAvailableFrames);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_post_job(ma_resource_manager* pResourceManager, const ma_job* pJob)
|
|
{
|
|
if (pResourceManager == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_job_queue_post(&pResourceManager->jobQueue, pJob);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_post_job_quit(ma_resource_manager* pResourceManager)
|
|
{
|
|
ma_job job = ma_job_init(MA_JOB_TYPE_QUIT);
|
|
return ma_resource_manager_post_job(pResourceManager, &job);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_next_job(ma_resource_manager* pResourceManager, ma_job* pJob)
|
|
{
|
|
if (pResourceManager == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_job_queue_next(&pResourceManager->jobQueue, pJob);
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.loadDataBufferNode.pResourceManager;
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.loadDataBufferNode.pDataBufferNode;
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE);
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
if (ma_resource_manager_data_buffer_node_result(pDataBufferNode) != MA_BUSY) {
|
|
result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
|
|
goto done;
|
|
}
|
|
|
|
if ((pJob->data.resourceManager.loadDataBufferNode.flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE) != 0) {
|
|
|
|
ma_decoder* pDecoder;
|
|
ma_job pageDataBufferNodeJob;
|
|
|
|
result = ma_resource_manager_data_buffer_node_init_supply_decoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW, pJob->data.resourceManager.loadDataBufferNode.flags, &pDecoder);
|
|
|
|
if (result == MA_BUSY) {
|
|
result = MA_ERROR;
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
if (pJob->data.resourceManager.loadDataBufferNode.pFilePath != NULL) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%s\". %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePath, ma_result_description(result));
|
|
} else {
|
|
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(_MSC_VER)
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_WARNING, "Failed to initialize data supply for \"%ls\", %s.\n", pJob->data.resourceManager.loadDataBufferNode.pFilePathW, ma_result_description(result));
|
|
#endif
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
pageDataBufferNodeJob = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE);
|
|
pageDataBufferNodeJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
|
|
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pResourceManager = pResourceManager;
|
|
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDataBufferNode = pDataBufferNode;
|
|
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDecoder = pDecoder;
|
|
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneNotification = pJob->data.resourceManager.loadDataBufferNode.pDoneNotification;
|
|
pageDataBufferNodeJob.data.resourceManager.pageDataBufferNode.pDoneFence = pJob->data.resourceManager.loadDataBufferNode.pDoneFence;
|
|
|
|
result = ma_resource_manager_post_job(pResourceManager, &pageDataBufferNodeJob);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_PAGE_DATA_BUFFER_NODE job. %s\n", ma_result_description(result));
|
|
ma_decoder_uninit(pDecoder);
|
|
ma_free(pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
} else {
|
|
result = MA_BUSY;
|
|
}
|
|
} else {
|
|
result = ma_resource_manager_data_buffer_node_init_supply_encoded(pResourceManager, pDataBufferNode, pJob->data.resourceManager.loadDataBufferNode.pFilePath, pJob->data.resourceManager.loadDataBufferNode.pFilePathW);
|
|
}
|
|
|
|
done:
|
|
ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePath, &pResourceManager->config.allocationCallbacks);
|
|
ma_free(pJob->data.resourceManager.loadDataBufferNode.pFilePathW, &pResourceManager->config.allocationCallbacks);
|
|
|
|
ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
|
|
|
|
if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pInitNotification);
|
|
}
|
|
if (pJob->data.resourceManager.loadDataBufferNode.pInitFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pInitFence);
|
|
}
|
|
|
|
if (result != MA_BUSY) {
|
|
if (pJob->data.resourceManager.loadDataBufferNode.pDoneNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.loadDataBufferNode.pDoneNotification);
|
|
}
|
|
if (pJob->data.resourceManager.loadDataBufferNode.pDoneFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.loadDataBufferNode.pDoneFence);
|
|
}
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
|
|
|
|
if (result == MA_BUSY) {
|
|
result = MA_SUCCESS;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob)
|
|
{
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.freeDataBufferNode.pResourceManager;
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode;
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
if (pJob->data.resourceManager.freeDataBufferNode.pDoneNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.freeDataBufferNode.pDoneNotification);
|
|
}
|
|
|
|
if (pJob->data.resourceManager.freeDataBufferNode.pDoneFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence);
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
|
|
|
|
ma_resource_manager_data_buffer_node_free(pResourceManager, pDataBufferNode);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_buffer_node* pDataBufferNode;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pResourceManager = (ma_resource_manager*)pJob->data.resourceManager.pageDataBufferNode.pResourceManager;
|
|
MA_ASSERT(pResourceManager != NULL);
|
|
|
|
pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode;
|
|
MA_ASSERT(pDataBufferNode != NULL);
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_node_result(pDataBufferNode);
|
|
if (result != MA_BUSY) {
|
|
goto done;
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_node_decode_next_page(pResourceManager, pDataBufferNode, (ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
|
|
|
|
if (result == MA_SUCCESS) {
|
|
ma_job newJob;
|
|
newJob = *pJob;
|
|
newJob.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode);
|
|
|
|
result = ma_resource_manager_post_job(pResourceManager, &newJob);
|
|
|
|
if (result == MA_SUCCESS) {
|
|
result = MA_BUSY;
|
|
}
|
|
}
|
|
|
|
done:
|
|
if (result != MA_BUSY) {
|
|
ma_decoder_uninit((ma_decoder*)pJob->data.resourceManager.pageDataBufferNode.pDecoder);
|
|
ma_free(pJob->data.resourceManager.pageDataBufferNode.pDecoder, &pResourceManager->config.allocationCallbacks);
|
|
}
|
|
|
|
if (result == MA_AT_END) {
|
|
result = MA_SUCCESS;
|
|
}
|
|
|
|
ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result);
|
|
|
|
if (result != MA_BUSY) {
|
|
if (pJob->data.resourceManager.pageDataBufferNode.pDoneNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.pageDataBufferNode.pDoneNotification);
|
|
}
|
|
|
|
if (pJob->data.resourceManager.pageDataBufferNode.pDoneFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.pageDataBufferNode.pDoneFence);
|
|
}
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1);
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_buffer* pDataBuffer;
|
|
ma_resource_manager_data_supply_type dataSupplyType = ma_resource_manager_data_supply_type_unknown;
|
|
ma_bool32 isConnectorInitialized = MA_FALSE;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.loadDataBuffer.pDataBuffer;
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
|
|
pResourceManager = pDataBuffer->pResourceManager;
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_result(pDataBuffer);
|
|
if (result != MA_BUSY) {
|
|
goto done;
|
|
} else {
|
|
result = MA_SUCCESS;
|
|
(void)result;
|
|
}
|
|
|
|
isConnectorInitialized = ma_resource_manager_data_buffer_has_connector(pDataBuffer);
|
|
if (isConnectorInitialized == MA_FALSE) {
|
|
dataSupplyType = ma_resource_manager_data_buffer_node_get_data_supply_type(pDataBuffer->pNode);
|
|
|
|
if (dataSupplyType != ma_resource_manager_data_supply_type_unknown) {
|
|
ma_resource_manager_data_source_config dataSourceConfig;
|
|
dataSourceConfig = ma_resource_manager_data_source_config_init();
|
|
dataSourceConfig.rangeBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeBegInPCMFrames;
|
|
dataSourceConfig.rangeEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.rangeEndInPCMFrames;
|
|
dataSourceConfig.loopPointBegInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointBegInPCMFrames;
|
|
dataSourceConfig.loopPointEndInPCMFrames = pJob->data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames;
|
|
dataSourceConfig.isLooping = pJob->data.resourceManager.loadDataBuffer.isLooping;
|
|
|
|
result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, &dataSourceConfig, pJob->data.resourceManager.loadDataBuffer.pInitNotification, pJob->data.resourceManager.loadDataBuffer.pInitFence);
|
|
if (result != MA_SUCCESS) {
|
|
ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to initialize connector for data buffer. %s.\n", ma_result_description(result));
|
|
goto done;
|
|
}
|
|
} else {
|
|
}
|
|
} else {
|
|
}
|
|
|
|
result = ma_resource_manager_data_buffer_node_result(pDataBuffer->pNode);
|
|
if (result == MA_BUSY || (result == MA_SUCCESS && isConnectorInitialized == MA_FALSE && dataSupplyType == ma_resource_manager_data_supply_type_unknown)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
done:
|
|
ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result);
|
|
|
|
if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pDoneNotification);
|
|
}
|
|
if (pJob->data.resourceManager.loadDataBuffer.pDoneFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pDoneFence);
|
|
}
|
|
|
|
if (ma_resource_manager_data_buffer_has_connector(pDataBuffer) == MA_FALSE && result != MA_SUCCESS) {
|
|
if (pJob->data.resourceManager.loadDataBuffer.pInitNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.loadDataBuffer.pInitNotification);
|
|
}
|
|
if (pJob->data.resourceManager.loadDataBuffer.pInitFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.loadDataBuffer.pInitFence);
|
|
}
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob)
|
|
{
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_buffer* pDataBuffer;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDataBuffer = (ma_resource_manager_data_buffer*)pJob->data.resourceManager.freeDataBuffer.pDataBuffer;
|
|
MA_ASSERT(pDataBuffer != NULL);
|
|
|
|
pResourceManager = pDataBuffer->pResourceManager;
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
ma_resource_manager_data_buffer_uninit_internal(pDataBuffer);
|
|
|
|
if (pJob->data.resourceManager.freeDataBuffer.pDoneNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.freeDataBuffer.pDoneNotification);
|
|
}
|
|
|
|
if (pJob->data.resourceManager.freeDataBuffer.pDoneFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence);
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1);
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_decoder_config decoderConfig;
|
|
ma_uint32 pageBufferSizeInBytes;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_stream* pDataStream;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.loadDataStream.pDataStream;
|
|
MA_ASSERT(pDataStream != NULL);
|
|
|
|
pResourceManager = pDataStream->pResourceManager;
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_BUSY) {
|
|
result = MA_INVALID_OPERATION;
|
|
goto done;
|
|
}
|
|
|
|
decoderConfig = ma_resource_manager__init_decoder_config(pResourceManager);
|
|
|
|
if (pJob->data.resourceManager.loadDataStream.pFilePath != NULL) {
|
|
result = ma_decoder_init_vfs(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePath, &decoderConfig, &pDataStream->decoder);
|
|
} else {
|
|
result = ma_decoder_init_vfs_w(pResourceManager->config.pVFS, pJob->data.resourceManager.loadDataStream.pFilePathW, &decoderConfig, &pDataStream->decoder);
|
|
}
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
|
|
if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) {
|
|
result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames);
|
|
if (result != MA_SUCCESS) {
|
|
goto done;
|
|
}
|
|
} else {
|
|
pDataStream->totalLengthInPCMFrames = 0;
|
|
}
|
|
|
|
pDataStream->isDecoderInitialized = MA_TRUE;
|
|
|
|
pageBufferSizeInBytes = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream) * 2 * ma_get_bytes_per_frame(pDataStream->decoder.outputFormat, pDataStream->decoder.outputChannels);
|
|
|
|
pDataStream->pPageData = ma_malloc(pageBufferSizeInBytes, &pResourceManager->config.allocationCallbacks);
|
|
if (pDataStream->pPageData == NULL) {
|
|
ma_decoder_uninit(&pDataStream->decoder);
|
|
result = MA_OUT_OF_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.loadDataStream.initialSeekPoint);
|
|
|
|
ma_resource_manager_data_stream_fill_pages(pDataStream);
|
|
|
|
result = MA_SUCCESS;
|
|
|
|
done:
|
|
ma_free(pJob->data.resourceManager.loadDataStream.pFilePath, &pResourceManager->config.allocationCallbacks);
|
|
ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks);
|
|
|
|
ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result);
|
|
|
|
if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.loadDataStream.pInitNotification);
|
|
}
|
|
if (pJob->data.resourceManager.loadDataStream.pInitFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence);
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob)
|
|
{
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_stream* pDataStream;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.freeDataStream.pDataStream;
|
|
MA_ASSERT(pDataStream != NULL);
|
|
|
|
pResourceManager = pDataStream->pResourceManager;
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
MA_ASSERT(ma_resource_manager_data_stream_result(pDataStream) == MA_UNAVAILABLE);
|
|
|
|
if (pDataStream->isDecoderInitialized) {
|
|
ma_decoder_uninit(&pDataStream->decoder);
|
|
}
|
|
|
|
if (pDataStream->pPageData != NULL) {
|
|
ma_free(pDataStream->pPageData, &pResourceManager->config.allocationCallbacks);
|
|
pDataStream->pPageData = NULL;
|
|
}
|
|
|
|
ma_data_source_uninit(&pDataStream->ds);
|
|
|
|
if (pJob->data.resourceManager.freeDataStream.pDoneNotification != NULL) {
|
|
ma_async_notification_signal(pJob->data.resourceManager.freeDataStream.pDoneNotification);
|
|
}
|
|
if (pJob->data.resourceManager.freeDataStream.pDoneFence != NULL) {
|
|
ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_stream* pDataStream;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.pageDataStream.pDataStream;
|
|
MA_ASSERT(pDataStream != NULL);
|
|
|
|
pResourceManager = pDataStream->pResourceManager;
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS) {
|
|
result = MA_INVALID_OPERATION;
|
|
goto done;
|
|
}
|
|
|
|
ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex);
|
|
|
|
done:
|
|
ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
|
|
return result;
|
|
}
|
|
|
|
static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_resource_manager* pResourceManager;
|
|
ma_resource_manager_data_stream* pDataStream;
|
|
|
|
MA_ASSERT(pJob != NULL);
|
|
|
|
pDataStream = (ma_resource_manager_data_stream*)pJob->data.resourceManager.seekDataStream.pDataStream;
|
|
MA_ASSERT(pDataStream != NULL);
|
|
|
|
pResourceManager = pDataStream->pResourceManager;
|
|
|
|
if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) {
|
|
return ma_resource_manager_post_job(pResourceManager, pJob);
|
|
}
|
|
|
|
if (ma_resource_manager_data_stream_result(pDataStream) != MA_SUCCESS || pDataStream->isDecoderInitialized == MA_FALSE) {
|
|
result = MA_INVALID_OPERATION;
|
|
goto done;
|
|
}
|
|
|
|
ma_decoder_seek_to_pcm_frame(&pDataStream->decoder, pJob->data.resourceManager.seekDataStream.frameIndex);
|
|
|
|
ma_resource_manager_data_stream_fill_pages(pDataStream);
|
|
|
|
ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1);
|
|
|
|
done:
|
|
ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_process_job(ma_resource_manager* pResourceManager, ma_job* pJob)
|
|
{
|
|
if (pResourceManager == NULL || pJob == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_job_process(pJob);
|
|
}
|
|
|
|
MA_API ma_result ma_resource_manager_process_next_job(ma_resource_manager* pResourceManager)
|
|
{
|
|
ma_result result;
|
|
ma_job job;
|
|
|
|
if (pResourceManager == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_resource_manager_next_job(pResourceManager, &job);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return ma_job_process(&job);
|
|
}
|
|
#else
|
|
static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob) { return ma_job_process__noop(pJob); }
|
|
#endif
|
|
|
|
#ifndef MA_NO_NODE_GRAPH
|
|
|
|
static ma_stack* ma_stack_init(size_t sizeInBytes, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_stack* pStack;
|
|
|
|
if (sizeInBytes == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
pStack = (ma_stack*)ma_malloc(sizeof(*pStack) - sizeof(pStack->_data) + sizeInBytes, pAllocationCallbacks);
|
|
if (pStack == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pStack->offset = 0;
|
|
pStack->sizeInBytes = sizeInBytes;
|
|
|
|
return pStack;
|
|
}
|
|
|
|
static void ma_stack_uninit(ma_stack* pStack, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pStack == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_free(pStack, pAllocationCallbacks);
|
|
}
|
|
|
|
static void* ma_stack_alloc(ma_stack* pStack, size_t sz)
|
|
{
|
|
void* p = (void*)((char*)pStack->_data + pStack->offset);
|
|
size_t* pSize = (size_t*)p;
|
|
|
|
sz = (sz + (sizeof(ma_uintptr) - 1)) & ~(sizeof(ma_uintptr) - 1);
|
|
if (pStack->offset + sz + sizeof(size_t) > pStack->sizeInBytes) {
|
|
return NULL;
|
|
}
|
|
|
|
pStack->offset += sz + sizeof(size_t);
|
|
|
|
*pSize = sz;
|
|
return (void*)((char*)p + sizeof(size_t));
|
|
}
|
|
|
|
static void ma_stack_free(ma_stack* pStack, void* p)
|
|
{
|
|
size_t* pSize;
|
|
|
|
if (p == NULL) {
|
|
return;
|
|
}
|
|
|
|
pSize = (size_t*)p - 1;
|
|
pStack->offset -= *pSize + sizeof(size_t);
|
|
}
|
|
|
|
#ifndef MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS
|
|
#define MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS 480
|
|
#endif
|
|
|
|
#ifndef MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL
|
|
#define MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL 524288
|
|
#endif
|
|
|
|
static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime);
|
|
|
|
MA_API void ma_debug_fill_pcm_frames_with_sine_wave(float* pFramesOut, ma_uint32 frameCount, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
|
|
{
|
|
#ifndef MA_NO_GENERATION
|
|
{
|
|
ma_waveform_config waveformConfig;
|
|
ma_waveform waveform;
|
|
|
|
waveformConfig = ma_waveform_config_init(format, channels, sampleRate, ma_waveform_type_sine, 1.0, 400);
|
|
ma_waveform_init(&waveformConfig, &waveform);
|
|
ma_waveform_read_pcm_frames(&waveform, pFramesOut, frameCount, NULL);
|
|
}
|
|
#else
|
|
{
|
|
(void)pFramesOut;
|
|
(void)frameCount;
|
|
(void)format;
|
|
(void)channels;
|
|
(void)sampleRate;
|
|
#if defined(MA_DEBUG_OUTPUT)
|
|
{
|
|
#if _MSC_VER
|
|
#pragma message ("ma_debug_fill_pcm_frames_with_sine_wave() will do nothing because MA_NO_GENERATION is enabled.")
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels)
|
|
{
|
|
ma_node_graph_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.channels = channels;
|
|
config.processingSizeInFrames = 0;
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading)
|
|
{
|
|
MA_ASSERT(pNodeGraph != NULL);
|
|
ma_atomic_exchange_32(&pNodeGraph->isReading, isReading);
|
|
}
|
|
|
|
#if 0
|
|
static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph)
|
|
{
|
|
MA_ASSERT(pNodeGraph != NULL);
|
|
return ma_atomic_load_32(&pNodeGraph->isReading);
|
|
}
|
|
#endif
|
|
|
|
static void ma_node_graph_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_node_graph* pNodeGraph = (ma_node_graph*)pNode;
|
|
ma_uint64 framesRead;
|
|
|
|
ma_node_graph_read_pcm_frames(pNodeGraph, ppFramesOut[0], *pFrameCountOut, &framesRead);
|
|
|
|
*pFrameCountOut = (ma_uint32)framesRead;
|
|
|
|
(void)ppFramesIn;
|
|
(void)pFrameCountIn;
|
|
}
|
|
|
|
static ma_node_vtable g_node_graph_node_vtable =
|
|
{
|
|
ma_node_graph_node_process_pcm_frames,
|
|
NULL,
|
|
0,
|
|
1,
|
|
0
|
|
};
|
|
|
|
static void ma_node_graph_endpoint_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
MA_ASSERT(pNode != NULL);
|
|
MA_ASSERT(ma_node_get_input_bus_count(pNode) == 1);
|
|
MA_ASSERT(ma_node_get_output_bus_count(pNode) == 1);
|
|
|
|
MA_ASSERT(ma_node_get_input_channels(pNode, 0) == ma_node_get_output_channels(pNode, 0));
|
|
|
|
(void)pNode;
|
|
(void)ppFramesIn;
|
|
(void)pFrameCountIn;
|
|
(void)ppFramesOut;
|
|
(void)pFrameCountOut;
|
|
|
|
#if 0
|
|
if (ppFramesIn != NULL) {
|
|
ma_copy_pcm_frames(ppFramesOut[0], ppFramesIn[0], *pFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNode, 0));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_node_vtable g_node_graph_endpoint_vtable =
|
|
{
|
|
ma_node_graph_endpoint_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
MA_NODE_FLAG_PASSTHROUGH
|
|
};
|
|
|
|
MA_API ma_result ma_node_graph_init(const ma_node_graph_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node_graph* pNodeGraph)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseConfig;
|
|
ma_node_config endpointConfig;
|
|
|
|
if (pNodeGraph == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNodeGraph);
|
|
pNodeGraph->processingSizeInFrames = pConfig->processingSizeInFrames;
|
|
|
|
baseConfig = ma_node_config_init();
|
|
baseConfig.vtable = &g_node_graph_node_vtable;
|
|
baseConfig.pOutputChannels = &pConfig->channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pNodeGraph->base);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
endpointConfig = ma_node_config_init();
|
|
endpointConfig.vtable = &g_node_graph_endpoint_vtable;
|
|
endpointConfig.pInputChannels = &pConfig->channels;
|
|
endpointConfig.pOutputChannels = &pConfig->channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &endpointConfig, pAllocationCallbacks, &pNodeGraph->endpoint);
|
|
if (result != MA_SUCCESS) {
|
|
ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->processingSizeInFrames > 0) {
|
|
pNodeGraph->pProcessingCache = (float*)ma_malloc(pConfig->processingSizeInFrames * pConfig->channels * sizeof(float), pAllocationCallbacks);
|
|
if (pNodeGraph->pProcessingCache == NULL) {
|
|
ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
|
|
ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
{
|
|
size_t preMixStackSizeInBytes = pConfig->preMixStackSizeInBytes;
|
|
if (preMixStackSizeInBytes == 0) {
|
|
preMixStackSizeInBytes = pConfig->channels * MA_DEFAULT_PREMIX_STACK_SIZE_PER_CHANNEL;
|
|
}
|
|
|
|
pNodeGraph->pPreMixStack = ma_stack_init(preMixStackSizeInBytes, pAllocationCallbacks);
|
|
if (pNodeGraph->pPreMixStack == NULL) {
|
|
ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
|
|
ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
|
|
if (pNodeGraph->pProcessingCache != NULL) {
|
|
ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks);
|
|
}
|
|
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_node_graph_uninit(ma_node_graph* pNodeGraph, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pNodeGraph == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(&pNodeGraph->endpoint, pAllocationCallbacks);
|
|
ma_node_uninit(&pNodeGraph->base, pAllocationCallbacks);
|
|
|
|
if (pNodeGraph->pProcessingCache != NULL) {
|
|
ma_free(pNodeGraph->pProcessingCache, pAllocationCallbacks);
|
|
pNodeGraph->pProcessingCache = NULL;
|
|
}
|
|
|
|
if (pNodeGraph->pPreMixStack != NULL) {
|
|
ma_stack_uninit(pNodeGraph->pPreMixStack, pAllocationCallbacks);
|
|
pNodeGraph->pPreMixStack = NULL;
|
|
}
|
|
}
|
|
|
|
MA_API ma_node* ma_node_graph_get_endpoint(ma_node_graph* pNodeGraph)
|
|
{
|
|
if (pNodeGraph == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return &pNodeGraph->endpoint;
|
|
}
|
|
|
|
MA_API ma_result ma_node_graph_read_pcm_frames(ma_node_graph* pNodeGraph, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint32 channels;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
if (pNodeGraph == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
channels = ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
|
|
|
|
totalFramesRead = 0;
|
|
while (totalFramesRead < frameCount) {
|
|
ma_uint32 framesJustRead;
|
|
ma_uint64 framesToRead;
|
|
float* pRunningFramesOut;
|
|
|
|
framesToRead = frameCount - totalFramesRead;
|
|
if (framesToRead > 0xFFFFFFFF) {
|
|
framesToRead = 0xFFFFFFFF;
|
|
}
|
|
|
|
pRunningFramesOut = (float*)ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels);
|
|
|
|
if (pNodeGraph->processingCacheFramesRemaining > 0) {
|
|
ma_uint32 framesToReadFromCache;
|
|
|
|
framesToReadFromCache = (ma_uint32)framesToRead;
|
|
if (framesToReadFromCache > pNodeGraph->processingCacheFramesRemaining) {
|
|
framesToReadFromCache = pNodeGraph->processingCacheFramesRemaining;
|
|
}
|
|
|
|
MA_COPY_MEMORY(pRunningFramesOut, pNodeGraph->pProcessingCache, framesToReadFromCache * channels * sizeof(float));
|
|
MA_MOVE_MEMORY(pNodeGraph->pProcessingCache, pNodeGraph->pProcessingCache + (framesToReadFromCache * channels), (pNodeGraph->processingCacheFramesRemaining - framesToReadFromCache) * channels * sizeof(float));
|
|
pNodeGraph->processingCacheFramesRemaining -= framesToReadFromCache;
|
|
|
|
totalFramesRead += framesToReadFromCache;
|
|
continue;
|
|
} else {
|
|
float* pReadDst = pRunningFramesOut;
|
|
|
|
if (pNodeGraph->processingSizeInFrames > 0) {
|
|
if (framesToRead < pNodeGraph->processingSizeInFrames) {
|
|
pReadDst = pNodeGraph->pProcessingCache;
|
|
}
|
|
|
|
framesToRead = pNodeGraph->processingSizeInFrames;
|
|
}
|
|
|
|
ma_node_graph_set_is_reading(pNodeGraph, MA_TRUE);
|
|
{
|
|
result = ma_node_read_pcm_frames(&pNodeGraph->endpoint, 0, pReadDst, (ma_uint32)framesToRead, &framesJustRead, ma_node_get_time(&pNodeGraph->endpoint));
|
|
}
|
|
ma_node_graph_set_is_reading(pNodeGraph, MA_FALSE);
|
|
|
|
if (pReadDst == pNodeGraph->pProcessingCache) {
|
|
pNodeGraph->processingCacheFramesRemaining = framesJustRead;
|
|
} else {
|
|
totalFramesRead += framesJustRead;
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (framesJustRead == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (totalFramesRead < frameCount) {
|
|
ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, ma_format_f32, channels), (frameCount - totalFramesRead), ma_format_f32, channels);
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = totalFramesRead;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_node_graph_get_channels(const ma_node_graph* pNodeGraph)
|
|
{
|
|
if (pNodeGraph == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_get_output_channels(&pNodeGraph->endpoint, 0);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_node_graph_get_time(const ma_node_graph* pNodeGraph)
|
|
{
|
|
if (pNodeGraph == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_get_time(&pNodeGraph->endpoint);
|
|
}
|
|
|
|
MA_API ma_result ma_node_graph_set_time(ma_node_graph* pNodeGraph, ma_uint64 globalTime)
|
|
{
|
|
if (pNodeGraph == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_node_set_time(&pNodeGraph->endpoint, globalTime);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_node_graph_get_processing_size_in_frames(const ma_node_graph* pNodeGraph)
|
|
{
|
|
if (pNodeGraph == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pNodeGraph->processingSizeInFrames;
|
|
}
|
|
|
|
#define MA_NODE_OUTPUT_BUS_FLAG_HAS_READ 0x01
|
|
|
|
static ma_result ma_node_output_bus_init(ma_node* pNode, ma_uint32 outputBusIndex, ma_uint32 channels, ma_node_output_bus* pOutputBus)
|
|
{
|
|
MA_ASSERT(pOutputBus != NULL);
|
|
MA_ASSERT(outputBusIndex < MA_MAX_NODE_BUS_COUNT);
|
|
MA_ASSERT(outputBusIndex < ma_node_get_output_bus_count(pNode));
|
|
MA_ASSERT(channels < 256);
|
|
|
|
MA_ZERO_OBJECT(pOutputBus);
|
|
|
|
if (channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pOutputBus->pNode = pNode;
|
|
pOutputBus->outputBusIndex = (ma_uint8)outputBusIndex;
|
|
pOutputBus->channels = (ma_uint8)channels;
|
|
pOutputBus->flags = MA_NODE_OUTPUT_BUS_FLAG_HAS_READ;
|
|
pOutputBus->volume = 1;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_node_output_bus_lock(ma_node_output_bus* pOutputBus)
|
|
{
|
|
ma_spinlock_lock(&pOutputBus->lock);
|
|
}
|
|
|
|
static void ma_node_output_bus_unlock(ma_node_output_bus* pOutputBus)
|
|
{
|
|
ma_spinlock_unlock(&pOutputBus->lock);
|
|
}
|
|
|
|
static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutputBus)
|
|
{
|
|
return pOutputBus->channels;
|
|
}
|
|
|
|
static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead)
|
|
{
|
|
if (hasRead) {
|
|
ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
|
|
} else {
|
|
ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ);
|
|
}
|
|
}
|
|
|
|
static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus)
|
|
{
|
|
return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0;
|
|
}
|
|
|
|
static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached)
|
|
{
|
|
ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached);
|
|
}
|
|
|
|
static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus)
|
|
{
|
|
return ma_atomic_load_32(&pOutputBus->isAttached);
|
|
}
|
|
|
|
static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, float volume)
|
|
{
|
|
MA_ASSERT(pOutputBus != NULL);
|
|
|
|
if (volume < 0.0f) {
|
|
volume = 0.0f;
|
|
}
|
|
|
|
ma_atomic_exchange_f32(&pOutputBus->volume, volume);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus)
|
|
{
|
|
return ma_atomic_load_f32((float*)&pOutputBus->volume);
|
|
}
|
|
|
|
static ma_result ma_node_input_bus_init(ma_uint32 channels, ma_node_input_bus* pInputBus)
|
|
{
|
|
MA_ASSERT(pInputBus != NULL);
|
|
MA_ASSERT(channels < 256);
|
|
|
|
MA_ZERO_OBJECT(pInputBus);
|
|
|
|
if (channels == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pInputBus->channels = (ma_uint8)channels;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_node_input_bus_lock(ma_node_input_bus* pInputBus)
|
|
{
|
|
MA_ASSERT(pInputBus != NULL);
|
|
|
|
ma_spinlock_lock(&pInputBus->lock);
|
|
}
|
|
|
|
static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus)
|
|
{
|
|
MA_ASSERT(pInputBus != NULL);
|
|
|
|
ma_spinlock_unlock(&pInputBus->lock);
|
|
}
|
|
|
|
static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus)
|
|
{
|
|
ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1);
|
|
}
|
|
|
|
static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus)
|
|
{
|
|
ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1);
|
|
}
|
|
|
|
static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus)
|
|
{
|
|
return ma_atomic_load_32(&pInputBus->nextCounter);
|
|
}
|
|
|
|
static ma_uint32 ma_node_input_bus_get_channels(const ma_node_input_bus* pInputBus)
|
|
{
|
|
return pInputBus->channels;
|
|
}
|
|
|
|
static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
|
|
{
|
|
MA_ASSERT(pInputBus != NULL);
|
|
MA_ASSERT(pOutputBus != NULL);
|
|
|
|
ma_node_output_bus_set_is_attached(pOutputBus, MA_FALSE);
|
|
|
|
ma_node_input_bus_lock(pInputBus);
|
|
{
|
|
ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev);
|
|
ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext);
|
|
|
|
if (pOldPrev != NULL) {
|
|
ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext);
|
|
}
|
|
if (pOldNext != NULL) {
|
|
ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev);
|
|
}
|
|
}
|
|
ma_node_input_bus_unlock(pInputBus);
|
|
|
|
ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL);
|
|
ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL);
|
|
pOutputBus->pInputNode = NULL;
|
|
pOutputBus->inputNodeInputBusIndex = 0;
|
|
|
|
while (ma_node_input_bus_get_next_counter(pInputBus) > 0) {
|
|
ma_yield();
|
|
}
|
|
|
|
while (ma_atomic_load_32(&pOutputBus->refCount) > 0) {
|
|
ma_yield();
|
|
}
|
|
|
|
}
|
|
|
|
#if 0
|
|
static void ma_node_input_bus_detach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
|
|
{
|
|
MA_ASSERT(pInputBus != NULL);
|
|
MA_ASSERT(pOutputBus != NULL);
|
|
|
|
ma_node_output_bus_lock(pOutputBus);
|
|
{
|
|
ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
|
|
}
|
|
ma_node_output_bus_unlock(pOutputBus);
|
|
}
|
|
#endif
|
|
|
|
static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus, ma_node* pNewInputNode, ma_uint32 inputNodeInputBusIndex)
|
|
{
|
|
MA_ASSERT(pInputBus != NULL);
|
|
MA_ASSERT(pOutputBus != NULL);
|
|
|
|
ma_node_output_bus_lock(pOutputBus);
|
|
{
|
|
ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode);
|
|
|
|
if (pOldInputNode != NULL) {
|
|
ma_node_input_bus_detach__no_output_bus_lock(pInputBus, pOutputBus);
|
|
}
|
|
|
|
pOutputBus->pInputNode = pNewInputNode;
|
|
pOutputBus->inputNodeInputBusIndex = (ma_uint8)inputNodeInputBusIndex;
|
|
|
|
ma_node_input_bus_lock(pInputBus);
|
|
{
|
|
ma_node_output_bus* pNewPrev = &pInputBus->head;
|
|
ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext);
|
|
|
|
ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev);
|
|
ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext);
|
|
|
|
ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus);
|
|
|
|
if (pNewNext != NULL) {
|
|
ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus);
|
|
}
|
|
}
|
|
ma_node_input_bus_unlock(pInputBus);
|
|
|
|
ma_node_output_bus_set_is_attached(pOutputBus, MA_TRUE);
|
|
}
|
|
ma_node_output_bus_unlock(pOutputBus);
|
|
}
|
|
|
|
static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, ma_node_output_bus* pOutputBus)
|
|
{
|
|
ma_node_output_bus* pNext;
|
|
|
|
MA_ASSERT(pInputBus != NULL);
|
|
|
|
if (pOutputBus == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
ma_node_input_bus_next_begin(pInputBus);
|
|
{
|
|
pNext = pOutputBus;
|
|
for (;;) {
|
|
pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext);
|
|
if (pNext == NULL) {
|
|
break;
|
|
}
|
|
|
|
if (ma_node_output_bus_is_attached(pNext) == MA_FALSE) {
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (pNext != NULL) {
|
|
ma_atomic_fetch_add_32(&pNext->refCount, 1);
|
|
}
|
|
|
|
ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1);
|
|
}
|
|
ma_node_input_bus_next_end(pInputBus);
|
|
|
|
return pNext;
|
|
}
|
|
|
|
static ma_node_output_bus* ma_node_input_bus_first(ma_node_input_bus* pInputBus)
|
|
{
|
|
return ma_node_input_bus_next(pInputBus, &pInputBus->head);
|
|
}
|
|
|
|
static ma_result ma_node_input_bus_read_pcm_frames(ma_node* pInputNode, ma_node_input_bus* pInputBus, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_node_output_bus* pOutputBus;
|
|
ma_node_output_bus* pFirst;
|
|
ma_uint32 inputChannels;
|
|
ma_bool32 doesOutputBufferHaveContent = MA_FALSE;
|
|
|
|
MA_ASSERT(pInputNode != NULL);
|
|
MA_ASSERT(pFramesRead != NULL);
|
|
|
|
*pFramesRead = 0;
|
|
|
|
inputChannels = ma_node_input_bus_get_channels(pInputBus);
|
|
|
|
pFirst = ma_node_input_bus_first(pInputBus);
|
|
if (pFirst == NULL) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
for (pOutputBus = pFirst; pOutputBus != NULL; pOutputBus = ma_node_input_bus_next(pInputBus, pOutputBus)) {
|
|
ma_uint32 framesProcessed = 0;
|
|
ma_bool32 isSilentOutput = MA_FALSE;
|
|
|
|
MA_ASSERT(pOutputBus->pNode != NULL);
|
|
MA_ASSERT(((ma_node_base*)pOutputBus->pNode)->vtable != NULL);
|
|
|
|
isSilentOutput = (((ma_node_base*)pOutputBus->pNode)->vtable->flags & MA_NODE_FLAG_SILENT_OUTPUT) != 0;
|
|
|
|
if (pFramesOut != NULL) {
|
|
while (framesProcessed < frameCount) {
|
|
float* pRunningFramesOut;
|
|
ma_uint32 framesToRead;
|
|
ma_uint32 framesJustRead = 0;
|
|
|
|
framesToRead = frameCount - framesProcessed;
|
|
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(pFramesOut, framesProcessed, inputChannels);
|
|
|
|
if (doesOutputBufferHaveContent == MA_FALSE) {
|
|
result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pRunningFramesOut, framesToRead, &framesJustRead, globalTime + framesProcessed);
|
|
} else {
|
|
ma_uint32 preMixBufferCapInFrames = ((ma_node_base*)pInputNode)->cachedDataCapInFramesPerBus;
|
|
float* pPreMixBuffer = (float*)ma_stack_alloc(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, preMixBufferCapInFrames * inputChannels * sizeof(float));
|
|
|
|
if (pPreMixBuffer == NULL) {
|
|
MA_ASSERT(MA_FALSE);
|
|
} else {
|
|
if (framesToRead > preMixBufferCapInFrames) {
|
|
framesToRead = preMixBufferCapInFrames;
|
|
}
|
|
|
|
result = ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, pPreMixBuffer, framesToRead, &framesJustRead, globalTime + framesProcessed);
|
|
if (result == MA_SUCCESS || result == MA_AT_END) {
|
|
if (isSilentOutput == MA_FALSE) {
|
|
ma_mix_pcm_frames_f32(pRunningFramesOut, pPreMixBuffer, framesJustRead, inputChannels, 1);
|
|
}
|
|
}
|
|
|
|
ma_stack_free(((ma_node_base*)pInputNode)->pNodeGraph->pPreMixStack, pPreMixBuffer);
|
|
pPreMixBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
framesProcessed += framesJustRead;
|
|
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (framesJustRead == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pOutputBus == pFirst && framesProcessed < frameCount) {
|
|
ma_silence_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, framesProcessed, ma_format_f32, inputChannels), (frameCount - framesProcessed), ma_format_f32, inputChannels);
|
|
}
|
|
|
|
if (isSilentOutput == MA_FALSE) {
|
|
doesOutputBufferHaveContent = MA_TRUE;
|
|
}
|
|
} else {
|
|
ma_node_read_pcm_frames(pOutputBus->pNode, pOutputBus->outputBusIndex, NULL, frameCount, &framesProcessed, globalTime);
|
|
}
|
|
}
|
|
|
|
if (doesOutputBufferHaveContent == MA_FALSE && pFramesOut != NULL) {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, inputChannels);
|
|
}
|
|
|
|
*pFramesRead = frameCount;
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_node_config ma_node_config_init(void)
|
|
{
|
|
ma_node_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.initialState = ma_node_state_started;
|
|
config.inputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
|
|
config.outputBusCount = MA_NODE_BUS_COUNT_UNKNOWN;
|
|
|
|
return config;
|
|
}
|
|
|
|
static ma_uint16 ma_node_config_get_cache_size_in_frames(const ma_node_config* pConfig, const ma_node_graph* pNodeGraph)
|
|
{
|
|
ma_uint32 cacheSizeInFrames;
|
|
|
|
(void)pConfig;
|
|
|
|
if (pNodeGraph->processingSizeInFrames > 0) {
|
|
cacheSizeInFrames = pNodeGraph->processingSizeInFrames;
|
|
} else {
|
|
cacheSizeInFrames = MA_DEFAULT_NODE_CACHE_CAP_IN_FRAMES_PER_BUS;
|
|
}
|
|
|
|
if (cacheSizeInFrames > 0xFFFF) {
|
|
cacheSizeInFrames = 0xFFFF;
|
|
}
|
|
|
|
return (ma_uint16)cacheSizeInFrames;
|
|
}
|
|
|
|
static ma_result ma_node_detach_full(ma_node* pNode);
|
|
|
|
static float* ma_node_get_cached_input_ptr(ma_node* pNode, ma_uint32 inputBusIndex)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_uint32 iInputBus;
|
|
float* pBasePtr;
|
|
|
|
MA_ASSERT(pNodeBase != NULL);
|
|
|
|
pBasePtr = pNodeBase->pCachedData;
|
|
for (iInputBus = 0; iInputBus < inputBusIndex; iInputBus += 1) {
|
|
pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
|
|
}
|
|
|
|
return pBasePtr;
|
|
}
|
|
|
|
static float* ma_node_get_cached_output_ptr(ma_node* pNode, ma_uint32 outputBusIndex)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_uint32 iInputBus;
|
|
ma_uint32 iOutputBus;
|
|
float* pBasePtr;
|
|
|
|
MA_ASSERT(pNodeBase != NULL);
|
|
|
|
pBasePtr = pNodeBase->pCachedData;
|
|
for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
|
|
pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iInputBus]);
|
|
}
|
|
|
|
for (iOutputBus = 0; iOutputBus < outputBusIndex; iOutputBus += 1) {
|
|
pBasePtr += pNodeBase->cachedDataCapInFramesPerBus * ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iOutputBus]);
|
|
}
|
|
|
|
return pBasePtr;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t inputBusOffset;
|
|
size_t outputBusOffset;
|
|
size_t cachedDataOffset;
|
|
ma_uint32 inputBusCount;
|
|
ma_uint32 outputBusCount;
|
|
} ma_node_heap_layout;
|
|
|
|
static ma_result ma_node_translate_bus_counts(const ma_node_config* pConfig, ma_uint32* pInputBusCount, ma_uint32* pOutputBusCount)
|
|
{
|
|
ma_uint32 inputBusCount;
|
|
ma_uint32 outputBusCount;
|
|
|
|
MA_ASSERT(pConfig != NULL);
|
|
MA_ASSERT(pInputBusCount != NULL);
|
|
MA_ASSERT(pOutputBusCount != NULL);
|
|
|
|
if (pConfig->vtable->inputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
|
|
inputBusCount = pConfig->inputBusCount;
|
|
} else {
|
|
inputBusCount = pConfig->vtable->inputBusCount;
|
|
|
|
if (pConfig->inputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->inputBusCount != pConfig->vtable->inputBusCount) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
if (pConfig->vtable->outputBusCount == MA_NODE_BUS_COUNT_UNKNOWN) {
|
|
outputBusCount = pConfig->outputBusCount;
|
|
} else {
|
|
outputBusCount = pConfig->vtable->outputBusCount;
|
|
|
|
if (pConfig->outputBusCount != MA_NODE_BUS_COUNT_UNKNOWN && pConfig->outputBusCount != pConfig->vtable->outputBusCount) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
if (inputBusCount > MA_MAX_NODE_BUS_COUNT || outputBusCount > MA_MAX_NODE_BUS_COUNT) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((inputBusCount > 0 && pConfig->pInputChannels == NULL) || (outputBusCount > 0 && pConfig->pOutputChannels == NULL)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if ((pConfig->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
|
|
if ((pConfig->vtable->inputBusCount != 0 && pConfig->vtable->inputBusCount != 1) || pConfig->vtable->outputBusCount != 1) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->pInputChannels[0] != pConfig->pOutputChannels[0]) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
}
|
|
|
|
*pInputBusCount = inputBusCount;
|
|
*pOutputBusCount = outputBusCount;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_node_get_heap_layout(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, ma_node_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
ma_uint32 inputBusCount;
|
|
ma_uint32 outputBusCount;
|
|
|
|
MA_ASSERT(pHeapLayout != NULL);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL || pConfig->vtable == NULL || pConfig->vtable->onProcess == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_node_translate_bus_counts(pConfig, &inputBusCount, &outputBusCount);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
if (inputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
|
|
pHeapLayout->inputBusOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_input_bus) * inputBusCount);
|
|
} else {
|
|
pHeapLayout->inputBusOffset = MA_SIZE_MAX;
|
|
}
|
|
|
|
if (outputBusCount > MA_MAX_NODE_LOCAL_BUS_COUNT) {
|
|
pHeapLayout->outputBusOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(sizeof(ma_node_output_bus) * outputBusCount);
|
|
} else {
|
|
pHeapLayout->outputBusOffset = MA_SIZE_MAX;
|
|
}
|
|
|
|
if (inputBusCount == 0 && outputBusCount == 1) {
|
|
pHeapLayout->cachedDataOffset = MA_SIZE_MAX;
|
|
} else {
|
|
size_t cachedDataSizeInBytes = 0;
|
|
ma_uint32 cacheCapInFrames;
|
|
ma_uint32 iBus;
|
|
|
|
cacheCapInFrames = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph);
|
|
|
|
for (iBus = 0; iBus < inputBusCount; iBus += 1) {
|
|
cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pInputChannels[iBus]);
|
|
}
|
|
|
|
for (iBus = 0; iBus < outputBusCount; iBus += 1) {
|
|
cachedDataSizeInBytes += cacheCapInFrames * ma_get_bytes_per_frame(ma_format_f32, pConfig->pOutputChannels[iBus]);
|
|
}
|
|
|
|
pHeapLayout->cachedDataOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(cachedDataSizeInBytes);
|
|
}
|
|
|
|
pHeapLayout->inputBusCount = inputBusCount;
|
|
pHeapLayout->outputBusCount = outputBusCount;
|
|
|
|
pHeapLayout->sizeInBytes = ma_align_64(pHeapLayout->sizeInBytes);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_node_get_heap_size(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_node_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, void* pHeap, ma_node* pNode)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_result result;
|
|
ma_node_heap_layout heapLayout;
|
|
ma_uint32 iInputBus;
|
|
ma_uint32 iOutputBus;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNodeBase);
|
|
|
|
result = ma_node_get_heap_layout(pNodeGraph, pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pNodeBase->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pNodeBase->pNodeGraph = pNodeGraph;
|
|
pNodeBase->vtable = pConfig->vtable;
|
|
pNodeBase->state = pConfig->initialState;
|
|
pNodeBase->stateTimes[ma_node_state_started] = 0;
|
|
pNodeBase->stateTimes[ma_node_state_stopped] = (ma_uint64)(ma_int64)-1;
|
|
pNodeBase->inputBusCount = heapLayout.inputBusCount;
|
|
pNodeBase->outputBusCount = heapLayout.outputBusCount;
|
|
|
|
if (heapLayout.inputBusOffset != MA_SIZE_MAX) {
|
|
pNodeBase->pInputBuses = (ma_node_input_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset);
|
|
} else {
|
|
pNodeBase->pInputBuses = pNodeBase->_inputBuses;
|
|
}
|
|
|
|
if (heapLayout.outputBusOffset != MA_SIZE_MAX) {
|
|
pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset);
|
|
} else {
|
|
pNodeBase->pOutputBuses = pNodeBase->_outputBuses;
|
|
}
|
|
|
|
if (heapLayout.cachedDataOffset != MA_SIZE_MAX) {
|
|
pNodeBase->pCachedData = (float*)ma_offset_ptr(pHeap, heapLayout.cachedDataOffset);
|
|
pNodeBase->cachedDataCapInFramesPerBus = ma_node_config_get_cache_size_in_frames(pConfig, pNodeGraph);
|
|
} else {
|
|
pNodeBase->pCachedData = NULL;
|
|
}
|
|
|
|
for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNodeBase); iInputBus += 1) {
|
|
result = ma_node_input_bus_init(pConfig->pInputChannels[iInputBus], &pNodeBase->pInputBuses[iInputBus]);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
|
|
result = ma_node_output_bus_init(pNodeBase, iOutputBus, pConfig->pOutputChannels[iOutputBus], &pNodeBase->pOutputBuses[iOutputBus]);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (pNodeBase->pCachedData != NULL) {
|
|
ma_uint32 iBus;
|
|
|
|
#if 1
|
|
for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
|
|
ma_silence_pcm_frames(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]));
|
|
}
|
|
for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
|
|
ma_silence_pcm_frames(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]));
|
|
}
|
|
#else
|
|
for (iBus = 0; iBus < ma_node_get_input_bus_count(pNodeBase); iBus += 1) {
|
|
ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_input_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[iBus]), 48000);
|
|
}
|
|
for (iBus = 0; iBus < ma_node_get_output_bus_count(pNodeBase); iBus += 1) {
|
|
ma_debug_fill_pcm_frames_with_sine_wave(ma_node_get_cached_output_ptr(pNode, iBus), pNodeBase->cachedDataCapInFramesPerBus, ma_format_f32, ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[iBus]), 48000);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_node_init(ma_node_graph* pNodeGraph, const ma_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_node* pNode)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_node_get_heap_size(pNodeGraph, pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_node_init_preallocated(pNodeGraph, pConfig, pHeap, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
((ma_node_base*)pNode)->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_node_uninit(ma_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_detach_full(pNode);
|
|
|
|
if (pNodeBase->_ownsHeap) {
|
|
ma_free(pNodeBase->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_node_graph* ma_node_get_node_graph(const ma_node* pNode)
|
|
{
|
|
if (pNode == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return ((const ma_node_base*)pNode)->pNodeGraph;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_node_get_input_bus_count(const ma_node* pNode)
|
|
{
|
|
if (pNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ((ma_node_base*)pNode)->inputBusCount;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_node_get_output_bus_count(const ma_node* pNode)
|
|
{
|
|
if (pNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ((ma_node_base*)pNode)->outputBusCount;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_node_get_input_channels(const ma_node* pNode, ma_uint32 inputBusIndex)
|
|
{
|
|
const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (inputBusIndex >= ma_node_get_input_bus_count(pNode)) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_input_bus_get_channels(&pNodeBase->pInputBuses[inputBusIndex]);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_node_get_output_channels(const ma_node* pNode, ma_uint32 outputBusIndex)
|
|
{
|
|
const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_output_bus_get_channels(&pNodeBase->pOutputBuses[outputBusIndex]);
|
|
}
|
|
|
|
static ma_result ma_node_detach_full(ma_node* pNode)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_uint32 iInputBus;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_node_detach_all_output_buses(pNode);
|
|
|
|
for (iInputBus = 0; iInputBus < ma_node_get_input_bus_count(pNode); iInputBus += 1) {
|
|
ma_node_input_bus* pInputBus;
|
|
ma_node_output_bus* pOutputBus;
|
|
|
|
pInputBus = &pNodeBase->pInputBuses[iInputBus];
|
|
|
|
for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext)) {
|
|
ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex);
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_node_detach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_node_base* pInputNodeBase;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_node_output_bus_lock(&pNodeBase->pOutputBuses[outputBusIndex]);
|
|
{
|
|
pInputNodeBase = (ma_node_base*)pNodeBase->pOutputBuses[outputBusIndex].pInputNode;
|
|
if (pInputNodeBase != NULL) {
|
|
ma_node_input_bus_detach__no_output_bus_lock(&pInputNodeBase->pInputBuses[pNodeBase->pOutputBuses[outputBusIndex].inputNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex]);
|
|
}
|
|
}
|
|
ma_node_output_bus_unlock(&pNodeBase->pOutputBuses[outputBusIndex]);
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_node_detach_all_output_buses(ma_node* pNode)
|
|
{
|
|
ma_uint32 iOutputBus;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNode); iOutputBus += 1) {
|
|
ma_node_detach_output_bus(pNode, iOutputBus);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_node_attach_output_bus(ma_node* pNode, ma_uint32 outputBusIndex, ma_node* pOtherNode, ma_uint32 otherNodeInputBusIndex)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_node_base* pOtherNodeBase = (ma_node_base*)pOtherNode;
|
|
|
|
if (pNodeBase == NULL || pOtherNodeBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pNodeBase == pOtherNodeBase) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (outputBusIndex >= ma_node_get_output_bus_count(pNode) || otherNodeInputBusIndex >= ma_node_get_input_bus_count(pOtherNode)) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (ma_node_get_output_channels(pNode, outputBusIndex) != ma_node_get_input_channels(pOtherNode, otherNodeInputBusIndex)) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_node_input_bus_attach(&pOtherNodeBase->pInputBuses[otherNodeInputBusIndex], &pNodeBase->pOutputBuses[outputBusIndex], pOtherNode, otherNodeInputBusIndex);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_node_set_output_bus_volume(ma_node* pNode, ma_uint32 outputBusIndex, float volume)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_node_output_bus_set_volume(&pNodeBase->pOutputBuses[outputBusIndex], volume);
|
|
}
|
|
|
|
MA_API float ma_node_get_output_bus_volume(const ma_node* pNode, ma_uint32 outputBusIndex)
|
|
{
|
|
const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (outputBusIndex >= ma_node_get_output_bus_count(pNode)) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]);
|
|
}
|
|
|
|
MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_exchange_i32(&pNodeBase->state, state);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_node_state ma_node_get_state(const ma_node* pNode)
|
|
{
|
|
const ma_node_base* pNodeBase = (const ma_node_base*)pNode;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return ma_node_state_stopped;
|
|
}
|
|
|
|
return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state);
|
|
}
|
|
|
|
MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime)
|
|
{
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (state != ma_node_state_started && state != ma_node_state_stopped) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state state)
|
|
{
|
|
if (pNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (state != ma_node_state_started && state != ma_node_state_stopped) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]);
|
|
}
|
|
|
|
MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime)
|
|
{
|
|
if (pNode == NULL) {
|
|
return ma_node_state_stopped;
|
|
}
|
|
|
|
return ma_node_get_state_by_time_range(pNode, globalTime, globalTime);
|
|
}
|
|
|
|
MA_API ma_node_state ma_node_get_state_by_time_range(const ma_node* pNode, ma_uint64 globalTimeBeg, ma_uint64 globalTimeEnd)
|
|
{
|
|
ma_node_state state;
|
|
|
|
if (pNode == NULL) {
|
|
return ma_node_state_stopped;
|
|
}
|
|
|
|
state = ma_node_get_state(pNode);
|
|
|
|
if (state == ma_node_state_stopped) {
|
|
return ma_node_state_stopped;
|
|
}
|
|
|
|
if (ma_node_get_state_time(pNode, ma_node_state_stopped) < globalTimeBeg) {
|
|
return ma_node_state_stopped;
|
|
}
|
|
|
|
if (ma_node_get_state_time(pNode, ma_node_state_started) > globalTimeEnd) {
|
|
return ma_node_state_stopped;
|
|
}
|
|
|
|
return ma_node_state_started;
|
|
}
|
|
|
|
MA_API ma_uint64 ma_node_get_time(const ma_node* pNode)
|
|
{
|
|
if (pNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime);
|
|
}
|
|
|
|
MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime)
|
|
{
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_node_process_pcm_frames_internal(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
|
|
if (pNodeBase->vtable->onProcess) {
|
|
pNodeBase->vtable->onProcess(pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
|
}
|
|
}
|
|
|
|
static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusIndex, float* pFramesOut, ma_uint32 frameCount, ma_uint32* pFramesRead, ma_uint64 globalTime)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 iInputBus;
|
|
ma_uint32 iOutputBus;
|
|
ma_uint32 inputBusCount;
|
|
ma_uint32 outputBusCount;
|
|
ma_uint32 totalFramesRead = 0;
|
|
float* ppFramesIn[MA_MAX_NODE_BUS_COUNT];
|
|
float* ppFramesOut[MA_MAX_NODE_BUS_COUNT];
|
|
ma_uint64 globalTimeBeg;
|
|
ma_uint64 globalTimeEnd;
|
|
ma_uint64 startTime;
|
|
ma_uint64 stopTime;
|
|
ma_uint32 timeOffsetBeg;
|
|
ma_uint32 timeOffsetEnd;
|
|
ma_uint32 frameCountIn;
|
|
ma_uint32 frameCountOut;
|
|
|
|
MA_ASSERT(pFramesRead != NULL);
|
|
if (pFramesRead == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pFramesRead = 0;
|
|
|
|
if (pNodeBase == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (outputBusIndex >= ma_node_get_output_bus_count(pNodeBase)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
globalTimeBeg = globalTime;
|
|
globalTimeEnd = globalTime + frameCount;
|
|
|
|
if (ma_node_get_state_by_time_range(pNode, globalTimeBeg, globalTimeEnd) != ma_node_state_started) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
startTime = ma_node_get_state_time(pNode, ma_node_state_started);
|
|
stopTime = ma_node_get_state_time(pNode, ma_node_state_stopped);
|
|
|
|
timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(startTime - globalTimeBeg) : 0;
|
|
timeOffsetEnd = (globalTimeEnd > stopTime) ? (ma_uint32)(globalTimeEnd - stopTime) : 0;
|
|
|
|
if (timeOffsetBeg > 0) {
|
|
MA_ASSERT(timeOffsetBeg <= frameCount);
|
|
if (timeOffsetBeg > frameCount) {
|
|
timeOffsetBeg = frameCount;
|
|
}
|
|
|
|
ma_silence_pcm_frames(pFramesOut, timeOffsetBeg, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
|
|
pFramesOut += timeOffsetBeg * ma_node_get_output_channels(pNode, outputBusIndex);
|
|
frameCount -= timeOffsetBeg;
|
|
}
|
|
|
|
if (timeOffsetEnd > 0) {
|
|
MA_ASSERT(timeOffsetEnd <= frameCount);
|
|
if (timeOffsetEnd > frameCount) {
|
|
timeOffsetEnd = frameCount;
|
|
}
|
|
|
|
frameCount -= timeOffsetEnd;
|
|
}
|
|
|
|
inputBusCount = ma_node_get_input_bus_count(pNode);
|
|
outputBusCount = ma_node_get_output_bus_count(pNode);
|
|
|
|
if (inputBusCount == 0 && outputBusCount == 1) {
|
|
frameCountIn = 0;
|
|
frameCountOut = frameCount;
|
|
|
|
ppFramesOut[0] = pFramesOut;
|
|
|
|
if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
|
|
ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, ma_node_get_output_channels(pNode, outputBusIndex));
|
|
}
|
|
|
|
ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
|
|
totalFramesRead = frameCountOut;
|
|
} else {
|
|
if ((pNodeBase->vtable->flags & MA_NODE_FLAG_PASSTHROUGH) != 0) {
|
|
MA_ASSERT(outputBusCount == inputBusCount);
|
|
MA_ASSERT(outputBusCount == 1);
|
|
MA_ASSERT(outputBusIndex == 0);
|
|
|
|
ppFramesOut[0] = pFramesOut;
|
|
ppFramesIn[0] = ppFramesOut[0];
|
|
|
|
result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[0], ppFramesIn[0], frameCount, &totalFramesRead, globalTime);
|
|
if (result == MA_SUCCESS) {
|
|
frameCountIn = totalFramesRead;
|
|
frameCountOut = totalFramesRead;
|
|
|
|
if (totalFramesRead > 0) {
|
|
ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);
|
|
}
|
|
|
|
MA_ASSERT(frameCountIn == totalFramesRead);
|
|
MA_ASSERT(frameCountOut == totalFramesRead);
|
|
}
|
|
} else {
|
|
ma_uint32 framesToProcessIn;
|
|
ma_uint32 framesToProcessOut;
|
|
ma_bool32 consumeNullInput = MA_FALSE;
|
|
|
|
framesToProcessOut = frameCount;
|
|
if (framesToProcessOut > pNodeBase->cachedDataCapInFramesPerBus) {
|
|
framesToProcessOut = pNodeBase->cachedDataCapInFramesPerBus;
|
|
}
|
|
|
|
framesToProcessIn = frameCount;
|
|
if (pNodeBase->vtable->onGetRequiredInputFrameCount) {
|
|
pNodeBase->vtable->onGetRequiredInputFrameCount(pNode, framesToProcessOut, &framesToProcessIn);
|
|
}
|
|
if (framesToProcessIn > pNodeBase->cachedDataCapInFramesPerBus) {
|
|
framesToProcessIn = pNodeBase->cachedDataCapInFramesPerBus;
|
|
}
|
|
|
|
MA_ASSERT(framesToProcessIn <= 0xFFFF);
|
|
MA_ASSERT(framesToProcessOut <= 0xFFFF);
|
|
|
|
if (ma_node_output_bus_has_read(&pNodeBase->pOutputBuses[outputBusIndex])) {
|
|
pNodeBase->cachedFrameCountOut = 0;
|
|
|
|
for (;;) {
|
|
frameCountOut = 0;
|
|
|
|
for (iOutputBus = 0; iOutputBus < outputBusCount; iOutputBus += 1) {
|
|
ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[iOutputBus], MA_FALSE);
|
|
ppFramesOut[iOutputBus] = ma_node_get_cached_output_ptr(pNode, iOutputBus);
|
|
}
|
|
|
|
if (pNodeBase->cachedFrameCountIn == 0) {
|
|
ma_uint32 maxFramesReadIn = 0;
|
|
|
|
for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
|
|
ma_uint32 framesRead;
|
|
|
|
ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus);
|
|
|
|
result = ma_node_input_bus_read_pcm_frames(pNodeBase, &pNodeBase->pInputBuses[iInputBus], ppFramesIn[iInputBus], framesToProcessIn, &framesRead, globalTime);
|
|
if (result != MA_SUCCESS) {
|
|
framesRead = 0;
|
|
}
|
|
|
|
if (framesRead < framesToProcessIn) {
|
|
ma_silence_pcm_frames(ppFramesIn[iInputBus] + (framesRead * ma_node_get_input_channels(pNodeBase, iInputBus)), (framesToProcessIn - framesRead), ma_format_f32, ma_node_get_input_channels(pNodeBase, iInputBus));
|
|
}
|
|
|
|
maxFramesReadIn = ma_max(maxFramesReadIn, framesRead);
|
|
}
|
|
|
|
pNodeBase->consumedFrameCountIn = 0;
|
|
|
|
pNodeBase->cachedFrameCountIn = (ma_uint16)maxFramesReadIn;
|
|
} else {
|
|
for (iInputBus = 0; iInputBus < inputBusCount; iInputBus += 1) {
|
|
ppFramesIn[iInputBus] = ma_node_get_cached_input_ptr(pNode, iInputBus) + (pNodeBase->consumedFrameCountIn * ma_node_get_input_channels(pNodeBase, iInputBus));
|
|
}
|
|
}
|
|
|
|
if (pFramesOut != NULL) {
|
|
ppFramesOut[outputBusIndex] = ma_offset_pcm_frames_ptr_f32(pFramesOut, pNodeBase->cachedFrameCountOut, ma_node_get_output_channels(pNode, outputBusIndex));
|
|
}
|
|
|
|
frameCountOut = (framesToProcessOut - pNodeBase->cachedFrameCountOut);
|
|
|
|
if ((pNodeBase->vtable->flags & MA_NODE_FLAG_CONTINUOUS_PROCESSING) != 0) {
|
|
frameCountIn = framesToProcessIn;
|
|
|
|
if ((pNodeBase->vtable->flags & MA_NODE_FLAG_ALLOW_NULL_INPUT) != 0 && pNodeBase->consumedFrameCountIn == 0 && pNodeBase->cachedFrameCountIn == 0) {
|
|
consumeNullInput = MA_TRUE;
|
|
} else {
|
|
consumeNullInput = MA_FALSE;
|
|
}
|
|
|
|
if (pNodeBase->cachedFrameCountIn < (ma_uint16)frameCountIn) {
|
|
pNodeBase->cachedFrameCountIn = (ma_uint16)frameCountIn;
|
|
}
|
|
} else {
|
|
frameCountIn = pNodeBase->cachedFrameCountIn;
|
|
consumeNullInput = MA_FALSE;
|
|
}
|
|
|
|
if (consumeNullInput) {
|
|
ma_node_process_pcm_frames_internal(pNode, NULL, &frameCountIn, ppFramesOut, &frameCountOut);
|
|
} else {
|
|
if (frameCountIn > 0 || (pNodeBase->vtable->flags & MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES) != 0) {
|
|
ma_node_process_pcm_frames_internal(pNode, (const float**)ppFramesIn, &frameCountIn, ppFramesOut, &frameCountOut);
|
|
} else {
|
|
frameCountOut = 0;
|
|
}
|
|
}
|
|
|
|
if (consumeNullInput == MA_FALSE) {
|
|
pNodeBase->consumedFrameCountIn += (ma_uint16)frameCountIn;
|
|
pNodeBase->cachedFrameCountIn -= (ma_uint16)frameCountIn;
|
|
}
|
|
|
|
pNodeBase->cachedFrameCountOut += (ma_uint16)frameCountOut;
|
|
|
|
if (pNodeBase->cachedFrameCountOut == framesToProcessOut || (frameCountOut == 0 && frameCountIn == 0)) {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (pFramesOut != NULL) {
|
|
ma_copy_pcm_frames(pFramesOut, ma_node_get_cached_output_ptr(pNodeBase, outputBusIndex), pNodeBase->cachedFrameCountOut, ma_format_f32, ma_node_get_output_channels(pNodeBase, outputBusIndex));
|
|
}
|
|
}
|
|
|
|
totalFramesRead = pNodeBase->cachedFrameCountOut;
|
|
|
|
ma_node_output_bus_set_has_read(&pNodeBase->pOutputBuses[outputBusIndex], MA_TRUE);
|
|
}
|
|
}
|
|
|
|
ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex]));
|
|
|
|
ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead);
|
|
|
|
*pFramesRead = totalFramesRead + timeOffsetBeg;
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_data_source_node_config ma_data_source_node_config_init(ma_data_source* pDataSource)
|
|
{
|
|
ma_data_source_node_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.pDataSource = pDataSource;
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_data_source_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_data_source_node* pDataSourceNode = (ma_data_source_node*)pNode;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_uint32 frameCount;
|
|
ma_uint64 framesRead = 0;
|
|
|
|
MA_ASSERT(pDataSourceNode != NULL);
|
|
MA_ASSERT(pDataSourceNode->pDataSource != NULL);
|
|
MA_ASSERT(ma_node_get_input_bus_count(pDataSourceNode) == 0);
|
|
MA_ASSERT(ma_node_get_output_bus_count(pDataSourceNode) == 1);
|
|
|
|
(void)ppFramesIn;
|
|
(void)pFrameCountIn;
|
|
|
|
frameCount = *pFrameCountOut;
|
|
|
|
MA_ASSERT(frameCount > 0);
|
|
|
|
if (ma_data_source_get_data_format(pDataSourceNode->pDataSource, &format, &channels, NULL, NULL, 0) == MA_SUCCESS) {
|
|
MA_ASSERT(format == ma_format_f32);
|
|
(void)format;
|
|
|
|
ma_data_source_read_pcm_frames(pDataSourceNode->pDataSource, ppFramesOut[0], frameCount, &framesRead);
|
|
}
|
|
|
|
*pFrameCountOut = (ma_uint32)framesRead;
|
|
}
|
|
|
|
static ma_node_vtable g_ma_data_source_node_vtable =
|
|
{
|
|
ma_data_source_node_process_pcm_frames,
|
|
NULL,
|
|
0,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_data_source_node_init(ma_node_graph* pNodeGraph, const ma_data_source_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_data_source_node* pDataSourceNode)
|
|
{
|
|
ma_result result;
|
|
ma_format format;
|
|
ma_uint32 channels;
|
|
ma_node_config baseConfig;
|
|
|
|
if (pDataSourceNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDataSourceNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_data_source_get_data_format(pConfig->pDataSource, &format, &channels, NULL, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
MA_ASSERT(format == ma_format_f32);
|
|
if (format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
baseConfig = pConfig->nodeConfig;
|
|
baseConfig.vtable = &g_ma_data_source_node_vtable;
|
|
|
|
if (baseConfig.pOutputChannels != NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
baseConfig.pOutputChannels = &channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDataSourceNode->base);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pDataSourceNode->pDataSource = pConfig->pDataSource;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_data_source_node_uninit(ma_data_source_node* pDataSourceNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_node_uninit(&pDataSourceNode->base, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_result ma_data_source_node_set_looping(ma_data_source_node* pDataSourceNode, ma_bool32 isLooping)
|
|
{
|
|
if (pDataSourceNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_data_source_set_looping(pDataSourceNode->pDataSource, isLooping);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_data_source_node_is_looping(ma_data_source_node* pDataSourceNode)
|
|
{
|
|
if (pDataSourceNode == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_data_source_is_looping(pDataSourceNode->pDataSource);
|
|
}
|
|
|
|
MA_API ma_splitter_node_config ma_splitter_node_config_init(ma_uint32 channels)
|
|
{
|
|
ma_splitter_node_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.channels = channels;
|
|
config.outputBusCount = 2;
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_splitter_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_node_base* pNodeBase = (ma_node_base*)pNode;
|
|
ma_uint32 iOutputBus;
|
|
ma_uint32 channels;
|
|
|
|
MA_ASSERT(pNodeBase != NULL);
|
|
MA_ASSERT(ma_node_get_input_bus_count(pNodeBase) == 1);
|
|
|
|
(void)pFrameCountIn;
|
|
|
|
channels = ma_node_get_input_channels(pNodeBase, 0);
|
|
|
|
for (iOutputBus = 0; iOutputBus < ma_node_get_output_bus_count(pNodeBase); iOutputBus += 1) {
|
|
ma_copy_pcm_frames(ppFramesOut[iOutputBus], ppFramesIn[0], *pFrameCountOut, ma_format_f32, channels);
|
|
}
|
|
}
|
|
|
|
static ma_node_vtable g_ma_splitter_node_vtable =
|
|
{
|
|
ma_splitter_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
MA_NODE_BUS_COUNT_UNKNOWN,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_splitter_node_init(ma_node_graph* pNodeGraph, const ma_splitter_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_splitter_node* pSplitterNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseConfig;
|
|
ma_uint32 pInputChannels[1];
|
|
ma_uint32 pOutputChannels[MA_MAX_NODE_BUS_COUNT];
|
|
ma_uint32 iOutputBus;
|
|
|
|
if (pSplitterNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pSplitterNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->outputBusCount > MA_MAX_NODE_BUS_COUNT) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pInputChannels[0] = pConfig->channels;
|
|
for (iOutputBus = 0; iOutputBus < pConfig->outputBusCount; iOutputBus += 1) {
|
|
pOutputChannels[iOutputBus] = pConfig->channels;
|
|
}
|
|
|
|
baseConfig = pConfig->nodeConfig;
|
|
baseConfig.vtable = &g_ma_splitter_node_vtable;
|
|
baseConfig.pInputChannels = pInputChannels;
|
|
baseConfig.pOutputChannels = pOutputChannels;
|
|
baseConfig.outputBusCount = pConfig->outputBusCount;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pSplitterNode->base);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_splitter_node_uninit(ma_splitter_node* pSplitterNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_node_uninit(pSplitterNode, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_biquad_node_config ma_biquad_node_config_init(ma_uint32 channels, float b0, float b1, float b2, float a0, float a1, float a2)
|
|
{
|
|
ma_biquad_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.biquad = ma_biquad_config_init(ma_format_f32, channels, b0, b1, b2, a0, a1, a2);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_biquad_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_biquad_process_pcm_frames(&pLPFNode->biquad, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_biquad_node_vtable =
|
|
{
|
|
ma_biquad_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_biquad_node_init(ma_node_graph* pNodeGraph, const ma_biquad_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_biquad_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->biquad.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_biquad_init(&pConfig->biquad, pAllocationCallbacks, &pNode->biquad);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_biquad_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->biquad.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->biquad.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_biquad_node_reinit(const ma_biquad_config* pConfig, ma_biquad_node* pNode)
|
|
{
|
|
ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
|
|
return ma_biquad_reinit(pConfig, &pLPFNode->biquad);
|
|
}
|
|
|
|
MA_API void ma_biquad_node_uninit(ma_biquad_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_biquad_node* pLPFNode = (ma_biquad_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_biquad_uninit(&pLPFNode->biquad, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_lpf_node_config ma_lpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
|
|
{
|
|
ma_lpf_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.lpf = ma_lpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_lpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_lpf_process_pcm_frames(&pLPFNode->lpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_lpf_node_vtable =
|
|
{
|
|
ma_lpf_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_lpf_node_init(ma_node_graph* pNodeGraph, const ma_lpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_lpf_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->lpf.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_lpf_init(&pConfig->lpf, pAllocationCallbacks, &pNode->lpf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_lpf_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->lpf.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->lpf.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_lpf_node_reinit(const ma_lpf_config* pConfig, ma_lpf_node* pNode)
|
|
{
|
|
ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_lpf_reinit(pConfig, &pLPFNode->lpf);
|
|
}
|
|
|
|
MA_API void ma_lpf_node_uninit(ma_lpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_lpf_node* pLPFNode = (ma_lpf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_lpf_uninit(&pLPFNode->lpf, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_hpf_node_config ma_hpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
|
|
{
|
|
ma_hpf_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.hpf = ma_hpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_hpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_hpf_process_pcm_frames(&pHPFNode->hpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_hpf_node_vtable =
|
|
{
|
|
ma_hpf_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_hpf_node_init(ma_node_graph* pNodeGraph, const ma_hpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hpf_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->hpf.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_hpf_init(&pConfig->hpf, pAllocationCallbacks, &pNode->hpf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_hpf_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->hpf.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->hpf.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_hpf_node_reinit(const ma_hpf_config* pConfig, ma_hpf_node* pNode)
|
|
{
|
|
ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_hpf_reinit(pConfig, &pHPFNode->hpf);
|
|
}
|
|
|
|
MA_API void ma_hpf_node_uninit(ma_hpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_hpf_node* pHPFNode = (ma_hpf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_hpf_uninit(&pHPFNode->hpf, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_bpf_node_config ma_bpf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double cutoffFrequency, ma_uint32 order)
|
|
{
|
|
ma_bpf_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.bpf = ma_bpf_config_init(ma_format_f32, channels, sampleRate, cutoffFrequency, order);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_bpf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_bpf_process_pcm_frames(&pBPFNode->bpf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_bpf_node_vtable =
|
|
{
|
|
ma_bpf_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_bpf_node_init(ma_node_graph* pNodeGraph, const ma_bpf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_bpf_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->bpf.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_bpf_init(&pConfig->bpf, pAllocationCallbacks, &pNode->bpf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_bpf_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->bpf.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->bpf.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_bpf_node_reinit(const ma_bpf_config* pConfig, ma_bpf_node* pNode)
|
|
{
|
|
ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_bpf_reinit(pConfig, &pBPFNode->bpf);
|
|
}
|
|
|
|
MA_API void ma_bpf_node_uninit(ma_bpf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bpf_node* pBPFNode = (ma_bpf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_bpf_uninit(&pBPFNode->bpf, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_notch_node_config ma_notch_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double q, double frequency)
|
|
{
|
|
ma_notch_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.notch = ma_notch2_config_init(ma_format_f32, channels, sampleRate, q, frequency);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_notch_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_notch_node* pBPFNode = (ma_notch_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_notch2_process_pcm_frames(&pBPFNode->notch, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_notch_node_vtable =
|
|
{
|
|
ma_notch_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_notch_node_init(ma_node_graph* pNodeGraph, const ma_notch_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_notch_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->notch.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_notch2_init(&pConfig->notch, pAllocationCallbacks, &pNode->notch);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_notch_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->notch.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->notch.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_notch_node_reinit(const ma_notch_config* pConfig, ma_notch_node* pNode)
|
|
{
|
|
ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_notch2_reinit(pConfig, &pNotchNode->notch);
|
|
}
|
|
|
|
MA_API void ma_notch_node_uninit(ma_notch_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_notch_node* pNotchNode = (ma_notch_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_notch2_uninit(&pNotchNode->notch, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_peak_node_config ma_peak_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
|
|
{
|
|
ma_peak_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.peak = ma_peak2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_peak_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_peak_node* pBPFNode = (ma_peak_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_peak2_process_pcm_frames(&pBPFNode->peak, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_peak_node_vtable =
|
|
{
|
|
ma_peak_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_peak_node_init(ma_node_graph* pNodeGraph, const ma_peak_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_peak_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->peak.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_peak2_init(&pConfig->peak, pAllocationCallbacks, &pNode->peak);
|
|
if (result != MA_SUCCESS) {
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_peak_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->peak.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->peak.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_peak_node_reinit(const ma_peak_config* pConfig, ma_peak_node* pNode)
|
|
{
|
|
ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_peak2_reinit(pConfig, &pPeakNode->peak);
|
|
}
|
|
|
|
MA_API void ma_peak_node_uninit(ma_peak_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_peak_node* pPeakNode = (ma_peak_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_peak2_uninit(&pPeakNode->peak, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_loshelf_node_config ma_loshelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
|
|
{
|
|
ma_loshelf_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.loshelf = ma_loshelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_loshelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_loshelf_node* pBPFNode = (ma_loshelf_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_loshelf2_process_pcm_frames(&pBPFNode->loshelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_loshelf_node_vtable =
|
|
{
|
|
ma_loshelf_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_loshelf_node_init(ma_node_graph* pNodeGraph, const ma_loshelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_loshelf_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->loshelf.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_loshelf2_init(&pConfig->loshelf, pAllocationCallbacks, &pNode->loshelf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_loshelf_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->loshelf.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->loshelf.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_loshelf_node_reinit(const ma_loshelf_config* pConfig, ma_loshelf_node* pNode)
|
|
{
|
|
ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_loshelf2_reinit(pConfig, &pLoshelfNode->loshelf);
|
|
}
|
|
|
|
MA_API void ma_loshelf_node_uninit(ma_loshelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_loshelf_node* pLoshelfNode = (ma_loshelf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_loshelf2_uninit(&pLoshelfNode->loshelf, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_hishelf_node_config ma_hishelf_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, double gainDB, double q, double frequency)
|
|
{
|
|
ma_hishelf_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.hishelf = ma_hishelf2_config_init(ma_format_f32, channels, sampleRate, gainDB, q, frequency);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_hishelf_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_hishelf_node* pBPFNode = (ma_hishelf_node*)pNode;
|
|
|
|
MA_ASSERT(pNode != NULL);
|
|
(void)pFrameCountIn;
|
|
|
|
ma_hishelf2_process_pcm_frames(&pBPFNode->hishelf, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_hishelf_node_vtable =
|
|
{
|
|
ma_hishelf_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
0
|
|
};
|
|
|
|
MA_API ma_result ma_hishelf_node_init(ma_node_graph* pNodeGraph, const ma_hishelf_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_hishelf_node* pNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pNode);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->hishelf.format != ma_format_f32) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_hishelf2_init(&pConfig->hishelf, pAllocationCallbacks, &pNode->hishelf);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_hishelf_node_vtable;
|
|
baseNodeConfig.pInputChannels = &pConfig->hishelf.channels;
|
|
baseNodeConfig.pOutputChannels = &pConfig->hishelf.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseNodeConfig, pAllocationCallbacks, pNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_hishelf_node_reinit(const ma_hishelf_config* pConfig, ma_hishelf_node* pNode)
|
|
{
|
|
ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_hishelf2_reinit(pConfig, &pHishelfNode->hishelf);
|
|
}
|
|
|
|
MA_API void ma_hishelf_node_uninit(ma_hishelf_node* pNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_hishelf_node* pHishelfNode = (ma_hishelf_node*)pNode;
|
|
|
|
if (pNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pNode, pAllocationCallbacks);
|
|
ma_hishelf2_uninit(&pHishelfNode->hishelf, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API ma_delay_node_config ma_delay_node_config_init(ma_uint32 channels, ma_uint32 sampleRate, ma_uint32 delayInFrames, float decay)
|
|
{
|
|
ma_delay_node_config config;
|
|
|
|
config.nodeConfig = ma_node_config_init();
|
|
config.delay = ma_delay_config_init(channels, sampleRate, delayInFrames, decay);
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_delay_node_process_pcm_frames(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_delay_node* pDelayNode = (ma_delay_node*)pNode;
|
|
|
|
(void)pFrameCountIn;
|
|
|
|
ma_delay_process_pcm_frames(&pDelayNode->delay, ppFramesOut[0], ppFramesIn[0], *pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_delay_node_vtable =
|
|
{
|
|
ma_delay_node_process_pcm_frames,
|
|
NULL,
|
|
1,
|
|
1,
|
|
MA_NODE_FLAG_CONTINUOUS_PROCESSING
|
|
};
|
|
|
|
MA_API ma_result ma_delay_node_init(ma_node_graph* pNodeGraph, const ma_delay_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_delay_node* pDelayNode)
|
|
{
|
|
ma_result result;
|
|
ma_node_config baseConfig;
|
|
|
|
if (pDelayNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pDelayNode);
|
|
|
|
result = ma_delay_init(&pConfig->delay, pAllocationCallbacks, &pDelayNode->delay);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
baseConfig = pConfig->nodeConfig;
|
|
baseConfig.vtable = &g_ma_delay_node_vtable;
|
|
baseConfig.pInputChannels = &pConfig->delay.channels;
|
|
baseConfig.pOutputChannels = &pConfig->delay.channels;
|
|
|
|
result = ma_node_init(pNodeGraph, &baseConfig, pAllocationCallbacks, &pDelayNode->baseNode);
|
|
if (result != MA_SUCCESS) {
|
|
ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API void ma_delay_node_uninit(ma_delay_node* pDelayNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_uninit(pDelayNode, pAllocationCallbacks);
|
|
ma_delay_uninit(&pDelayNode->delay, pAllocationCallbacks);
|
|
}
|
|
|
|
MA_API void ma_delay_node_set_wet(ma_delay_node* pDelayNode, float value)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_delay_set_wet(&pDelayNode->delay, value);
|
|
}
|
|
|
|
MA_API float ma_delay_node_get_wet(const ma_delay_node* pDelayNode)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_delay_get_wet(&pDelayNode->delay);
|
|
}
|
|
|
|
MA_API void ma_delay_node_set_dry(ma_delay_node* pDelayNode, float value)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_delay_set_dry(&pDelayNode->delay, value);
|
|
}
|
|
|
|
MA_API float ma_delay_node_get_dry(const ma_delay_node* pDelayNode)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_delay_get_dry(&pDelayNode->delay);
|
|
}
|
|
|
|
MA_API void ma_delay_node_set_decay(ma_delay_node* pDelayNode, float value)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_delay_set_decay(&pDelayNode->delay, value);
|
|
}
|
|
|
|
MA_API float ma_delay_node_get_decay(const ma_delay_node* pDelayNode)
|
|
{
|
|
if (pDelayNode == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_delay_get_decay(&pDelayNode->delay);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_NO_ENGINE) && !defined(MA_NO_NODE_GRAPH)
|
|
|
|
#define MA_SEEK_TARGET_NONE (~(ma_uint64)0)
|
|
|
|
static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd)
|
|
{
|
|
MA_ASSERT(pSound != NULL);
|
|
ma_atomic_exchange_32(&pSound->atEnd, atEnd);
|
|
|
|
#if 0
|
|
if (atEnd) {
|
|
if (pSound->endCallback != NULL) {
|
|
pSound->endCallback(pSound->pEndCallbackUserData, pSound);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound)
|
|
{
|
|
MA_ASSERT(pSound != NULL);
|
|
return ma_atomic_load_32(&pSound->atEnd);
|
|
}
|
|
|
|
MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags)
|
|
{
|
|
ma_engine_node_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.pEngine = pEngine;
|
|
config.type = type;
|
|
config.isPitchDisabled = (flags & MA_SOUND_FLAG_NO_PITCH) != 0;
|
|
config.isSpatializationDisabled = (flags & MA_SOUND_FLAG_NO_SPATIALIZATION) != 0;
|
|
config.monoExpansionMode = pEngine->monoExpansionMode;
|
|
config.resampling = pEngine->pitchResamplingConfig;
|
|
|
|
return config;
|
|
}
|
|
|
|
static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode)
|
|
{
|
|
ma_bool32 isUpdateRequired = MA_FALSE;
|
|
float newPitch;
|
|
|
|
MA_ASSERT(pEngineNode != NULL);
|
|
|
|
newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire);
|
|
|
|
if (pEngineNode->oldPitch != newPitch) {
|
|
pEngineNode->oldPitch = newPitch;
|
|
isUpdateRequired = MA_TRUE;
|
|
}
|
|
|
|
if (pEngineNode->oldDopplerPitch != pEngineNode->spatializer.dopplerPitch) {
|
|
pEngineNode->oldDopplerPitch = pEngineNode->spatializer.dopplerPitch;
|
|
isUpdateRequired = MA_TRUE;
|
|
}
|
|
|
|
if (isUpdateRequired) {
|
|
float basePitch = (float)pEngineNode->sampleRate / ma_engine_get_sample_rate(pEngineNode->pEngine);
|
|
ma_resampler_set_rate_ratio(&pEngineNode->resampler, basePitch * pEngineNode->oldPitch * pEngineNode->oldDopplerPitch);
|
|
}
|
|
}
|
|
|
|
static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngineNode)
|
|
{
|
|
MA_ASSERT(pEngineNode != NULL);
|
|
|
|
return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire);
|
|
}
|
|
|
|
static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode)
|
|
{
|
|
MA_ASSERT(pEngineNode != NULL);
|
|
|
|
return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire);
|
|
}
|
|
|
|
static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float volume)
|
|
{
|
|
if (pEngineNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_atomic_float_set(&pEngineNode->volume, volume);
|
|
|
|
if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) {
|
|
ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume);
|
|
} else {
|
|
ma_gainer_set_gain(&pEngineNode->volumeGainer, volume);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, float* pVolume)
|
|
{
|
|
if (pVolume == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pVolume = 0.0f;
|
|
|
|
if (pEngineNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_uint32 frameCountIn;
|
|
ma_uint32 frameCountOut;
|
|
ma_uint32 totalFramesProcessedIn;
|
|
ma_uint32 totalFramesProcessedOut;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_bool32 isPitchingEnabled;
|
|
ma_bool32 isFadingEnabled;
|
|
ma_bool32 isSpatializationEnabled;
|
|
ma_bool32 isPanningEnabled;
|
|
ma_bool32 isVolumeSmoothingEnabled;
|
|
|
|
frameCountIn = *pFrameCountIn;
|
|
frameCountOut = *pFrameCountOut;
|
|
|
|
channelsIn = ma_spatializer_get_input_channels(&pEngineNode->spatializer);
|
|
channelsOut = ma_spatializer_get_output_channels(&pEngineNode->spatializer);
|
|
|
|
totalFramesProcessedIn = 0;
|
|
totalFramesProcessedOut = 0;
|
|
|
|
{
|
|
ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames);
|
|
if (fadeLengthInFrames != ~(ma_uint64)0) {
|
|
float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg);
|
|
float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd);
|
|
ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames);
|
|
if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) {
|
|
fadeStartOffsetInFrames = 0;
|
|
} else {
|
|
fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine);
|
|
}
|
|
|
|
ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames);
|
|
|
|
ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0);
|
|
}
|
|
}
|
|
|
|
isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode);
|
|
isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1;
|
|
isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode);
|
|
isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1;
|
|
isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0;
|
|
|
|
while (totalFramesProcessedOut < frameCountOut) {
|
|
|
|
const float* pRunningFramesIn;
|
|
float* pRunningFramesOut;
|
|
float* pWorkingBuffer;
|
|
float temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE / sizeof(float)];
|
|
ma_uint32 tempCapInFrames = ma_countof(temp) / channelsIn;
|
|
ma_uint32 framesAvailableIn;
|
|
ma_uint32 framesAvailableOut;
|
|
ma_uint32 framesJustProcessedIn;
|
|
ma_uint32 framesJustProcessedOut;
|
|
ma_bool32 isWorkingBufferValid = MA_FALSE;
|
|
|
|
framesAvailableIn = frameCountIn - totalFramesProcessedIn;
|
|
framesAvailableOut = frameCountOut - totalFramesProcessedOut;
|
|
|
|
pRunningFramesIn = ma_offset_pcm_frames_const_ptr_f32(ppFramesIn[0], totalFramesProcessedIn, channelsIn);
|
|
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesProcessedOut, channelsOut);
|
|
|
|
if (channelsIn == channelsOut) {
|
|
pWorkingBuffer = pRunningFramesOut;
|
|
} else {
|
|
pWorkingBuffer = temp;
|
|
if (framesAvailableOut > tempCapInFrames) {
|
|
framesAvailableOut = tempCapInFrames;
|
|
}
|
|
}
|
|
|
|
if (isPitchingEnabled) {
|
|
ma_uint64 resampleFrameCountIn = framesAvailableIn;
|
|
ma_uint64 resampleFrameCountOut = framesAvailableOut;
|
|
|
|
ma_resampler_process_pcm_frames(&pEngineNode->resampler, pRunningFramesIn, &resampleFrameCountIn, pWorkingBuffer, &resampleFrameCountOut);
|
|
isWorkingBufferValid = MA_TRUE;
|
|
|
|
framesJustProcessedIn = (ma_uint32)resampleFrameCountIn;
|
|
framesJustProcessedOut = (ma_uint32)resampleFrameCountOut;
|
|
} else {
|
|
framesJustProcessedIn = ma_min(framesAvailableIn, framesAvailableOut);
|
|
framesJustProcessedOut = framesJustProcessedIn;
|
|
}
|
|
|
|
if (isFadingEnabled) {
|
|
if (isWorkingBufferValid) {
|
|
ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);
|
|
} else {
|
|
ma_fader_process_pcm_frames(&pEngineNode->fader, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
|
|
isWorkingBufferValid = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isVolumeSmoothingEnabled) {
|
|
if (isWorkingBufferValid) {
|
|
ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut);
|
|
} else {
|
|
ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut);
|
|
isWorkingBufferValid = MA_TRUE;
|
|
}
|
|
}
|
|
|
|
if (isWorkingBufferValid == MA_FALSE) {
|
|
pWorkingBuffer = (float*)pRunningFramesIn;
|
|
}
|
|
|
|
if (isSpatializationEnabled) {
|
|
ma_uint32 iListener;
|
|
|
|
if (pEngineNode->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pEngineNode->pinnedListenerIndex < ma_engine_get_listener_count(pEngineNode->pEngine)) {
|
|
iListener = pEngineNode->pinnedListenerIndex;
|
|
} else {
|
|
ma_vec3f spatializerPosition = ma_spatializer_get_position(&pEngineNode->spatializer);
|
|
iListener = ma_engine_find_closest_listener(pEngineNode->pEngine, spatializerPosition.x, spatializerPosition.y, spatializerPosition.z);
|
|
}
|
|
|
|
ma_spatializer_process_pcm_frames(&pEngineNode->spatializer, &pEngineNode->pEngine->listeners[iListener], pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut);
|
|
} else {
|
|
float volume;
|
|
ma_engine_node_get_volume(pEngineNode, &volume);
|
|
|
|
if (channelsIn == channelsOut) {
|
|
if (isVolumeSmoothingEnabled) {
|
|
ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut);
|
|
} else {
|
|
ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume);
|
|
}
|
|
} else {
|
|
ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode);
|
|
|
|
if (!isVolumeSmoothingEnabled) {
|
|
ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isPanningEnabled) {
|
|
ma_panner_process_pcm_frames(&pEngineNode->panner, pRunningFramesOut, pRunningFramesOut, framesJustProcessedOut);
|
|
}
|
|
|
|
totalFramesProcessedIn += framesJustProcessedIn;
|
|
totalFramesProcessedOut += framesJustProcessedOut;
|
|
|
|
if (framesJustProcessedOut == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
*pFrameCountIn = totalFramesProcessedIn;
|
|
*pFrameCountOut = totalFramesProcessedOut;
|
|
}
|
|
|
|
static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_sound* pSound = (ma_sound*)pNode;
|
|
ma_uint32 frameCount = *pFrameCountOut;
|
|
ma_uint32 totalFramesRead = 0;
|
|
ma_format dataSourceFormat;
|
|
ma_uint32 dataSourceChannels;
|
|
ma_uint8 temp[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
|
|
ma_uint32 tempCapInFrames;
|
|
ma_uint64 seekTarget;
|
|
|
|
(void)ppFramesIn;
|
|
(void)pFrameCountIn;
|
|
|
|
if (ma_sound_at_end(pSound)) {
|
|
ma_sound_stop(pSound);
|
|
|
|
if (pSound->endCallback != NULL) {
|
|
pSound->endCallback(pSound->pEndCallbackUserData, pSound);
|
|
}
|
|
|
|
*pFrameCountOut = 0;
|
|
return;
|
|
}
|
|
|
|
seekTarget = ma_atomic_load_64(&pSound->seekTarget);
|
|
if (seekTarget != MA_SEEK_TARGET_NONE) {
|
|
ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget);
|
|
|
|
ma_node_set_time(pSound, seekTarget);
|
|
|
|
ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE);
|
|
}
|
|
|
|
ma_engine_node_update_pitch_if_required(&pSound->engineNode);
|
|
|
|
result = ma_data_source_get_data_format(pSound->pDataSource, &dataSourceFormat, &dataSourceChannels, NULL, NULL, 0);
|
|
if (result == MA_SUCCESS) {
|
|
tempCapInFrames = sizeof(temp) / ma_get_bytes_per_frame(dataSourceFormat, dataSourceChannels);
|
|
|
|
while (totalFramesRead < frameCount) {
|
|
ma_uint32 framesRemaining = frameCount - totalFramesRead;
|
|
ma_uint64 framesJustRead;
|
|
ma_uint32 frameCountIn;
|
|
ma_uint32 frameCountOut;
|
|
const float* pRunningFramesIn;
|
|
float* pRunningFramesOut;
|
|
|
|
if (pSound->processingCacheFramesRemaining > 0) {
|
|
pRunningFramesIn = pSound->pProcessingCache;
|
|
frameCountIn = pSound->processingCacheFramesRemaining;
|
|
|
|
pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_node_get_output_channels(pNode, 0));
|
|
frameCountOut = framesRemaining;
|
|
|
|
ma_engine_node_process_pcm_frames__general(&pSound->engineNode, &pRunningFramesIn, &frameCountIn, &pRunningFramesOut, &frameCountOut);
|
|
|
|
MA_ASSERT(frameCountIn <= pSound->processingCacheFramesRemaining);
|
|
pSound->processingCacheFramesRemaining -= frameCountIn;
|
|
|
|
if (pSound->processingCacheFramesRemaining > 0) {
|
|
MA_MOVE_MEMORY(pSound->pProcessingCache, ma_offset_pcm_frames_ptr_f32(pSound->pProcessingCache, frameCountIn, dataSourceChannels), pSound->processingCacheFramesRemaining * ma_get_bytes_per_frame(ma_format_f32, dataSourceChannels));
|
|
}
|
|
|
|
totalFramesRead += (ma_uint32)frameCountOut;
|
|
|
|
if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (dataSourceFormat == ma_format_f32) {
|
|
result = ma_data_source_read_pcm_frames(pSound->pDataSource, pSound->pProcessingCache, pSound->processingCacheCap, &framesJustRead);
|
|
} else {
|
|
ma_uint64 totalFramesConverted = 0;
|
|
|
|
while (totalFramesConverted < pSound->processingCacheCap) {
|
|
ma_uint64 framesConverted;
|
|
ma_uint32 framesToConvertThisIteration = pSound->processingCacheCap - (ma_uint32)totalFramesConverted;
|
|
if (framesToConvertThisIteration > tempCapInFrames) {
|
|
framesToConvertThisIteration = tempCapInFrames;
|
|
}
|
|
|
|
result = ma_data_source_read_pcm_frames(pSound->pDataSource, temp, framesToConvertThisIteration, &framesConverted);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
ma_convert_pcm_frames_format(ma_offset_pcm_frames_ptr_f32(pSound->pProcessingCache, totalFramesConverted, dataSourceChannels), ma_format_f32, temp, dataSourceFormat, framesConverted, dataSourceChannels, ma_dither_mode_none);
|
|
totalFramesConverted += framesConverted;
|
|
}
|
|
|
|
framesJustRead = totalFramesConverted;
|
|
}
|
|
|
|
MA_ASSERT(framesJustRead <= pSound->processingCacheCap);
|
|
pSound->processingCacheFramesRemaining = (ma_uint32)framesJustRead;
|
|
|
|
if (result == MA_AT_END) {
|
|
ma_sound_set_at_end(pSound, MA_TRUE);
|
|
}
|
|
|
|
if (result != MA_SUCCESS || ma_sound_at_end(pSound)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
*pFrameCountOut = totalFramesRead;
|
|
}
|
|
|
|
static void ma_engine_node_process_pcm_frames__group(ma_node* pNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut)
|
|
{
|
|
ma_engine_node_update_pitch_if_required((ma_engine_node*)pNode);
|
|
|
|
ma_engine_node_process_pcm_frames__general((ma_engine_node*)pNode, ppFramesIn, pFrameCountIn, ppFramesOut, pFrameCountOut);
|
|
}
|
|
|
|
static ma_node_vtable g_ma_engine_node_vtable__sound =
|
|
{
|
|
ma_engine_node_process_pcm_frames__sound,
|
|
NULL,
|
|
0,
|
|
1,
|
|
0
|
|
};
|
|
|
|
static ma_node_vtable g_ma_engine_node_vtable__group =
|
|
{
|
|
ma_engine_node_process_pcm_frames__group,
|
|
NULL,
|
|
1,
|
|
1,
|
|
MA_NODE_FLAG_DIFFERENT_PROCESSING_RATES
|
|
};
|
|
|
|
static ma_node_config ma_engine_node_base_node_config_init(const ma_engine_node_config* pConfig)
|
|
{
|
|
ma_node_config baseNodeConfig;
|
|
|
|
if (pConfig->type == ma_engine_node_type_sound) {
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_engine_node_vtable__sound;
|
|
baseNodeConfig.initialState = ma_node_state_stopped;
|
|
} else {
|
|
baseNodeConfig = ma_node_config_init();
|
|
baseNodeConfig.vtable = &g_ma_engine_node_vtable__group;
|
|
baseNodeConfig.initialState = ma_node_state_started;
|
|
}
|
|
|
|
return baseNodeConfig;
|
|
}
|
|
|
|
static ma_spatializer_config ma_engine_node_spatializer_config_init(const ma_node_config* pBaseNodeConfig)
|
|
{
|
|
return ma_spatializer_config_init(pBaseNodeConfig->pInputChannels[0], pBaseNodeConfig->pOutputChannels[0]);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
size_t sizeInBytes;
|
|
size_t baseNodeOffset;
|
|
size_t resamplerOffset;
|
|
size_t spatializerOffset;
|
|
size_t gainerOffset;
|
|
} ma_engine_node_heap_layout;
|
|
|
|
static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout)
|
|
{
|
|
ma_result result;
|
|
size_t tempHeapSize;
|
|
ma_node_config baseNodeConfig;
|
|
ma_resampler_config resamplerConfig;
|
|
ma_spatializer_config spatializerConfig;
|
|
ma_gainer_config gainerConfig;
|
|
ma_uint32 sampleRate;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT};
|
|
|
|
MA_ASSERT(pHeapLayout);
|
|
|
|
MA_ZERO_OBJECT(pHeapLayout);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pConfig->pEngine == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pHeapLayout->sizeInBytes = 0;
|
|
|
|
sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pConfig->pEngine);
|
|
channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
|
|
channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
|
|
|
|
baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
|
|
baseNodeConfig.pInputChannels = &channelsIn;
|
|
baseNodeConfig.pOutputChannels = &channelsOut;
|
|
|
|
result = ma_node_get_heap_size(ma_engine_get_node_graph(pConfig->pEngine), &baseNodeConfig, &tempHeapSize);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->baseNodeOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
|
|
|
|
resamplerConfig = pConfig->resampling;
|
|
resamplerConfig.format = ma_format_f32;
|
|
resamplerConfig.channels = channelsIn;
|
|
resamplerConfig.sampleRateIn = sampleRate;
|
|
resamplerConfig.sampleRateOut = ma_engine_get_sample_rate(pConfig->pEngine);
|
|
|
|
result = ma_resampler_get_heap_size(&resamplerConfig, &tempHeapSize);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->resamplerOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
|
|
|
|
spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
|
|
|
|
if (spatializerConfig.channelsIn == 2) {
|
|
spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
|
|
}
|
|
|
|
result = ma_spatializer_get_heap_size(&spatializerConfig, &tempHeapSize);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->spatializerOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
|
|
|
|
if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
|
|
gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
|
|
|
|
result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes;
|
|
pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_node_get_heap_size(const ma_engine_node_config* pConfig, size_t* pHeapSizeInBytes)
|
|
{
|
|
ma_result result;
|
|
ma_engine_node_heap_layout heapLayout;
|
|
|
|
if (pHeapSizeInBytes == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
*pHeapSizeInBytes = 0;
|
|
|
|
result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pHeapSizeInBytes = heapLayout.sizeInBytes;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* pConfig, void* pHeap, ma_engine_node* pEngineNode)
|
|
{
|
|
ma_result result;
|
|
ma_engine_node_heap_layout heapLayout;
|
|
ma_node_config baseNodeConfig;
|
|
ma_resampler_config resamplerConfig;
|
|
ma_fader_config faderConfig;
|
|
ma_spatializer_config spatializerConfig;
|
|
ma_panner_config pannerConfig;
|
|
ma_gainer_config gainerConfig;
|
|
ma_uint32 channelsIn;
|
|
ma_uint32 channelsOut;
|
|
ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT};
|
|
|
|
if (pEngineNode == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pEngineNode);
|
|
|
|
result = ma_engine_node_get_heap_layout(pConfig, &heapLayout);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->pinnedListenerIndex != MA_LISTENER_INDEX_CLOSEST && pConfig->pinnedListenerIndex >= ma_engine_get_listener_count(pConfig->pEngine)) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pEngineNode->_pHeap = pHeap;
|
|
MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes);
|
|
|
|
pEngineNode->pEngine = pConfig->pEngine;
|
|
pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine);
|
|
pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
|
|
pEngineNode->monoExpansionMode = pConfig->monoExpansionMode;
|
|
ma_atomic_float_set(&pEngineNode->volume, 1);
|
|
pEngineNode->pitch = 1;
|
|
pEngineNode->oldPitch = 1;
|
|
pEngineNode->oldDopplerPitch = 1;
|
|
pEngineNode->isPitchDisabled = pConfig->isPitchDisabled;
|
|
pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled;
|
|
pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex;
|
|
ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1);
|
|
ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1);
|
|
ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0));
|
|
ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0));
|
|
|
|
channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine);
|
|
channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine);
|
|
|
|
if (pEngineNode->sampleRate != ma_engine_get_sample_rate(pEngineNode->pEngine)) {
|
|
pEngineNode->isPitchDisabled = MA_FALSE;
|
|
}
|
|
|
|
baseNodeConfig = ma_engine_node_base_node_config_init(pConfig);
|
|
baseNodeConfig.pInputChannels = &channelsIn;
|
|
baseNodeConfig.pOutputChannels = &channelsOut;
|
|
|
|
result = ma_node_init_preallocated(&pConfig->pEngine->nodeGraph, &baseNodeConfig, ma_offset_ptr(pHeap, heapLayout.baseNodeOffset), &pEngineNode->baseNode);
|
|
if (result != MA_SUCCESS) {
|
|
goto error0;
|
|
}
|
|
|
|
resamplerConfig = pConfig->resampling;
|
|
resamplerConfig.format = ma_format_f32;
|
|
resamplerConfig.channels = baseNodeConfig.pInputChannels[0];
|
|
resamplerConfig.sampleRateIn = pEngineNode->sampleRate;
|
|
resamplerConfig.sampleRateOut = ma_engine_get_sample_rate(pEngineNode->pEngine);
|
|
|
|
result = ma_resampler_init_preallocated(&resamplerConfig, ma_offset_ptr(pHeap, heapLayout.resamplerOffset), &pEngineNode->resampler);
|
|
if (result != MA_SUCCESS) {
|
|
goto error1;
|
|
}
|
|
|
|
faderConfig = ma_fader_config_init(ma_format_f32, baseNodeConfig.pInputChannels[0], ma_engine_get_sample_rate(pEngineNode->pEngine));
|
|
|
|
result = ma_fader_init(&faderConfig, &pEngineNode->fader);
|
|
if (result != MA_SUCCESS) {
|
|
goto error2;
|
|
}
|
|
|
|
spatializerConfig = ma_engine_node_spatializer_config_init(&baseNodeConfig);
|
|
spatializerConfig.gainSmoothTimeInFrames = pEngineNode->pEngine->gainSmoothTimeInFrames;
|
|
|
|
if (spatializerConfig.channelsIn == 2) {
|
|
spatializerConfig.pChannelMapIn = defaultStereoChannelMap;
|
|
}
|
|
|
|
result = ma_spatializer_init_preallocated(&spatializerConfig, ma_offset_ptr(pHeap, heapLayout.spatializerOffset), &pEngineNode->spatializer);
|
|
if (result != MA_SUCCESS) {
|
|
goto error2;
|
|
}
|
|
|
|
pannerConfig = ma_panner_config_init(ma_format_f32, baseNodeConfig.pOutputChannels[0]);
|
|
|
|
result = ma_panner_init(&pannerConfig, &pEngineNode->panner);
|
|
if (result != MA_SUCCESS) {
|
|
goto error3;
|
|
}
|
|
|
|
if (pConfig->volumeSmoothTimeInPCMFrames > 0) {
|
|
gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames);
|
|
|
|
result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer);
|
|
if (result != MA_SUCCESS) {
|
|
goto error3;
|
|
}
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
|
|
error3: ma_spatializer_uninit(&pEngineNode->spatializer, NULL);
|
|
error2: ma_resampler_uninit(&pEngineNode->resampler, NULL);
|
|
error1: ma_node_uninit(&pEngineNode->baseNode, NULL);
|
|
error0: return result;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_node_init(const ma_engine_node_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_engine_node* pEngineNode)
|
|
{
|
|
ma_result result;
|
|
size_t heapSizeInBytes;
|
|
void* pHeap;
|
|
|
|
result = ma_engine_node_get_heap_size(pConfig, &heapSizeInBytes);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (heapSizeInBytes > 0) {
|
|
pHeap = ma_malloc(heapSizeInBytes, pAllocationCallbacks);
|
|
if (pHeap == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
} else {
|
|
pHeap = NULL;
|
|
}
|
|
|
|
result = ma_engine_node_init_preallocated(pConfig, pHeap, pEngineNode);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pHeap, pAllocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
pEngineNode->_ownsHeap = MA_TRUE;
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks);
|
|
|
|
if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) {
|
|
ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks);
|
|
}
|
|
|
|
ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks);
|
|
ma_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks);
|
|
|
|
if (pEngineNode->_ownsHeap) {
|
|
ma_free(pEngineNode->_pHeap, pAllocationCallbacks);
|
|
}
|
|
}
|
|
|
|
MA_API ma_sound_config ma_sound_config_init(void)
|
|
{
|
|
return ma_sound_config_init_2(NULL);
|
|
}
|
|
|
|
MA_API ma_sound_config ma_sound_config_init_2(ma_engine* pEngine)
|
|
{
|
|
ma_sound_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
if (pEngine != NULL) {
|
|
config.monoExpansionMode = pEngine->monoExpansionMode;
|
|
config.pitchResampling = pEngine->pitchResamplingConfig;
|
|
} else {
|
|
config.monoExpansionMode = ma_mono_expansion_mode_default;
|
|
|
|
config.pitchResampling = ma_resampler_config_init(ma_format_f32, 0, 0, 0, ma_resample_algorithm_linear);
|
|
config.pitchResampling.linear.lpfOrder = 0;
|
|
}
|
|
|
|
config.rangeEndInPCMFrames = ~((ma_uint64)0);
|
|
config.loopPointEndInPCMFrames = ~((ma_uint64)0);
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_sound_group_config ma_sound_group_config_init(void)
|
|
{
|
|
return ma_sound_group_config_init_2(NULL);
|
|
}
|
|
|
|
MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine)
|
|
{
|
|
ma_sound_group_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
|
|
if (pEngine != NULL) {
|
|
config.monoExpansionMode = pEngine->monoExpansionMode;
|
|
config.pitchResampling = pEngine->pitchResamplingConfig;
|
|
} else {
|
|
config.monoExpansionMode = ma_mono_expansion_mode_default;
|
|
|
|
config.pitchResampling = ma_resampler_config_init(ma_format_f32, 0, 0, 0, ma_resample_algorithm_linear);
|
|
config.pitchResampling.linear.lpfOrder = 0;
|
|
}
|
|
|
|
return config;
|
|
}
|
|
|
|
MA_API ma_engine_config ma_engine_config_init(void)
|
|
{
|
|
ma_engine_config config;
|
|
|
|
MA_ZERO_OBJECT(&config);
|
|
config.listenerCount = 1;
|
|
config.monoExpansionMode = ma_mono_expansion_mode_default;
|
|
config.resourceManagerResampling = ma_resampler_config_init(ma_format_unknown, 0, 0, 0, ma_resample_algorithm_linear);
|
|
|
|
config.pitchResampling = ma_resampler_config_init(ma_format_f32, 0, 0, 0, ma_resample_algorithm_linear);
|
|
config.pitchResampling.linear.lpfOrder = 0;
|
|
|
|
return config;
|
|
}
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
static void ma_engine_data_callback_internal(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount)
|
|
{
|
|
ma_engine* pEngine = (ma_engine*)pDevice->pUserData;
|
|
|
|
(void)pFramesIn;
|
|
|
|
#if !defined(MA_NO_RESOURCE_MANAGER) && defined(MA_EMSCRIPTEN)
|
|
{
|
|
if (pEngine->pResourceManager != NULL) {
|
|
if ((pEngine->pResourceManager->config.flags & MA_RESOURCE_MANAGER_FLAG_NO_THREADING) != 0) {
|
|
ma_resource_manager_process_next_job(pEngine->pResourceManager);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ma_engine_read_pcm_frames(pEngine, pFramesOut, frameCount, NULL);
|
|
}
|
|
|
|
static ma_uint32 ma_device__get_processing_size_in_frames(ma_device* pDevice)
|
|
{
|
|
if (pDevice->playback.intermediaryBufferCap > 0) {
|
|
return pDevice->playback.intermediaryBufferCap;
|
|
} else {
|
|
return pDevice->playback.internalPeriodSizeInFrames;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine)
|
|
{
|
|
ma_result result;
|
|
ma_node_graph_config nodeGraphConfig;
|
|
ma_engine_config engineConfig;
|
|
ma_spatializer_listener_config listenerConfig;
|
|
ma_uint32 iListener;
|
|
|
|
if (pEngine == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pEngine);
|
|
|
|
if (pConfig != NULL) {
|
|
engineConfig = *pConfig;
|
|
} else {
|
|
engineConfig = ma_engine_config_init();
|
|
}
|
|
|
|
pEngine->monoExpansionMode = engineConfig.monoExpansionMode;
|
|
pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames;
|
|
pEngine->onProcess = engineConfig.onProcess;
|
|
pEngine->pProcessUserData = engineConfig.pProcessUserData;
|
|
pEngine->pitchResamplingConfig = engineConfig.pitchResampling;
|
|
ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks);
|
|
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
{
|
|
pEngine->pResourceManager = engineConfig.pResourceManager;
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
pEngine->pDevice = engineConfig.pDevice;
|
|
|
|
if (pEngine->pDevice == NULL && engineConfig.noDevice == MA_FALSE) {
|
|
ma_device_config deviceConfig;
|
|
|
|
pEngine->pDevice = (ma_device*)ma_malloc(sizeof(*pEngine->pDevice), &pEngine->allocationCallbacks);
|
|
if (pEngine->pDevice == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
deviceConfig = ma_device_config_init(ma_device_type_playback);
|
|
deviceConfig.playback.pDeviceID = engineConfig.pPlaybackDeviceID;
|
|
deviceConfig.playback.format = ma_format_f32;
|
|
deviceConfig.playback.channels = engineConfig.channels;
|
|
deviceConfig.sampleRate = engineConfig.sampleRate;
|
|
deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal;
|
|
deviceConfig.pUserData = pEngine;
|
|
deviceConfig.notificationCallback = engineConfig.notificationCallback;
|
|
deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames;
|
|
deviceConfig.periodSizeInMilliseconds = engineConfig.periodSizeInMilliseconds;
|
|
deviceConfig.noPreSilencedOutputBuffer = MA_TRUE;
|
|
deviceConfig.noClip = MA_TRUE;
|
|
|
|
if (engineConfig.pContext == NULL) {
|
|
ma_context_config contextConfig = ma_context_config_init();
|
|
contextConfig.allocationCallbacks = pEngine->allocationCallbacks;
|
|
contextConfig.pLog = engineConfig.pLog;
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
{
|
|
if (contextConfig.pLog == NULL && engineConfig.pResourceManager != NULL) {
|
|
contextConfig.pLog = ma_resource_manager_get_log(engineConfig.pResourceManager);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
result = ma_device_init_ex(NULL, 0, &contextConfig, &deviceConfig, pEngine->pDevice);
|
|
} else {
|
|
result = ma_device_init(engineConfig.pContext, &deviceConfig, pEngine->pDevice);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
|
|
pEngine->pDevice = NULL;
|
|
return result;
|
|
}
|
|
|
|
pEngine->ownsDevice = MA_TRUE;
|
|
}
|
|
|
|
if (pEngine->pDevice != NULL) {
|
|
engineConfig.channels = pEngine->pDevice->playback.channels;
|
|
engineConfig.sampleRate = pEngine->pDevice->sampleRate;
|
|
|
|
engineConfig.periodSizeInFrames = ma_device__get_processing_size_in_frames(pEngine->pDevice);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (engineConfig.channels == 0 || engineConfig.sampleRate == 0) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pEngine->sampleRate = engineConfig.sampleRate;
|
|
|
|
if (engineConfig.pLog != NULL) {
|
|
pEngine->pLog = engineConfig.pLog;
|
|
} else {
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
pEngine->pLog = ma_device_get_log(pEngine->pDevice);
|
|
}
|
|
#else
|
|
{
|
|
pEngine->pLog = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
nodeGraphConfig = ma_node_graph_config_init(engineConfig.channels);
|
|
nodeGraphConfig.processingSizeInFrames = engineConfig.periodSizeInFrames;
|
|
nodeGraphConfig.preMixStackSizeInBytes = engineConfig.preMixStackSizeInBytes;
|
|
|
|
result = ma_node_graph_init(&nodeGraphConfig, &pEngine->allocationCallbacks, &pEngine->nodeGraph);
|
|
if (result != MA_SUCCESS) {
|
|
goto on_error_1;
|
|
}
|
|
|
|
if (engineConfig.listenerCount == 0) {
|
|
engineConfig.listenerCount = 1;
|
|
}
|
|
|
|
if (engineConfig.listenerCount > MA_ENGINE_MAX_LISTENERS) {
|
|
result = MA_INVALID_ARGS;
|
|
goto on_error_1;
|
|
}
|
|
|
|
for (iListener = 0; iListener < engineConfig.listenerCount; iListener += 1) {
|
|
listenerConfig = ma_spatializer_listener_config_init(ma_node_graph_get_channels(&pEngine->nodeGraph));
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
if (pEngine->pDevice != NULL) {
|
|
}
|
|
}
|
|
#endif
|
|
|
|
result = ma_spatializer_listener_init(&listenerConfig, &pEngine->allocationCallbacks, &pEngine->listeners[iListener]);
|
|
if (result != MA_SUCCESS) {
|
|
goto on_error_2;
|
|
}
|
|
|
|
pEngine->listenerCount += 1;
|
|
}
|
|
|
|
pEngine->gainSmoothTimeInFrames = engineConfig.gainSmoothTimeInFrames;
|
|
if (pEngine->gainSmoothTimeInFrames == 0) {
|
|
ma_uint32 gainSmoothTimeInMilliseconds = engineConfig.gainSmoothTimeInMilliseconds;
|
|
if (gainSmoothTimeInMilliseconds == 0) {
|
|
gainSmoothTimeInMilliseconds = 8;
|
|
}
|
|
|
|
pEngine->gainSmoothTimeInFrames = (gainSmoothTimeInMilliseconds * ma_engine_get_sample_rate(pEngine)) / 1000;
|
|
}
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
{
|
|
if (pEngine->pResourceManager == NULL) {
|
|
ma_resource_manager_config resourceManagerConfig;
|
|
|
|
pEngine->pResourceManager = (ma_resource_manager*)ma_malloc(sizeof(*pEngine->pResourceManager), &pEngine->allocationCallbacks);
|
|
if (pEngine->pResourceManager == NULL) {
|
|
result = MA_OUT_OF_MEMORY;
|
|
goto on_error_2;
|
|
}
|
|
|
|
resourceManagerConfig = ma_resource_manager_config_init();
|
|
resourceManagerConfig.pLog = pEngine->pLog;
|
|
resourceManagerConfig.decodedFormat = ma_format_f32;
|
|
resourceManagerConfig.decodedChannels = 0;
|
|
resourceManagerConfig.decodedSampleRate = ma_engine_get_sample_rate(pEngine);
|
|
ma_allocation_callbacks_init_copy(&resourceManagerConfig.allocationCallbacks, &pEngine->allocationCallbacks);
|
|
resourceManagerConfig.pVFS = engineConfig.pResourceManagerVFS;
|
|
resourceManagerConfig.resampling = engineConfig.resourceManagerResampling;
|
|
|
|
#if defined(MA_EMSCRIPTEN) && !defined(__EMSCRIPTEN_PTHREADS__)
|
|
{
|
|
resourceManagerConfig.jobThreadCount = 0;
|
|
resourceManagerConfig.flags |= MA_RESOURCE_MANAGER_FLAG_NO_THREADING;
|
|
}
|
|
#endif
|
|
|
|
result = ma_resource_manager_init(&resourceManagerConfig, pEngine->pResourceManager);
|
|
if (result != MA_SUCCESS) {
|
|
goto on_error_3;
|
|
}
|
|
|
|
pEngine->ownsResourceManager = MA_TRUE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
pEngine->inlinedSoundLock = 0;
|
|
pEngine->pInlinedSoundHead = NULL;
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
if (engineConfig.noAutoStart == MA_FALSE && pEngine->pDevice != NULL) {
|
|
result = ma_engine_start(pEngine);
|
|
if (result != MA_SUCCESS) {
|
|
goto on_error_4;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return MA_SUCCESS;
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
on_error_4:
|
|
#endif
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
on_error_3:
|
|
if (pEngine->ownsResourceManager) {
|
|
ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
|
|
}
|
|
#endif
|
|
on_error_2:
|
|
for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
|
|
ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
|
|
}
|
|
|
|
ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
|
|
on_error_1:
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
if (pEngine->ownsDevice) {
|
|
ma_device_uninit(pEngine->pDevice);
|
|
ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
MA_API void ma_engine_uninit(ma_engine* pEngine)
|
|
{
|
|
ma_uint32 iListener;
|
|
|
|
if (pEngine == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
if (pEngine->ownsDevice) {
|
|
ma_device_uninit(pEngine->pDevice);
|
|
ma_free(pEngine->pDevice, &pEngine->allocationCallbacks);
|
|
} else {
|
|
if (pEngine->pDevice != NULL) {
|
|
ma_device_stop(pEngine->pDevice);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ma_spinlock_lock(&pEngine->inlinedSoundLock);
|
|
{
|
|
for (;;) {
|
|
ma_sound_inlined* pSoundToDelete = pEngine->pInlinedSoundHead;
|
|
if (pSoundToDelete == NULL) {
|
|
break;
|
|
}
|
|
|
|
pEngine->pInlinedSoundHead = pSoundToDelete->pNext;
|
|
|
|
ma_sound_uninit(&pSoundToDelete->sound);
|
|
ma_free(pSoundToDelete, &pEngine->allocationCallbacks);
|
|
}
|
|
}
|
|
ma_spinlock_unlock(&pEngine->inlinedSoundLock);
|
|
|
|
for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
|
|
ma_spatializer_listener_uninit(&pEngine->listeners[iListener], &pEngine->allocationCallbacks);
|
|
}
|
|
|
|
ma_node_graph_uninit(&pEngine->nodeGraph, &pEngine->allocationCallbacks);
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
if (pEngine->ownsResourceManager) {
|
|
ma_resource_manager_uninit(pEngine->pResourceManager);
|
|
ma_free(pEngine->pResourceManager, &pEngine->allocationCallbacks);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 framesRead = 0;
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = 0;
|
|
}
|
|
|
|
result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pFramesRead != NULL) {
|
|
*pFramesRead = framesRead;
|
|
}
|
|
|
|
if (pEngine->onProcess) {
|
|
pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return &pEngine->nodeGraph;
|
|
}
|
|
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
#if !defined(MA_NO_RESOURCE_MANAGER)
|
|
{
|
|
return pEngine->pResourceManager;
|
|
}
|
|
#else
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_device* ma_engine_get_device(ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
return pEngine->pDevice;
|
|
}
|
|
#else
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_log* ma_engine_get_log(ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (pEngine->pLog != NULL) {
|
|
return pEngine->pLog;
|
|
} else {
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
return ma_device_get_log(ma_engine_get_device(pEngine));
|
|
}
|
|
#else
|
|
{
|
|
return NULL;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine)
|
|
{
|
|
return ma_node_graph_get_endpoint(&pEngine->nodeGraph);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine)
|
|
{
|
|
return ma_node_graph_get_time(&pEngine->nodeGraph);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine)
|
|
{
|
|
return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine);
|
|
}
|
|
|
|
MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime)
|
|
{
|
|
return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime);
|
|
}
|
|
|
|
MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime)
|
|
{
|
|
return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine)
|
|
{
|
|
return ma_engine_get_time_in_pcm_frames(pEngine);
|
|
}
|
|
|
|
MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime)
|
|
{
|
|
return ma_engine_set_time_in_pcm_frames(pEngine, globalTime);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine)
|
|
{
|
|
return ma_node_graph_get_channels(&pEngine->nodeGraph);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pEngine->sampleRate;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_start(ma_engine* pEngine)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pEngine == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
if (pEngine->pDevice != NULL) {
|
|
result = ma_device_start(pEngine->pDevice);
|
|
} else {
|
|
result = MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
result = MA_INVALID_OPERATION;
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_stop(ma_engine* pEngine)
|
|
{
|
|
ma_result result;
|
|
|
|
if (pEngine == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
#if !defined(MA_NO_DEVICE_IO)
|
|
{
|
|
if (pEngine->pDevice != NULL) {
|
|
result = ma_device_stop(pEngine->pDevice);
|
|
} else {
|
|
result = MA_INVALID_OPERATION;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
result = MA_INVALID_OPERATION;
|
|
}
|
|
#endif
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume);
|
|
}
|
|
|
|
MA_API float ma_engine_get_volume(ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
|
|
}
|
|
|
|
MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB)
|
|
{
|
|
return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB));
|
|
}
|
|
|
|
MA_API float ma_engine_get_gain_db(ma_engine* pEngine)
|
|
{
|
|
return ma_volume_linear_to_db(ma_engine_get_volume(pEngine));
|
|
}
|
|
|
|
MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine)
|
|
{
|
|
if (pEngine == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return pEngine->listenerCount;
|
|
}
|
|
|
|
MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ)
|
|
{
|
|
ma_uint32 iListener;
|
|
ma_uint32 iListenerClosest;
|
|
float closestLen2 = MA_FLT_MAX;
|
|
|
|
if (pEngine == NULL || pEngine->listenerCount == 1) {
|
|
return 0;
|
|
}
|
|
|
|
iListenerClosest = 0;
|
|
for (iListener = 0; iListener < pEngine->listenerCount; iListener += 1) {
|
|
if (ma_engine_listener_is_enabled(pEngine, iListener)) {
|
|
float len2 = ma_vec3f_len2(ma_vec3f_sub(ma_spatializer_listener_get_position(&pEngine->listeners[iListener]), ma_vec3f_init_3f(absolutePosX, absolutePosY, absolutePosZ)));
|
|
if (closestLen2 > len2) {
|
|
closestLen2 = len2;
|
|
iListenerClosest = iListener;
|
|
}
|
|
}
|
|
}
|
|
|
|
MA_ASSERT(iListenerClosest < 255);
|
|
return iListenerClosest;
|
|
}
|
|
|
|
MA_API void ma_engine_listener_set_position(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_set_position(&pEngine->listeners[listenerIndex], x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_engine_listener_get_position(const ma_engine* pEngine, ma_uint32 listenerIndex)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_spatializer_listener_get_position(&pEngine->listeners[listenerIndex]);
|
|
}
|
|
|
|
MA_API void ma_engine_listener_set_direction(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_set_direction(&pEngine->listeners[listenerIndex], x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_engine_listener_get_direction(const ma_engine* pEngine, ma_uint32 listenerIndex)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return ma_vec3f_init_3f(0, 0, -1);
|
|
}
|
|
|
|
return ma_spatializer_listener_get_direction(&pEngine->listeners[listenerIndex]);
|
|
}
|
|
|
|
MA_API void ma_engine_listener_set_velocity(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_set_velocity(&pEngine->listeners[listenerIndex], x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_engine_listener_get_velocity(const ma_engine* pEngine, ma_uint32 listenerIndex)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_spatializer_listener_get_velocity(&pEngine->listeners[listenerIndex]);
|
|
}
|
|
|
|
MA_API void ma_engine_listener_set_cone(ma_engine* pEngine, ma_uint32 listenerIndex, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_set_cone(&pEngine->listeners[listenerIndex], innerAngleInRadians, outerAngleInRadians, outerGain);
|
|
}
|
|
|
|
MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 listenerIndex, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
|
|
{
|
|
if (pInnerAngleInRadians != NULL) {
|
|
*pInnerAngleInRadians = 0;
|
|
}
|
|
|
|
if (pOuterAngleInRadians != NULL) {
|
|
*pOuterAngleInRadians = 0;
|
|
}
|
|
|
|
if (pOuterGain != NULL) {
|
|
*pOuterGain = 0;
|
|
}
|
|
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
|
|
}
|
|
|
|
MA_API void ma_engine_listener_set_world_up(ma_engine* pEngine, ma_uint32 listenerIndex, float x, float y, float z)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_set_world_up(&pEngine->listeners[listenerIndex], x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_engine_listener_get_world_up(const ma_engine* pEngine, ma_uint32 listenerIndex)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return ma_vec3f_init_3f(0, 1, 0);
|
|
}
|
|
|
|
return ma_spatializer_listener_get_world_up(&pEngine->listeners[listenerIndex]);
|
|
}
|
|
|
|
MA_API void ma_engine_listener_set_enabled(ma_engine* pEngine, ma_uint32 listenerIndex, ma_bool32 isEnabled)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_listener_set_enabled(&pEngine->listeners[listenerIndex], isEnabled);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_engine_listener_is_enabled(const ma_engine* pEngine, ma_uint32 listenerIndex)
|
|
{
|
|
if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_spatializer_listener_is_enabled(&pEngine->listeners[listenerIndex]);
|
|
}
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePath, ma_node* pNode, ma_uint32 nodeInputBusIndex)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_sound_inlined* pSound = NULL;
|
|
ma_sound_inlined* pNextSound = NULL;
|
|
|
|
if (pEngine == NULL || pFilePath == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pNode == NULL) {
|
|
pNode = ma_node_graph_get_endpoint(&pEngine->nodeGraph);
|
|
nodeInputBusIndex = 0;
|
|
}
|
|
|
|
ma_spinlock_lock(&pEngine->inlinedSoundLock);
|
|
{
|
|
ma_uint32 soundFlags = 0;
|
|
|
|
for (pNextSound = pEngine->pInlinedSoundHead; pNextSound != NULL; pNextSound = pNextSound->pNext) {
|
|
if (ma_sound_at_end(&pNextSound->sound)) {
|
|
pSound = pNextSound;
|
|
ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (pSound != NULL) {
|
|
if (pEngine->pInlinedSoundHead == pSound) {
|
|
pEngine->pInlinedSoundHead = pSound->pNext;
|
|
}
|
|
|
|
if (pSound->pPrev != NULL) {
|
|
pSound->pPrev->pNext = pSound->pNext;
|
|
}
|
|
if (pSound->pNext != NULL) {
|
|
pSound->pNext->pPrev = pSound->pPrev;
|
|
}
|
|
|
|
ma_sound_uninit(&pNextSound->sound);
|
|
} else {
|
|
pSound = (ma_sound_inlined*)ma_malloc(sizeof(*pSound), &pEngine->allocationCallbacks);
|
|
}
|
|
|
|
if (pSound != NULL) {
|
|
soundFlags |= MA_SOUND_FLAG_ASYNC;
|
|
soundFlags |= MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT;
|
|
soundFlags |= MA_SOUND_FLAG_NO_PITCH;
|
|
soundFlags |= MA_SOUND_FLAG_NO_SPATIALIZATION;
|
|
|
|
result = ma_sound_init_from_file(pEngine, pFilePath, soundFlags, NULL, NULL, &pSound->sound);
|
|
if (result == MA_SUCCESS) {
|
|
result = ma_node_attach_output_bus(pSound, 0, pNode, nodeInputBusIndex);
|
|
if (result == MA_SUCCESS) {
|
|
pSound->pNext = pEngine->pInlinedSoundHead;
|
|
pSound->pPrev = NULL;
|
|
|
|
pEngine->pInlinedSoundHead = pSound;
|
|
if (pSound->pNext != NULL) {
|
|
pSound->pNext->pPrev = pSound;
|
|
}
|
|
} else {
|
|
ma_free(pSound, &pEngine->allocationCallbacks);
|
|
}
|
|
} else {
|
|
ma_free(pSound, &pEngine->allocationCallbacks);
|
|
}
|
|
} else {
|
|
result = MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
ma_spinlock_unlock(&pEngine->inlinedSoundLock);
|
|
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_sound_start(&pSound->sound);
|
|
if (result != MA_SUCCESS) {
|
|
ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE);
|
|
return result;
|
|
}
|
|
|
|
ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1);
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup)
|
|
{
|
|
return ma_engine_play_sound_ex(pEngine, pFilePath, pGroup, 0);
|
|
}
|
|
#endif
|
|
|
|
static ma_result ma_sound_preinit(ma_engine* pEngine, ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pSound);
|
|
pSound->seekTarget = MA_SEEK_TARGET_NONE;
|
|
|
|
if (pEngine == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
|
|
{
|
|
ma_result result;
|
|
ma_engine_node_config engineNodeConfig;
|
|
ma_engine_node_type type;
|
|
|
|
MA_ASSERT(pEngine != NULL);
|
|
MA_ASSERT(pSound != NULL);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pSound->pDataSource = pConfig->pDataSource;
|
|
|
|
if (pConfig->pDataSource != NULL) {
|
|
type = ma_engine_node_type_sound;
|
|
} else {
|
|
type = ma_engine_node_type_group;
|
|
}
|
|
|
|
engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags);
|
|
engineNodeConfig.channelsIn = pConfig->channelsIn;
|
|
engineNodeConfig.channelsOut = pConfig->channelsOut;
|
|
engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames;
|
|
engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode;
|
|
|
|
if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) {
|
|
engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames;
|
|
}
|
|
|
|
if (pConfig->pDataSource != NULL) {
|
|
result = ma_data_source_get_data_format(pConfig->pDataSource, NULL, &engineNodeConfig.channelsIn, &engineNodeConfig.sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (engineNodeConfig.channelsIn == 0) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
if (engineNodeConfig.channelsOut == MA_SOUND_SOURCE_CHANNEL_COUNT) {
|
|
engineNodeConfig.channelsOut = engineNodeConfig.channelsIn;
|
|
}
|
|
}
|
|
|
|
result = ma_engine_node_init(&engineNodeConfig, &pEngine->allocationCallbacks, &pSound->engineNode);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig->pInitialAttachment == NULL) {
|
|
if ((pConfig->flags & MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT) == 0) {
|
|
result = ma_node_attach_output_bus(pSound, 0, ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0);
|
|
}
|
|
} else {
|
|
result = ma_node_attach_output_bus(pSound, 0, pConfig->pInitialAttachment, pConfig->initialAttachmentInputBusIndex);
|
|
}
|
|
|
|
if (result != MA_SUCCESS) {
|
|
ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
if (pSound->pDataSource != NULL) {
|
|
pSound->processingCacheFramesRemaining = 0;
|
|
pSound->processingCacheCap = ma_node_graph_get_processing_size_in_frames(&pEngine->nodeGraph);
|
|
if (pSound->processingCacheCap == 0) {
|
|
pSound->processingCacheCap = 512;
|
|
}
|
|
|
|
pSound->pProcessingCache = (float*)ma_calloc(pSound->processingCacheCap * ma_get_bytes_per_frame(ma_format_f32, engineNodeConfig.channelsIn), &pEngine->allocationCallbacks);
|
|
if (pSound->pProcessingCache == NULL) {
|
|
ma_engine_node_uninit(&pSound->engineNode, &pEngine->allocationCallbacks);
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (pConfig->rangeBegInPCMFrames != 0 || pConfig->rangeEndInPCMFrames != ~((ma_uint64)0)) {
|
|
ma_data_source_set_range_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->rangeBegInPCMFrames, pConfig->rangeEndInPCMFrames);
|
|
}
|
|
|
|
if (pConfig->loopPointBegInPCMFrames != 0 || pConfig->loopPointEndInPCMFrames != ~((ma_uint64)0)) {
|
|
ma_data_source_set_loop_point_in_pcm_frames(ma_sound_get_data_source(pSound), pConfig->loopPointBegInPCMFrames, pConfig->loopPointEndInPCMFrames);
|
|
}
|
|
|
|
ma_sound_set_looping(pSound, pConfig->isLooping || ((pConfig->flags & MA_SOUND_FLAG_LOOPING) != 0));
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
MA_API ma_result ma_sound_init_from_file_internal(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
ma_uint32 flags;
|
|
ma_sound_config config;
|
|
ma_resource_manager_pipeline_notifications notifications;
|
|
|
|
flags = pConfig->flags | MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT;
|
|
if (pConfig->isLooping) {
|
|
flags |= MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING;
|
|
}
|
|
|
|
pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
|
|
if (pSound->pResourceManagerDataSource == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
notifications = pConfig->initNotifications;
|
|
if (pConfig->pDoneFence != NULL && notifications.done.pFence == NULL) {
|
|
notifications.done.pFence = pConfig->pDoneFence;
|
|
}
|
|
|
|
if (notifications.done.pFence) { ma_fence_acquire(notifications.done.pFence); }
|
|
{
|
|
ma_resource_manager_data_source_config resourceManagerDataSourceConfig = ma_resource_manager_data_source_config_init();
|
|
resourceManagerDataSourceConfig.pFilePath = pConfig->pFilePath;
|
|
resourceManagerDataSourceConfig.pFilePathW = pConfig->pFilePathW;
|
|
resourceManagerDataSourceConfig.flags = flags;
|
|
resourceManagerDataSourceConfig.pNotifications = ¬ifications;
|
|
resourceManagerDataSourceConfig.initialSeekPointInPCMFrames = pConfig->initialSeekPointInPCMFrames;
|
|
resourceManagerDataSourceConfig.rangeBegInPCMFrames = pConfig->rangeBegInPCMFrames;
|
|
resourceManagerDataSourceConfig.rangeEndInPCMFrames = pConfig->rangeEndInPCMFrames;
|
|
resourceManagerDataSourceConfig.loopPointBegInPCMFrames = pConfig->loopPointBegInPCMFrames;
|
|
resourceManagerDataSourceConfig.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames;
|
|
resourceManagerDataSourceConfig.isLooping = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_LOOPING) != 0;
|
|
|
|
result = ma_resource_manager_data_source_init_ex(pEngine->pResourceManager, &resourceManagerDataSourceConfig, pSound->pResourceManagerDataSource);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
|
|
goto done;
|
|
}
|
|
|
|
pSound->ownsDataSource = MA_TRUE;
|
|
|
|
config = *pConfig;
|
|
config.pFilePath = NULL;
|
|
config.pFilePathW = NULL;
|
|
config.pDataSource = pSound->pResourceManagerDataSource;
|
|
|
|
result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
|
|
ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
|
|
MA_ZERO_OBJECT(pSound);
|
|
goto done;
|
|
}
|
|
}
|
|
done:
|
|
if (notifications.done.pFence) { ma_fence_release(notifications.done.pFence); }
|
|
return result;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_init_from_file(ma_engine* pEngine, const char* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)
|
|
{
|
|
ma_sound_config config;
|
|
|
|
if (pFilePath == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
config = ma_sound_config_init_2(pEngine);
|
|
config.pFilePath = pFilePath;
|
|
config.flags = flags;
|
|
config.pInitialAttachment = pGroup;
|
|
config.pDoneFence = pDoneFence;
|
|
|
|
return ma_sound_init_ex(pEngine, &config, pSound);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_init_from_file_w(ma_engine* pEngine, const wchar_t* pFilePath, ma_uint32 flags, ma_sound_group* pGroup, ma_fence* pDoneFence, ma_sound* pSound)
|
|
{
|
|
ma_sound_config config;
|
|
|
|
if (pFilePath == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
config = ma_sound_config_init_2(pEngine);
|
|
config.pFilePathW = pFilePath;
|
|
config.flags = flags;
|
|
config.pInitialAttachment = pGroup;
|
|
config.pDoneFence = pDoneFence;
|
|
|
|
return ma_sound_init_ex(pEngine, &config, pSound);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistingSound, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)
|
|
{
|
|
ma_result result;
|
|
ma_sound_config config;
|
|
|
|
result = ma_sound_preinit(pEngine, pSound);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pExistingSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pExistingSound->pResourceManagerDataSource == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks);
|
|
if (pSound->pResourceManagerDataSource == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
|
|
result = ma_resource_manager_data_source_init_copy(pEngine->pResourceManager, pExistingSound->pResourceManagerDataSource, pSound->pResourceManagerDataSource);
|
|
if (result != MA_SUCCESS) {
|
|
ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
|
|
return result;
|
|
}
|
|
|
|
config = ma_sound_config_init_2(pEngine);
|
|
config.pDataSource = pSound->pResourceManagerDataSource;
|
|
config.flags = flags;
|
|
config.pInitialAttachment = pGroup;
|
|
config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode;
|
|
config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames;
|
|
|
|
result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound);
|
|
if (result != MA_SUCCESS) {
|
|
ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
|
|
ma_free(pSound->pResourceManagerDataSource, &pEngine->allocationCallbacks);
|
|
MA_ZERO_OBJECT(pSound);
|
|
return result;
|
|
}
|
|
|
|
pSound->ownsDataSource = MA_TRUE;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
#endif
|
|
|
|
MA_API ma_result ma_sound_init_from_data_source(ma_engine* pEngine, ma_data_source* pDataSource, ma_uint32 flags, ma_sound_group* pGroup, ma_sound* pSound)
|
|
{
|
|
ma_sound_config config = ma_sound_config_init_2(pEngine);
|
|
config.pDataSource = pDataSource;
|
|
config.flags = flags;
|
|
config.pInitialAttachment = pGroup;
|
|
return ma_sound_init_ex(pEngine, &config, pSound);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pConfig, ma_sound* pSound)
|
|
{
|
|
ma_result result;
|
|
|
|
result = ma_sound_preinit(pEngine, pSound);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
pSound->endCallback = pConfig->endCallback;
|
|
pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData;
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) {
|
|
return ma_sound_init_from_file_internal(pEngine, pConfig, pSound);
|
|
} else
|
|
#endif
|
|
{
|
|
return ma_sound_init_from_data_source_internal(pEngine, pConfig, pSound);
|
|
}
|
|
}
|
|
|
|
MA_API void ma_sound_uninit(ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_engine_node_uninit(&pSound->engineNode, &pSound->engineNode.pEngine->allocationCallbacks);
|
|
|
|
if (pSound->pProcessingCache != NULL) {
|
|
ma_free(pSound->pProcessingCache, &pSound->engineNode.pEngine->allocationCallbacks);
|
|
pSound->pProcessingCache = NULL;
|
|
}
|
|
|
|
#ifndef MA_NO_RESOURCE_MANAGER
|
|
if (pSound->ownsDataSource) {
|
|
ma_resource_manager_data_source_uninit(pSound->pResourceManagerDataSource);
|
|
ma_free(pSound->pResourceManagerDataSource, &pSound->engineNode.pEngine->allocationCallbacks);
|
|
pSound->pDataSource = NULL;
|
|
}
|
|
#else
|
|
MA_ASSERT(pSound->ownsDataSource == MA_FALSE);
|
|
#endif
|
|
}
|
|
|
|
MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pSound->engineNode.pEngine;
|
|
}
|
|
|
|
MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
return pSound->pDataSource;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_start(ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (ma_sound_is_playing(pSound)) {
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
if (ma_sound_at_end(pSound)) {
|
|
ma_result result = ma_data_source_seek_to_pcm_frame(pSound->pDataSource, 0);
|
|
if (result != MA_SUCCESS && result != MA_NOT_IMPLEMENTED) {
|
|
return result;
|
|
}
|
|
|
|
ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE);
|
|
}
|
|
|
|
ma_node_set_state(pSound, ma_node_state_started);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_stop(ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_node_set_state(pSound, ma_node_state_stopped);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds)
|
|
{
|
|
ma_uint64 sampleRate;
|
|
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
|
|
|
|
return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000);
|
|
}
|
|
|
|
MA_API void ma_sound_reset_start_time(ma_sound* pSound)
|
|
{
|
|
ma_sound_set_start_time_in_pcm_frames(pSound, 0);
|
|
}
|
|
|
|
MA_API void ma_sound_reset_stop_time(ma_sound* pSound)
|
|
{
|
|
ma_sound_set_stop_time_in_pcm_frames(pSound, ~(ma_uint64)0);
|
|
}
|
|
|
|
MA_API void ma_sound_reset_fade(ma_sound* pSound)
|
|
{
|
|
ma_sound_set_fade_in_pcm_frames(pSound, 0, 1, 0);
|
|
}
|
|
|
|
MA_API void ma_sound_reset_stop_time_and_fade(ma_sound* pSound)
|
|
{
|
|
ma_sound_reset_stop_time(pSound);
|
|
ma_sound_reset_fade(pSound);
|
|
}
|
|
|
|
MA_API void ma_sound_set_volume(ma_sound* pSound, float volume)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_engine_node_set_volume(&pSound->engineNode, volume);
|
|
}
|
|
|
|
MA_API float ma_sound_get_volume(const ma_sound* pSound)
|
|
{
|
|
float volume = 0;
|
|
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
ma_engine_node_get_volume(&pSound->engineNode, &volume);
|
|
|
|
return volume;
|
|
}
|
|
|
|
MA_API void ma_sound_set_pan(ma_sound* pSound, float pan)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_panner_set_pan(&pSound->engineNode.panner, pan);
|
|
}
|
|
|
|
MA_API float ma_sound_get_pan(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_panner_get_pan(&pSound->engineNode.panner);
|
|
}
|
|
|
|
MA_API void ma_sound_set_pan_mode(ma_sound* pSound, ma_pan_mode panMode)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_panner_set_mode(&pSound->engineNode.panner, panMode);
|
|
}
|
|
|
|
MA_API ma_pan_mode ma_sound_get_pan_mode(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return ma_pan_mode_balance;
|
|
}
|
|
|
|
return ma_panner_get_mode(&pSound->engineNode.panner);
|
|
}
|
|
|
|
MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pitch <= 0) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release);
|
|
}
|
|
|
|
MA_API float ma_sound_get_pitch(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_atomic_load_f32(&pSound->engineNode.pitch);
|
|
}
|
|
|
|
MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_engine_node_is_spatialization_enabled(&pSound->engineNode);
|
|
}
|
|
|
|
MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 listenerIndex)
|
|
{
|
|
if (pSound == NULL || listenerIndex >= ma_engine_get_listener_count(ma_sound_get_engine(pSound))) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_LISTENER_INDEX_CLOSEST;
|
|
}
|
|
|
|
return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound)
|
|
{
|
|
ma_uint32 listenerIndex;
|
|
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
listenerIndex = ma_sound_get_pinned_listener_index(pSound);
|
|
if (listenerIndex == MA_LISTENER_INDEX_CLOSEST) {
|
|
ma_vec3f position = ma_sound_get_position(pSound);
|
|
return ma_engine_find_closest_listener(ma_sound_get_engine(pSound), position.x, position.y, position.z);
|
|
}
|
|
|
|
return listenerIndex;
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_get_direction_to_listener(const ma_sound* pSound)
|
|
{
|
|
ma_vec3f relativePos;
|
|
ma_engine* pEngine;
|
|
|
|
if (pSound == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, -1);
|
|
}
|
|
|
|
pEngine = ma_sound_get_engine(pSound);
|
|
if (pEngine == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, -1);
|
|
}
|
|
|
|
ma_spatializer_get_relative_position_and_direction(&pSound->engineNode.spatializer, &pEngine->listeners[ma_sound_get_listener_index(pSound)], &relativePos, NULL);
|
|
|
|
return ma_vec3f_normalize(ma_vec3f_neg(relativePos));
|
|
}
|
|
|
|
MA_API void ma_sound_set_position(ma_sound* pSound, float x, float y, float z)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_position(&pSound->engineNode.spatializer, x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_get_position(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_spatializer_get_position(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_direction(ma_sound* pSound, float x, float y, float z)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_direction(&pSound->engineNode.spatializer, x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_get_direction(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_spatializer_get_direction(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_velocity(ma_sound* pSound, float x, float y, float z)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_velocity(&pSound->engineNode.spatializer, x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_get_velocity(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return ma_vec3f_init_3f(0, 0, 0);
|
|
}
|
|
|
|
return ma_spatializer_get_velocity(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_attenuation_model(ma_sound* pSound, ma_attenuation_model attenuationModel)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_attenuation_model(&pSound->engineNode.spatializer, attenuationModel);
|
|
}
|
|
|
|
MA_API ma_attenuation_model ma_sound_get_attenuation_model(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return ma_attenuation_model_none;
|
|
}
|
|
|
|
return ma_spatializer_get_attenuation_model(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_positioning(ma_sound* pSound, ma_positioning positioning)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_positioning(&pSound->engineNode.spatializer, positioning);
|
|
}
|
|
|
|
MA_API ma_positioning ma_sound_get_positioning(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return ma_positioning_absolute;
|
|
}
|
|
|
|
return ma_spatializer_get_positioning(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_rolloff(ma_sound* pSound, float rolloff)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_rolloff(&pSound->engineNode.spatializer, rolloff);
|
|
}
|
|
|
|
MA_API float ma_sound_get_rolloff(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_spatializer_get_rolloff(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_min_gain(ma_sound* pSound, float minGain)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_min_gain(&pSound->engineNode.spatializer, minGain);
|
|
}
|
|
|
|
MA_API float ma_sound_get_min_gain(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_spatializer_get_min_gain(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_max_gain(ma_sound* pSound, float maxGain)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_max_gain(&pSound->engineNode.spatializer, maxGain);
|
|
}
|
|
|
|
MA_API float ma_sound_get_max_gain(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_spatializer_get_max_gain(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_min_distance(ma_sound* pSound, float minDistance)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_min_distance(&pSound->engineNode.spatializer, minDistance);
|
|
}
|
|
|
|
MA_API float ma_sound_get_min_distance(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_spatializer_get_min_distance(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_max_distance(ma_sound* pSound, float maxDistance)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_max_distance(&pSound->engineNode.spatializer, maxDistance);
|
|
}
|
|
|
|
MA_API float ma_sound_get_max_distance(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_spatializer_get_max_distance(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_cone(ma_sound* pSound, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_cone(&pSound->engineNode.spatializer, innerAngleInRadians, outerAngleInRadians, outerGain);
|
|
}
|
|
|
|
MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
|
|
{
|
|
if (pInnerAngleInRadians != NULL) {
|
|
*pInnerAngleInRadians = 0;
|
|
}
|
|
|
|
if (pOuterAngleInRadians != NULL) {
|
|
*pOuterAngleInRadians = 0;
|
|
}
|
|
|
|
if (pOuterGain != NULL) {
|
|
*pOuterGain = 0;
|
|
}
|
|
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
|
|
}
|
|
|
|
MA_API void ma_sound_set_doppler_factor(ma_sound* pSound, float dopplerFactor)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_doppler_factor(&pSound->engineNode.spatializer, dopplerFactor);
|
|
}
|
|
|
|
MA_API float ma_sound_get_doppler_factor(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_spatializer_get_doppler_factor(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float directionalAttenuationFactor)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_spatializer_set_directional_attenuation_factor(&pSound->engineNode.spatializer, directionalAttenuationFactor);
|
|
}
|
|
|
|
MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 1;
|
|
}
|
|
|
|
return ma_spatializer_get_directional_attenuation_factor(&pSound->engineNode.spatializer);
|
|
}
|
|
|
|
MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0));
|
|
}
|
|
|
|
MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000);
|
|
}
|
|
|
|
MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg);
|
|
ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd);
|
|
ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames);
|
|
ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames);
|
|
}
|
|
|
|
MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds)
|
|
{
|
|
ma_uint32 sampleRate;
|
|
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
|
|
|
|
ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000);
|
|
}
|
|
|
|
MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
return ma_fader_get_current_volume(&pSound->engineNode.fader);
|
|
}
|
|
|
|
MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_node_set_state_time(pSound, ma_node_state_started, absoluteGlobalTimeInFrames);
|
|
}
|
|
|
|
MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_sound_set_start_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
|
|
}
|
|
|
|
MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0);
|
|
}
|
|
|
|
MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000);
|
|
}
|
|
|
|
MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (fadeLengthInFrames > 0) {
|
|
if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) {
|
|
fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames;
|
|
}
|
|
|
|
ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames);
|
|
}
|
|
|
|
ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames);
|
|
}
|
|
|
|
MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds)
|
|
{
|
|
ma_uint32 sampleRate;
|
|
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
|
|
|
|
ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started;
|
|
}
|
|
|
|
MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_node_get_time(pSound);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound)
|
|
{
|
|
ma_uint32 sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound));
|
|
if (sampleRate == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / sampleRate;
|
|
}
|
|
|
|
MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping)
|
|
{
|
|
if (pSound == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return;
|
|
}
|
|
|
|
ma_data_source_set_looping(pSound->pDataSource, isLooping);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_data_source_is_looping(pSound->pDataSource);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
|
|
return ma_sound_get_at_end(pSound);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
ma_atomic_exchange_64(&pSound->seekTarget, frameIndex);
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_seek_to_second(ma_sound* pSound, float seekPointInSeconds)
|
|
{
|
|
ma_uint64 frameIndex;
|
|
ma_uint32 sampleRate;
|
|
ma_result result;
|
|
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
frameIndex = (ma_uint64)(seekPointInSeconds * sampleRate);
|
|
|
|
return ma_sound_seek_to_pcm_frame(pSound, frameIndex);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_get_data_format(const ma_sound* pSound, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
ma_uint32 channels;
|
|
|
|
if (pFormat != NULL) {
|
|
*pFormat = ma_format_f32;
|
|
}
|
|
|
|
channels = ma_node_get_input_channels(&pSound->engineNode, 0);
|
|
if (pChannels != NULL) {
|
|
*pChannels = channels;
|
|
}
|
|
|
|
if (pSampleRate != NULL) {
|
|
*pSampleRate = pSound->engineNode.resampler.sampleRateIn;
|
|
}
|
|
|
|
if (pChannelMap != NULL) {
|
|
ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, channels);
|
|
}
|
|
|
|
return MA_SUCCESS;
|
|
} else {
|
|
return ma_data_source_get_data_format(pSound->pDataSource, pFormat, pChannels, pSampleRate, pChannelMap, channelMapCap);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_sound_get_cursor_in_pcm_frames(const ma_sound* pSound, ma_uint64* pCursor)
|
|
{
|
|
ma_uint64 seekTarget;
|
|
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
seekTarget = ma_atomic_load_64(&pSound->seekTarget);
|
|
if (seekTarget != MA_SEEK_TARGET_NONE) {
|
|
*pCursor = seekTarget;
|
|
return MA_SUCCESS;
|
|
} else {
|
|
return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor);
|
|
}
|
|
}
|
|
|
|
MA_API ma_result ma_sound_get_length_in_pcm_frames(const ma_sound* pSound, ma_uint64* pLength)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return ma_data_source_get_length_in_pcm_frames(pSound->pDataSource, pLength);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_get_cursor_in_seconds(const ma_sound* pSound, float* pCursor)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 cursorInPCMFrames;
|
|
ma_uint32 sampleRate;
|
|
|
|
if (pCursor != NULL) {
|
|
*pCursor = 0;
|
|
}
|
|
|
|
result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0);
|
|
if (result != MA_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
*pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_get_length_in_seconds(const ma_sound* pSound, float* pLength)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData)
|
|
{
|
|
if (pSound == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
if (pSound->pDataSource == NULL) {
|
|
return MA_INVALID_OPERATION;
|
|
}
|
|
|
|
pSound->endCallback = callback;
|
|
pSound->pEndCallbackUserData = pUserData;
|
|
|
|
return MA_SUCCESS;
|
|
}
|
|
|
|
MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup)
|
|
{
|
|
ma_sound_group_config config = ma_sound_group_config_init_2(pEngine);
|
|
config.flags = flags;
|
|
config.pInitialAttachment = pParentGroup;
|
|
return ma_sound_group_init_ex(pEngine, &config, pGroup);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup)
|
|
{
|
|
ma_sound_config soundConfig;
|
|
|
|
if (pGroup == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
MA_ZERO_OBJECT(pGroup);
|
|
|
|
if (pConfig == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
|
|
soundConfig = *pConfig;
|
|
soundConfig.pFilePath = NULL;
|
|
soundConfig.pFilePathW = NULL;
|
|
soundConfig.pDataSource = NULL;
|
|
|
|
soundConfig.flags |= MA_SOUND_FLAG_NO_SPATIALIZATION;
|
|
|
|
return ma_sound_init_ex(pEngine, &soundConfig, pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_uninit(ma_sound_group* pGroup)
|
|
{
|
|
ma_sound_uninit(pGroup);
|
|
}
|
|
|
|
MA_API ma_engine* ma_sound_group_get_engine(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_engine(pGroup);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_group_start(ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_start(pGroup);
|
|
}
|
|
|
|
MA_API ma_result ma_sound_group_stop(ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_stop(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_volume(ma_sound_group* pGroup, float volume)
|
|
{
|
|
ma_sound_set_volume(pGroup, volume);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_volume(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_volume(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_pan(ma_sound_group* pGroup, float pan)
|
|
{
|
|
ma_sound_set_pan(pGroup, pan);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_pan(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_pan(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_pan_mode(ma_sound_group* pGroup, ma_pan_mode panMode)
|
|
{
|
|
ma_sound_set_pan_mode(pGroup, panMode);
|
|
}
|
|
|
|
MA_API ma_pan_mode ma_sound_group_get_pan_mode(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_pan_mode(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_pitch(ma_sound_group* pGroup, float pitch)
|
|
{
|
|
ma_sound_set_pitch(pGroup, pitch);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_pitch(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_pitch(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_spatialization_enabled(ma_sound_group* pGroup, ma_bool32 enabled)
|
|
{
|
|
ma_sound_set_spatialization_enabled(pGroup, enabled);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_sound_group_is_spatialization_enabled(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_is_spatialization_enabled(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_pinned_listener_index(ma_sound_group* pGroup, ma_uint32 listenerIndex)
|
|
{
|
|
ma_sound_set_pinned_listener_index(pGroup, listenerIndex);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_sound_group_get_pinned_listener_index(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_pinned_listener_index(pGroup);
|
|
}
|
|
|
|
MA_API ma_uint32 ma_sound_group_get_listener_index(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_listener_index(pGroup);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_group_get_direction_to_listener(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_direction_to_listener(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_position(ma_sound_group* pGroup, float x, float y, float z)
|
|
{
|
|
ma_sound_set_position(pGroup, x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_group_get_position(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_position(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_direction(ma_sound_group* pGroup, float x, float y, float z)
|
|
{
|
|
ma_sound_set_direction(pGroup, x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_group_get_direction(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_direction(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_velocity(ma_sound_group* pGroup, float x, float y, float z)
|
|
{
|
|
ma_sound_set_velocity(pGroup, x, y, z);
|
|
}
|
|
|
|
MA_API ma_vec3f ma_sound_group_get_velocity(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_velocity(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_attenuation_model(ma_sound_group* pGroup, ma_attenuation_model attenuationModel)
|
|
{
|
|
ma_sound_set_attenuation_model(pGroup, attenuationModel);
|
|
}
|
|
|
|
MA_API ma_attenuation_model ma_sound_group_get_attenuation_model(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_attenuation_model(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_positioning(ma_sound_group* pGroup, ma_positioning positioning)
|
|
{
|
|
ma_sound_set_positioning(pGroup, positioning);
|
|
}
|
|
|
|
MA_API ma_positioning ma_sound_group_get_positioning(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_positioning(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_rolloff(ma_sound_group* pGroup, float rolloff)
|
|
{
|
|
ma_sound_set_rolloff(pGroup, rolloff);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_rolloff(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_rolloff(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_min_gain(ma_sound_group* pGroup, float minGain)
|
|
{
|
|
ma_sound_set_min_gain(pGroup, minGain);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_min_gain(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_min_gain(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_max_gain(ma_sound_group* pGroup, float maxGain)
|
|
{
|
|
ma_sound_set_max_gain(pGroup, maxGain);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_max_gain(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_max_gain(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_min_distance(ma_sound_group* pGroup, float minDistance)
|
|
{
|
|
ma_sound_set_min_distance(pGroup, minDistance);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_min_distance(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_min_distance(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_max_distance(ma_sound_group* pGroup, float maxDistance)
|
|
{
|
|
ma_sound_set_max_distance(pGroup, maxDistance);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_max_distance(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_max_distance(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_cone(ma_sound_group* pGroup, float innerAngleInRadians, float outerAngleInRadians, float outerGain)
|
|
{
|
|
ma_sound_set_cone(pGroup, innerAngleInRadians, outerAngleInRadians, outerGain);
|
|
}
|
|
|
|
MA_API void ma_sound_group_get_cone(const ma_sound_group* pGroup, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain)
|
|
{
|
|
ma_sound_get_cone(pGroup, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_doppler_factor(ma_sound_group* pGroup, float dopplerFactor)
|
|
{
|
|
ma_sound_set_doppler_factor(pGroup, dopplerFactor);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_doppler_factor(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_doppler_factor(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_directional_attenuation_factor(ma_sound_group* pGroup, float directionalAttenuationFactor)
|
|
{
|
|
ma_sound_set_directional_attenuation_factor(pGroup, directionalAttenuationFactor);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_directional_attenuation_factor(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_directional_attenuation_factor(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_fade_in_pcm_frames(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames)
|
|
{
|
|
ma_sound_set_fade_in_pcm_frames(pGroup, volumeBeg, volumeEnd, fadeLengthInFrames);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_fade_in_milliseconds(ma_sound_group* pGroup, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds)
|
|
{
|
|
ma_sound_set_fade_in_milliseconds(pGroup, volumeBeg, volumeEnd, fadeLengthInMilliseconds);
|
|
}
|
|
|
|
MA_API float ma_sound_group_get_current_fade_volume(ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_current_fade_volume(pGroup);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_start_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
|
|
{
|
|
ma_sound_set_start_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_start_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
|
|
{
|
|
ma_sound_set_start_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_stop_time_in_pcm_frames(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInFrames)
|
|
{
|
|
ma_sound_set_stop_time_in_pcm_frames(pGroup, absoluteGlobalTimeInFrames);
|
|
}
|
|
|
|
MA_API void ma_sound_group_set_stop_time_in_milliseconds(ma_sound_group* pGroup, ma_uint64 absoluteGlobalTimeInMilliseconds)
|
|
{
|
|
ma_sound_set_stop_time_in_milliseconds(pGroup, absoluteGlobalTimeInMilliseconds);
|
|
}
|
|
|
|
MA_API ma_bool32 ma_sound_group_is_playing(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_is_playing(pGroup);
|
|
}
|
|
|
|
MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGroup)
|
|
{
|
|
return ma_sound_get_time_in_pcm_frames(pGroup);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING))
|
|
#if !defined(MA_DR_WAV_IMPLEMENTATION)
|
|
#ifndef ma_dr_wav_c
|
|
#define ma_dr_wav_c
|
|
#ifdef __MRC__
|
|
#pragma options opt off
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
#ifndef MA_DR_WAV_NO_STDIO
|
|
#include <stdio.h>
|
|
#ifndef MA_DR_WAV_NO_WCHAR
|
|
#include <wchar.h>
|
|
#endif
|
|
#endif
|
|
#ifndef MA_DR_WAV_ASSERT
|
|
#include <assert.h>
|
|
#define MA_DR_WAV_ASSERT(expression) assert(expression)
|
|
#endif
|
|
#ifndef MA_DR_WAV_MALLOC
|
|
#define MA_DR_WAV_MALLOC(sz) malloc((sz))
|
|
#endif
|
|
#ifndef MA_DR_WAV_REALLOC
|
|
#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz))
|
|
#endif
|
|
#ifndef MA_DR_WAV_FREE
|
|
#define MA_DR_WAV_FREE(p) free((p))
|
|
#endif
|
|
#ifndef MA_DR_WAV_COPY_MEMORY
|
|
#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
|
|
#endif
|
|
#ifndef MA_DR_WAV_ZERO_MEMORY
|
|
#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
|
|
#endif
|
|
#ifndef MA_DR_WAV_ZERO_OBJECT
|
|
#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p))
|
|
#endif
|
|
#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0]))
|
|
#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
|
|
#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b))
|
|
#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b))
|
|
#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x))))
|
|
#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset))
|
|
#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32
|
|
#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32))
|
|
#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF))
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
|
#define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
|
|
#define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
|
|
#elif defined(__clang__)
|
|
#if defined(__has_builtin)
|
|
#if __has_builtin(__builtin_bswap16)
|
|
#define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
|
|
#endif
|
|
#if __has_builtin(__builtin_bswap32)
|
|
#define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
|
|
#endif
|
|
#if __has_builtin(__builtin_bswap64)
|
|
#define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
|
|
#endif
|
|
#endif
|
|
#elif defined(__GNUC__)
|
|
#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
|
|
#define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
|
|
#endif
|
|
#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
|
#define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
|
|
#endif
|
|
#endif
|
|
MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
|
|
{
|
|
if (pMajor) {
|
|
*pMajor = MA_DR_WAV_VERSION_MAJOR;
|
|
}
|
|
if (pMinor) {
|
|
*pMinor = MA_DR_WAV_VERSION_MINOR;
|
|
}
|
|
if (pRevision) {
|
|
*pRevision = MA_DR_WAV_VERSION_REVISION;
|
|
}
|
|
}
|
|
MA_API const char* ma_dr_wav_version_string(void)
|
|
{
|
|
return MA_DR_WAV_VERSION_STRING;
|
|
}
|
|
#ifndef MA_DR_WAV_MAX_SAMPLE_RATE
|
|
#define MA_DR_WAV_MAX_SAMPLE_RATE 384000
|
|
#endif
|
|
#ifndef MA_DR_WAV_MAX_CHANNELS
|
|
#define MA_DR_WAV_MAX_CHANNELS 256
|
|
#endif
|
|
#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE
|
|
#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64
|
|
#endif
|
|
static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00};
|
|
static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
|
|
static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
|
|
static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
|
|
static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A};
|
|
static MA_INLINE int ma_dr_wav__is_little_endian(void)
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
return MA_TRUE;
|
|
#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
|
|
return MA_TRUE;
|
|
#else
|
|
int n = 1;
|
|
return (*(char*)&n) == 1;
|
|
#endif
|
|
}
|
|
static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 16; ++i) {
|
|
guid[i] = data[i];
|
|
}
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n)
|
|
{
|
|
#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC
|
|
#if defined(_MSC_VER)
|
|
return _byteswap_ushort(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
return __builtin_bswap16(n);
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & 0xFF00) >> 8) |
|
|
((n & 0x00FF) << 8);
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n)
|
|
{
|
|
#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC
|
|
#if defined(_MSC_VER)
|
|
return _byteswap_ulong(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT)
|
|
ma_uint32 r;
|
|
__asm__ __volatile__ (
|
|
#if defined(MA_64BIT)
|
|
"rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
|
|
#else
|
|
"rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
|
|
#endif
|
|
);
|
|
return r;
|
|
#else
|
|
return __builtin_bswap32(n);
|
|
#endif
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & 0xFF000000) >> 24) |
|
|
((n & 0x00FF0000) >> 8) |
|
|
((n & 0x0000FF00) << 8) |
|
|
((n & 0x000000FF) << 24);
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n)
|
|
{
|
|
#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC
|
|
#if defined(_MSC_VER)
|
|
return _byteswap_uint64(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
return __builtin_bswap64(n);
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |
|
|
((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |
|
|
((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |
|
|
((n & ((ma_uint64)0x000000FF << 32)) >> 8) |
|
|
((n & ((ma_uint64)0xFF000000 )) << 8) |
|
|
((n & ((ma_uint64)0x00FF0000 )) << 24) |
|
|
((n & ((ma_uint64)0x0000FF00 )) << 40) |
|
|
((n & ((ma_uint64)0x000000FF )) << 56);
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n)
|
|
{
|
|
return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n);
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]);
|
|
}
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p)
|
|
{
|
|
ma_uint8 t;
|
|
t = p[0];
|
|
p[0] = p[2];
|
|
p[2] = t;
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
ma_uint8* pSample = pSamples + (iSample*3);
|
|
ma_dr_wav__bswap_s24(pSample);
|
|
}
|
|
}
|
|
static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n)
|
|
{
|
|
return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n);
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]);
|
|
}
|
|
}
|
|
static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n)
|
|
{
|
|
return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n);
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]);
|
|
}
|
|
}
|
|
static MA_INLINE float ma_dr_wav__bswap_f32(float n)
|
|
{
|
|
union {
|
|
ma_uint32 i;
|
|
float f;
|
|
} x;
|
|
x.f = n;
|
|
x.i = ma_dr_wav__bswap32(x.i);
|
|
return x.f;
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < sampleCount; iSample += 1) {
|
|
pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]);
|
|
}
|
|
}
|
|
static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample)
|
|
{
|
|
switch (bytesPerSample)
|
|
{
|
|
case 1:
|
|
{
|
|
} break;
|
|
case 2:
|
|
{
|
|
ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount);
|
|
} break;
|
|
case 3:
|
|
{
|
|
ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount);
|
|
} break;
|
|
case 4:
|
|
{
|
|
ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount);
|
|
} break;
|
|
case 8:
|
|
{
|
|
ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount);
|
|
} break;
|
|
default:
|
|
{
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
} break;
|
|
}
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container)
|
|
{
|
|
if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) {
|
|
return MA_TRUE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data)
|
|
{
|
|
return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data)
|
|
{
|
|
return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8);
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container)
|
|
{
|
|
if (ma_dr_wav_is_container_be(container)) {
|
|
return ma_dr_wav_bytes_to_u16_be(data);
|
|
} else {
|
|
return ma_dr_wav_bytes_to_u16_le(data);
|
|
}
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data)
|
|
{
|
|
return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24);
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data)
|
|
{
|
|
return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24);
|
|
}
|
|
MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container)
|
|
{
|
|
if (ma_dr_wav_is_container_be(container)) {
|
|
return ma_dr_wav_bytes_to_u32_be(data);
|
|
} else {
|
|
return ma_dr_wav_bytes_to_u32_le(data);
|
|
}
|
|
}
|
|
MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data)
|
|
{
|
|
ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1];
|
|
ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0);
|
|
ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0);
|
|
ma_uint64 significand = (hi << 32) | lo;
|
|
int sign = exponent >> 15;
|
|
exponent &= 0x7FFF;
|
|
if (exponent == 0 && significand == 0) {
|
|
return 0;
|
|
} else if (exponent == 0x7FFF) {
|
|
return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;
|
|
}
|
|
exponent -= 16383;
|
|
if (exponent > 63) {
|
|
return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX;
|
|
} else if (exponent < 1) {
|
|
return 0;
|
|
}
|
|
significand >>= (63 - exponent);
|
|
if (sign) {
|
|
return -(ma_int64)significand;
|
|
} else {
|
|
return (ma_int64)significand;
|
|
}
|
|
}
|
|
MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_DR_WAV_MALLOC(sz);
|
|
}
|
|
MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_DR_WAV_REALLOC(p, sz);
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
MA_DR_WAV_FREE(p);
|
|
}
|
|
MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks == NULL) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks->onMalloc != NULL) {
|
|
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
|
|
}
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
|
|
}
|
|
return NULL;
|
|
}
|
|
MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks == NULL) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
|
|
}
|
|
if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
|
|
void* p2;
|
|
p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
|
|
if (p2 == NULL) {
|
|
return NULL;
|
|
}
|
|
if (p != NULL) {
|
|
MA_DR_WAV_COPY_MEMORY(p2, p, szOld);
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
}
|
|
return p2;
|
|
}
|
|
return NULL;
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (p == NULL || pAllocationCallbacks == NULL) {
|
|
return;
|
|
}
|
|
if (pAllocationCallbacks->onFree != NULL) {
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
}
|
|
}
|
|
MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
return *pAllocationCallbacks;
|
|
} else {
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
allocationCallbacks.pUserData = NULL;
|
|
allocationCallbacks.onMalloc = ma_dr_wav__malloc_default;
|
|
allocationCallbacks.onRealloc = ma_dr_wav__realloc_default;
|
|
allocationCallbacks.onFree = ma_dr_wav__free_default;
|
|
return allocationCallbacks;
|
|
}
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag)
|
|
{
|
|
return
|
|
formatTag == MA_DR_WAVE_FORMAT_ADPCM ||
|
|
formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM;
|
|
}
|
|
MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize)
|
|
{
|
|
return (unsigned int)(chunkSize % 2);
|
|
}
|
|
MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize)
|
|
{
|
|
return (unsigned int)(chunkSize % 8);
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut);
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount);
|
|
MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut)
|
|
{
|
|
if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) {
|
|
ma_uint8 sizeInBytes[4];
|
|
if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) {
|
|
return MA_AT_END;
|
|
}
|
|
if (onRead(pUserData, sizeInBytes, 4) != 4) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container);
|
|
pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes);
|
|
*pRunningBytesReadOut += 8;
|
|
} else if (container == ma_dr_wav_container_w64) {
|
|
ma_uint8 sizeInBytes[8];
|
|
if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) {
|
|
return MA_AT_END;
|
|
}
|
|
if (onRead(pUserData, sizeInBytes, 8) != 8) {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24;
|
|
pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes);
|
|
*pRunningBytesReadOut += 24;
|
|
} else {
|
|
return MA_INVALID_FILE;
|
|
}
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)
|
|
{
|
|
ma_uint64 bytesRemainingToSeek = offset;
|
|
while (bytesRemainingToSeek > 0) {
|
|
if (bytesRemainingToSeek > 0x7FFFFFFF) {
|
|
if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingToSeek -= 0x7FFFFFFF;
|
|
} else {
|
|
if (!onSeek(pUserData, (int)bytesRemainingToSeek, MA_DR_WAV_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingToSeek = 0;
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData)
|
|
{
|
|
if (offset <= 0x7FFFFFFF) {
|
|
return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_SET);
|
|
}
|
|
if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset -= 0x7FFFFFFF;
|
|
for (;;) {
|
|
if (offset <= 0x7FFFFFFF) {
|
|
return onSeek(pUserData, (int)offset, MA_DR_WAV_SEEK_CUR);
|
|
}
|
|
if (!onSeek(pUserData, 0x7FFFFFFF, MA_DR_WAV_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset -= 0x7FFFFFFF;
|
|
}
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)
|
|
{
|
|
size_t bytesRead;
|
|
MA_DR_WAV_ASSERT(onRead != NULL);
|
|
MA_DR_WAV_ASSERT(pCursor != NULL);
|
|
bytesRead = onRead(pUserData, pBufferOut, bytesToRead);
|
|
*pCursor += bytesRead;
|
|
return bytesRead;
|
|
}
|
|
#if 0
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor)
|
|
{
|
|
MA_DR_WAV_ASSERT(onSeek != NULL);
|
|
MA_DR_WAV_ASSERT(pCursor != NULL);
|
|
if (!onSeek(pUserData, offset, origin)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (origin == MA_DR_WAV_SEEK_SET) {
|
|
*pCursor = offset;
|
|
} else {
|
|
*pCursor += offset;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
#define MA_DR_WAV_SMPL_BYTES 36
|
|
#define MA_DR_WAV_SMPL_LOOP_BYTES 24
|
|
#define MA_DR_WAV_INST_BYTES 7
|
|
#define MA_DR_WAV_ACID_BYTES 24
|
|
#define MA_DR_WAV_CUE_BYTES 4
|
|
#define MA_DR_WAV_BEXT_BYTES 602
|
|
#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256
|
|
#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32
|
|
#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32
|
|
#define MA_DR_WAV_BEXT_RESERVED_BYTES 180
|
|
#define MA_DR_WAV_BEXT_UMID_BYTES 64
|
|
#define MA_DR_WAV_CUE_POINT_BYTES 24
|
|
#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4
|
|
#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20
|
|
#define MA_DR_WAV_METADATA_ALIGNMENT 8
|
|
typedef enum
|
|
{
|
|
ma_dr_wav__metadata_parser_stage_count,
|
|
ma_dr_wav__metadata_parser_stage_read
|
|
} ma_dr_wav__metadata_parser_stage;
|
|
typedef struct
|
|
{
|
|
ma_dr_wav_read_proc onRead;
|
|
ma_dr_wav_seek_proc onSeek;
|
|
void *pReadSeekUserData;
|
|
ma_dr_wav__metadata_parser_stage stage;
|
|
ma_dr_wav_metadata *pMetadata;
|
|
ma_uint32 metadataCount;
|
|
ma_uint8 *pData;
|
|
ma_uint8 *pDataCursor;
|
|
ma_uint64 metadataCursor;
|
|
ma_uint64 extraCapacity;
|
|
} ma_dr_wav__metadata_parser;
|
|
MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser)
|
|
{
|
|
ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity;
|
|
if (cap > MA_SIZE_MAX) {
|
|
return 0;
|
|
}
|
|
return (size_t)cap;
|
|
}
|
|
MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align)
|
|
{
|
|
ma_uint8* pResult;
|
|
if (align) {
|
|
ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align;
|
|
if (modulo != 0) {
|
|
pParser->pDataCursor += align - modulo;
|
|
}
|
|
}
|
|
pResult = pParser->pDataCursor;
|
|
MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser)));
|
|
pParser->pDataCursor += size;
|
|
return pResult;
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align)
|
|
{
|
|
size_t extra = bytes + (align ? (align - 1) : 0);
|
|
pParser->extraCapacity += extra;
|
|
}
|
|
MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) {
|
|
pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData);
|
|
pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData);
|
|
pParser->pDataCursor = pParser->pData;
|
|
if (pParser->pData == NULL) {
|
|
return MA_OUT_OF_MEMORY;
|
|
}
|
|
pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1);
|
|
pParser->metadataCursor = 0;
|
|
}
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor != NULL) {
|
|
return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor);
|
|
} else {
|
|
return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead);
|
|
}
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata)
|
|
{
|
|
ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES];
|
|
ma_uint64 totalBytesRead = 0;
|
|
size_t bytesJustRead;
|
|
if (pMetadata == NULL) {
|
|
return 0;
|
|
}
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
MA_DR_WAV_ASSERT(pChunkHeader != NULL);
|
|
if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) {
|
|
ma_uint32 iSampleLoop;
|
|
pMetadata->type = ma_dr_wav_metadata_type_smpl;
|
|
pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0);
|
|
pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4);
|
|
pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8);
|
|
pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12);
|
|
pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16);
|
|
pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20);
|
|
pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24);
|
|
pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28);
|
|
pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32);
|
|
if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) {
|
|
pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT);
|
|
for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) {
|
|
ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES];
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead);
|
|
if (bytesJustRead == sizeof(smplLoopData)) {
|
|
pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0);
|
|
pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4);
|
|
pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8);
|
|
pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12);
|
|
pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16);
|
|
pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
|
|
pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL);
|
|
ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead);
|
|
}
|
|
}
|
|
}
|
|
return totalBytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata)
|
|
{
|
|
ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES];
|
|
ma_uint64 totalBytesRead = 0;
|
|
size_t bytesJustRead;
|
|
if (pMetadata == NULL) {
|
|
return 0;
|
|
}
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
if (bytesJustRead == sizeof(cueHeaderSectionData)) {
|
|
pMetadata->type = ma_dr_wav_metadata_type_cue;
|
|
pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData);
|
|
if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) {
|
|
pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL);
|
|
if (pMetadata->data.cue.cuePointCount > 0) {
|
|
ma_uint32 iCuePoint;
|
|
for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
|
|
ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES];
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead);
|
|
if (bytesJustRead == sizeof(cuePointData)) {
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0);
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4);
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8];
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9];
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10];
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11];
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12);
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16);
|
|
pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return totalBytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)
|
|
{
|
|
ma_uint8 instData[MA_DR_WAV_INST_BYTES];
|
|
ma_uint64 bytesRead;
|
|
if (pMetadata == NULL) {
|
|
return 0;
|
|
}
|
|
bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
if (bytesRead == sizeof(instData)) {
|
|
pMetadata->type = ma_dr_wav_metadata_type_inst;
|
|
pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0];
|
|
pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1];
|
|
pMetadata->data.inst.gainDecibels = (ma_int8)instData[2];
|
|
pMetadata->data.inst.lowNote = (ma_int8)instData[3];
|
|
pMetadata->data.inst.highNote = (ma_int8)instData[4];
|
|
pMetadata->data.inst.lowVelocity = (ma_int8)instData[5];
|
|
pMetadata->data.inst.highVelocity = (ma_int8)instData[6];
|
|
}
|
|
return bytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata)
|
|
{
|
|
ma_uint8 acidData[MA_DR_WAV_ACID_BYTES];
|
|
ma_uint64 bytesRead;
|
|
if (pMetadata == NULL) {
|
|
return 0;
|
|
}
|
|
bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
if (bytesRead == sizeof(acidData)) {
|
|
pMetadata->type = ma_dr_wav_metadata_type_acid;
|
|
pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0);
|
|
pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4);
|
|
pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6);
|
|
pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8);
|
|
pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12);
|
|
pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16);
|
|
pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18);
|
|
pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20);
|
|
}
|
|
return bytesRead;
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__strlen(const char* str)
|
|
{
|
|
size_t result = 0;
|
|
while (*str++) {
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead)
|
|
{
|
|
size_t result = 0;
|
|
while (*str++ && result < maxToRead) {
|
|
result += 1;
|
|
}
|
|
return result;
|
|
}
|
|
MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead)
|
|
{
|
|
size_t len = ma_dr_wav__strlen_clamped(str, maxToRead);
|
|
if (len) {
|
|
char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1);
|
|
MA_DR_WAV_ASSERT(result != NULL);
|
|
MA_DR_WAV_COPY_MEMORY(result, str, len);
|
|
result[len] = '\0';
|
|
return result;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
typedef struct
|
|
{
|
|
const void* pBuffer;
|
|
size_t sizeInBytes;
|
|
size_t cursor;
|
|
} ma_dr_wav_buffer_reader;
|
|
MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader)
|
|
{
|
|
MA_DR_WAV_ASSERT(pBuffer != NULL);
|
|
MA_DR_WAV_ASSERT(pReader != NULL);
|
|
MA_DR_WAV_ZERO_OBJECT(pReader);
|
|
pReader->pBuffer = pBuffer;
|
|
pReader->sizeInBytes = sizeInBytes;
|
|
pReader->cursor = 0;
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader)
|
|
{
|
|
MA_DR_WAV_ASSERT(pReader != NULL);
|
|
return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor);
|
|
}
|
|
MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek)
|
|
{
|
|
MA_DR_WAV_ASSERT(pReader != NULL);
|
|
if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) {
|
|
return MA_BAD_SEEK;
|
|
}
|
|
pReader->cursor += bytesToSeek;
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
size_t bytesRemaining;
|
|
MA_DR_WAV_ASSERT(pReader != NULL);
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = 0;
|
|
}
|
|
bytesRemaining = (pReader->sizeInBytes - pReader->cursor);
|
|
if (bytesToRead > bytesRemaining) {
|
|
bytesToRead = bytesRemaining;
|
|
}
|
|
if (pDst == NULL) {
|
|
result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead);
|
|
} else {
|
|
MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead);
|
|
pReader->cursor += bytesToRead;
|
|
}
|
|
MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes);
|
|
if (result == MA_SUCCESS) {
|
|
if (pBytesRead != NULL) {
|
|
*pBytesRead = bytesToRead;
|
|
}
|
|
}
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst)
|
|
{
|
|
ma_result result;
|
|
size_t bytesRead;
|
|
ma_uint8 data[2];
|
|
MA_DR_WAV_ASSERT(pReader != NULL);
|
|
MA_DR_WAV_ASSERT(pDst != NULL);
|
|
*pDst = 0;
|
|
result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
|
|
if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {
|
|
return result;
|
|
}
|
|
*pDst = ma_dr_wav_bytes_to_u16(data);
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst)
|
|
{
|
|
ma_result result;
|
|
size_t bytesRead;
|
|
ma_uint8 data[4];
|
|
MA_DR_WAV_ASSERT(pReader != NULL);
|
|
MA_DR_WAV_ASSERT(pDst != NULL);
|
|
*pDst = 0;
|
|
result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead);
|
|
if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) {
|
|
return result;
|
|
}
|
|
*pDst = ma_dr_wav_bytes_to_u32(data);
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize)
|
|
{
|
|
ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES];
|
|
size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
if (bytesRead == sizeof(bextData)) {
|
|
ma_dr_wav_buffer_reader reader;
|
|
ma_uint32 timeReferenceLow;
|
|
ma_uint32 timeReferenceHigh;
|
|
size_t extraBytes;
|
|
pMetadata->type = ma_dr_wav_metadata_type_bext;
|
|
if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) {
|
|
pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
|
|
ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
|
|
pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
|
|
ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
|
|
pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
|
|
ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
|
|
ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL);
|
|
ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL);
|
|
ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow);
|
|
ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh);
|
|
pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow;
|
|
ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version);
|
|
pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1);
|
|
ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL);
|
|
ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue);
|
|
ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange);
|
|
ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel);
|
|
ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness);
|
|
ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness);
|
|
MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES));
|
|
extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES);
|
|
if (extraBytes > 0) {
|
|
pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL);
|
|
bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL);
|
|
pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory);
|
|
} else {
|
|
pMetadata->data.bext.pCodingHistory = NULL;
|
|
pMetadata->data.bext.codingHistorySize = 0;
|
|
}
|
|
}
|
|
}
|
|
return bytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type)
|
|
{
|
|
ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES];
|
|
ma_uint64 totalBytesRead = 0;
|
|
size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
if (bytesJustRead == sizeof(cueIDBuffer)) {
|
|
ma_uint32 sizeIncludingNullTerminator;
|
|
pMetadata->type = type;
|
|
pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer);
|
|
sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
|
|
if (sizeIncludingNullTerminator > 0) {
|
|
pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1;
|
|
pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
|
|
ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead);
|
|
} else {
|
|
pMetadata->data.labelOrNote.stringLength = 0;
|
|
pMetadata->data.labelOrNote.pString = NULL;
|
|
}
|
|
}
|
|
return totalBytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize)
|
|
{
|
|
ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES];
|
|
ma_uint64 totalBytesRead = 0;
|
|
size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead);
|
|
MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read);
|
|
if (bytesJustRead == sizeof(buffer)) {
|
|
ma_uint32 sizeIncludingNullTerminator;
|
|
pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region;
|
|
pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0);
|
|
pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4);
|
|
pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8];
|
|
pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9];
|
|
pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10];
|
|
pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11];
|
|
pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12);
|
|
pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14);
|
|
pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16);
|
|
pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18);
|
|
sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
|
|
if (sizeIncludingNullTerminator > 0) {
|
|
pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1;
|
|
pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
|
|
ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead);
|
|
} else {
|
|
pMetadata->data.labelledCueRegion.stringLength = 0;
|
|
pMetadata->data.labelledCueRegion.pString = NULL;
|
|
}
|
|
}
|
|
return totalBytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type)
|
|
{
|
|
ma_uint64 bytesRead = 0;
|
|
ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize;
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
pParser->metadataCount += 1;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1);
|
|
} else {
|
|
ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
|
|
pMetadata->type = type;
|
|
if (stringSizeWithNullTerminator > 0) {
|
|
pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1;
|
|
pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL);
|
|
bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL);
|
|
if (bytesRead == chunkSize) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
} else {
|
|
pMetadata->data.infoText.stringLength = 0;
|
|
pMetadata->data.infoText.pString = NULL;
|
|
pParser->metadataCursor += 1;
|
|
}
|
|
}
|
|
return bytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location)
|
|
{
|
|
ma_uint64 bytesRead = 0;
|
|
if (location == ma_dr_wav_metadata_location_invalid) {
|
|
return 0;
|
|
}
|
|
if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) {
|
|
return 0;
|
|
}
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
pParser->metadataCount += 1;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1);
|
|
} else {
|
|
ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor];
|
|
pMetadata->type = ma_dr_wav_metadata_type_unknown;
|
|
pMetadata->data.unknown.chunkLocation = location;
|
|
pMetadata->data.unknown.id[0] = pChunkId[0];
|
|
pMetadata->data.unknown.id[1] = pChunkId[1];
|
|
pMetadata->data.unknown.id[2] = pChunkId[2];
|
|
pMetadata->data.unknown.id[3] = pChunkId[3];
|
|
pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize;
|
|
pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);
|
|
bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL);
|
|
if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
return bytesRead;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID)
|
|
{
|
|
return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID);
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes)
|
|
{
|
|
const ma_uint8 *pChunkID = pChunkHeader->id.fourcc;
|
|
ma_uint64 bytesRead = 0;
|
|
if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) {
|
|
if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) {
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
ma_uint8 buffer[4];
|
|
size_t bytesJustRead;
|
|
if (!pParser->onSeek(pParser->pReadSeekUserData, 28, MA_DR_WAV_SEEK_CUR)) {
|
|
return bytesRead;
|
|
}
|
|
bytesRead += 28;
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
|
|
if (bytesJustRead == sizeof(buffer)) {
|
|
ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer);
|
|
ma_uint64 calculatedLoopCount;
|
|
calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES;
|
|
if (calculatedLoopCount == loopCount) {
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead);
|
|
if (bytesJustRead == sizeof(buffer)) {
|
|
ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer);
|
|
pParser->metadataCount += 1;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT);
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1);
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
|
|
if (bytesRead == pChunkHeader->sizeInBytes) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) {
|
|
if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) {
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
pParser->metadataCount += 1;
|
|
} else {
|
|
bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
|
|
if (bytesRead == pChunkHeader->sizeInBytes) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) {
|
|
if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) {
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
pParser->metadataCount += 1;
|
|
} else {
|
|
bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]);
|
|
if (bytesRead == pChunkHeader->sizeInBytes) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) {
|
|
if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) {
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
size_t cueCount;
|
|
pParser->metadataCount += 1;
|
|
cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT);
|
|
} else {
|
|
bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]);
|
|
if (bytesRead == pChunkHeader->sizeInBytes) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) {
|
|
if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) {
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1];
|
|
size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES;
|
|
size_t bytesJustRead;
|
|
buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0';
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead);
|
|
if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) {
|
|
return bytesRead;
|
|
}
|
|
allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
|
|
buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0';
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead);
|
|
if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) {
|
|
return bytesRead;
|
|
}
|
|
allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
|
|
buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0';
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead);
|
|
if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) {
|
|
return bytesRead;
|
|
}
|
|
allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1;
|
|
allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES + 1;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1);
|
|
pParser->metadataCount += 1;
|
|
} else {
|
|
bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes);
|
|
if (bytesRead == pChunkHeader->sizeInBytes) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) {
|
|
ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid;
|
|
while (bytesRead < pChunkHeader->sizeInBytes) {
|
|
ma_uint8 subchunkId[4];
|
|
ma_uint8 subchunkSizeBuffer[4];
|
|
ma_uint64 subchunkDataSize;
|
|
ma_uint64 subchunkBytesRead = 0;
|
|
ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead);
|
|
if (bytesJustRead != sizeof(subchunkId)) {
|
|
break;
|
|
}
|
|
if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) {
|
|
listType = ma_dr_wav_metadata_location_inside_adtl_list;
|
|
continue;
|
|
} else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) {
|
|
listType = ma_dr_wav_metadata_location_inside_info_list;
|
|
continue;
|
|
}
|
|
bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead);
|
|
if (bytesJustRead != sizeof(subchunkSizeBuffer)) {
|
|
break;
|
|
}
|
|
subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer);
|
|
if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) {
|
|
if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) {
|
|
ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
pParser->metadataCount += 1;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1);
|
|
} else {
|
|
subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note);
|
|
if (subchunkBytesRead == subchunkDataSize) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) {
|
|
if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) {
|
|
ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
|
|
if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) {
|
|
pParser->metadataCount += 1;
|
|
ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1);
|
|
} else {
|
|
subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize);
|
|
if (subchunkBytesRead == subchunkDataSize) {
|
|
pParser->metadataCursor += 1;
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_location, "IARL")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_location);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_organization, "ICMS")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_organization);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_keywords, "IKEY")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_keywords);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_medium, "IMED")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_medium);
|
|
} else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_description, "ISBJ")) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_description);
|
|
} else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {
|
|
subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType);
|
|
}
|
|
bytesRead += subchunkBytesRead;
|
|
MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize);
|
|
if (subchunkBytesRead < subchunkDataSize) {
|
|
ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead;
|
|
if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR)) {
|
|
break;
|
|
}
|
|
bytesRead += bytesToSeek;
|
|
}
|
|
if ((subchunkDataSize % 2) == 1) {
|
|
if (!pParser->onSeek(pParser->pReadSeekUserData, 1, MA_DR_WAV_SEEK_CUR)) {
|
|
break;
|
|
}
|
|
bytesRead += 1;
|
|
}
|
|
}
|
|
} else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) {
|
|
bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level);
|
|
}
|
|
return bytesRead;
|
|
}
|
|
MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav)
|
|
{
|
|
ma_uint32 bytesPerFrame;
|
|
if ((pWav->bitsPerSample & 0x7) == 0) {
|
|
bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3;
|
|
} else {
|
|
bytesPerFrame = pWav->fmt.blockAlign;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
|
|
if (bytesPerFrame != pWav->fmt.channels) {
|
|
return 0;
|
|
}
|
|
}
|
|
return bytesPerFrame;
|
|
}
|
|
MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT)
|
|
{
|
|
if (pFMT == NULL) {
|
|
return 0;
|
|
}
|
|
if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) {
|
|
return pFMT->formatTag;
|
|
} else {
|
|
return ma_dr_wav_bytes_to_u16(pFMT->subFormat);
|
|
}
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pReadSeekTellUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pWav == NULL || onRead == NULL || onSeek == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));
|
|
pWav->onRead = onRead;
|
|
pWav->onSeek = onSeek;
|
|
pWav->onTell = onTell;
|
|
pWav->pUserData = pReadSeekTellUserData;
|
|
pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
|
|
if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags)
|
|
{
|
|
ma_result result;
|
|
ma_uint64 cursor;
|
|
ma_bool32 sequential;
|
|
ma_uint8 riff[4];
|
|
ma_dr_wav_fmt fmt;
|
|
unsigned short translatedFormatTag;
|
|
ma_uint64 dataChunkSize = 0;
|
|
ma_uint64 sampleCountFromFactChunk = 0;
|
|
ma_uint64 metadataStartPos;
|
|
ma_dr_wav__metadata_parser metadataParser;
|
|
ma_bool8 isProcessingMetadata = MA_FALSE;
|
|
ma_bool8 foundChunk_fmt = MA_FALSE;
|
|
ma_bool8 foundChunk_data = MA_FALSE;
|
|
ma_bool8 isAIFCFormType = MA_FALSE;
|
|
ma_uint64 aiffFrameCount = 0;
|
|
cursor = 0;
|
|
sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0;
|
|
MA_DR_WAV_ZERO_OBJECT(&fmt);
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav_fourcc_equal(riff, "RIFF")) {
|
|
pWav->container = ma_dr_wav_container_riff;
|
|
} else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) {
|
|
pWav->container = ma_dr_wav_container_rifx;
|
|
} else if (ma_dr_wav_fourcc_equal(riff, "riff")) {
|
|
int i;
|
|
ma_uint8 riff2[12];
|
|
pWav->container = ma_dr_wav_container_w64;
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) {
|
|
return MA_FALSE;
|
|
}
|
|
for (i = 0; i < 12; ++i) {
|
|
if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else if (ma_dr_wav_fourcc_equal(riff, "RF64")) {
|
|
pWav->container = ma_dr_wav_container_rf64;
|
|
} else if (ma_dr_wav_fourcc_equal(riff, "FORM")) {
|
|
pWav->container = ma_dr_wav_container_aiff;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) {
|
|
ma_uint8 chunkSizeBytes[4];
|
|
ma_uint8 wave[4];
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
|
|
if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) {
|
|
}
|
|
} else if (pWav->container == ma_dr_wav_container_rf64) {
|
|
if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) {
|
|
return MA_FALSE;
|
|
}
|
|
} else if (pWav->container == ma_dr_wav_container_w64) {
|
|
ma_uint8 chunkSizeBytes[8];
|
|
ma_uint8 wave[16];
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint8 chunkSizeBytes[4];
|
|
ma_uint8 aiff[4];
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) {
|
|
isAIFCFormType = MA_FALSE;
|
|
} else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) {
|
|
isAIFCFormType = MA_TRUE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
if (pWav->container == ma_dr_wav_container_rf64) {
|
|
ma_uint8 sizeBytes[8];
|
|
ma_uint64 bytesRemainingInChunk;
|
|
ma_dr_wav_chunk_header header;
|
|
result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingInChunk = header.sizeInBytes + header.paddingSize;
|
|
if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingInChunk -= 8;
|
|
cursor += 8;
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingInChunk -= 8;
|
|
dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes);
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingInChunk -= 8;
|
|
sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes);
|
|
if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor += bytesRemainingInChunk;
|
|
}
|
|
metadataStartPos = cursor;
|
|
isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0);
|
|
if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) {
|
|
isProcessingMetadata = MA_FALSE;
|
|
}
|
|
MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser));
|
|
if (isProcessingMetadata) {
|
|
metadataParser.onRead = pWav->onRead;
|
|
metadataParser.onSeek = pWav->onSeek;
|
|
metadataParser.pReadSeekUserData = pWav->pUserData;
|
|
metadataParser.stage = ma_dr_wav__metadata_parser_stage_count;
|
|
}
|
|
for (;;) {
|
|
ma_dr_wav_chunk_header header;
|
|
ma_uint64 chunkSize;
|
|
result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
chunkSize = header.sizeInBytes;
|
|
if (!sequential && onChunk != NULL) {
|
|
ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt);
|
|
if (callbackBytesRead > 0) {
|
|
if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) ||
|
|
((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) {
|
|
ma_uint8 fmtData[16];
|
|
foundChunk_fmt = MA_TRUE;
|
|
if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor += sizeof(fmtData);
|
|
fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container);
|
|
fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container);
|
|
fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container);
|
|
fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container);
|
|
fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container);
|
|
fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container);
|
|
fmt.extendedSize = 0;
|
|
fmt.validBitsPerSample = 0;
|
|
fmt.channelMask = 0;
|
|
MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat));
|
|
if (header.sizeInBytes > 16) {
|
|
ma_uint8 fmt_cbSize[2];
|
|
int bytesReadSoFar = 0;
|
|
if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor += sizeof(fmt_cbSize);
|
|
bytesReadSoFar = 18;
|
|
fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container);
|
|
if (fmt.extendedSize > 0) {
|
|
if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
|
|
if (fmt.extendedSize != 22) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
|
|
ma_uint8 fmtext[22];
|
|
if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) {
|
|
return MA_FALSE;
|
|
}
|
|
fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container);
|
|
fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container);
|
|
ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat);
|
|
} else {
|
|
if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, MA_DR_WAV_SEEK_CUR) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
cursor += fmt.extendedSize;
|
|
bytesReadSoFar += fmt.extendedSize;
|
|
}
|
|
if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), MA_DR_WAV_SEEK_CUR) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor += (header.sizeInBytes - bytesReadSoFar);
|
|
}
|
|
if (header.paddingSize > 0) {
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) {
|
|
break;
|
|
}
|
|
cursor += header.paddingSize;
|
|
}
|
|
continue;
|
|
}
|
|
if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) ||
|
|
((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) {
|
|
foundChunk_data = MA_TRUE;
|
|
pWav->dataChunkDataPos = cursor;
|
|
if (pWav->container != ma_dr_wav_container_rf64) {
|
|
dataChunkSize = chunkSize;
|
|
}
|
|
if (sequential || !isProcessingMetadata) {
|
|
break;
|
|
} else {
|
|
chunkSize += header.paddingSize;
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
|
|
break;
|
|
}
|
|
cursor += chunkSize;
|
|
continue;
|
|
}
|
|
}
|
|
if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) ||
|
|
((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) {
|
|
if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) {
|
|
ma_uint8 sampleCount[4];
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) {
|
|
return MA_FALSE;
|
|
}
|
|
chunkSize -= 4;
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
|
|
sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container);
|
|
} else {
|
|
sampleCountFromFactChunk = 0;
|
|
}
|
|
} else if (pWav->container == ma_dr_wav_container_w64) {
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) {
|
|
return MA_FALSE;
|
|
}
|
|
chunkSize -= 8;
|
|
} else if (pWav->container == ma_dr_wav_container_rf64) {
|
|
}
|
|
chunkSize += header.paddingSize;
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
|
|
break;
|
|
}
|
|
cursor += chunkSize;
|
|
continue;
|
|
}
|
|
if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) {
|
|
ma_uint8 commData[24];
|
|
ma_uint32 commDataBytesToRead;
|
|
ma_uint16 channels;
|
|
ma_uint32 frameCount;
|
|
ma_uint16 sampleSizeInBits;
|
|
ma_int64 sampleRate;
|
|
ma_uint16 compressionFormat;
|
|
foundChunk_fmt = MA_TRUE;
|
|
if (isAIFCFormType) {
|
|
commDataBytesToRead = 24;
|
|
if (header.sizeInBytes < commDataBytesToRead) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
commDataBytesToRead = 18;
|
|
if (header.sizeInBytes != commDataBytesToRead) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) {
|
|
return MA_FALSE;
|
|
}
|
|
channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container);
|
|
frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container);
|
|
sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container);
|
|
sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8);
|
|
if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) {
|
|
return MA_FALSE;
|
|
}
|
|
if (isAIFCFormType) {
|
|
const ma_uint8* type = commData + 18;
|
|
if (ma_dr_wav_fourcc_equal(type, "NONE")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_PCM;
|
|
} else if (ma_dr_wav_fourcc_equal(type, "raw ")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_PCM;
|
|
if (sampleSizeInBits == 8) {
|
|
pWav->aiff.isUnsigned = MA_TRUE;
|
|
}
|
|
} else if (ma_dr_wav_fourcc_equal(type, "sowt")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_PCM;
|
|
pWav->aiff.isLE = MA_TRUE;
|
|
} else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT;
|
|
} else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_ALAW;
|
|
} else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_MULAW;
|
|
} else if (ma_dr_wav_fourcc_equal(type, "ima4")) {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM;
|
|
sampleSizeInBits = 4;
|
|
(void)compressionFormat;
|
|
(void)sampleSizeInBits;
|
|
return MA_FALSE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
compressionFormat = MA_DR_WAVE_FORMAT_PCM;
|
|
}
|
|
aiffFrameCount = frameCount;
|
|
fmt.formatTag = compressionFormat;
|
|
fmt.channels = channels;
|
|
fmt.sampleRate = (ma_uint32)sampleRate;
|
|
fmt.bitsPerSample = sampleSizeInBits;
|
|
fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8);
|
|
fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate;
|
|
if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
fmt.blockAlign = 34 * fmt.channels;
|
|
}
|
|
if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) {
|
|
if (fmt.bitsPerSample > 8) {
|
|
fmt.bitsPerSample = 8;
|
|
fmt.blockAlign = fmt.channels;
|
|
}
|
|
}
|
|
fmt.bitsPerSample += (fmt.bitsPerSample & 7);
|
|
if (isAIFCFormType) {
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor += (chunkSize - commDataBytesToRead);
|
|
}
|
|
continue;
|
|
}
|
|
if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) {
|
|
ma_uint8 offsetAndBlockSizeData[8];
|
|
ma_uint32 offset;
|
|
foundChunk_data = MA_TRUE;
|
|
if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container);
|
|
pWav->dataChunkDataPos = cursor + offset;
|
|
dataChunkSize = chunkSize;
|
|
if (dataChunkSize > offset) {
|
|
dataChunkSize -= offset;
|
|
} else {
|
|
dataChunkSize = 0;
|
|
}
|
|
if (sequential) {
|
|
if (foundChunk_fmt) {
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor += offset;
|
|
break;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
chunkSize += header.paddingSize;
|
|
chunkSize -= sizeof(offsetAndBlockSizeData);
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
|
|
break;
|
|
}
|
|
cursor += chunkSize;
|
|
continue;
|
|
}
|
|
}
|
|
if (isProcessingMetadata) {
|
|
ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
|
|
if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) {
|
|
break;
|
|
}
|
|
}
|
|
chunkSize += header.paddingSize;
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) {
|
|
break;
|
|
}
|
|
cursor += chunkSize;
|
|
}
|
|
if (!foundChunk_fmt || !foundChunk_data) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) ||
|
|
(fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) ||
|
|
(fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) ||
|
|
fmt.blockAlign == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
translatedFormatTag = fmt.formatTag;
|
|
if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
|
|
translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container);
|
|
}
|
|
if (!sequential) {
|
|
if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) {
|
|
return MA_FALSE;
|
|
}
|
|
cursor = pWav->dataChunkDataPos;
|
|
}
|
|
if (isProcessingMetadata && metadataParser.metadataCount > 0) {
|
|
if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks);
|
|
if (result != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
metadataParser.stage = ma_dr_wav__metadata_parser_stage_read;
|
|
for (;;) {
|
|
ma_dr_wav_chunk_header header;
|
|
ma_uint64 metadataBytesRead;
|
|
result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header);
|
|
if (result != MA_SUCCESS) {
|
|
break;
|
|
}
|
|
metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown);
|
|
if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) {
|
|
ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
pWav->pMetadata = metadataParser.pMetadata;
|
|
pWav->metadataCount = metadataParser.metadataCount;
|
|
}
|
|
if (pWav->onTell != NULL && pWav->onSeek != NULL) {
|
|
if (pWav->onSeek(pWav->pUserData, 0, MA_DR_WAV_SEEK_END) == MA_TRUE) {
|
|
ma_int64 fileSize;
|
|
if (pWav->onTell(pWav->pUserData, &fileSize)) {
|
|
if (dataChunkSize + pWav->dataChunkDataPos > (ma_uint64)fileSize) {
|
|
dataChunkSize = (ma_uint64)fileSize - pWav->dataChunkDataPos;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) {
|
|
dataChunkSize = 0;
|
|
for (;;) {
|
|
ma_uint8 temp[4096];
|
|
size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp));
|
|
dataChunkSize += bytesRead;
|
|
if (bytesRead < sizeof(temp)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) {
|
|
ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pWav->fmt = fmt;
|
|
pWav->sampleRate = fmt.sampleRate;
|
|
pWav->channels = fmt.channels;
|
|
pWav->bitsPerSample = fmt.bitsPerSample;
|
|
pWav->translatedFormatTag = translatedFormatTag;
|
|
if (!ma_dr_wav__is_compressed_format_tag(translatedFormatTag)) {
|
|
ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame > 0) {
|
|
dataChunkSize -= (dataChunkSize % bytesPerFrame);
|
|
}
|
|
}
|
|
pWav->bytesRemaining = dataChunkSize;
|
|
pWav->dataChunkDataSize = dataChunkSize;
|
|
if (sampleCountFromFactChunk != 0) {
|
|
pWav->totalPCMFrameCount = sampleCountFromFactChunk;
|
|
} else if (aiffFrameCount != 0) {
|
|
pWav->totalPCMFrameCount = aiffFrameCount;
|
|
} else {
|
|
ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame;
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
|
|
ma_uint64 totalBlockHeaderSizeInBytes;
|
|
ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
|
|
if ((blockCount * fmt.blockAlign) < dataChunkSize) {
|
|
blockCount += 1;
|
|
}
|
|
totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels);
|
|
pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
ma_uint64 totalBlockHeaderSizeInBytes;
|
|
ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
|
|
if ((blockCount * fmt.blockAlign) < dataChunkSize) {
|
|
blockCount += 1;
|
|
}
|
|
totalBlockHeaderSizeInBytes = blockCount * (4*fmt.channels);
|
|
pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels;
|
|
pWav->totalPCMFrameCount += blockCount;
|
|
}
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
if (pWav->channels > 2) {
|
|
ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) {
|
|
ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
|
|
ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
|
|
pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
ma_uint64 blockCount = dataChunkSize / fmt.blockAlign;
|
|
pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels;
|
|
}
|
|
#endif
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_ex(pWav, onRead, onSeek, onTell, NULL, pUserData, NULL, 0, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, ma_dr_wav_chunk_proc onChunk, void* pReadSeekTellUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pReadSeekTellUserData, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (!ma_dr_wav_preinit(pWav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);
|
|
}
|
|
MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav)
|
|
{
|
|
ma_dr_wav_metadata *result = pWav->pMetadata;
|
|
pWav->pMetadata = NULL;
|
|
pWav->metadataCount = 0;
|
|
return result;
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize)
|
|
{
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
|
|
return pWav->onWrite(pWav->pUserData, pData, dataSize);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte)
|
|
{
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
|
|
return pWav->onWrite(pWav->pUserData, &byte, 1);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)
|
|
{
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
|
|
if (!ma_dr_wav__is_little_endian()) {
|
|
value = ma_dr_wav__bswap16(value);
|
|
}
|
|
return ma_dr_wav__write(pWav, &value, 2);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)
|
|
{
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
|
|
if (!ma_dr_wav__is_little_endian()) {
|
|
value = ma_dr_wav__bswap32(value);
|
|
}
|
|
return ma_dr_wav__write(pWav, &value, 4);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)
|
|
{
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
|
|
if (!ma_dr_wav__is_little_endian()) {
|
|
value = ma_dr_wav__bswap64(value);
|
|
}
|
|
return ma_dr_wav__write(pWav, &value, 8);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value)
|
|
{
|
|
union {
|
|
ma_uint32 u32;
|
|
float f32;
|
|
} u;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->onWrite != NULL);
|
|
u.f32 = value;
|
|
if (!ma_dr_wav__is_little_endian()) {
|
|
u.u32 = ma_dr_wav__bswap32(u.u32);
|
|
}
|
|
return ma_dr_wav__write(pWav, &u.u32, 4);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize)
|
|
{
|
|
if (pWav == NULL) {
|
|
return dataSize;
|
|
}
|
|
return ma_dr_wav__write(pWav, pData, dataSize);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte)
|
|
{
|
|
if (pWav == NULL) {
|
|
return 1;
|
|
}
|
|
return ma_dr_wav__write_byte(pWav, byte);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value)
|
|
{
|
|
if (pWav == NULL) {
|
|
return 2;
|
|
}
|
|
return ma_dr_wav__write_u16ne_to_le(pWav, value);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value)
|
|
{
|
|
if (pWav == NULL) {
|
|
return 4;
|
|
}
|
|
return ma_dr_wav__write_u32ne_to_le(pWav, value);
|
|
}
|
|
#if 0
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value)
|
|
{
|
|
if (pWav == NULL) {
|
|
return 8;
|
|
}
|
|
return ma_dr_wav__write_u64ne_to_le(pWav, value);
|
|
}
|
|
#endif
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value)
|
|
{
|
|
if (pWav == NULL) {
|
|
return 4;
|
|
}
|
|
return ma_dr_wav__write_f32ne_to_le(pWav, value);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize)
|
|
{
|
|
size_t len;
|
|
if (pWav == NULL) {
|
|
return bufFixedSize;
|
|
}
|
|
len = ma_dr_wav__strlen_clamped(str, bufFixedSize);
|
|
ma_dr_wav__write_or_count(pWav, str, len);
|
|
if (len < bufFixedSize) {
|
|
size_t i;
|
|
for (i = 0; i < bufFixedSize - len; ++i) {
|
|
ma_dr_wav__write_byte(pWav, 0);
|
|
}
|
|
}
|
|
return bufFixedSize;
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount)
|
|
{
|
|
size_t bytesWritten = 0;
|
|
ma_bool32 hasListAdtl = MA_FALSE;
|
|
ma_bool32 hasListInfo = MA_FALSE;
|
|
ma_uint32 iMetadata;
|
|
if (pMetadatas == NULL || metadataCount == 0) {
|
|
return 0;
|
|
}
|
|
for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
|
|
ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
|
|
ma_uint32 chunkSize = 0;
|
|
if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) {
|
|
hasListInfo = MA_TRUE;
|
|
}
|
|
if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) {
|
|
hasListAdtl = MA_TRUE;
|
|
}
|
|
switch (pMetadata->type) {
|
|
case ma_dr_wav_metadata_type_smpl:
|
|
{
|
|
ma_uint32 iLoop;
|
|
chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
|
|
for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) {
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleOffset);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleOffset);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount);
|
|
}
|
|
if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) {
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes);
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_inst:
|
|
{
|
|
chunkSize = MA_DR_WAV_INST_BYTES;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1);
|
|
} break;
|
|
case ma_dr_wav_metadata_type_cue:
|
|
{
|
|
ma_uint32 iCuePoint;
|
|
chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount);
|
|
for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) {
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleOffset);
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_acid:
|
|
{
|
|
chunkSize = MA_DR_WAV_ACID_BYTES;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1);
|
|
bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator);
|
|
bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo);
|
|
} break;
|
|
case ma_dr_wav_metadata_type_bext:
|
|
{
|
|
char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES];
|
|
ma_uint32 timeReferenceLow;
|
|
ma_uint32 timeReferenceHigh;
|
|
chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES);
|
|
bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES);
|
|
bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate));
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime));
|
|
timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF);
|
|
timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness);
|
|
MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf));
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf));
|
|
if (pMetadata->data.bext.codingHistorySize > 0) {
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize);
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_unknown:
|
|
{
|
|
if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) {
|
|
chunkSize = pMetadata->data.unknown.dataSizeInBytes;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes);
|
|
}
|
|
} break;
|
|
default: break;
|
|
}
|
|
if ((chunkSize % 2) != 0) {
|
|
bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
|
|
}
|
|
}
|
|
if (hasListInfo) {
|
|
ma_uint32 chunkSize = 4;
|
|
for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
|
|
ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
|
|
if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) {
|
|
chunkSize += 8;
|
|
chunkSize += pMetadata->data.infoText.stringLength + 1;
|
|
} else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {
|
|
chunkSize += 8;
|
|
chunkSize += pMetadata->data.unknown.dataSizeInBytes;
|
|
}
|
|
if ((chunkSize % 2) != 0) {
|
|
chunkSize += 1;
|
|
}
|
|
}
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4);
|
|
for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
|
|
ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
|
|
ma_uint32 subchunkSize = 0;
|
|
if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) {
|
|
const char* pID = NULL;
|
|
switch (pMetadata->type) {
|
|
case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break;
|
|
case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break;
|
|
case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break;
|
|
case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break;
|
|
case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break;
|
|
case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break;
|
|
case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break;
|
|
case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break;
|
|
case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break;
|
|
case ma_dr_wav_metadata_type_list_info_location: pID = "IARL"; break;
|
|
case ma_dr_wav_metadata_type_list_info_organization: pID = "ICMS"; break;
|
|
case ma_dr_wav_metadata_type_list_info_keywords: pID = "IKEY"; break;
|
|
case ma_dr_wav_metadata_type_list_info_medium: pID = "IMED"; break;
|
|
case ma_dr_wav_metadata_type_list_info_description: pID = "ISBJ"; break;
|
|
default: break;
|
|
}
|
|
MA_DR_WAV_ASSERT(pID != NULL);
|
|
if (pMetadata->data.infoText.stringLength) {
|
|
subchunkSize = pMetadata->data.infoText.stringLength + 1;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength);
|
|
bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
|
|
}
|
|
} else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) {
|
|
if (pMetadata->data.unknown.dataSizeInBytes) {
|
|
subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
|
|
}
|
|
}
|
|
if ((subchunkSize % 2) != 0) {
|
|
bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
|
|
}
|
|
}
|
|
}
|
|
if (hasListAdtl) {
|
|
ma_uint32 chunkSize = 4;
|
|
for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
|
|
ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
|
|
switch (pMetadata->type)
|
|
{
|
|
case ma_dr_wav_metadata_type_list_label:
|
|
case ma_dr_wav_metadata_type_list_note:
|
|
{
|
|
chunkSize += 8;
|
|
chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
|
|
if (pMetadata->data.labelOrNote.stringLength > 0) {
|
|
chunkSize += pMetadata->data.labelOrNote.stringLength + 1;
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_list_labelled_cue_region:
|
|
{
|
|
chunkSize += 8;
|
|
chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
|
|
if (pMetadata->data.labelledCueRegion.stringLength > 0) {
|
|
chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_unknown:
|
|
{
|
|
if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {
|
|
chunkSize += 8;
|
|
chunkSize += pMetadata->data.unknown.dataSizeInBytes;
|
|
}
|
|
} break;
|
|
default: break;
|
|
}
|
|
if ((chunkSize % 2) != 0) {
|
|
chunkSize += 1;
|
|
}
|
|
}
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4);
|
|
for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) {
|
|
ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata];
|
|
ma_uint32 subchunkSize = 0;
|
|
switch (pMetadata->type)
|
|
{
|
|
case ma_dr_wav_metadata_type_list_label:
|
|
case ma_dr_wav_metadata_type_list_note:
|
|
{
|
|
if (pMetadata->data.labelOrNote.stringLength > 0) {
|
|
const char *pID = NULL;
|
|
if (pMetadata->type == ma_dr_wav_metadata_type_list_label) {
|
|
pID = "labl";
|
|
}
|
|
else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) {
|
|
pID = "note";
|
|
}
|
|
MA_DR_WAV_ASSERT(pID != NULL);
|
|
MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL);
|
|
subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4);
|
|
subchunkSize += pMetadata->data.labelOrNote.stringLength + 1;
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength);
|
|
bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_list_labelled_cue_region:
|
|
{
|
|
subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES;
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4);
|
|
if (pMetadata->data.labelledCueRegion.stringLength > 0) {
|
|
subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1;
|
|
}
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect);
|
|
bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage);
|
|
if (pMetadata->data.labelledCueRegion.stringLength > 0) {
|
|
MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength);
|
|
bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0');
|
|
}
|
|
} break;
|
|
case ma_dr_wav_metadata_type_unknown:
|
|
{
|
|
if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) {
|
|
subchunkSize = pMetadata->data.unknown.dataSizeInBytes;
|
|
MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4);
|
|
bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize);
|
|
bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize);
|
|
}
|
|
} break;
|
|
default: break;
|
|
}
|
|
if ((subchunkSize % 2) != 0) {
|
|
bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0);
|
|
}
|
|
}
|
|
}
|
|
MA_DR_WAV_ASSERT((bytesWritten % 2) == 0);
|
|
return bytesWritten;
|
|
}
|
|
MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
|
|
{
|
|
ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize);
|
|
if (chunkSize > 0xFFFFFFFFUL) {
|
|
chunkSize = 0xFFFFFFFFUL;
|
|
}
|
|
return (ma_uint32)chunkSize;
|
|
}
|
|
MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize)
|
|
{
|
|
if (dataChunkSize <= 0xFFFFFFFFUL) {
|
|
return (ma_uint32)dataChunkSize;
|
|
} else {
|
|
return 0xFFFFFFFFUL;
|
|
}
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize)
|
|
{
|
|
ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize);
|
|
return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize)
|
|
{
|
|
return 24 + dataChunkSize;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata)
|
|
{
|
|
ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize);
|
|
if (chunkSize > 0xFFFFFFFFUL) {
|
|
chunkSize = 0xFFFFFFFFUL;
|
|
}
|
|
return chunkSize;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize)
|
|
{
|
|
return dataChunkSize;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pWav == NULL || onWrite == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!isSequential && onSeek == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav));
|
|
pWav->onWrite = onWrite;
|
|
pWav->onSeek = onSeek;
|
|
pWav->pUserData = pUserData;
|
|
pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
|
|
if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->fmt.formatTag = (ma_uint16)pFormat->format;
|
|
pWav->fmt.channels = (ma_uint16)pFormat->channels;
|
|
pWav->fmt.sampleRate = pFormat->sampleRate;
|
|
pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8);
|
|
pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8);
|
|
pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample;
|
|
pWav->fmt.extendedSize = 0;
|
|
pWav->isSequentialWrite = isSequential;
|
|
return MA_TRUE;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount)
|
|
{
|
|
size_t runningPos = 0;
|
|
ma_uint64 initialDataChunkSize = 0;
|
|
ma_uint64 chunkSizeFMT;
|
|
if (pWav->isSequentialWrite) {
|
|
initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8;
|
|
if (pFormat->container == ma_dr_wav_container_riff) {
|
|
if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize;
|
|
if (pFormat->container == ma_dr_wav_container_riff) {
|
|
ma_uint32 chunkSizeRIFF = 36 + (ma_uint32)initialDataChunkSize;
|
|
runningPos += ma_dr_wav__write(pWav, "RIFF", 4);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF);
|
|
runningPos += ma_dr_wav__write(pWav, "WAVE", 4);
|
|
} else if (pFormat->container == ma_dr_wav_container_w64) {
|
|
ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize;
|
|
runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16);
|
|
runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF);
|
|
runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16);
|
|
} else if (pFormat->container == ma_dr_wav_container_rf64) {
|
|
runningPos += ma_dr_wav__write(pWav, "RF64", 4);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
|
|
runningPos += ma_dr_wav__write(pWav, "WAVE", 4);
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFormat->container == ma_dr_wav_container_rf64) {
|
|
ma_uint32 initialds64ChunkSize = 28;
|
|
ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize;
|
|
runningPos += ma_dr_wav__write(pWav, "ds64", 4);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize);
|
|
runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize);
|
|
runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize);
|
|
runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0);
|
|
}
|
|
if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) {
|
|
chunkSizeFMT = 16;
|
|
runningPos += ma_dr_wav__write(pWav, "fmt ", 4);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT);
|
|
} else if (pFormat->container == ma_dr_wav_container_w64) {
|
|
chunkSizeFMT = 40;
|
|
runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16);
|
|
runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT);
|
|
}
|
|
runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag);
|
|
runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec);
|
|
runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign);
|
|
runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample);
|
|
if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) {
|
|
runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount);
|
|
}
|
|
pWav->dataChunkDataPos = runningPos;
|
|
if (pFormat->container == ma_dr_wav_container_riff) {
|
|
ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize;
|
|
runningPos += ma_dr_wav__write(pWav, "data", 4);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA);
|
|
} else if (pFormat->container == ma_dr_wav_container_w64) {
|
|
ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize;
|
|
runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16);
|
|
runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA);
|
|
} else if (pFormat->container == ma_dr_wav_container_rf64) {
|
|
runningPos += ma_dr_wav__write(pWav, "data", 4);
|
|
runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF);
|
|
}
|
|
pWav->container = pFormat->container;
|
|
pWav->channels = (ma_uint16)pFormat->channels;
|
|
pWav->sampleRate = pFormat->sampleRate;
|
|
pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample;
|
|
pWav->translatedFormatTag = (ma_uint16)pFormat->format;
|
|
pWav->dataChunkDataPos = runningPos;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_write__internal(pWav, pFormat, 0);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFormat == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
|
|
{
|
|
if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->pMetadata = pMetadata;
|
|
pWav->metadataCount = metadataCount;
|
|
return ma_dr_wav_init_write__internal(pWav, pFormat, 0);
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount)
|
|
{
|
|
ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0);
|
|
ma_uint64 riffChunkSizeBytes;
|
|
ma_uint64 fileSizeBytes = 0;
|
|
if (pFormat->container == ma_dr_wav_container_riff) {
|
|
riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount);
|
|
fileSizeBytes = (8 + riffChunkSizeBytes);
|
|
} else if (pFormat->container == ma_dr_wav_container_w64) {
|
|
riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes);
|
|
fileSizeBytes = riffChunkSizeBytes;
|
|
} else if (pFormat->container == ma_dr_wav_container_rf64) {
|
|
riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount);
|
|
fileSizeBytes = (8 + riffChunkSizeBytes);
|
|
}
|
|
return fileSizeBytes;
|
|
}
|
|
#ifndef MA_DR_WAV_NO_STDIO
|
|
MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite)
|
|
{
|
|
return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData);
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
|
|
{
|
|
int whence = SEEK_SET;
|
|
if (origin == MA_DR_WAV_SEEK_CUR) {
|
|
whence = SEEK_CUR;
|
|
} else if (origin == MA_DR_WAV_SEEK_END) {
|
|
whence = SEEK_END;
|
|
}
|
|
return fseek((FILE*)pUserData, offset, whence) == 0;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_stdio(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
FILE* pFileStdio = (FILE*)pUserData;
|
|
ma_int64 result;
|
|
MA_DR_WAV_ASSERT(pFileStdio != NULL);
|
|
MA_DR_WAV_ASSERT(pCursor != NULL);
|
|
#if defined(_WIN32) && !defined(NXDK)
|
|
#if defined(_MSC_VER) && _MSC_VER > 1200
|
|
result = _ftelli64(pFileStdio);
|
|
#else
|
|
result = ftell(pFileStdio);
|
|
#endif
|
|
#else
|
|
result = ftell(pFileStdio);
|
|
#endif
|
|
*pCursor = result;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bool32 result;
|
|
result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, ma_dr_wav__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
|
|
if (result != MA_TRUE) {
|
|
fclose(pFile);
|
|
return result;
|
|
}
|
|
result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
|
|
if (result != MA_TRUE) {
|
|
fclose(pFile);
|
|
return result;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
FILE* pFile;
|
|
if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_WCHAR
|
|
MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
FILE* pFile;
|
|
if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks);
|
|
}
|
|
#endif
|
|
MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
FILE* pFile;
|
|
if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_WCHAR
|
|
MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
FILE* pFile;
|
|
if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks);
|
|
}
|
|
#endif
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bool32 result;
|
|
result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
|
|
if (result != MA_TRUE) {
|
|
fclose(pFile);
|
|
return result;
|
|
}
|
|
result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
|
|
if (result != MA_TRUE) {
|
|
fclose(pFile);
|
|
return result;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
FILE* pFile;
|
|
if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_WCHAR
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
FILE* pFile;
|
|
if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks);
|
|
}
|
|
#endif
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFormat == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_WCHAR
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFormat == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
|
|
}
|
|
#endif
|
|
#endif
|
|
MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
|
|
size_t bytesRemaining;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos);
|
|
bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos;
|
|
if (bytesToRead > bytesRemaining) {
|
|
bytesToRead = bytesRemaining;
|
|
}
|
|
if (bytesToRead > 0) {
|
|
MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead);
|
|
pWav->memoryStream.currentReadPos += bytesToRead;
|
|
}
|
|
return bytesToRead;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
|
|
{
|
|
ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
|
|
ma_int64 newCursor;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
if (origin == MA_DR_WAV_SEEK_SET) {
|
|
newCursor = 0;
|
|
} else if (origin == MA_DR_WAV_SEEK_CUR) {
|
|
newCursor = (ma_int64)pWav->memoryStream.currentReadPos;
|
|
} else if (origin == MA_DR_WAV_SEEK_END) {
|
|
newCursor = (ma_int64)pWav->memoryStream.dataSize;
|
|
} else {
|
|
MA_DR_WAV_ASSERT(!"Invalid seek origin");
|
|
return MA_FALSE;
|
|
}
|
|
newCursor += offset;
|
|
if (newCursor < 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((size_t)newCursor > pWav->memoryStream.dataSize) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->memoryStream.currentReadPos = (size_t)newCursor;
|
|
return MA_TRUE;
|
|
}
|
|
MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite)
|
|
{
|
|
ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
|
|
size_t bytesRemaining;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos);
|
|
bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos;
|
|
if (bytesRemaining < bytesToWrite) {
|
|
void* pNewData;
|
|
size_t newDataCapacity = (pWav->memoryStreamWrite.dataCapacity == 0) ? 256 : pWav->memoryStreamWrite.dataCapacity * 2;
|
|
if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) {
|
|
newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite;
|
|
}
|
|
pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks);
|
|
if (pNewData == NULL) {
|
|
return 0;
|
|
}
|
|
*pWav->memoryStreamWrite.ppData = pNewData;
|
|
pWav->memoryStreamWrite.dataCapacity = newDataCapacity;
|
|
}
|
|
MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite);
|
|
pWav->memoryStreamWrite.currentWritePos += bytesToWrite;
|
|
if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) {
|
|
pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos;
|
|
}
|
|
*pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize;
|
|
return bytesToWrite;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin)
|
|
{
|
|
ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
|
|
ma_int64 newCursor;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
if (origin == MA_DR_WAV_SEEK_SET) {
|
|
newCursor = 0;
|
|
} else if (origin == MA_DR_WAV_SEEK_CUR) {
|
|
newCursor = (ma_int64)pWav->memoryStreamWrite.currentWritePos;
|
|
} else if (origin == MA_DR_WAV_SEEK_END) {
|
|
newCursor = (ma_int64)pWav->memoryStreamWrite.dataSize;
|
|
} else {
|
|
MA_DR_WAV_ASSERT(!"Invalid seek origin");
|
|
return MA_FALSE;
|
|
}
|
|
newCursor += offset;
|
|
if (newCursor < 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((size_t)newCursor > pWav->memoryStreamWrite.dataSize) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->memoryStreamWrite.currentWritePos = (size_t)newCursor;
|
|
return MA_TRUE;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav__on_tell_memory(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_dr_wav* pWav = (ma_dr_wav*)pUserData;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(pCursor != NULL);
|
|
*pCursor = (ma_int64)pWav->memoryStream.currentReadPos;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (data == NULL || dataSize == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->memoryStream.data = (const ma_uint8*)data;
|
|
pWav->memoryStream.dataSize = dataSize;
|
|
pWav->memoryStream.currentReadPos = 0;
|
|
return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (data == NULL || dataSize == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, ma_dr_wav__on_tell_memory, pWav, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->memoryStream.data = (const ma_uint8*)data;
|
|
pWav->memoryStream.dataSize = dataSize;
|
|
pWav->memoryStream.currentReadPos = 0;
|
|
return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA);
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (ppData == NULL || pDataSize == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
*ppData = NULL;
|
|
*pDataSize = 0;
|
|
if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->memoryStreamWrite.ppData = ppData;
|
|
pWav->memoryStreamWrite.pDataSize = pDataSize;
|
|
pWav->memoryStreamWrite.dataSize = 0;
|
|
pWav->memoryStreamWrite.dataCapacity = 0;
|
|
pWav->memoryStreamWrite.currentWritePos = 0;
|
|
return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pFormat == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav)
|
|
{
|
|
ma_result result = MA_SUCCESS;
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
if (pWav->onWrite != NULL) {
|
|
ma_uint32 paddingSize = 0;
|
|
if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) {
|
|
paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize);
|
|
} else {
|
|
paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize);
|
|
}
|
|
if (paddingSize > 0) {
|
|
ma_uint64 paddingData = 0;
|
|
ma_dr_wav__write(pWav, &paddingData, paddingSize);
|
|
}
|
|
if (pWav->onSeek && !pWav->isSequentialWrite) {
|
|
if (pWav->container == ma_dr_wav_container_riff) {
|
|
if (pWav->onSeek(pWav->pUserData, 4, MA_DR_WAV_SEEK_SET)) {
|
|
ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
|
|
ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize);
|
|
}
|
|
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, MA_DR_WAV_SEEK_SET)) {
|
|
ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize);
|
|
ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize);
|
|
}
|
|
} else if (pWav->container == ma_dr_wav_container_w64) {
|
|
if (pWav->onSeek(pWav->pUserData, 16, MA_DR_WAV_SEEK_SET)) {
|
|
ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize);
|
|
ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);
|
|
}
|
|
if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, MA_DR_WAV_SEEK_SET)) {
|
|
ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize);
|
|
ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);
|
|
}
|
|
} else if (pWav->container == ma_dr_wav_container_rf64) {
|
|
int ds64BodyPos = 12 + 8;
|
|
if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, MA_DR_WAV_SEEK_SET)) {
|
|
ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount);
|
|
ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize);
|
|
}
|
|
if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, MA_DR_WAV_SEEK_SET)) {
|
|
ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize);
|
|
ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize);
|
|
}
|
|
}
|
|
}
|
|
if (pWav->isSequentialWrite) {
|
|
if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) {
|
|
result = MA_INVALID_FILE;
|
|
}
|
|
}
|
|
} else {
|
|
ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_STDIO
|
|
if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) {
|
|
fclose((FILE*)pWav->pUserData);
|
|
}
|
|
#endif
|
|
return result;
|
|
}
|
|
MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut)
|
|
{
|
|
size_t bytesRead;
|
|
ma_uint32 bytesPerFrame;
|
|
if (pWav == NULL || bytesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (bytesToRead > pWav->bytesRemaining) {
|
|
bytesToRead = (size_t)pWav->bytesRemaining;
|
|
}
|
|
if (bytesToRead == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut != NULL) {
|
|
bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead);
|
|
} else {
|
|
bytesRead = 0;
|
|
while (bytesRead < bytesToRead) {
|
|
size_t bytesToSeek = (bytesToRead - bytesRead);
|
|
if (bytesToSeek > 0x7FFFFFFF) {
|
|
bytesToSeek = 0x7FFFFFFF;
|
|
}
|
|
if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, MA_DR_WAV_SEEK_CUR) == MA_FALSE) {
|
|
break;
|
|
}
|
|
bytesRead += bytesToSeek;
|
|
}
|
|
while (bytesRead < bytesToRead) {
|
|
ma_uint8 buffer[4096];
|
|
size_t bytesSeeked;
|
|
size_t bytesToSeek = (bytesToRead - bytesRead);
|
|
if (bytesToSeek > sizeof(buffer)) {
|
|
bytesToSeek = sizeof(buffer);
|
|
}
|
|
bytesSeeked = pWav->onRead(pWav->pUserData, buffer, bytesToSeek);
|
|
bytesRead += bytesSeeked;
|
|
if (bytesSeeked < bytesToSeek) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
pWav->readCursorInPCMFrames += bytesRead / bytesPerFrame;
|
|
pWav->bytesRemaining -= bytesRead;
|
|
return bytesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
|
|
{
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint64 bytesToRead;
|
|
ma_uint64 framesRemainingInFile;
|
|
if (pWav == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
|
|
return 0;
|
|
}
|
|
framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames;
|
|
if (framesToRead > framesRemainingInFile) {
|
|
framesToRead = framesRemainingInFile;
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesToRead = framesToRead * bytesPerFrame;
|
|
if (bytesToRead > MA_SIZE_MAX) {
|
|
bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame;
|
|
}
|
|
if (bytesToRead == 0) {
|
|
return 0;
|
|
}
|
|
return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL) {
|
|
ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = 0;
|
|
if (ma_dr_wav_is_container_be(pWav->container)) {
|
|
if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) {
|
|
if (ma_dr_wav__is_little_endian()) {
|
|
framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
|
|
} else {
|
|
framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
|
|
}
|
|
goto post_process;
|
|
}
|
|
}
|
|
if (ma_dr_wav__is_little_endian()) {
|
|
framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut);
|
|
} else {
|
|
framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut);
|
|
}
|
|
post_process:
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) {
|
|
if (pBufferOut != NULL) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) {
|
|
((ma_uint8*)pBufferOut)[iSample] += 128;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav)
|
|
{
|
|
if (pWav->onWrite != NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, MA_DR_WAV_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
|
|
MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm);
|
|
} else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
MA_DR_WAV_ZERO_OBJECT(&pWav->ima);
|
|
} else {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
}
|
|
}
|
|
pWav->readCursorInPCMFrames = 0;
|
|
pWav->bytesRemaining = pWav->dataChunkDataSize;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex)
|
|
{
|
|
if (pWav == NULL || pWav->onSeek == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pWav->onWrite != NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pWav->totalPCMFrameCount == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
if (targetFrameIndex > pWav->totalPCMFrameCount) {
|
|
targetFrameIndex = pWav->totalPCMFrameCount;
|
|
}
|
|
if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) {
|
|
if (targetFrameIndex < pWav->readCursorInPCMFrames) {
|
|
if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (targetFrameIndex > pWav->readCursorInPCMFrames) {
|
|
ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames;
|
|
ma_int16 devnull[2048];
|
|
while (offsetInFrames > 0) {
|
|
ma_uint64 framesRead = 0;
|
|
ma_uint64 framesToRead = offsetInFrames;
|
|
if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) {
|
|
framesToRead = ma_dr_wav_countof(devnull)/pWav->channels;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
|
|
framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull);
|
|
} else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull);
|
|
} else {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
}
|
|
if (framesRead != framesToRead) {
|
|
return MA_FALSE;
|
|
}
|
|
offsetInFrames -= framesRead;
|
|
}
|
|
}
|
|
} else {
|
|
ma_uint64 totalSizeInBytes;
|
|
ma_uint64 currentBytePos;
|
|
ma_uint64 targetBytePos;
|
|
ma_uint64 offset;
|
|
ma_uint32 bytesPerFrame;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame;
|
|
currentBytePos = totalSizeInBytes - pWav->bytesRemaining;
|
|
targetBytePos = targetFrameIndex * bytesPerFrame;
|
|
if (currentBytePos < targetBytePos) {
|
|
offset = (targetBytePos - currentBytePos);
|
|
} else {
|
|
if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset = targetBytePos;
|
|
}
|
|
while (offset > 0) {
|
|
int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset);
|
|
if (!pWav->onSeek(pWav->pUserData, offset32, MA_DR_WAV_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
pWav->readCursorInPCMFrames += offset32 / bytesPerFrame;
|
|
pWav->bytesRemaining -= offset32;
|
|
offset -= offset32;
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor)
|
|
{
|
|
if (pCursor == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
*pCursor = 0;
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
*pCursor = pWav->readCursorInPCMFrames;
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength)
|
|
{
|
|
if (pLength == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
*pLength = 0;
|
|
if (pWav == NULL) {
|
|
return MA_INVALID_ARGS;
|
|
}
|
|
*pLength = pWav->totalPCMFrameCount;
|
|
return MA_SUCCESS;
|
|
}
|
|
MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData)
|
|
{
|
|
size_t bytesWritten;
|
|
if (pWav == NULL || bytesToWrite == 0 || pData == NULL) {
|
|
return 0;
|
|
}
|
|
bytesWritten = pWav->onWrite(pWav->pUserData, pData, bytesToWrite);
|
|
pWav->dataChunkDataSize += bytesWritten;
|
|
return bytesWritten;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
|
|
{
|
|
ma_uint64 bytesToWrite;
|
|
ma_uint64 bytesWritten;
|
|
const ma_uint8* pRunningData;
|
|
if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
|
|
return 0;
|
|
}
|
|
bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
|
|
if (bytesToWrite > MA_SIZE_MAX) {
|
|
return 0;
|
|
}
|
|
bytesWritten = 0;
|
|
pRunningData = (const ma_uint8*)pData;
|
|
while (bytesToWrite > 0) {
|
|
size_t bytesJustWritten;
|
|
ma_uint64 bytesToWriteThisIteration;
|
|
bytesToWriteThisIteration = bytesToWrite;
|
|
MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);
|
|
bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData);
|
|
if (bytesJustWritten == 0) {
|
|
break;
|
|
}
|
|
bytesToWrite -= bytesJustWritten;
|
|
bytesWritten += bytesJustWritten;
|
|
pRunningData += bytesJustWritten;
|
|
}
|
|
return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
|
|
{
|
|
ma_uint64 bytesToWrite;
|
|
ma_uint64 bytesWritten;
|
|
ma_uint32 bytesPerSample;
|
|
const ma_uint8* pRunningData;
|
|
if (pWav == NULL || framesToWrite == 0 || pData == NULL) {
|
|
return 0;
|
|
}
|
|
bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8);
|
|
if (bytesToWrite > MA_SIZE_MAX) {
|
|
return 0;
|
|
}
|
|
bytesWritten = 0;
|
|
pRunningData = (const ma_uint8*)pData;
|
|
bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels;
|
|
if (bytesPerSample == 0) {
|
|
return 0;
|
|
}
|
|
while (bytesToWrite > 0) {
|
|
ma_uint8 temp[4096];
|
|
ma_uint32 sampleCount;
|
|
size_t bytesJustWritten;
|
|
ma_uint64 bytesToWriteThisIteration;
|
|
bytesToWriteThisIteration = bytesToWrite;
|
|
MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX);
|
|
sampleCount = sizeof(temp)/bytesPerSample;
|
|
if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) {
|
|
bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample;
|
|
}
|
|
MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration);
|
|
ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample);
|
|
bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp);
|
|
if (bytesJustWritten == 0) {
|
|
break;
|
|
}
|
|
bytesToWrite -= bytesJustWritten;
|
|
bytesWritten += bytesJustWritten;
|
|
pRunningData += bytesJustWritten;
|
|
}
|
|
return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData)
|
|
{
|
|
if (ma_dr_wav__is_little_endian()) {
|
|
return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData);
|
|
} else {
|
|
return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData);
|
|
}
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
static const ma_int32 adaptationTable[] = {
|
|
230, 230, 230, 230, 307, 409, 512, 614,
|
|
768, 614, 512, 409, 307, 230, 230, 230
|
|
};
|
|
static const ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 };
|
|
static const ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 };
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(framesToRead > 0);
|
|
while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
|
|
MA_DR_WAV_ASSERT(framesToRead > 0);
|
|
if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) {
|
|
if (pWav->channels == 1) {
|
|
ma_uint8 header[7];
|
|
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
|
|
return totalFramesRead;
|
|
}
|
|
pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
|
|
pWav->msadpcm.predictor[0] = header[0];
|
|
pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1);
|
|
pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3);
|
|
pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5);
|
|
pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0];
|
|
pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1];
|
|
pWav->msadpcm.cachedFrameCount = 2;
|
|
if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff2Table)) {
|
|
return totalFramesRead;
|
|
}
|
|
} else {
|
|
ma_uint8 header[14];
|
|
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
|
|
return totalFramesRead;
|
|
}
|
|
pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
|
|
pWav->msadpcm.predictor[0] = header[0];
|
|
pWav->msadpcm.predictor[1] = header[1];
|
|
pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2);
|
|
pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4);
|
|
pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6);
|
|
pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8);
|
|
pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10);
|
|
pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12);
|
|
pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0];
|
|
pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0];
|
|
pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1];
|
|
pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[1][1];
|
|
pWav->msadpcm.cachedFrameCount = 2;
|
|
if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff2Table) ||
|
|
pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) {
|
|
return totalFramesRead;
|
|
}
|
|
}
|
|
}
|
|
while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
|
|
if (pBufferOut != NULL) {
|
|
ma_uint32 iSample = 0;
|
|
for (iSample = 0; iSample < pWav->channels; iSample += 1) {
|
|
pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample];
|
|
}
|
|
pBufferOut += pWav->channels;
|
|
}
|
|
framesToRead -= 1;
|
|
totalFramesRead += 1;
|
|
pWav->readCursorInPCMFrames += 1;
|
|
pWav->msadpcm.cachedFrameCount -= 1;
|
|
}
|
|
if (framesToRead == 0) {
|
|
break;
|
|
}
|
|
if (pWav->msadpcm.cachedFrameCount == 0) {
|
|
if (pWav->msadpcm.bytesRemainingInBlock == 0) {
|
|
continue;
|
|
} else {
|
|
ma_uint8 nibbles;
|
|
ma_int32 nibble0;
|
|
ma_int32 nibble1;
|
|
if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) {
|
|
return totalFramesRead;
|
|
}
|
|
pWav->msadpcm.bytesRemainingInBlock -= 1;
|
|
nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; }
|
|
nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; }
|
|
if (pWav->channels == 1) {
|
|
ma_int32 newSample0;
|
|
ma_int32 newSample1;
|
|
if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff2Table)) {
|
|
return totalFramesRead;
|
|
}
|
|
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
|
|
newSample0 += nibble0 * pWav->msadpcm.delta[0];
|
|
newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767);
|
|
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
|
|
if (pWav->msadpcm.delta[0] < 16) {
|
|
pWav->msadpcm.delta[0] = 16;
|
|
}
|
|
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
|
|
pWav->msadpcm.prevFrames[0][1] = newSample0;
|
|
newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
|
|
newSample1 += nibble1 * pWav->msadpcm.delta[0];
|
|
newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767);
|
|
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8;
|
|
if (pWav->msadpcm.delta[0] < 16) {
|
|
pWav->msadpcm.delta[0] = 16;
|
|
}
|
|
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
|
|
pWav->msadpcm.prevFrames[0][1] = newSample1;
|
|
pWav->msadpcm.cachedFrames[2] = newSample0;
|
|
pWav->msadpcm.cachedFrames[3] = newSample1;
|
|
pWav->msadpcm.cachedFrameCount = 2;
|
|
} else {
|
|
ma_int32 newSample0;
|
|
ma_int32 newSample1;
|
|
if (pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[0] >= ma_dr_wav_countof(coeff2Table)) {
|
|
return totalFramesRead;
|
|
}
|
|
newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8;
|
|
newSample0 += nibble0 * pWav->msadpcm.delta[0];
|
|
newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767);
|
|
pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8;
|
|
if (pWav->msadpcm.delta[0] < 16) {
|
|
pWav->msadpcm.delta[0] = 16;
|
|
}
|
|
pWav->msadpcm.prevFrames[0][0] = pWav->msadpcm.prevFrames[0][1];
|
|
pWav->msadpcm.prevFrames[0][1] = newSample0;
|
|
if (pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff1Table) || pWav->msadpcm.predictor[1] >= ma_dr_wav_countof(coeff2Table)) {
|
|
return totalFramesRead;
|
|
}
|
|
newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8;
|
|
newSample1 += nibble1 * pWav->msadpcm.delta[1];
|
|
newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767);
|
|
pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8;
|
|
if (pWav->msadpcm.delta[1] < 16) {
|
|
pWav->msadpcm.delta[1] = 16;
|
|
}
|
|
pWav->msadpcm.prevFrames[1][0] = pWav->msadpcm.prevFrames[1][1];
|
|
pWav->msadpcm.prevFrames[1][1] = newSample1;
|
|
pWav->msadpcm.cachedFrames[2] = newSample0;
|
|
pWav->msadpcm.cachedFrames[3] = newSample1;
|
|
pWav->msadpcm.cachedFrameCount = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_uint32 iChannel;
|
|
static const ma_int32 indexTable[16] = {
|
|
-1, -1, -1, -1, 2, 4, 6, 8,
|
|
-1, -1, -1, -1, 2, 4, 6, 8
|
|
};
|
|
static const ma_int32 stepTable[89] = {
|
|
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
|
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
|
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
|
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
|
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
|
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
|
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
|
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
|
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
|
};
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
MA_DR_WAV_ASSERT(framesToRead > 0);
|
|
while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
|
|
MA_DR_WAV_ASSERT(framesToRead > 0);
|
|
if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) {
|
|
if (pWav->channels == 1) {
|
|
ma_uint8 header[4];
|
|
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
|
|
return totalFramesRead;
|
|
}
|
|
pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
|
|
if (header[2] >= ma_dr_wav_countof(stepTable)) {
|
|
pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR);
|
|
pWav->ima.bytesRemainingInBlock = 0;
|
|
return totalFramesRead;
|
|
}
|
|
pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0);
|
|
pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
|
|
pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0];
|
|
pWav->ima.cachedFrameCount = 1;
|
|
} else {
|
|
ma_uint8 header[8];
|
|
if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) {
|
|
return totalFramesRead;
|
|
}
|
|
pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header);
|
|
if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) {
|
|
pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, MA_DR_WAV_SEEK_CUR);
|
|
pWav->ima.bytesRemainingInBlock = 0;
|
|
return totalFramesRead;
|
|
}
|
|
pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0);
|
|
pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
|
|
pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4);
|
|
pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
|
|
pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0];
|
|
pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1];
|
|
pWav->ima.cachedFrameCount = 1;
|
|
}
|
|
}
|
|
while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) {
|
|
if (pBufferOut != NULL) {
|
|
ma_uint32 iSample;
|
|
for (iSample = 0; iSample < pWav->channels; iSample += 1) {
|
|
pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample];
|
|
}
|
|
pBufferOut += pWav->channels;
|
|
}
|
|
framesToRead -= 1;
|
|
totalFramesRead += 1;
|
|
pWav->readCursorInPCMFrames += 1;
|
|
pWav->ima.cachedFrameCount -= 1;
|
|
}
|
|
if (framesToRead == 0) {
|
|
break;
|
|
}
|
|
if (pWav->ima.cachedFrameCount == 0) {
|
|
if (pWav->ima.bytesRemainingInBlock == 0) {
|
|
continue;
|
|
} else {
|
|
pWav->ima.cachedFrameCount = 8;
|
|
for (iChannel = 0; iChannel < pWav->channels; ++iChannel) {
|
|
ma_uint32 iByte;
|
|
ma_uint8 nibbles[4];
|
|
if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) {
|
|
pWav->ima.cachedFrameCount = 0;
|
|
return totalFramesRead;
|
|
}
|
|
pWav->ima.bytesRemainingInBlock -= 4;
|
|
for (iByte = 0; iByte < 4; ++iByte) {
|
|
ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0);
|
|
ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4);
|
|
ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]];
|
|
ma_int32 predictor = pWav->ima.predictor[iChannel];
|
|
ma_int32 diff = step >> 3;
|
|
if (nibble0 & 1) diff += step >> 2;
|
|
if (nibble0 & 2) diff += step >> 1;
|
|
if (nibble0 & 4) diff += step;
|
|
if (nibble0 & 8) diff = -diff;
|
|
predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);
|
|
pWav->ima.predictor[iChannel] = predictor;
|
|
pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
|
|
pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor;
|
|
step = stepTable[pWav->ima.stepIndex[iChannel]];
|
|
predictor = pWav->ima.predictor[iChannel];
|
|
diff = step >> 3;
|
|
if (nibble1 & 1) diff += step >> 2;
|
|
if (nibble1 & 2) diff += step >> 1;
|
|
if (nibble1 & 4) diff += step;
|
|
if (nibble1 & 8) diff = -diff;
|
|
predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767);
|
|
pWav->ima.predictor[iChannel] = predictor;
|
|
pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1);
|
|
pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
#ifndef MA_DR_WAV_NO_CONVERSION_API
|
|
static const unsigned short ma_dr_wav_gAlawTable[256] = {
|
|
0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580,
|
|
0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0,
|
|
0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600,
|
|
0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00,
|
|
0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58,
|
|
0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58,
|
|
0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960,
|
|
0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0,
|
|
0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80,
|
|
0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40,
|
|
0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00,
|
|
0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500,
|
|
0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8,
|
|
0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8,
|
|
0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0,
|
|
0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350
|
|
};
|
|
static const unsigned short ma_dr_wav_gMulawTable[256] = {
|
|
0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84,
|
|
0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84,
|
|
0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004,
|
|
0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844,
|
|
0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64,
|
|
0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74,
|
|
0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C,
|
|
0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000,
|
|
0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C,
|
|
0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C,
|
|
0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC,
|
|
0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC,
|
|
0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C,
|
|
0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C,
|
|
0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084,
|
|
0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
|
|
};
|
|
static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn)
|
|
{
|
|
return (short)ma_dr_wav_gAlawTable[sampleIn];
|
|
}
|
|
static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn)
|
|
{
|
|
return (short)ma_dr_wav_gMulawTable[sampleIn];
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
|
|
{
|
|
size_t i;
|
|
if (bytesPerSample == 1) {
|
|
ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 2) {
|
|
for (i = 0; i < totalSampleCount; ++i) {
|
|
*pOut++ = ((const ma_int16*)pIn)[i];
|
|
}
|
|
return;
|
|
}
|
|
if (bytesPerSample == 3) {
|
|
ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 4) {
|
|
ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample > 8) {
|
|
MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
|
|
return;
|
|
}
|
|
for (i = 0; i < totalSampleCount; ++i) {
|
|
ma_uint64 sample = 0;
|
|
unsigned int shift = (8 - bytesPerSample) * 8;
|
|
unsigned int j;
|
|
for (j = 0; j < bytesPerSample; j += 1) {
|
|
MA_DR_WAV_ASSERT(j < 8);
|
|
sample |= (ma_uint64)(pIn[j]) << shift;
|
|
shift += 8;
|
|
}
|
|
pIn += j;
|
|
*pOut++ = (ma_int16)((ma_int64)sample >> 48);
|
|
}
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
|
|
{
|
|
if (bytesPerSample == 4) {
|
|
ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount);
|
|
return;
|
|
} else if (bytesPerSample == 8) {
|
|
ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount);
|
|
return;
|
|
} else {
|
|
MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
|
|
return;
|
|
}
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < samplesRead; iSample += 1) {
|
|
pBufferOut[iSample] = -pBufferOut[iSample];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead);
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < samplesRead; iSample += 1) {
|
|
pBufferOut[iSample] = -pBufferOut[iSample];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
if (pWav == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
|
|
}
|
|
if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) {
|
|
framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
|
|
return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
|
|
return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
|
|
return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
|
|
return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) {
|
|
return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut);
|
|
}
|
|
return 0;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
|
|
ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
|
|
ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
int r;
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
int x = pIn[i];
|
|
r = x << 8;
|
|
r = r - 32768;
|
|
pOut[i] = (short)r;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
int r;
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8;
|
|
r = x >> 8;
|
|
pOut[i] = (short)r;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount)
|
|
{
|
|
int r;
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
int x = pIn[i];
|
|
r = x >> 16;
|
|
pOut[i] = (short)r;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount)
|
|
{
|
|
int r;
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
float x = pIn[i];
|
|
float c;
|
|
c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
c = c + 1;
|
|
r = (int)(c * 32767.5f);
|
|
r = r - 32768;
|
|
pOut[i] = (short)r;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount)
|
|
{
|
|
int r;
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
double x = pIn[i];
|
|
double c;
|
|
c = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
c = c + 1;
|
|
r = (int)(c * 32767.5);
|
|
r = r - 32768;
|
|
pOut[i] = (short)r;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]);
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]);
|
|
}
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
|
|
{
|
|
unsigned int i;
|
|
if (bytesPerSample == 1) {
|
|
ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 2) {
|
|
ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 3) {
|
|
ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 4) {
|
|
ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample > 8) {
|
|
MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
ma_uint64 sample = 0;
|
|
unsigned int shift = (8 - bytesPerSample) * 8;
|
|
unsigned int j;
|
|
for (j = 0; j < bytesPerSample; j += 1) {
|
|
MA_DR_WAV_ASSERT(j < 8);
|
|
sample |= (ma_uint64)(pIn[j]) << shift;
|
|
shift += 8;
|
|
}
|
|
pIn += j;
|
|
*pOut++ = (float)((ma_int64)sample / 9223372036854775807.0);
|
|
}
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample)
|
|
{
|
|
if (bytesPerSample == 4) {
|
|
unsigned int i;
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = ((const float*)pIn)[i];
|
|
}
|
|
return;
|
|
} else if (bytesPerSample == 8) {
|
|
ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount);
|
|
return;
|
|
} else {
|
|
MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut));
|
|
return;
|
|
}
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_int16 samples16[2048];
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
|
|
pBufferOut += framesRead*pWav->channels;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < samplesRead; iSample += 1) {
|
|
pBufferOut[iSample] = -pBufferOut[iSample];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead);
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < samplesRead; iSample += 1) {
|
|
pBufferOut[iSample] = -pBufferOut[iSample];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
if (pWav == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
|
|
}
|
|
if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) {
|
|
framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
|
|
return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
|
|
return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
|
|
return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
|
|
return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut);
|
|
}
|
|
return 0;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
|
|
ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
|
|
ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = (pIn[i] / 256.0f) * 2 - 1;
|
|
}
|
|
#else
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
float x = pIn[i];
|
|
x = x * 0.00784313725490196078f;
|
|
x = x - 1;
|
|
*pOut++ = x;
|
|
}
|
|
#endif
|
|
}
|
|
MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = pIn[i] * 0.000030517578125f;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
double x;
|
|
ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8);
|
|
ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16);
|
|
ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24);
|
|
x = (double)((ma_int32)(a | b | c) >> 8);
|
|
*pOut++ = (float)(x * 0.00000011920928955078125);
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = (float)(pIn[i] / 2147483648.0);
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = (float)pIn[i];
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f;
|
|
}
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
|
|
{
|
|
unsigned int i;
|
|
if (bytesPerSample == 1) {
|
|
ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 2) {
|
|
ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 3) {
|
|
ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount);
|
|
return;
|
|
}
|
|
if (bytesPerSample == 4) {
|
|
for (i = 0; i < totalSampleCount; ++i) {
|
|
*pOut++ = ((const ma_int32*)pIn)[i];
|
|
}
|
|
return;
|
|
}
|
|
if (bytesPerSample > 8) {
|
|
MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
|
|
return;
|
|
}
|
|
for (i = 0; i < totalSampleCount; ++i) {
|
|
ma_uint64 sample = 0;
|
|
unsigned int shift = (8 - bytesPerSample) * 8;
|
|
unsigned int j;
|
|
for (j = 0; j < bytesPerSample; j += 1) {
|
|
MA_DR_WAV_ASSERT(j < 8);
|
|
sample |= (ma_uint64)(pIn[j]) << shift;
|
|
shift += 8;
|
|
}
|
|
pIn += j;
|
|
*pOut++ = (ma_int32)((ma_int64)sample >> 32);
|
|
}
|
|
}
|
|
MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample)
|
|
{
|
|
if (bytesPerSample == 4) {
|
|
ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount);
|
|
return;
|
|
} else if (bytesPerSample == 8) {
|
|
ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount);
|
|
return;
|
|
} else {
|
|
MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut));
|
|
return;
|
|
}
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut);
|
|
}
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_int16 samples16[2048];
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels));
|
|
pBufferOut += framesRead*pWav->channels;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample);
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < samplesRead; iSample += 1) {
|
|
pBufferOut[iSample] = -pBufferOut[iSample];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead;
|
|
ma_uint8 sampleData[4096] = {0};
|
|
ma_uint32 bytesPerFrame;
|
|
ma_uint32 bytesPerSample;
|
|
ma_uint64 samplesRead;
|
|
bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav);
|
|
if (bytesPerFrame == 0) {
|
|
return 0;
|
|
}
|
|
bytesPerSample = bytesPerFrame / pWav->channels;
|
|
if (bytesPerSample == 0 || (bytesPerFrame % pWav->channels) != 0) {
|
|
return 0;
|
|
}
|
|
totalFramesRead = 0;
|
|
while (framesToRead > 0) {
|
|
ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame);
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData);
|
|
if (framesRead == 0) {
|
|
break;
|
|
}
|
|
MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration);
|
|
samplesRead = framesRead * pWav->channels;
|
|
if ((samplesRead * bytesPerSample) > sizeof(sampleData)) {
|
|
MA_DR_WAV_ASSERT(MA_FALSE);
|
|
break;
|
|
}
|
|
ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead);
|
|
#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT
|
|
{
|
|
if (pWav->container == ma_dr_wav_container_aiff) {
|
|
ma_uint64 iSample;
|
|
for (iSample = 0; iSample < samplesRead; iSample += 1) {
|
|
pBufferOut[iSample] = -pBufferOut[iSample];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
pBufferOut += samplesRead;
|
|
framesToRead -= framesRead;
|
|
totalFramesRead += framesRead;
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
if (pWav == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL);
|
|
}
|
|
if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) {
|
|
framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels;
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) {
|
|
return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) {
|
|
return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) {
|
|
return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) {
|
|
return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut);
|
|
}
|
|
if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) {
|
|
return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut);
|
|
}
|
|
return 0;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) {
|
|
ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut);
|
|
if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) {
|
|
ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels);
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = ((int)pIn[i] - 128) << 24;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = pIn[i] << 16;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
unsigned int s0 = pIn[i*3 + 0];
|
|
unsigned int s1 = pIn[i*3 + 1];
|
|
unsigned int s2 = pIn[i*3 + 2];
|
|
ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24));
|
|
*pOut++ = sample32;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = (ma_int32)(2147483648.0f * pIn[i]);
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = (ma_int32)(2147483648.0 * pIn[i]);
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i = 0; i < sampleCount; ++i) {
|
|
*pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16;
|
|
}
|
|
}
|
|
MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount)
|
|
{
|
|
size_t i;
|
|
if (pOut == NULL || pIn == NULL) {
|
|
return;
|
|
}
|
|
for (i= 0; i < sampleCount; ++i) {
|
|
*pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16;
|
|
}
|
|
}
|
|
MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)
|
|
{
|
|
ma_uint64 sampleDataSize;
|
|
ma_int16* pSampleData;
|
|
ma_uint64 framesRead;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
if (pWav->channels == 0 || pWav->totalPCMFrameCount > MA_SIZE_MAX / pWav->channels / sizeof(ma_int16)) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16);
|
|
if (sampleDataSize > MA_SIZE_MAX) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
|
|
if (pSampleData == NULL) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
|
|
if (framesRead != pWav->totalPCMFrameCount) {
|
|
ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
ma_dr_wav_uninit(pWav);
|
|
if (sampleRate) {
|
|
*sampleRate = pWav->sampleRate;
|
|
}
|
|
if (channels) {
|
|
*channels = pWav->channels;
|
|
}
|
|
if (totalFrameCount) {
|
|
*totalFrameCount = pWav->totalPCMFrameCount;
|
|
}
|
|
return pSampleData;
|
|
}
|
|
MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)
|
|
{
|
|
ma_uint64 sampleDataSize;
|
|
float* pSampleData;
|
|
ma_uint64 framesRead;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
if (pWav->channels == 0 || pWav->totalPCMFrameCount > MA_SIZE_MAX / pWav->channels / sizeof(float)) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float);
|
|
if (sampleDataSize > MA_SIZE_MAX) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
|
|
if (pSampleData == NULL) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
|
|
if (framesRead != pWav->totalPCMFrameCount) {
|
|
ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
ma_dr_wav_uninit(pWav);
|
|
if (sampleRate) {
|
|
*sampleRate = pWav->sampleRate;
|
|
}
|
|
if (channels) {
|
|
*channels = pWav->channels;
|
|
}
|
|
if (totalFrameCount) {
|
|
*totalFrameCount = pWav->totalPCMFrameCount;
|
|
}
|
|
return pSampleData;
|
|
}
|
|
MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount)
|
|
{
|
|
ma_uint64 sampleDataSize;
|
|
ma_int32* pSampleData;
|
|
ma_uint64 framesRead;
|
|
MA_DR_WAV_ASSERT(pWav != NULL);
|
|
if (pWav->channels == 0 || pWav->totalPCMFrameCount > MA_SIZE_MAX / pWav->channels / sizeof(ma_int32)) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32);
|
|
if (sampleDataSize > MA_SIZE_MAX) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks);
|
|
if (pSampleData == NULL) {
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData);
|
|
if (framesRead != pWav->totalPCMFrameCount) {
|
|
ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks);
|
|
ma_dr_wav_uninit(pWav);
|
|
return NULL;
|
|
}
|
|
ma_dr_wav_uninit(pWav);
|
|
if (sampleRate) {
|
|
*sampleRate = pWav->sampleRate;
|
|
}
|
|
if (channels) {
|
|
*channels = pWav->channels;
|
|
}
|
|
if (totalFrameCount) {
|
|
*totalFrameCount = pWav->totalPCMFrameCount;
|
|
}
|
|
return pSampleData;
|
|
}
|
|
MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init(&wav, onRead, onSeek, onTell, pUserData, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_STDIO
|
|
MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
#ifndef MA_DR_WAV_NO_WCHAR
|
|
MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
#endif
|
|
#endif
|
|
MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_wav wav;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalFrameCountOut) {
|
|
*totalFrameCountOut = 0;
|
|
}
|
|
if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut);
|
|
}
|
|
#endif
|
|
MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks);
|
|
} else {
|
|
ma_dr_wav__free_default(p, NULL);
|
|
}
|
|
}
|
|
MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data)
|
|
{
|
|
return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8);
|
|
}
|
|
MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data)
|
|
{
|
|
return (ma_int16)ma_dr_wav_bytes_to_u16(data);
|
|
}
|
|
MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data)
|
|
{
|
|
return ma_dr_wav_bytes_to_u32_le(data);
|
|
}
|
|
MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data)
|
|
{
|
|
union {
|
|
ma_uint32 u32;
|
|
float f32;
|
|
} value;
|
|
value.u32 = ma_dr_wav_bytes_to_u32(data);
|
|
return value.f32;
|
|
}
|
|
MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data)
|
|
{
|
|
return (ma_int32)ma_dr_wav_bytes_to_u32(data);
|
|
}
|
|
MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data)
|
|
{
|
|
return
|
|
((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) |
|
|
((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56);
|
|
}
|
|
MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data)
|
|
{
|
|
return (ma_int64)ma_dr_wav_bytes_to_u64(data);
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16])
|
|
{
|
|
int i;
|
|
for (i = 0; i < 16; i += 1) {
|
|
if (a[i] != b[i]) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b)
|
|
{
|
|
return
|
|
a[0] == b[0] &&
|
|
a[1] == b[1] &&
|
|
a[2] == b[2] &&
|
|
a[3] == b[3];
|
|
}
|
|
#ifdef __MRC__
|
|
#pragma options opt reset
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING)
|
|
#if !defined(MA_DR_FLAC_IMPLEMENTATION)
|
|
#ifndef ma_dr_flac_c
|
|
#define ma_dr_flac_c
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic push
|
|
#if __GNUC__ >= 7
|
|
#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
|
|
#endif
|
|
#endif
|
|
#ifdef __linux__
|
|
#ifndef _BSD_SOURCE
|
|
#define _BSD_SOURCE
|
|
#endif
|
|
#ifndef _DEFAULT_SOURCE
|
|
#define _DEFAULT_SOURCE
|
|
#endif
|
|
#ifndef __USE_BSD
|
|
#define __USE_BSD
|
|
#endif
|
|
#include <endian.h>
|
|
#endif
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#if !defined(MA_DR_FLAC_NO_SIMD)
|
|
#if defined(MA_X64) || defined(MA_X86)
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2)
|
|
#define MA_DR_FLAC_SUPPORT_SSE2
|
|
#endif
|
|
#if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41)
|
|
#define MA_DR_FLAC_SUPPORT_SSE41
|
|
#endif
|
|
#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)))
|
|
#if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2)
|
|
#define MA_DR_FLAC_SUPPORT_SSE2
|
|
#endif
|
|
#if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41)
|
|
#define MA_DR_FLAC_SUPPORT_SSE41
|
|
#endif
|
|
#endif
|
|
#if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include)
|
|
#if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include(<emmintrin.h>)
|
|
#define MA_DR_FLAC_SUPPORT_SSE2
|
|
#endif
|
|
#if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include(<smmintrin.h>)
|
|
#define MA_DR_FLAC_SUPPORT_SSE41
|
|
#endif
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE41)
|
|
#include <smmintrin.h>
|
|
#elif defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
#include <emmintrin.h>
|
|
#endif
|
|
#endif
|
|
#if defined(MA_ARM)
|
|
#if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
|
|
#define MA_DR_FLAC_SUPPORT_NEON
|
|
#include <arm_neon.h>
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64))
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
#if _MSC_VER >= 1400
|
|
#include <intrin.h>
|
|
static void ma_dr_flac__cpuid(int info[4], int fid)
|
|
{
|
|
__cpuid(info, fid);
|
|
}
|
|
#else
|
|
#define MA_DR_FLAC_NO_CPUID
|
|
#endif
|
|
#else
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
static void ma_dr_flac__cpuid(int info[4], int fid)
|
|
{
|
|
#if defined(MA_X86) && defined(__PIC__)
|
|
__asm__ __volatile__ (
|
|
"xchg{l} {%%}ebx, %k1;"
|
|
"cpuid;"
|
|
"xchg{l} {%%}ebx, %k1;"
|
|
: "=a"(info[0]), "=&r"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
|
|
);
|
|
#else
|
|
__asm__ __volatile__ (
|
|
"cpuid" : "=a"(info[0]), "=b"(info[1]), "=c"(info[2]), "=d"(info[3]) : "a"(fid), "c"(0)
|
|
);
|
|
#endif
|
|
}
|
|
#else
|
|
#define MA_DR_FLAC_NO_CPUID
|
|
#endif
|
|
#endif
|
|
#else
|
|
#define MA_DR_FLAC_NO_CPUID
|
|
#endif
|
|
static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
#if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2)
|
|
#if defined(MA_X64)
|
|
return MA_TRUE;
|
|
#elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)
|
|
return MA_TRUE;
|
|
#else
|
|
#if defined(MA_DR_FLAC_NO_CPUID)
|
|
return MA_FALSE;
|
|
#else
|
|
int info[4];
|
|
ma_dr_flac__cpuid(info, 1);
|
|
return (info[3] & (1 << 26)) != 0;
|
|
#endif
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE41)
|
|
#if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41)
|
|
#if defined(__SSE4_1__) || defined(__AVX__)
|
|
return MA_TRUE;
|
|
#else
|
|
#if defined(MA_DR_FLAC_NO_CPUID)
|
|
return MA_FALSE;
|
|
#else
|
|
int info[4];
|
|
ma_dr_flac__cpuid(info, 1);
|
|
return (info[2] & (1 << 19)) != 0;
|
|
#endif
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__)
|
|
#define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
|
|
#elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)))
|
|
#define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
|
|
#elif defined(__clang__)
|
|
#if defined(__has_builtin)
|
|
#if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl)
|
|
#define MA_DR_FLAC_HAS_LZCNT_INTRINSIC
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__)
|
|
#define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
|
|
#define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
|
|
#elif defined(__clang__)
|
|
#if defined(__has_builtin)
|
|
#if __has_builtin(__builtin_bswap16)
|
|
#define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
|
|
#endif
|
|
#if __has_builtin(__builtin_bswap32)
|
|
#define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
|
|
#endif
|
|
#if __has_builtin(__builtin_bswap64)
|
|
#define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
|
|
#endif
|
|
#endif
|
|
#elif defined(__GNUC__)
|
|
#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
|
|
#define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
|
|
#endif
|
|
#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
|
|
#define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
|
|
#endif
|
|
#elif defined(__WATCOMC__) && defined(__386__)
|
|
#define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
|
|
#define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
|
|
#define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
|
|
extern __inline ma_uint16 _watcom_bswap16(ma_uint16);
|
|
extern __inline ma_uint32 _watcom_bswap32(ma_uint32);
|
|
extern __inline ma_uint64 _watcom_bswap64(ma_uint64);
|
|
#pragma aux _watcom_bswap16 = \
|
|
"xchg al, ah" \
|
|
parm [ax] \
|
|
value [ax] \
|
|
modify nomemory;
|
|
#pragma aux _watcom_bswap32 = \
|
|
"bswap eax" \
|
|
parm [eax] \
|
|
value [eax] \
|
|
modify nomemory;
|
|
#pragma aux _watcom_bswap64 = \
|
|
"bswap eax" \
|
|
"bswap edx" \
|
|
"xchg eax,edx" \
|
|
parm [eax edx] \
|
|
value [eax edx] \
|
|
modify nomemory;
|
|
#endif
|
|
#ifndef MA_DR_FLAC_ASSERT
|
|
#include <assert.h>
|
|
#define MA_DR_FLAC_ASSERT(expression) assert(expression)
|
|
#endif
|
|
#ifndef MA_DR_FLAC_MALLOC
|
|
#define MA_DR_FLAC_MALLOC(sz) malloc((sz))
|
|
#endif
|
|
#ifndef MA_DR_FLAC_REALLOC
|
|
#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz))
|
|
#endif
|
|
#ifndef MA_DR_FLAC_FREE
|
|
#define MA_DR_FLAC_FREE(p) free((p))
|
|
#endif
|
|
#ifndef MA_DR_FLAC_COPY_MEMORY
|
|
#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
|
|
#endif
|
|
#ifndef MA_DR_FLAC_ZERO_MEMORY
|
|
#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
|
|
#endif
|
|
#ifndef MA_DR_FLAC_ZERO_OBJECT
|
|
#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p)))
|
|
#endif
|
|
#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64
|
|
#define MA_DR_FLAC_SUBFRAME_CONSTANT 0
|
|
#define MA_DR_FLAC_SUBFRAME_VERBATIM 1
|
|
#define MA_DR_FLAC_SUBFRAME_FIXED 8
|
|
#define MA_DR_FLAC_SUBFRAME_LPC 32
|
|
#define MA_DR_FLAC_SUBFRAME_RESERVED 255
|
|
#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0
|
|
#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1
|
|
#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0
|
|
#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8
|
|
#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9
|
|
#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10
|
|
#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18
|
|
#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36
|
|
#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12
|
|
#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a))
|
|
MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
|
|
{
|
|
if (pMajor) {
|
|
*pMajor = MA_DR_FLAC_VERSION_MAJOR;
|
|
}
|
|
if (pMinor) {
|
|
*pMinor = MA_DR_FLAC_VERSION_MINOR;
|
|
}
|
|
if (pRevision) {
|
|
*pRevision = MA_DR_FLAC_VERSION_REVISION;
|
|
}
|
|
}
|
|
MA_API const char* ma_dr_flac_version_string(void)
|
|
{
|
|
return MA_DR_FLAC_VERSION_STRING;
|
|
}
|
|
#if defined(__has_feature)
|
|
#if __has_feature(thread_sanitizer)
|
|
#define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread")))
|
|
#else
|
|
#define MA_DR_FLAC_NO_THREAD_SANITIZE
|
|
#endif
|
|
#else
|
|
#define MA_DR_FLAC_NO_THREAD_SANITIZE
|
|
#endif
|
|
#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
|
|
static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE;
|
|
#endif
|
|
#ifndef MA_DR_FLAC_NO_CPUID
|
|
static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE;
|
|
static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE;
|
|
MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)
|
|
{
|
|
static ma_bool32 isCPUCapsInitialized = MA_FALSE;
|
|
if (!isCPUCapsInitialized) {
|
|
#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
|
|
int info[4] = {0};
|
|
ma_dr_flac__cpuid(info, 0x80000001);
|
|
ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0;
|
|
#endif
|
|
ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2();
|
|
ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41();
|
|
isCPUCapsInitialized = MA_TRUE;
|
|
}
|
|
}
|
|
#else
|
|
static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE;
|
|
static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
#if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON)
|
|
#if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64))
|
|
return MA_TRUE;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
}
|
|
MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void)
|
|
{
|
|
ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon();
|
|
#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
|
|
ma_dr_flac__gIsLZCNTSupported = MA_TRUE;
|
|
#endif
|
|
}
|
|
#endif
|
|
static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void)
|
|
{
|
|
#if defined(MA_X86) || defined(MA_X64)
|
|
return MA_TRUE;
|
|
#elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN
|
|
return MA_TRUE;
|
|
#else
|
|
int n = 1;
|
|
return (*(char*)&n) == 1;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n)
|
|
{
|
|
#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
return _byteswap_ushort(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
return __builtin_bswap16(n);
|
|
#elif defined(__WATCOMC__) && defined(__386__)
|
|
return _watcom_bswap16(n);
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & 0xFF00) >> 8) |
|
|
((n & 0x00FF) << 8);
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n)
|
|
{
|
|
#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
return _byteswap_ulong(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
#if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(__ARM_ARCH_6M__) && !defined(MA_64BIT)
|
|
ma_uint32 r;
|
|
__asm__ __volatile__ (
|
|
#if defined(MA_64BIT)
|
|
"rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n)
|
|
#else
|
|
"rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n)
|
|
#endif
|
|
);
|
|
return r;
|
|
#else
|
|
return __builtin_bswap32(n);
|
|
#endif
|
|
#elif defined(__WATCOMC__) && defined(__386__)
|
|
return _watcom_bswap32(n);
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & 0xFF000000) >> 24) |
|
|
((n & 0x00FF0000) >> 8) |
|
|
((n & 0x0000FF00) << 8) |
|
|
((n & 0x000000FF) << 24);
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n)
|
|
{
|
|
#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC
|
|
#if defined(_MSC_VER) && !defined(__clang__)
|
|
return _byteswap_uint64(n);
|
|
#elif defined(__GNUC__) || defined(__clang__)
|
|
return __builtin_bswap64(n);
|
|
#elif defined(__WATCOMC__) && defined(__386__)
|
|
return _watcom_bswap64(n);
|
|
#else
|
|
#error "This compiler does not support the byte swap intrinsic."
|
|
#endif
|
|
#else
|
|
return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) |
|
|
((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) |
|
|
((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) |
|
|
((n & ((ma_uint64)0x000000FF << 32)) >> 8) |
|
|
((n & ((ma_uint64)0xFF000000 )) << 8) |
|
|
((n & ((ma_uint64)0x00FF0000 )) << 24) |
|
|
((n & ((ma_uint64)0x0000FF00 )) << 40) |
|
|
((n & ((ma_uint64)0x000000FF )) << 56);
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n)
|
|
{
|
|
if (ma_dr_flac__is_little_endian()) {
|
|
return ma_dr_flac__swap_endian_uint16(n);
|
|
}
|
|
return n;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n)
|
|
{
|
|
if (ma_dr_flac__is_little_endian()) {
|
|
return ma_dr_flac__swap_endian_uint32(n);
|
|
}
|
|
return n;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData)
|
|
{
|
|
const ma_uint8* pNum = (ma_uint8*)pData;
|
|
return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3);
|
|
}
|
|
static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n)
|
|
{
|
|
if (ma_dr_flac__is_little_endian()) {
|
|
return ma_dr_flac__swap_endian_uint64(n);
|
|
}
|
|
return n;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n)
|
|
{
|
|
if (!ma_dr_flac__is_little_endian()) {
|
|
return ma_dr_flac__swap_endian_uint32(n);
|
|
}
|
|
return n;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData)
|
|
{
|
|
const ma_uint8* pNum = (ma_uint8*)pData;
|
|
return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n)
|
|
{
|
|
ma_uint32 result = 0;
|
|
result |= (n & 0x7F000000) >> 3;
|
|
result |= (n & 0x007F0000) >> 2;
|
|
result |= (n & 0x00007F00) >> 1;
|
|
result |= (n & 0x0000007F) >> 0;
|
|
return result;
|
|
}
|
|
static ma_uint8 ma_dr_flac__crc8_table[] = {
|
|
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
|
|
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
|
|
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
|
|
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
|
|
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
|
|
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
|
|
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
|
|
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
|
|
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
|
|
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
|
|
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
|
|
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
|
|
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
|
|
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
|
|
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
|
|
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
|
|
};
|
|
static ma_uint16 ma_dr_flac__crc16_table[] = {
|
|
0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011,
|
|
0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022,
|
|
0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072,
|
|
0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041,
|
|
0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2,
|
|
0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1,
|
|
0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1,
|
|
0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082,
|
|
0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192,
|
|
0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1,
|
|
0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1,
|
|
0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2,
|
|
0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151,
|
|
0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162,
|
|
0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132,
|
|
0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101,
|
|
0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312,
|
|
0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321,
|
|
0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371,
|
|
0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342,
|
|
0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1,
|
|
0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2,
|
|
0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2,
|
|
0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381,
|
|
0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291,
|
|
0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2,
|
|
0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2,
|
|
0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1,
|
|
0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252,
|
|
0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261,
|
|
0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231,
|
|
0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202
|
|
};
|
|
static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data)
|
|
{
|
|
return ma_dr_flac__crc8_table[crc ^ data];
|
|
}
|
|
static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count)
|
|
{
|
|
#ifdef MA_DR_FLAC_NO_CRC
|
|
(void)crc;
|
|
(void)data;
|
|
(void)count;
|
|
return 0;
|
|
#else
|
|
#if 0
|
|
ma_uint8 p = 0x07;
|
|
for (int i = count-1; i >= 0; --i) {
|
|
ma_uint8 bit = (data & (1 << i)) >> i;
|
|
if (crc & 0x80) {
|
|
crc = ((crc << 1) | bit) ^ p;
|
|
} else {
|
|
crc = ((crc << 1) | bit);
|
|
}
|
|
}
|
|
return crc;
|
|
#else
|
|
ma_uint32 wholeBytes;
|
|
ma_uint32 leftoverBits;
|
|
ma_uint64 leftoverDataMask;
|
|
static ma_uint64 leftoverDataMaskTable[8] = {
|
|
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
|
|
};
|
|
MA_DR_FLAC_ASSERT(count <= 32);
|
|
wholeBytes = count >> 3;
|
|
leftoverBits = count - (wholeBytes*8);
|
|
leftoverDataMask = leftoverDataMaskTable[leftoverBits];
|
|
switch (wholeBytes) {
|
|
case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
|
|
case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
|
|
case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
|
|
case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
|
|
case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]);
|
|
}
|
|
return crc;
|
|
#endif
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data)
|
|
{
|
|
return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data];
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data)
|
|
{
|
|
#ifdef MA_64BIT
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));
|
|
#endif
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF));
|
|
crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF));
|
|
return crc;
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount)
|
|
{
|
|
switch (byteCount)
|
|
{
|
|
#ifdef MA_64BIT
|
|
case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF));
|
|
case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF));
|
|
case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF));
|
|
case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF));
|
|
#endif
|
|
case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF));
|
|
case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF));
|
|
case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF));
|
|
case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF));
|
|
}
|
|
return crc;
|
|
}
|
|
#if 0
|
|
static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count)
|
|
{
|
|
#ifdef MA_DR_FLAC_NO_CRC
|
|
(void)crc;
|
|
(void)data;
|
|
(void)count;
|
|
return 0;
|
|
#else
|
|
#if 0
|
|
ma_uint16 p = 0x8005;
|
|
for (int i = count-1; i >= 0; --i) {
|
|
ma_uint16 bit = (data & (1ULL << i)) >> i;
|
|
if (r & 0x8000) {
|
|
r = ((r << 1) | bit) ^ p;
|
|
} else {
|
|
r = ((r << 1) | bit);
|
|
}
|
|
}
|
|
return crc;
|
|
#else
|
|
ma_uint32 wholeBytes;
|
|
ma_uint32 leftoverBits;
|
|
ma_uint64 leftoverDataMask;
|
|
static ma_uint64 leftoverDataMaskTable[8] = {
|
|
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
|
|
};
|
|
MA_DR_FLAC_ASSERT(count <= 64);
|
|
wholeBytes = count >> 3;
|
|
leftoverBits = count & 7;
|
|
leftoverDataMask = leftoverDataMaskTable[leftoverBits];
|
|
switch (wholeBytes) {
|
|
default:
|
|
case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits)));
|
|
case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits)));
|
|
case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits)));
|
|
case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits)));
|
|
case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
|
|
}
|
|
return crc;
|
|
#endif
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count)
|
|
{
|
|
#ifdef MA_DR_FLAC_NO_CRC
|
|
(void)crc;
|
|
(void)data;
|
|
(void)count;
|
|
return 0;
|
|
#else
|
|
ma_uint32 wholeBytes;
|
|
ma_uint32 leftoverBits;
|
|
ma_uint64 leftoverDataMask;
|
|
static ma_uint64 leftoverDataMaskTable[8] = {
|
|
0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F
|
|
};
|
|
MA_DR_FLAC_ASSERT(count <= 64);
|
|
wholeBytes = count >> 3;
|
|
leftoverBits = count & 7;
|
|
leftoverDataMask = leftoverDataMaskTable[leftoverBits];
|
|
switch (wholeBytes) {
|
|
default:
|
|
case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits)));
|
|
case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits)));
|
|
case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits)));
|
|
case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits)));
|
|
case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits)));
|
|
case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits)));
|
|
case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits)));
|
|
case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits)));
|
|
case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)];
|
|
}
|
|
return crc;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count)
|
|
{
|
|
#ifdef MA_64BIT
|
|
return ma_dr_flac_crc16__64bit(crc, data, count);
|
|
#else
|
|
return ma_dr_flac_crc16__32bit(crc, data, count);
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef MA_64BIT
|
|
#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64
|
|
#else
|
|
#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32
|
|
#endif
|
|
#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache))
|
|
#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8)
|
|
#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits)
|
|
#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount)))
|
|
#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount))
|
|
#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount))
|
|
#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)))
|
|
#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1)))
|
|
#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2))
|
|
#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0]))
|
|
#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line)
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs)
|
|
{
|
|
bs->crc16 = 0;
|
|
bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
|
|
}
|
|
static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs)
|
|
{
|
|
if (bs->crc16CacheIgnoredBytes == 0) {
|
|
bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache);
|
|
} else {
|
|
bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes);
|
|
bs->crc16CacheIgnoredBytes = 0;
|
|
}
|
|
}
|
|
static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs)
|
|
{
|
|
MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0);
|
|
if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) {
|
|
ma_dr_flac__update_crc16(bs);
|
|
} else {
|
|
bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes);
|
|
bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
|
|
}
|
|
return bs->crc16;
|
|
}
|
|
#endif
|
|
static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs)
|
|
{
|
|
size_t bytesRead;
|
|
size_t alignedL1LineCount;
|
|
if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
|
|
bs->cache = bs->cacheL2[bs->nextL2Line++];
|
|
return MA_TRUE;
|
|
}
|
|
if (bs->unalignedByteCount > 0) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs));
|
|
bs->nextL2Line = 0;
|
|
if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) {
|
|
bs->cache = bs->cacheL2[bs->nextL2Line++];
|
|
return MA_TRUE;
|
|
}
|
|
alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs);
|
|
bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));
|
|
if (bs->unalignedByteCount > 0) {
|
|
bs->unalignedCache = bs->cacheL2[alignedL1LineCount];
|
|
}
|
|
if (alignedL1LineCount > 0) {
|
|
size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount;
|
|
size_t i;
|
|
for (i = alignedL1LineCount; i > 0; --i) {
|
|
bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1];
|
|
}
|
|
bs->nextL2Line = (ma_uint32)offset;
|
|
bs->cache = bs->cacheL2[bs->nextL2Line++];
|
|
return MA_TRUE;
|
|
} else {
|
|
bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs)
|
|
{
|
|
size_t bytesRead;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__update_crc16(bs);
|
|
#endif
|
|
if (ma_dr_flac__reload_l1_cache_from_l2(bs)) {
|
|
bs->cache = ma_dr_flac__be2host__cache_line(bs->cache);
|
|
bs->consumedBits = 0;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs->cache;
|
|
#endif
|
|
return MA_TRUE;
|
|
}
|
|
bytesRead = bs->unalignedByteCount;
|
|
if (bytesRead == 0) {
|
|
bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs));
|
|
bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8;
|
|
bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache);
|
|
bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs));
|
|
bs->unalignedByteCount = 0;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs->cache >> bs->consumedBits;
|
|
bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3;
|
|
#endif
|
|
return MA_TRUE;
|
|
}
|
|
static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs)
|
|
{
|
|
bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs);
|
|
bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
|
|
bs->cache = 0;
|
|
bs->unalignedByteCount = 0;
|
|
bs->unalignedCache = 0;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = 0;
|
|
bs->crc16CacheIgnoredBytes = 0;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut)
|
|
{
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pResultOut != NULL);
|
|
MA_DR_FLAC_ASSERT(bitCount > 0);
|
|
MA_DR_FLAC_ASSERT(bitCount <= 32);
|
|
if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
#ifdef MA_64BIT
|
|
*pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
|
|
bs->consumedBits += bitCount;
|
|
bs->cache <<= bitCount;
|
|
#else
|
|
if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
|
|
*pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount);
|
|
bs->consumedBits += bitCount;
|
|
bs->cache <<= bitCount;
|
|
} else {
|
|
*pResultOut = (ma_uint32)bs->cache;
|
|
bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
|
|
bs->cache = 0;
|
|
}
|
|
#endif
|
|
return MA_TRUE;
|
|
} else {
|
|
ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
|
|
ma_uint32 bitCountLo = bitCount - bitCountHi;
|
|
ma_uint32 resultHi;
|
|
MA_DR_FLAC_ASSERT(bitCountHi > 0);
|
|
MA_DR_FLAC_ASSERT(bitCountHi < 32);
|
|
resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi);
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
*pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo);
|
|
bs->consumedBits += bitCountLo;
|
|
bs->cache <<= bitCountLo;
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult)
|
|
{
|
|
ma_uint32 result;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pResult != NULL);
|
|
MA_DR_FLAC_ASSERT(bitCount > 0);
|
|
MA_DR_FLAC_ASSERT(bitCount <= 32);
|
|
if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (bitCount < 32) {
|
|
ma_uint32 signbit;
|
|
signbit = ((result >> (bitCount-1)) & 0x01);
|
|
result |= (~signbit + 1) << bitCount;
|
|
}
|
|
*pResult = (ma_int32)result;
|
|
return MA_TRUE;
|
|
}
|
|
#ifdef MA_64BIT
|
|
static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut)
|
|
{
|
|
ma_uint32 resultHi;
|
|
ma_uint32 resultLo;
|
|
MA_DR_FLAC_ASSERT(bitCount <= 64);
|
|
MA_DR_FLAC_ASSERT(bitCount > 32);
|
|
if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) {
|
|
return MA_FALSE;
|
|
}
|
|
*pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo);
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
#if 0
|
|
static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut)
|
|
{
|
|
ma_uint64 result;
|
|
ma_uint64 signbit;
|
|
MA_DR_FLAC_ASSERT(bitCount <= 64);
|
|
if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) {
|
|
return MA_FALSE;
|
|
}
|
|
signbit = ((result >> (bitCount-1)) & 0x01);
|
|
result |= (~signbit + 1) << bitCount;
|
|
*pResultOut = (ma_int64)result;
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult)
|
|
{
|
|
ma_uint32 result;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pResult != NULL);
|
|
MA_DR_FLAC_ASSERT(bitCount > 0);
|
|
MA_DR_FLAC_ASSERT(bitCount <= 16);
|
|
if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
|
|
return MA_FALSE;
|
|
}
|
|
*pResult = (ma_uint16)result;
|
|
return MA_TRUE;
|
|
}
|
|
#if 0
|
|
static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult)
|
|
{
|
|
ma_int32 result;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pResult != NULL);
|
|
MA_DR_FLAC_ASSERT(bitCount > 0);
|
|
MA_DR_FLAC_ASSERT(bitCount <= 16);
|
|
if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {
|
|
return MA_FALSE;
|
|
}
|
|
*pResult = (ma_int16)result;
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult)
|
|
{
|
|
ma_uint32 result;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pResult != NULL);
|
|
MA_DR_FLAC_ASSERT(bitCount > 0);
|
|
MA_DR_FLAC_ASSERT(bitCount <= 8);
|
|
if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) {
|
|
return MA_FALSE;
|
|
}
|
|
*pResult = (ma_uint8)result;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult)
|
|
{
|
|
ma_int32 result;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pResult != NULL);
|
|
MA_DR_FLAC_ASSERT(bitCount > 0);
|
|
MA_DR_FLAC_ASSERT(bitCount <= 8);
|
|
if (!ma_dr_flac__read_int32(bs, bitCount, &result)) {
|
|
return MA_FALSE;
|
|
}
|
|
*pResult = (ma_int8)result;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek)
|
|
{
|
|
if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
bs->consumedBits += (ma_uint32)bitsToSeek;
|
|
bs->cache <<= bitsToSeek;
|
|
return MA_TRUE;
|
|
} else {
|
|
bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
|
|
bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
|
|
bs->cache = 0;
|
|
#ifdef MA_64BIT
|
|
while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
|
|
ma_uint64 bin;
|
|
if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
|
|
return MA_FALSE;
|
|
}
|
|
bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
|
|
}
|
|
#else
|
|
while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) {
|
|
ma_uint32 bin;
|
|
if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) {
|
|
return MA_FALSE;
|
|
}
|
|
bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
|
|
}
|
|
#endif
|
|
while (bitsToSeek >= 8) {
|
|
ma_uint8 bin;
|
|
if (!ma_dr_flac__read_uint8(bs, 8, &bin)) {
|
|
return MA_FALSE;
|
|
}
|
|
bitsToSeek -= 8;
|
|
}
|
|
if (bitsToSeek > 0) {
|
|
ma_uint8 bin;
|
|
if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) {
|
|
return MA_FALSE;
|
|
}
|
|
bitsToSeek = 0;
|
|
}
|
|
MA_DR_FLAC_ASSERT(bitsToSeek == 0);
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs)
|
|
{
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
|
|
return MA_FALSE;
|
|
}
|
|
for (;;) {
|
|
ma_uint8 hi;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__reset_crc16(bs);
|
|
#endif
|
|
if (!ma_dr_flac__read_uint8(bs, 8, &hi)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (hi == 0xFF) {
|
|
ma_uint8 lo;
|
|
if (!ma_dr_flac__read_uint8(bs, 6, &lo)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (lo == 0x3E) {
|
|
return MA_TRUE;
|
|
} else {
|
|
if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC)
|
|
#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
|
|
#endif
|
|
#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__)
|
|
#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
|
|
#endif
|
|
#if defined(__WATCOMC__) && defined(__386__)
|
|
#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM
|
|
#endif
|
|
#ifdef __MRC__
|
|
#include <intrinsics.h>
|
|
#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC
|
|
#endif
|
|
static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x)
|
|
{
|
|
ma_uint32 n;
|
|
static ma_uint32 clz_table_4[] = {
|
|
0,
|
|
4,
|
|
3, 3,
|
|
2, 2, 2, 2,
|
|
1, 1, 1, 1, 1, 1, 1, 1
|
|
};
|
|
if (x == 0) {
|
|
return sizeof(x)*8;
|
|
}
|
|
n = clz_table_4[x >> (sizeof(x)*8 - 4)];
|
|
if (n == 0) {
|
|
#ifdef MA_64BIT
|
|
if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; }
|
|
if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; }
|
|
if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; }
|
|
if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; }
|
|
#else
|
|
if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; }
|
|
if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; }
|
|
if ((x & 0xF0000000) == 0) { n += 4; x <<= 4; }
|
|
#endif
|
|
n += clz_table_4[x >> (sizeof(x)*8 - 4)];
|
|
}
|
|
return n - 1;
|
|
}
|
|
#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
|
|
static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void)
|
|
{
|
|
#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5)
|
|
return MA_TRUE;
|
|
#elif defined(__MRC__)
|
|
return MA_TRUE;
|
|
#else
|
|
#ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC
|
|
return ma_dr_flac__gIsLZCNTSupported;
|
|
#else
|
|
return MA_FALSE;
|
|
#endif
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x)
|
|
{
|
|
#if defined(_MSC_VER)
|
|
#ifdef MA_64BIT
|
|
return (ma_uint32)__lzcnt64(x);
|
|
#else
|
|
return (ma_uint32)__lzcnt(x);
|
|
#endif
|
|
#else
|
|
#if defined(__GNUC__) || defined(__clang__)
|
|
#if defined(MA_X64)
|
|
{
|
|
ma_uint64 r;
|
|
__asm__ __volatile__ (
|
|
"rep; bsr{q %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
|
|
);
|
|
return (ma_uint32)r;
|
|
}
|
|
#elif defined(MA_X86)
|
|
{
|
|
ma_uint32 r;
|
|
__asm__ __volatile__ (
|
|
"rep; bsr{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc"
|
|
);
|
|
return r;
|
|
}
|
|
#elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(__ARM_ARCH_6M__) && !(defined(__thumb__) && !defined(__thumb2__)) && !defined(MA_64BIT)
|
|
{
|
|
unsigned int r;
|
|
__asm__ __volatile__ (
|
|
#if defined(MA_64BIT)
|
|
"clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x)
|
|
#else
|
|
"clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x)
|
|
#endif
|
|
);
|
|
return r;
|
|
}
|
|
#else
|
|
if (x == 0) {
|
|
return sizeof(x)*8;
|
|
}
|
|
#ifdef MA_64BIT
|
|
return (ma_uint32)__builtin_clzll((ma_uint64)x);
|
|
#else
|
|
return (ma_uint32)__builtin_clzl((ma_uint32)x);
|
|
#endif
|
|
#endif
|
|
#else
|
|
#error "This compiler does not support the lzcnt intrinsic."
|
|
#endif
|
|
#endif
|
|
}
|
|
#endif
|
|
#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
|
|
#include <intrin.h>
|
|
static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x)
|
|
{
|
|
ma_uint32 n;
|
|
if (x == 0) {
|
|
return sizeof(x)*8;
|
|
}
|
|
#ifdef MA_64BIT
|
|
_BitScanReverse64((unsigned long*)&n, x);
|
|
#else
|
|
_BitScanReverse((unsigned long*)&n, x);
|
|
#endif
|
|
return sizeof(x)*8 - n - 1;
|
|
}
|
|
#endif
|
|
#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM
|
|
static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32);
|
|
#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT
|
|
#pragma aux ma_dr_flac__clz_watcom_lzcnt = \
|
|
"db 0F3h, 0Fh, 0BDh, 0C0h" \
|
|
parm [eax] \
|
|
value [eax] \
|
|
modify nomemory;
|
|
#else
|
|
#pragma aux ma_dr_flac__clz_watcom = \
|
|
"bsr eax, eax" \
|
|
"xor eax, 31" \
|
|
parm [eax] nomemory \
|
|
value [eax] \
|
|
modify exact [eax] nomemory;
|
|
#endif
|
|
#endif
|
|
static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x)
|
|
{
|
|
#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT
|
|
if (ma_dr_flac__is_lzcnt_supported()) {
|
|
return ma_dr_flac__clz_lzcnt(x);
|
|
} else
|
|
#endif
|
|
{
|
|
#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC
|
|
return ma_dr_flac__clz_msvc(x);
|
|
#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT)
|
|
return ma_dr_flac__clz_watcom_lzcnt(x);
|
|
#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM)
|
|
return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x);
|
|
#elif defined(__MRC__)
|
|
return __cntlzw(x);
|
|
#else
|
|
return ma_dr_flac__clz_software(x);
|
|
#endif
|
|
}
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut)
|
|
{
|
|
ma_uint32 zeroCounter = 0;
|
|
ma_uint32 setBitOffsetPlus1;
|
|
while (bs->cache == 0) {
|
|
zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (bs->cache == 1) {
|
|
*pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1;
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);
|
|
setBitOffsetPlus1 += 1;
|
|
if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
bs->consumedBits += setBitOffsetPlus1;
|
|
bs->cache <<= setBitOffsetPlus1;
|
|
*pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart)
|
|
{
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(offsetFromStart > 0);
|
|
if (offsetFromStart > 0x7FFFFFFF) {
|
|
ma_uint64 bytesRemaining = offsetFromStart;
|
|
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemaining -= 0x7FFFFFFF;
|
|
while (bytesRemaining > 0x7FFFFFFF) {
|
|
if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemaining -= 0x7FFFFFFF;
|
|
}
|
|
if (bytesRemaining > 0) {
|
|
if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, MA_DR_FLAC_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
ma_dr_flac__reset_cache(bs);
|
|
return MA_TRUE;
|
|
}
|
|
static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut)
|
|
{
|
|
ma_uint8 crc;
|
|
ma_uint64 result;
|
|
ma_uint8 utf8[7] = {0};
|
|
int byteCount;
|
|
int i;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pNumberOut != NULL);
|
|
MA_DR_FLAC_ASSERT(pCRCOut != NULL);
|
|
crc = *pCRCOut;
|
|
if (!ma_dr_flac__read_uint8(bs, 8, utf8)) {
|
|
*pNumberOut = 0;
|
|
return MA_AT_END;
|
|
}
|
|
crc = ma_dr_flac_crc8(crc, utf8[0], 8);
|
|
if ((utf8[0] & 0x80) == 0) {
|
|
*pNumberOut = utf8[0];
|
|
*pCRCOut = crc;
|
|
return MA_SUCCESS;
|
|
}
|
|
if ((utf8[0] & 0xE0) == 0xC0) {
|
|
byteCount = 2;
|
|
} else if ((utf8[0] & 0xF0) == 0xE0) {
|
|
byteCount = 3;
|
|
} else if ((utf8[0] & 0xF8) == 0xF0) {
|
|
byteCount = 4;
|
|
} else if ((utf8[0] & 0xFC) == 0xF8) {
|
|
byteCount = 5;
|
|
} else if ((utf8[0] & 0xFE) == 0xFC) {
|
|
byteCount = 6;
|
|
} else if ((utf8[0] & 0xFF) == 0xFE) {
|
|
byteCount = 7;
|
|
} else {
|
|
*pNumberOut = 0;
|
|
return MA_CRC_MISMATCH;
|
|
}
|
|
MA_DR_FLAC_ASSERT(byteCount > 1);
|
|
result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1)));
|
|
for (i = 1; i < byteCount; ++i) {
|
|
if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) {
|
|
*pNumberOut = 0;
|
|
return MA_AT_END;
|
|
}
|
|
crc = ma_dr_flac_crc8(crc, utf8[i], 8);
|
|
result = (result << 6) | (utf8[i] & 0x3F);
|
|
}
|
|
*pNumberOut = result;
|
|
*pCRCOut = crc;
|
|
return MA_SUCCESS;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x)
|
|
{
|
|
#if 1
|
|
ma_uint32 result = 0;
|
|
while (x > 0) {
|
|
result += 1;
|
|
x >>= 1;
|
|
}
|
|
return result;
|
|
#endif
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision)
|
|
{
|
|
return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32;
|
|
}
|
|
#if defined(__clang__)
|
|
__attribute__((no_sanitize("signed-integer-overflow")))
|
|
#endif
|
|
static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_int32 prediction = 0;
|
|
MA_DR_FLAC_ASSERT(order <= 32);
|
|
switch (order)
|
|
{
|
|
case 32: prediction += coefficients[31] * pDecodedSamples[-32];
|
|
case 31: prediction += coefficients[30] * pDecodedSamples[-31];
|
|
case 30: prediction += coefficients[29] * pDecodedSamples[-30];
|
|
case 29: prediction += coefficients[28] * pDecodedSamples[-29];
|
|
case 28: prediction += coefficients[27] * pDecodedSamples[-28];
|
|
case 27: prediction += coefficients[26] * pDecodedSamples[-27];
|
|
case 26: prediction += coefficients[25] * pDecodedSamples[-26];
|
|
case 25: prediction += coefficients[24] * pDecodedSamples[-25];
|
|
case 24: prediction += coefficients[23] * pDecodedSamples[-24];
|
|
case 23: prediction += coefficients[22] * pDecodedSamples[-23];
|
|
case 22: prediction += coefficients[21] * pDecodedSamples[-22];
|
|
case 21: prediction += coefficients[20] * pDecodedSamples[-21];
|
|
case 20: prediction += coefficients[19] * pDecodedSamples[-20];
|
|
case 19: prediction += coefficients[18] * pDecodedSamples[-19];
|
|
case 18: prediction += coefficients[17] * pDecodedSamples[-18];
|
|
case 17: prediction += coefficients[16] * pDecodedSamples[-17];
|
|
case 16: prediction += coefficients[15] * pDecodedSamples[-16];
|
|
case 15: prediction += coefficients[14] * pDecodedSamples[-15];
|
|
case 14: prediction += coefficients[13] * pDecodedSamples[-14];
|
|
case 13: prediction += coefficients[12] * pDecodedSamples[-13];
|
|
case 12: prediction += coefficients[11] * pDecodedSamples[-12];
|
|
case 11: prediction += coefficients[10] * pDecodedSamples[-11];
|
|
case 10: prediction += coefficients[ 9] * pDecodedSamples[-10];
|
|
case 9: prediction += coefficients[ 8] * pDecodedSamples[- 9];
|
|
case 8: prediction += coefficients[ 7] * pDecodedSamples[- 8];
|
|
case 7: prediction += coefficients[ 6] * pDecodedSamples[- 7];
|
|
case 6: prediction += coefficients[ 5] * pDecodedSamples[- 6];
|
|
case 5: prediction += coefficients[ 4] * pDecodedSamples[- 5];
|
|
case 4: prediction += coefficients[ 3] * pDecodedSamples[- 4];
|
|
case 3: prediction += coefficients[ 2] * pDecodedSamples[- 3];
|
|
case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2];
|
|
case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1];
|
|
}
|
|
return (ma_int32)(prediction >> shift);
|
|
}
|
|
static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_int64 prediction;
|
|
MA_DR_FLAC_ASSERT(order <= 32);
|
|
#ifndef MA_64BIT
|
|
if (order == 8)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
|
|
prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
|
|
}
|
|
else if (order == 7)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
|
|
}
|
|
else if (order == 3)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
}
|
|
else if (order == 6)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
}
|
|
else if (order == 5)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
}
|
|
else if (order == 4)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
}
|
|
else if (order == 12)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
|
|
prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
|
|
prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
|
|
prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
|
|
prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
|
|
prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];
|
|
}
|
|
else if (order == 2)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
}
|
|
else if (order == 1)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
}
|
|
else if (order == 10)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
|
|
prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
|
|
prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
|
|
prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
|
|
}
|
|
else if (order == 9)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
|
|
prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
|
|
prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
|
|
}
|
|
else if (order == 11)
|
|
{
|
|
prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1];
|
|
prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2];
|
|
prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3];
|
|
prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4];
|
|
prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5];
|
|
prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6];
|
|
prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7];
|
|
prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8];
|
|
prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9];
|
|
prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10];
|
|
prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
|
|
}
|
|
else
|
|
{
|
|
int j;
|
|
prediction = 0;
|
|
for (j = 0; j < (int)order; ++j) {
|
|
prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1];
|
|
}
|
|
}
|
|
#endif
|
|
#ifdef MA_64BIT
|
|
prediction = 0;
|
|
switch (order)
|
|
{
|
|
case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32];
|
|
case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31];
|
|
case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30];
|
|
case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29];
|
|
case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28];
|
|
case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27];
|
|
case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26];
|
|
case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25];
|
|
case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24];
|
|
case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23];
|
|
case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22];
|
|
case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21];
|
|
case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20];
|
|
case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19];
|
|
case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18];
|
|
case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17];
|
|
case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16];
|
|
case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15];
|
|
case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14];
|
|
case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13];
|
|
case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12];
|
|
case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11];
|
|
case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10];
|
|
case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9];
|
|
case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8];
|
|
case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7];
|
|
case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6];
|
|
case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5];
|
|
case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4];
|
|
case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3];
|
|
case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2];
|
|
case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1];
|
|
}
|
|
#endif
|
|
return (ma_int32)(prediction >> shift);
|
|
}
|
|
#if 0
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
ma_uint32 i;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
|
|
for (i = 0; i < count; ++i) {
|
|
ma_uint32 zeroCounter = 0;
|
|
for (;;) {
|
|
ma_uint8 bit;
|
|
if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (bit == 0) {
|
|
zeroCounter += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ma_uint32 decodedRice;
|
|
if (riceParam > 0) {
|
|
if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
decodedRice = 0;
|
|
}
|
|
decodedRice |= (zeroCounter << riceParam);
|
|
if ((decodedRice & 0x01)) {
|
|
decodedRice = ~(decodedRice >> 1);
|
|
} else {
|
|
decodedRice = (decodedRice >> 1);
|
|
}
|
|
if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
|
|
pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
|
|
} else {
|
|
pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
#if 0
|
|
static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
|
|
{
|
|
ma_uint32 zeroCounter = 0;
|
|
ma_uint32 decodedRice;
|
|
for (;;) {
|
|
ma_uint8 bit;
|
|
if (!ma_dr_flac__read_uint8(bs, 1, &bit)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (bit == 0) {
|
|
zeroCounter += 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (riceParam > 0) {
|
|
if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
decodedRice = 0;
|
|
}
|
|
*pZeroCounterOut = zeroCounter;
|
|
*pRiceParamPartOut = decodedRice;
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
#if 0
|
|
static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
|
|
{
|
|
ma_dr_flac_cache_t riceParamMask;
|
|
ma_uint32 zeroCounter;
|
|
ma_uint32 setBitOffsetPlus1;
|
|
ma_uint32 riceParamPart;
|
|
ma_uint32 riceLength;
|
|
MA_DR_FLAC_ASSERT(riceParam > 0);
|
|
riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam);
|
|
zeroCounter = 0;
|
|
while (bs->cache == 0) {
|
|
zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs);
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache);
|
|
zeroCounter += setBitOffsetPlus1;
|
|
setBitOffsetPlus1 += 1;
|
|
riceLength = setBitOffsetPlus1 + riceParam;
|
|
if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength));
|
|
bs->consumedBits += riceLength;
|
|
bs->cache <<= riceLength;
|
|
} else {
|
|
ma_uint32 bitCountLo;
|
|
ma_dr_flac_cache_t resultHi;
|
|
bs->consumedBits += riceLength;
|
|
bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1);
|
|
bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs);
|
|
resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam);
|
|
if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__update_crc16(bs);
|
|
#endif
|
|
bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
|
|
bs->consumedBits = 0;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs->cache;
|
|
#endif
|
|
} else {
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo));
|
|
bs->consumedBits += bitCountLo;
|
|
bs->cache <<= bitCountLo;
|
|
}
|
|
pZeroCounterOut[0] = zeroCounter;
|
|
pRiceParamPartOut[0] = riceParamPart;
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut)
|
|
{
|
|
ma_uint32 riceParamPlus1 = riceParam + 1;
|
|
ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1);
|
|
ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
|
|
ma_dr_flac_cache_t bs_cache = bs->cache;
|
|
ma_uint32 bs_consumedBits = bs->consumedBits;
|
|
ma_uint32 lzcount = ma_dr_flac__clz(bs_cache);
|
|
if (lzcount < sizeof(bs_cache)*8) {
|
|
pZeroCounterOut[0] = lzcount;
|
|
extract_rice_param_part:
|
|
bs_cache <<= lzcount;
|
|
bs_consumedBits += lzcount;
|
|
if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
|
|
pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift);
|
|
bs_cache <<= riceParamPlus1;
|
|
bs_consumedBits += riceParamPlus1;
|
|
} else {
|
|
ma_uint32 riceParamPartHi;
|
|
ma_uint32 riceParamPartLo;
|
|
ma_uint32 riceParamPartLoBitCount;
|
|
riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift);
|
|
riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
|
|
MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
|
|
if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__update_crc16(bs);
|
|
#endif
|
|
bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
|
|
bs_consumedBits = riceParamPartLoBitCount;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs_cache;
|
|
#endif
|
|
} else {
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
bs_cache = bs->cache;
|
|
bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
|
|
}
|
|
riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount)));
|
|
pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo;
|
|
bs_cache <<= riceParamPartLoBitCount;
|
|
}
|
|
} else {
|
|
ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits);
|
|
for (;;) {
|
|
if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__update_crc16(bs);
|
|
#endif
|
|
bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
|
|
bs_consumedBits = 0;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs_cache;
|
|
#endif
|
|
} else {
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
bs_cache = bs->cache;
|
|
bs_consumedBits = bs->consumedBits;
|
|
}
|
|
lzcount = ma_dr_flac__clz(bs_cache);
|
|
zeroCounter += lzcount;
|
|
if (lzcount < sizeof(bs_cache)*8) {
|
|
break;
|
|
}
|
|
}
|
|
pZeroCounterOut[0] = zeroCounter;
|
|
goto extract_rice_param_part;
|
|
}
|
|
bs->cache = bs_cache;
|
|
bs->consumedBits = bs_consumedBits;
|
|
return MA_TRUE;
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam)
|
|
{
|
|
ma_uint32 riceParamPlus1 = riceParam + 1;
|
|
ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1;
|
|
ma_dr_flac_cache_t bs_cache = bs->cache;
|
|
ma_uint32 bs_consumedBits = bs->consumedBits;
|
|
ma_uint32 lzcount = ma_dr_flac__clz(bs_cache);
|
|
if (lzcount < sizeof(bs_cache)*8) {
|
|
extract_rice_param_part:
|
|
bs_cache <<= lzcount;
|
|
bs_consumedBits += lzcount;
|
|
if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) {
|
|
bs_cache <<= riceParamPlus1;
|
|
bs_consumedBits += riceParamPlus1;
|
|
} else {
|
|
ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits;
|
|
MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32);
|
|
if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__update_crc16(bs);
|
|
#endif
|
|
bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
|
|
bs_consumedBits = riceParamPartLoBitCount;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs_cache;
|
|
#endif
|
|
} else {
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
bs_cache = bs->cache;
|
|
bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount;
|
|
}
|
|
bs_cache <<= riceParamPartLoBitCount;
|
|
}
|
|
} else {
|
|
for (;;) {
|
|
if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) {
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_dr_flac__update_crc16(bs);
|
|
#endif
|
|
bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]);
|
|
bs_consumedBits = 0;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
bs->crc16Cache = bs_cache;
|
|
#endif
|
|
} else {
|
|
if (!ma_dr_flac__reload_cache(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
bs_cache = bs->cache;
|
|
bs_consumedBits = bs->consumedBits;
|
|
}
|
|
lzcount = ma_dr_flac__clz(bs_cache);
|
|
if (lzcount < sizeof(bs_cache)*8) {
|
|
break;
|
|
}
|
|
}
|
|
goto extract_rice_param_part;
|
|
}
|
|
bs->cache = bs_cache;
|
|
bs->consumedBits = bs_consumedBits;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
|
|
ma_uint32 zeroCountPart0;
|
|
ma_uint32 riceParamPart0;
|
|
ma_uint32 riceParamMask;
|
|
ma_uint32 i;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
|
|
(void)bitsPerSample;
|
|
(void)order;
|
|
(void)shift;
|
|
(void)coefficients;
|
|
riceParamMask = (ma_uint32)~((~0UL) << riceParam);
|
|
i = 0;
|
|
while (i < count) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamPart0 &= riceParamMask;
|
|
riceParamPart0 |= (zeroCountPart0 << riceParam);
|
|
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
|
|
pSamplesOut[i] = riceParamPart0;
|
|
i += 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
|
|
ma_uint32 zeroCountPart0 = 0;
|
|
ma_uint32 zeroCountPart1 = 0;
|
|
ma_uint32 zeroCountPart2 = 0;
|
|
ma_uint32 zeroCountPart3 = 0;
|
|
ma_uint32 riceParamPart0 = 0;
|
|
ma_uint32 riceParamPart1 = 0;
|
|
ma_uint32 riceParamPart2 = 0;
|
|
ma_uint32 riceParamPart3 = 0;
|
|
ma_uint32 riceParamMask;
|
|
const ma_int32* pSamplesOutEnd;
|
|
ma_uint32 i;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
|
|
if (lpcOrder == 0) {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
|
|
}
|
|
riceParamMask = (ma_uint32)~((~0UL) << riceParam);
|
|
pSamplesOutEnd = pSamplesOut + (count & ~3);
|
|
if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
|
|
while (pSamplesOut < pSamplesOutEnd) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamPart0 &= riceParamMask;
|
|
riceParamPart1 &= riceParamMask;
|
|
riceParamPart2 &= riceParamMask;
|
|
riceParamPart3 &= riceParamMask;
|
|
riceParamPart0 |= (zeroCountPart0 << riceParam);
|
|
riceParamPart1 |= (zeroCountPart1 << riceParam);
|
|
riceParamPart2 |= (zeroCountPart2 << riceParam);
|
|
riceParamPart3 |= (zeroCountPart3 << riceParam);
|
|
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
|
|
riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
|
|
riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
|
|
riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
|
|
pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
|
|
pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
|
|
pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
|
|
pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
|
|
pSamplesOut += 4;
|
|
}
|
|
} else {
|
|
while (pSamplesOut < pSamplesOutEnd) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamPart0 &= riceParamMask;
|
|
riceParamPart1 &= riceParamMask;
|
|
riceParamPart2 &= riceParamMask;
|
|
riceParamPart3 &= riceParamMask;
|
|
riceParamPart0 |= (zeroCountPart0 << riceParam);
|
|
riceParamPart1 |= (zeroCountPart1 << riceParam);
|
|
riceParamPart2 |= (zeroCountPart2 << riceParam);
|
|
riceParamPart3 |= (zeroCountPart3 << riceParam);
|
|
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
|
|
riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01];
|
|
riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01];
|
|
riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01];
|
|
pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
|
|
pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1);
|
|
pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2);
|
|
pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3);
|
|
pSamplesOut += 4;
|
|
}
|
|
}
|
|
i = (count & ~3);
|
|
while (i < count) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamPart0 &= riceParamMask;
|
|
riceParamPart0 |= (zeroCountPart0 << riceParam);
|
|
riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01];
|
|
if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
|
|
pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
|
|
} else {
|
|
pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0);
|
|
}
|
|
i += 1;
|
|
pSamplesOut += 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b)
|
|
{
|
|
__m128i r;
|
|
r = _mm_packs_epi32(a, b);
|
|
r = _mm_shuffle_epi32(r, _MM_SHUFFLE(3, 1, 2, 0));
|
|
r = _mm_shufflehi_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
|
|
r = _mm_shufflelo_epi16(r, _MM_SHUFFLE(3, 1, 2, 0));
|
|
return r;
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE41)
|
|
static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a)
|
|
{
|
|
return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128()));
|
|
}
|
|
static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x)
|
|
{
|
|
__m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
__m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2));
|
|
return _mm_add_epi32(x64, x32);
|
|
}
|
|
static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x)
|
|
{
|
|
return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2)));
|
|
}
|
|
static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count)
|
|
{
|
|
__m128i lo = _mm_srli_epi64(x, count);
|
|
__m128i hi = _mm_srai_epi32(x, count);
|
|
hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0));
|
|
return _mm_or_si128(lo, hi);
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
int i;
|
|
ma_uint32 riceParamMask;
|
|
ma_int32* pDecodedSamples = pSamplesOut;
|
|
ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
|
|
ma_uint32 zeroCountParts0 = 0;
|
|
ma_uint32 zeroCountParts1 = 0;
|
|
ma_uint32 zeroCountParts2 = 0;
|
|
ma_uint32 zeroCountParts3 = 0;
|
|
ma_uint32 riceParamParts0 = 0;
|
|
ma_uint32 riceParamParts1 = 0;
|
|
ma_uint32 riceParamParts2 = 0;
|
|
ma_uint32 riceParamParts3 = 0;
|
|
__m128i coefficients128_0;
|
|
__m128i coefficients128_4;
|
|
__m128i coefficients128_8;
|
|
__m128i samples128_0;
|
|
__m128i samples128_4;
|
|
__m128i samples128_8;
|
|
__m128i riceParamMask128;
|
|
const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
|
|
riceParamMask = (ma_uint32)~((~0UL) << riceParam);
|
|
riceParamMask128 = _mm_set1_epi32(riceParamMask);
|
|
coefficients128_0 = _mm_setzero_si128();
|
|
coefficients128_4 = _mm_setzero_si128();
|
|
coefficients128_8 = _mm_setzero_si128();
|
|
samples128_0 = _mm_setzero_si128();
|
|
samples128_4 = _mm_setzero_si128();
|
|
samples128_8 = _mm_setzero_si128();
|
|
#if 1
|
|
{
|
|
int runningOrder = order;
|
|
if (runningOrder >= 4) {
|
|
coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
|
|
samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break;
|
|
case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break;
|
|
case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
|
|
}
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder >= 4) {
|
|
coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
|
|
samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break;
|
|
case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break;
|
|
case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
|
|
}
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder == 4) {
|
|
coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
|
|
samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break;
|
|
case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break;
|
|
case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
|
|
}
|
|
runningOrder = 0;
|
|
}
|
|
coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
|
|
coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
|
|
coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
|
|
}
|
|
#else
|
|
switch (order)
|
|
{
|
|
case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];
|
|
case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];
|
|
case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];
|
|
case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
|
|
case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
|
|
case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
|
|
case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
|
|
case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
|
|
case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
|
|
case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
|
|
case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
|
|
case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
|
|
}
|
|
#endif
|
|
while (pDecodedSamples < pDecodedSamplesEnd) {
|
|
__m128i prediction128;
|
|
__m128i zeroCountPart128;
|
|
__m128i riceParamPart128;
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
|
|
return MA_FALSE;
|
|
}
|
|
zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
|
|
riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
|
|
riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
|
|
riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
|
|
riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01)));
|
|
if (order <= 4) {
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0);
|
|
prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
|
|
prediction128 = _mm_srai_epi32(prediction128, shift);
|
|
prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
|
|
samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
|
|
riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
|
|
}
|
|
} else if (order <= 8) {
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4);
|
|
prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
|
|
prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
|
|
prediction128 = _mm_srai_epi32(prediction128, shift);
|
|
prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
|
|
samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
|
|
samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
|
|
riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
|
|
}
|
|
} else {
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8);
|
|
prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4));
|
|
prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0));
|
|
prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128);
|
|
prediction128 = _mm_srai_epi32(prediction128, shift);
|
|
prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
|
|
samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
|
|
samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
|
|
samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
|
|
riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
|
|
}
|
|
}
|
|
_mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
|
|
pDecodedSamples += 4;
|
|
}
|
|
i = (count & ~3);
|
|
while (i < (int)count) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamParts0 &= riceParamMask;
|
|
riceParamParts0 |= (zeroCountParts0 << riceParam);
|
|
riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
|
|
pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
|
|
i += 1;
|
|
pDecodedSamples += 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
int i;
|
|
ma_uint32 riceParamMask;
|
|
ma_int32* pDecodedSamples = pSamplesOut;
|
|
ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
|
|
ma_uint32 zeroCountParts0 = 0;
|
|
ma_uint32 zeroCountParts1 = 0;
|
|
ma_uint32 zeroCountParts2 = 0;
|
|
ma_uint32 zeroCountParts3 = 0;
|
|
ma_uint32 riceParamParts0 = 0;
|
|
ma_uint32 riceParamParts1 = 0;
|
|
ma_uint32 riceParamParts2 = 0;
|
|
ma_uint32 riceParamParts3 = 0;
|
|
__m128i coefficients128_0;
|
|
__m128i coefficients128_4;
|
|
__m128i coefficients128_8;
|
|
__m128i samples128_0;
|
|
__m128i samples128_4;
|
|
__m128i samples128_8;
|
|
__m128i prediction128;
|
|
__m128i riceParamMask128;
|
|
const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
|
|
MA_DR_FLAC_ASSERT(order <= 12);
|
|
riceParamMask = (ma_uint32)~((~0UL) << riceParam);
|
|
riceParamMask128 = _mm_set1_epi32(riceParamMask);
|
|
prediction128 = _mm_setzero_si128();
|
|
coefficients128_0 = _mm_setzero_si128();
|
|
coefficients128_4 = _mm_setzero_si128();
|
|
coefficients128_8 = _mm_setzero_si128();
|
|
samples128_0 = _mm_setzero_si128();
|
|
samples128_4 = _mm_setzero_si128();
|
|
samples128_8 = _mm_setzero_si128();
|
|
#if 1
|
|
{
|
|
int runningOrder = order;
|
|
if (runningOrder >= 4) {
|
|
coefficients128_0 = _mm_loadu_si128((const __m128i*)(coefficients + 0));
|
|
samples128_0 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 4));
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: coefficients128_0 = _mm_set_epi32(0, coefficients[2], coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], pSamplesOut[-3], 0); break;
|
|
case 2: coefficients128_0 = _mm_set_epi32(0, 0, coefficients[1], coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], pSamplesOut[-2], 0, 0); break;
|
|
case 1: coefficients128_0 = _mm_set_epi32(0, 0, 0, coefficients[0]); samples128_0 = _mm_set_epi32(pSamplesOut[-1], 0, 0, 0); break;
|
|
}
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder >= 4) {
|
|
coefficients128_4 = _mm_loadu_si128((const __m128i*)(coefficients + 4));
|
|
samples128_4 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 8));
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: coefficients128_4 = _mm_set_epi32(0, coefficients[6], coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], pSamplesOut[-7], 0); break;
|
|
case 2: coefficients128_4 = _mm_set_epi32(0, 0, coefficients[5], coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], pSamplesOut[-6], 0, 0); break;
|
|
case 1: coefficients128_4 = _mm_set_epi32(0, 0, 0, coefficients[4]); samples128_4 = _mm_set_epi32(pSamplesOut[-5], 0, 0, 0); break;
|
|
}
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder == 4) {
|
|
coefficients128_8 = _mm_loadu_si128((const __m128i*)(coefficients + 8));
|
|
samples128_8 = _mm_loadu_si128((const __m128i*)(pSamplesOut - 12));
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: coefficients128_8 = _mm_set_epi32(0, coefficients[10], coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], pSamplesOut[-11], 0); break;
|
|
case 2: coefficients128_8 = _mm_set_epi32(0, 0, coefficients[9], coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], pSamplesOut[-10], 0, 0); break;
|
|
case 1: coefficients128_8 = _mm_set_epi32(0, 0, 0, coefficients[8]); samples128_8 = _mm_set_epi32(pSamplesOut[-9], 0, 0, 0); break;
|
|
}
|
|
runningOrder = 0;
|
|
}
|
|
coefficients128_0 = _mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(0, 1, 2, 3));
|
|
coefficients128_4 = _mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(0, 1, 2, 3));
|
|
coefficients128_8 = _mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(0, 1, 2, 3));
|
|
}
|
|
#else
|
|
switch (order)
|
|
{
|
|
case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12];
|
|
case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11];
|
|
case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10];
|
|
case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9];
|
|
case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8];
|
|
case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7];
|
|
case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6];
|
|
case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5];
|
|
case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4];
|
|
case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3];
|
|
case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2];
|
|
case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1];
|
|
}
|
|
#endif
|
|
while (pDecodedSamples < pDecodedSamplesEnd) {
|
|
__m128i zeroCountPart128;
|
|
__m128i riceParamPart128;
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) {
|
|
return MA_FALSE;
|
|
}
|
|
zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0);
|
|
riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0);
|
|
riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128);
|
|
riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam));
|
|
riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1)));
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = _mm_xor_si128(prediction128, prediction128);
|
|
switch (order)
|
|
{
|
|
case 12:
|
|
case 11: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(1, 1, 0, 0))));
|
|
case 10:
|
|
case 9: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_8, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_8, _MM_SHUFFLE(3, 3, 2, 2))));
|
|
case 8:
|
|
case 7: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(1, 1, 0, 0))));
|
|
case 6:
|
|
case 5: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_4, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_4, _MM_SHUFFLE(3, 3, 2, 2))));
|
|
case 4:
|
|
case 3: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(1, 1, 0, 0)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(1, 1, 0, 0))));
|
|
case 2:
|
|
case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2))));
|
|
}
|
|
prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128);
|
|
prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift);
|
|
prediction128 = _mm_add_epi32(riceParamPart128, prediction128);
|
|
samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4);
|
|
samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4);
|
|
samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4);
|
|
riceParamPart128 = _mm_alignr_epi8(_mm_setzero_si128(), riceParamPart128, 4);
|
|
}
|
|
_mm_storeu_si128((__m128i*)pDecodedSamples, samples128_0);
|
|
pDecodedSamples += 4;
|
|
}
|
|
i = (count & ~3);
|
|
while (i < (int)count) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamParts0 &= riceParamMask;
|
|
riceParamParts0 |= (zeroCountParts0 << riceParam);
|
|
riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01];
|
|
pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
|
|
i += 1;
|
|
pDecodedSamples += 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
|
|
if (lpcOrder > 0 && lpcOrder <= 12) {
|
|
if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
|
|
} else {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
|
|
}
|
|
} else {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x)
|
|
{
|
|
vst1q_s32(p+0, x.val[0]);
|
|
vst1q_s32(p+4, x.val[1]);
|
|
}
|
|
static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x)
|
|
{
|
|
vst1q_u32(p+0, x.val[0]);
|
|
vst1q_u32(p+4, x.val[1]);
|
|
}
|
|
static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x)
|
|
{
|
|
vst1q_f32(p+0, x.val[0]);
|
|
vst1q_f32(p+4, x.val[1]);
|
|
}
|
|
static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x)
|
|
{
|
|
vst1q_s16(p, vcombine_s16(x.val[0], x.val[1]));
|
|
}
|
|
static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x)
|
|
{
|
|
vst1q_u16(p, vcombine_u16(x.val[0], x.val[1]));
|
|
}
|
|
static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0)
|
|
{
|
|
ma_int32 x[4];
|
|
x[3] = x3;
|
|
x[2] = x2;
|
|
x[1] = x1;
|
|
x[0] = x0;
|
|
return vld1q_s32(x);
|
|
}
|
|
static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b)
|
|
{
|
|
return vextq_s32(b, a, 1);
|
|
}
|
|
static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b)
|
|
{
|
|
return vextq_u32(b, a, 1);
|
|
}
|
|
static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x)
|
|
{
|
|
int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x));
|
|
return vpadd_s32(r, r);
|
|
}
|
|
static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x)
|
|
{
|
|
return vadd_s64(vget_high_s64(x), vget_low_s64(x));
|
|
}
|
|
static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x)
|
|
{
|
|
return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x)));
|
|
}
|
|
static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x)
|
|
{
|
|
return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF));
|
|
}
|
|
static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x)
|
|
{
|
|
return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF));
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
int i;
|
|
ma_uint32 riceParamMask;
|
|
ma_int32* pDecodedSamples = pSamplesOut;
|
|
ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
|
|
ma_uint32 zeroCountParts[4];
|
|
ma_uint32 riceParamParts[4];
|
|
int32x4_t coefficients128_0;
|
|
int32x4_t coefficients128_4;
|
|
int32x4_t coefficients128_8;
|
|
int32x4_t samples128_0;
|
|
int32x4_t samples128_4;
|
|
int32x4_t samples128_8;
|
|
uint32x4_t riceParamMask128;
|
|
int32x4_t riceParam128;
|
|
int32x2_t shift64;
|
|
uint32x4_t one128;
|
|
const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
|
|
riceParamMask = (ma_uint32)~((~0UL) << riceParam);
|
|
riceParamMask128 = vdupq_n_u32(riceParamMask);
|
|
riceParam128 = vdupq_n_s32(riceParam);
|
|
shift64 = vdup_n_s32(-shift);
|
|
one128 = vdupq_n_u32(1);
|
|
{
|
|
int runningOrder = order;
|
|
ma_int32 tempC[4] = {0, 0, 0, 0};
|
|
ma_int32 tempS[4] = {0, 0, 0, 0};
|
|
if (runningOrder >= 4) {
|
|
coefficients128_0 = vld1q_s32(coefficients + 0);
|
|
samples128_0 = vld1q_s32(pSamplesOut - 4);
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
|
|
case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
|
|
case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
|
|
}
|
|
coefficients128_0 = vld1q_s32(tempC);
|
|
samples128_0 = vld1q_s32(tempS);
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder >= 4) {
|
|
coefficients128_4 = vld1q_s32(coefficients + 4);
|
|
samples128_4 = vld1q_s32(pSamplesOut - 8);
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
|
|
case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
|
|
case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
|
|
}
|
|
coefficients128_4 = vld1q_s32(tempC);
|
|
samples128_4 = vld1q_s32(tempS);
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder == 4) {
|
|
coefficients128_8 = vld1q_s32(coefficients + 8);
|
|
samples128_8 = vld1q_s32(pSamplesOut - 12);
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
|
|
case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
|
|
case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
|
|
}
|
|
coefficients128_8 = vld1q_s32(tempC);
|
|
samples128_8 = vld1q_s32(tempS);
|
|
runningOrder = 0;
|
|
}
|
|
coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);
|
|
coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);
|
|
coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);
|
|
}
|
|
while (pDecodedSamples < pDecodedSamplesEnd) {
|
|
int32x4_t prediction128;
|
|
int32x2_t prediction64;
|
|
uint32x4_t zeroCountPart128;
|
|
uint32x4_t riceParamPart128;
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
|
|
return MA_FALSE;
|
|
}
|
|
zeroCountPart128 = vld1q_u32(zeroCountParts);
|
|
riceParamPart128 = vld1q_u32(riceParamParts);
|
|
riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
|
|
riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
|
|
riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
|
|
if (order <= 4) {
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = vmulq_s32(coefficients128_0, samples128_0);
|
|
prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
|
|
prediction64 = vshl_s32(prediction64, shift64);
|
|
prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
|
|
samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
|
|
riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
|
|
}
|
|
} else if (order <= 8) {
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = vmulq_s32(coefficients128_4, samples128_4);
|
|
prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
|
|
prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
|
|
prediction64 = vshl_s32(prediction64, shift64);
|
|
prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
|
|
samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
|
|
samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
|
|
riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
|
|
}
|
|
} else {
|
|
for (i = 0; i < 4; i += 1) {
|
|
prediction128 = vmulq_s32(coefficients128_8, samples128_8);
|
|
prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4);
|
|
prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0);
|
|
prediction64 = ma_dr_flac__vhaddq_s32(prediction128);
|
|
prediction64 = vshl_s32(prediction64, shift64);
|
|
prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128)));
|
|
samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);
|
|
samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
|
|
samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0);
|
|
riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
|
|
}
|
|
}
|
|
vst1q_s32(pDecodedSamples, samples128_0);
|
|
pDecodedSamples += 4;
|
|
}
|
|
i = (count & ~3);
|
|
while (i < (int)count) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamParts[0] &= riceParamMask;
|
|
riceParamParts[0] |= (zeroCountParts[0] << riceParam);
|
|
riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
|
|
pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples);
|
|
i += 1;
|
|
pDecodedSamples += 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
int i;
|
|
ma_uint32 riceParamMask;
|
|
ma_int32* pDecodedSamples = pSamplesOut;
|
|
ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3);
|
|
ma_uint32 zeroCountParts[4];
|
|
ma_uint32 riceParamParts[4];
|
|
int32x4_t coefficients128_0;
|
|
int32x4_t coefficients128_4;
|
|
int32x4_t coefficients128_8;
|
|
int32x4_t samples128_0;
|
|
int32x4_t samples128_4;
|
|
int32x4_t samples128_8;
|
|
uint32x4_t riceParamMask128;
|
|
int32x4_t riceParam128;
|
|
int64x1_t shift64;
|
|
uint32x4_t one128;
|
|
int64x2_t prediction128 = { 0 };
|
|
uint32x4_t zeroCountPart128;
|
|
uint32x4_t riceParamPart128;
|
|
const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF};
|
|
riceParamMask = (ma_uint32)~((~0UL) << riceParam);
|
|
riceParamMask128 = vdupq_n_u32(riceParamMask);
|
|
riceParam128 = vdupq_n_s32(riceParam);
|
|
shift64 = vdup_n_s64(-shift);
|
|
one128 = vdupq_n_u32(1);
|
|
{
|
|
int runningOrder = order;
|
|
ma_int32 tempC[4] = {0, 0, 0, 0};
|
|
ma_int32 tempS[4] = {0, 0, 0, 0};
|
|
if (runningOrder >= 4) {
|
|
coefficients128_0 = vld1q_s32(coefficients + 0);
|
|
samples128_0 = vld1q_s32(pSamplesOut - 4);
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: tempC[2] = coefficients[2]; tempS[1] = pSamplesOut[-3];
|
|
case 2: tempC[1] = coefficients[1]; tempS[2] = pSamplesOut[-2];
|
|
case 1: tempC[0] = coefficients[0]; tempS[3] = pSamplesOut[-1];
|
|
}
|
|
coefficients128_0 = vld1q_s32(tempC);
|
|
samples128_0 = vld1q_s32(tempS);
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder >= 4) {
|
|
coefficients128_4 = vld1q_s32(coefficients + 4);
|
|
samples128_4 = vld1q_s32(pSamplesOut - 8);
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: tempC[2] = coefficients[6]; tempS[1] = pSamplesOut[-7];
|
|
case 2: tempC[1] = coefficients[5]; tempS[2] = pSamplesOut[-6];
|
|
case 1: tempC[0] = coefficients[4]; tempS[3] = pSamplesOut[-5];
|
|
}
|
|
coefficients128_4 = vld1q_s32(tempC);
|
|
samples128_4 = vld1q_s32(tempS);
|
|
runningOrder = 0;
|
|
}
|
|
if (runningOrder == 4) {
|
|
coefficients128_8 = vld1q_s32(coefficients + 8);
|
|
samples128_8 = vld1q_s32(pSamplesOut - 12);
|
|
runningOrder -= 4;
|
|
} else {
|
|
switch (runningOrder) {
|
|
case 3: tempC[2] = coefficients[10]; tempS[1] = pSamplesOut[-11];
|
|
case 2: tempC[1] = coefficients[ 9]; tempS[2] = pSamplesOut[-10];
|
|
case 1: tempC[0] = coefficients[ 8]; tempS[3] = pSamplesOut[- 9];
|
|
}
|
|
coefficients128_8 = vld1q_s32(tempC);
|
|
samples128_8 = vld1q_s32(tempS);
|
|
runningOrder = 0;
|
|
}
|
|
coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0);
|
|
coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4);
|
|
coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8);
|
|
}
|
|
while (pDecodedSamples < pDecodedSamplesEnd) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) ||
|
|
!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) {
|
|
return MA_FALSE;
|
|
}
|
|
zeroCountPart128 = vld1q_u32(zeroCountParts);
|
|
riceParamPart128 = vld1q_u32(riceParamParts);
|
|
riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128);
|
|
riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128));
|
|
riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128));
|
|
for (i = 0; i < 4; i += 1) {
|
|
int64x1_t prediction64;
|
|
prediction128 = veorq_s64(prediction128, prediction128);
|
|
switch (order)
|
|
{
|
|
case 12:
|
|
case 11: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_8), vget_low_s32(samples128_8)));
|
|
case 10:
|
|
case 9: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_8), vget_high_s32(samples128_8)));
|
|
case 8:
|
|
case 7: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_4), vget_low_s32(samples128_4)));
|
|
case 6:
|
|
case 5: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_4), vget_high_s32(samples128_4)));
|
|
case 4:
|
|
case 3: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_low_s32(coefficients128_0), vget_low_s32(samples128_0)));
|
|
case 2:
|
|
case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0)));
|
|
}
|
|
prediction64 = ma_dr_flac__vhaddq_s64(prediction128);
|
|
prediction64 = vshl_s64(prediction64, shift64);
|
|
prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0)));
|
|
samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8);
|
|
samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4);
|
|
samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0);
|
|
riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128);
|
|
}
|
|
vst1q_s32(pDecodedSamples, samples128_0);
|
|
pDecodedSamples += 4;
|
|
}
|
|
i = (count & ~3);
|
|
while (i < (int)count) {
|
|
if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) {
|
|
return MA_FALSE;
|
|
}
|
|
riceParamParts[0] &= riceParamMask;
|
|
riceParamParts[0] |= (zeroCountParts[0] << riceParam);
|
|
riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01];
|
|
pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples);
|
|
i += 1;
|
|
pDecodedSamples += 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
|
|
if (lpcOrder > 0 && lpcOrder <= 12) {
|
|
if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
|
|
} else {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut);
|
|
}
|
|
} else {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
|
|
}
|
|
}
|
|
#endif
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE41)
|
|
if (ma_dr_flac__gIsSSE41Supported) {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported) {
|
|
return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
|
|
#else
|
|
return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut);
|
|
#endif
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam)
|
|
{
|
|
ma_uint32 i;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
for (i = 0; i < count; ++i) {
|
|
if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
#if defined(__clang__)
|
|
__attribute__((no_sanitize("signed-integer-overflow")))
|
|
#endif
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut)
|
|
{
|
|
ma_uint32 i;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31);
|
|
MA_DR_FLAC_ASSERT(pSamplesOut != NULL);
|
|
for (i = 0; i < count; ++i) {
|
|
if (unencodedBitsPerSample > 0) {
|
|
if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
pSamplesOut[i] = 0;
|
|
}
|
|
if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) {
|
|
pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
|
|
} else {
|
|
pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i);
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_uint8 residualMethod;
|
|
ma_uint8 partitionOrder;
|
|
ma_uint32 samplesInPartition;
|
|
ma_uint32 partitionsRemaining;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(blockSize != 0);
|
|
MA_DR_FLAC_ASSERT(pDecodedSamples != NULL);
|
|
if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
|
|
return MA_FALSE;
|
|
}
|
|
pDecodedSamples += lpcOrder;
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (partitionOrder > 8) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((blockSize / (1 << partitionOrder)) < lpcOrder) {
|
|
return MA_FALSE;
|
|
}
|
|
samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder;
|
|
partitionsRemaining = (1 << partitionOrder);
|
|
for (;;) {
|
|
ma_uint8 riceParam = 0;
|
|
if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (riceParam == 15) {
|
|
riceParam = 0xFF;
|
|
}
|
|
} else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
|
|
if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (riceParam == 31) {
|
|
riceParam = 0xFF;
|
|
}
|
|
}
|
|
if (riceParam != 0xFF) {
|
|
if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
ma_uint8 unencodedBitsPerSample = 0;
|
|
if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
pDecodedSamples += samplesInPartition;
|
|
if (partitionsRemaining == 1) {
|
|
break;
|
|
}
|
|
partitionsRemaining -= 1;
|
|
if (partitionOrder != 0) {
|
|
samplesInPartition = blockSize / (1 << partitionOrder);
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order)
|
|
{
|
|
ma_uint8 residualMethod;
|
|
ma_uint8 partitionOrder;
|
|
ma_uint32 samplesInPartition;
|
|
ma_uint32 partitionsRemaining;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(blockSize != 0);
|
|
if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (partitionOrder > 8) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((blockSize / (1 << partitionOrder)) <= order) {
|
|
return MA_FALSE;
|
|
}
|
|
samplesInPartition = (blockSize / (1 << partitionOrder)) - order;
|
|
partitionsRemaining = (1 << partitionOrder);
|
|
for (;;)
|
|
{
|
|
ma_uint8 riceParam = 0;
|
|
if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) {
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (riceParam == 15) {
|
|
riceParam = 0xFF;
|
|
}
|
|
} else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) {
|
|
if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (riceParam == 31) {
|
|
riceParam = 0xFF;
|
|
}
|
|
}
|
|
if (riceParam != 0xFF) {
|
|
if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
ma_uint8 unencodedBitsPerSample = 0;
|
|
if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (partitionsRemaining == 1) {
|
|
break;
|
|
}
|
|
partitionsRemaining -= 1;
|
|
samplesInPartition = blockSize / (1 << partitionOrder);
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_uint32 i;
|
|
ma_int32 sample;
|
|
if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
|
|
return MA_FALSE;
|
|
}
|
|
for (i = 0; i < blockSize; ++i) {
|
|
pDecodedSamples[i] = sample;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_uint32 i;
|
|
for (i = 0; i < blockSize; ++i) {
|
|
ma_int32 sample;
|
|
if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
|
|
return MA_FALSE;
|
|
}
|
|
pDecodedSamples[i] = sample;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_uint32 i;
|
|
static ma_int32 lpcCoefficientsTable[5][4] = {
|
|
{0, 0, 0, 0},
|
|
{1, 0, 0, 0},
|
|
{2, -1, 0, 0},
|
|
{3, -3, 1, 0},
|
|
{4, -6, 4, -1}
|
|
};
|
|
for (i = 0; i < lpcOrder; ++i) {
|
|
ma_int32 sample;
|
|
if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) {
|
|
return MA_FALSE;
|
|
}
|
|
pDecodedSamples[i] = sample;
|
|
}
|
|
if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples)
|
|
{
|
|
ma_uint8 i;
|
|
ma_uint8 lpcPrecision;
|
|
ma_int8 lpcShift;
|
|
ma_int32 coefficients[32];
|
|
for (i = 0; i < lpcOrder; ++i) {
|
|
ma_int32 sample;
|
|
if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) {
|
|
return MA_FALSE;
|
|
}
|
|
pDecodedSamples[i] = sample;
|
|
}
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (lpcPrecision == 15) {
|
|
return MA_FALSE;
|
|
}
|
|
lpcPrecision += 1;
|
|
if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (lpcShift < 0) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients));
|
|
for (i = 0; i < lpcOrder; ++i) {
|
|
if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header)
|
|
{
|
|
const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000};
|
|
const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1};
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(header != NULL);
|
|
for (;;) {
|
|
ma_uint8 crc8 = 0xCE;
|
|
ma_uint8 reserved = 0;
|
|
ma_uint8 blockingStrategy = 0;
|
|
ma_uint8 blockSize = 0;
|
|
ma_uint8 sampleRate = 0;
|
|
ma_uint8 channelAssignment = 0;
|
|
ma_uint8 bitsPerSample = 0;
|
|
ma_bool32 isVariableBlockSize;
|
|
if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (reserved == 1) {
|
|
continue;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, reserved, 1);
|
|
if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1);
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (blockSize == 0) {
|
|
continue;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, blockSize, 4);
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4);
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (channelAssignment > 10) {
|
|
continue;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4);
|
|
if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (bitsPerSample == 3 || bitsPerSample == 7) {
|
|
continue;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3);
|
|
if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (reserved == 1) {
|
|
continue;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, reserved, 1);
|
|
isVariableBlockSize = blockingStrategy == 1;
|
|
if (isVariableBlockSize) {
|
|
ma_uint64 pcmFrameNumber;
|
|
ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8);
|
|
if (result != MA_SUCCESS) {
|
|
if (result == MA_AT_END) {
|
|
return MA_FALSE;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
header->flacFrameNumber = 0;
|
|
header->pcmFrameNumber = pcmFrameNumber;
|
|
} else {
|
|
ma_uint64 flacFrameNumber = 0;
|
|
ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8);
|
|
if (result != MA_SUCCESS) {
|
|
if (result == MA_AT_END) {
|
|
return MA_FALSE;
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
header->flacFrameNumber = (ma_uint32)flacFrameNumber;
|
|
header->pcmFrameNumber = 0;
|
|
}
|
|
MA_DR_FLAC_ASSERT(blockSize > 0);
|
|
if (blockSize == 1) {
|
|
header->blockSizeInPCMFrames = 192;
|
|
} else if (blockSize <= 5) {
|
|
MA_DR_FLAC_ASSERT(blockSize >= 2);
|
|
header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2));
|
|
} else if (blockSize == 6) {
|
|
if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8);
|
|
header->blockSizeInPCMFrames += 1;
|
|
} else if (blockSize == 7) {
|
|
if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16);
|
|
if (header->blockSizeInPCMFrames == 0xFFFF) {
|
|
return MA_FALSE;
|
|
}
|
|
header->blockSizeInPCMFrames += 1;
|
|
} else {
|
|
MA_DR_FLAC_ASSERT(blockSize >= 8);
|
|
header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8));
|
|
}
|
|
if (sampleRate <= 11) {
|
|
header->sampleRate = sampleRateTable[sampleRate];
|
|
} else if (sampleRate == 12) {
|
|
if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8);
|
|
header->sampleRate *= 1000;
|
|
} else if (sampleRate == 13) {
|
|
if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);
|
|
} else if (sampleRate == 14) {
|
|
if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) {
|
|
return MA_FALSE;
|
|
}
|
|
crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16);
|
|
header->sampleRate *= 10;
|
|
} else {
|
|
continue;
|
|
}
|
|
header->channelAssignment = channelAssignment;
|
|
header->bitsPerSample = bitsPerSampleTable[bitsPerSample];
|
|
if (header->bitsPerSample == 0) {
|
|
header->bitsPerSample = streaminfoBitsPerSample;
|
|
}
|
|
if (header->bitsPerSample != streaminfoBitsPerSample) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) {
|
|
return MA_FALSE;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
if (header->crc8 != crc8) {
|
|
continue;
|
|
}
|
|
#endif
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe)
|
|
{
|
|
ma_uint8 header;
|
|
int type;
|
|
if (!ma_dr_flac__read_uint8(bs, 8, &header)) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((header & 0x80) != 0) {
|
|
return MA_FALSE;
|
|
}
|
|
pSubframe->lpcOrder = 0;
|
|
type = (header & 0x7E) >> 1;
|
|
if (type == 0) {
|
|
pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT;
|
|
} else if (type == 1) {
|
|
pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM;
|
|
} else {
|
|
if ((type & 0x20) != 0) {
|
|
pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC;
|
|
pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1;
|
|
} else if ((type & 0x08) != 0) {
|
|
pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED;
|
|
pSubframe->lpcOrder = (ma_uint8)(type & 0x07);
|
|
if (pSubframe->lpcOrder > 4) {
|
|
pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;
|
|
pSubframe->lpcOrder = 0;
|
|
}
|
|
} else {
|
|
pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED;
|
|
}
|
|
}
|
|
if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) {
|
|
return MA_FALSE;
|
|
}
|
|
pSubframe->wastedBitsPerSample = 0;
|
|
if ((header & 0x01) == 1) {
|
|
unsigned int wastedBitsPerSample;
|
|
if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) {
|
|
return MA_FALSE;
|
|
}
|
|
pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut)
|
|
{
|
|
ma_dr_flac_subframe* pSubframe;
|
|
ma_uint32 subframeBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(frame != NULL);
|
|
pSubframe = frame->subframes + subframeIndex;
|
|
if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {
|
|
return MA_FALSE;
|
|
}
|
|
subframeBitsPerSample = frame->header.bitsPerSample;
|
|
if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
|
|
subframeBitsPerSample += 1;
|
|
} else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
|
|
subframeBitsPerSample += 1;
|
|
}
|
|
if (subframeBitsPerSample > 32) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
|
|
return MA_FALSE;
|
|
}
|
|
subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
|
|
pSubframe->pSamplesS32 = pDecodedSamplesOut;
|
|
if (frame->header.blockSizeInPCMFrames < pSubframe->lpcOrder) {
|
|
return MA_FALSE;
|
|
}
|
|
switch (pSubframe->subframeType)
|
|
{
|
|
case MA_DR_FLAC_SUBFRAME_CONSTANT:
|
|
{
|
|
ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
|
|
} break;
|
|
case MA_DR_FLAC_SUBFRAME_VERBATIM:
|
|
{
|
|
ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32);
|
|
} break;
|
|
case MA_DR_FLAC_SUBFRAME_FIXED:
|
|
{
|
|
ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
|
|
} break;
|
|
case MA_DR_FLAC_SUBFRAME_LPC:
|
|
{
|
|
ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32);
|
|
} break;
|
|
default: return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex)
|
|
{
|
|
ma_dr_flac_subframe* pSubframe;
|
|
ma_uint32 subframeBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(bs != NULL);
|
|
MA_DR_FLAC_ASSERT(frame != NULL);
|
|
pSubframe = frame->subframes + subframeIndex;
|
|
if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) {
|
|
return MA_FALSE;
|
|
}
|
|
subframeBitsPerSample = frame->header.bitsPerSample;
|
|
if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) {
|
|
subframeBitsPerSample += 1;
|
|
} else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) {
|
|
subframeBitsPerSample += 1;
|
|
}
|
|
if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) {
|
|
return MA_FALSE;
|
|
}
|
|
subframeBitsPerSample -= pSubframe->wastedBitsPerSample;
|
|
pSubframe->pSamplesS32 = NULL;
|
|
switch (pSubframe->subframeType)
|
|
{
|
|
case MA_DR_FLAC_SUBFRAME_CONSTANT:
|
|
{
|
|
if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) {
|
|
return MA_FALSE;
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_SUBFRAME_VERBATIM:
|
|
{
|
|
unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample;
|
|
if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
|
|
return MA_FALSE;
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_SUBFRAME_FIXED:
|
|
{
|
|
unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
|
|
if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
|
|
return MA_FALSE;
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_SUBFRAME_LPC:
|
|
{
|
|
ma_uint8 lpcPrecision;
|
|
unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample;
|
|
if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (lpcPrecision == 15) {
|
|
return MA_FALSE;
|
|
}
|
|
lpcPrecision += 1;
|
|
bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5;
|
|
if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) {
|
|
return MA_FALSE;
|
|
}
|
|
} break;
|
|
default: return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment)
|
|
{
|
|
ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2};
|
|
MA_DR_FLAC_ASSERT(channelAssignment <= 10);
|
|
return lookup[channelAssignment];
|
|
}
|
|
static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac)
|
|
{
|
|
int channelCount;
|
|
int i;
|
|
ma_uint8 paddingSizeInBits;
|
|
ma_uint16 desiredCRC16;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_uint16 actualCRC16;
|
|
#endif
|
|
MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes));
|
|
if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) {
|
|
return MA_ERROR;
|
|
}
|
|
channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
|
|
if (channelCount != (int)pFlac->channels) {
|
|
return MA_ERROR;
|
|
}
|
|
for (i = 0; i < channelCount; ++i) {
|
|
if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) {
|
|
return MA_ERROR;
|
|
}
|
|
}
|
|
paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7);
|
|
if (paddingSizeInBits > 0) {
|
|
ma_uint8 padding = 0;
|
|
if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) {
|
|
return MA_AT_END;
|
|
}
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);
|
|
#endif
|
|
if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
|
|
return MA_AT_END;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
if (actualCRC16 != desiredCRC16) {
|
|
return MA_CRC_MISMATCH;
|
|
}
|
|
#endif
|
|
pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
|
|
return MA_SUCCESS;
|
|
}
|
|
static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac)
|
|
{
|
|
int channelCount;
|
|
int i;
|
|
ma_uint16 desiredCRC16;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_uint16 actualCRC16;
|
|
#endif
|
|
channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
|
|
for (i = 0; i < channelCount; ++i) {
|
|
if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) {
|
|
return MA_ERROR;
|
|
}
|
|
}
|
|
if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) {
|
|
return MA_ERROR;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs);
|
|
#endif
|
|
if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) {
|
|
return MA_AT_END;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
if (actualCRC16 != desiredCRC16) {
|
|
return MA_CRC_MISMATCH;
|
|
}
|
|
#endif
|
|
return MA_SUCCESS;
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac)
|
|
{
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
for (;;) {
|
|
ma_result result;
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
result = ma_dr_flac__decode_flac_frame(pFlac);
|
|
if (result != MA_SUCCESS) {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
continue;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame)
|
|
{
|
|
ma_uint64 firstPCMFrame;
|
|
ma_uint64 lastPCMFrame;
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber;
|
|
if (firstPCMFrame == 0) {
|
|
firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames;
|
|
}
|
|
lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
|
|
if (lastPCMFrame > 0) {
|
|
lastPCMFrame -= 1;
|
|
}
|
|
if (pFirstPCMFrame) {
|
|
*pFirstPCMFrame = firstPCMFrame;
|
|
}
|
|
if (pLastPCMFrame) {
|
|
*pLastPCMFrame = lastPCMFrame;
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac)
|
|
{
|
|
ma_bool32 result;
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes);
|
|
MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
|
|
pFlac->currentPCMFrame = 0;
|
|
return result;
|
|
}
|
|
static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac)
|
|
{
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
return ma_dr_flac__seek_flac_frame(pFlac);
|
|
}
|
|
static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek)
|
|
{
|
|
ma_uint64 pcmFramesRead = 0;
|
|
while (pcmFramesToSeek > 0) {
|
|
if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
|
|
if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) {
|
|
pcmFramesRead += pcmFramesToSeek;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek;
|
|
pcmFramesToSeek = 0;
|
|
} else {
|
|
pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
pcmFramesToSeek -= pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining = 0;
|
|
}
|
|
}
|
|
}
|
|
pFlac->currentPCMFrame += pcmFramesRead;
|
|
return pcmFramesRead;
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
|
|
{
|
|
ma_bool32 isMidFrame = MA_FALSE;
|
|
ma_uint64 runningPCMFrameCount;
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
if (pcmFrameIndex >= pFlac->currentPCMFrame) {
|
|
runningPCMFrameCount = pFlac->currentPCMFrame;
|
|
if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
isMidFrame = MA_TRUE;
|
|
}
|
|
} else {
|
|
runningPCMFrameCount = 0;
|
|
if (!ma_dr_flac__seek_to_first_frame(pFlac)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
for (;;) {
|
|
ma_uint64 pcmFrameCountInThisFLACFrame;
|
|
ma_uint64 firstPCMFrameInFLACFrame = 0;
|
|
ma_uint64 lastPCMFrameInFLACFrame = 0;
|
|
ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
|
|
pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
|
|
if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
|
|
ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
|
|
if (!isMidFrame) {
|
|
ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
goto next_iteration;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
|
|
}
|
|
} else {
|
|
if (!isMidFrame) {
|
|
ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
goto next_iteration;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining = 0;
|
|
isMidFrame = MA_FALSE;
|
|
}
|
|
if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
next_iteration:
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
#if !defined(MA_DR_FLAC_NO_CRC)
|
|
#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f
|
|
static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset)
|
|
{
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL);
|
|
MA_DR_FLAC_ASSERT(targetByte >= rangeLo);
|
|
MA_DR_FLAC_ASSERT(targetByte <= rangeHi);
|
|
*pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes;
|
|
for (;;) {
|
|
ma_uint64 lastTargetByte = targetByte;
|
|
if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) {
|
|
if (targetByte == 0) {
|
|
ma_dr_flac__seek_to_first_frame(pFlac);
|
|
return MA_FALSE;
|
|
}
|
|
targetByte = rangeLo + ((rangeHi - rangeLo)/2);
|
|
rangeHi = targetByte;
|
|
} else {
|
|
MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame));
|
|
#if 1
|
|
if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
|
|
targetByte = rangeLo + ((rangeHi - rangeLo)/2);
|
|
rangeHi = targetByte;
|
|
} else {
|
|
break;
|
|
}
|
|
#else
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
targetByte = rangeLo + ((rangeHi - rangeLo)/2);
|
|
rangeHi = targetByte;
|
|
} else {
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
if(targetByte == lastTargetByte) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
|
|
MA_DR_FLAC_ASSERT(targetByte <= rangeHi);
|
|
*pLastSuccessfulSeekOffset = targetByte;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset)
|
|
{
|
|
#if 0
|
|
if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) {
|
|
if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset;
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi)
|
|
{
|
|
ma_uint64 targetByte;
|
|
ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount;
|
|
ma_uint64 pcmRangeHi = 0;
|
|
ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1;
|
|
ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo;
|
|
ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
|
|
targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO);
|
|
if (targetByte > byteRangeHi) {
|
|
targetByte = byteRangeHi;
|
|
}
|
|
for (;;) {
|
|
if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) {
|
|
ma_uint64 newPCMRangeLo;
|
|
ma_uint64 newPCMRangeHi;
|
|
ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi);
|
|
if (pcmRangeLo == newPCMRangeLo) {
|
|
if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) {
|
|
break;
|
|
}
|
|
if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
|
|
return MA_TRUE;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
pcmRangeLo = newPCMRangeLo;
|
|
pcmRangeHi = newPCMRangeHi;
|
|
if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) {
|
|
if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) {
|
|
return MA_TRUE;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f);
|
|
if (pcmRangeLo > pcmFrameIndex) {
|
|
byteRangeHi = lastSuccessfulSeekOffset;
|
|
if (byteRangeLo > byteRangeHi) {
|
|
byteRangeLo = byteRangeHi;
|
|
}
|
|
targetByte = byteRangeLo + ((byteRangeHi - byteRangeLo) / 2);
|
|
if (targetByte < byteRangeLo) {
|
|
targetByte = byteRangeLo;
|
|
}
|
|
} else {
|
|
if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) {
|
|
if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) {
|
|
return MA_TRUE;
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
byteRangeLo = lastSuccessfulSeekOffset;
|
|
if (byteRangeHi < byteRangeLo) {
|
|
byteRangeHi = byteRangeLo;
|
|
}
|
|
targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio);
|
|
if (targetByte > byteRangeHi) {
|
|
targetByte = byteRangeHi;
|
|
}
|
|
if (closestSeekOffsetBeforeTargetPCMFrame < lastSuccessfulSeekOffset) {
|
|
closestSeekOffsetBeforeTargetPCMFrame = lastSuccessfulSeekOffset;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
ma_dr_flac__seek_to_first_frame(pFlac);
|
|
return MA_FALSE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
|
|
{
|
|
ma_uint64 byteRangeLo;
|
|
ma_uint64 byteRangeHi;
|
|
ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096;
|
|
if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pcmFrameIndex < seekForwardThreshold) {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex;
|
|
}
|
|
byteRangeLo = pFlac->firstFLACFramePosInBytes;
|
|
byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
|
|
return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi);
|
|
}
|
|
#endif
|
|
static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
|
|
{
|
|
ma_uint32 iClosestSeekpoint = 0;
|
|
ma_bool32 isMidFrame = MA_FALSE;
|
|
ma_uint64 runningPCMFrameCount;
|
|
ma_uint32 iSeekpoint;
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) {
|
|
return MA_FALSE;
|
|
}
|
|
for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) {
|
|
if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) {
|
|
break;
|
|
}
|
|
iClosestSeekpoint = iSeekpoint;
|
|
}
|
|
if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) {
|
|
return MA_FALSE;
|
|
}
|
|
#if !defined(MA_DR_FLAC_NO_CRC)
|
|
if (pFlac->totalPCMFrameCount > 0) {
|
|
ma_uint64 byteRangeLo;
|
|
ma_uint64 byteRangeHi;
|
|
byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f);
|
|
byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset;
|
|
if (iClosestSeekpoint < pFlac->seekpointCount-1) {
|
|
ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1;
|
|
if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) {
|
|
byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1;
|
|
}
|
|
}
|
|
if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
|
|
if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL);
|
|
if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) {
|
|
runningPCMFrameCount = pFlac->currentPCMFrame;
|
|
if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
isMidFrame = MA_TRUE;
|
|
}
|
|
} else {
|
|
runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame;
|
|
if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
for (;;) {
|
|
ma_uint64 pcmFrameCountInThisFLACFrame;
|
|
ma_uint64 firstPCMFrameInFLACFrame = 0;
|
|
ma_uint64 lastPCMFrameInFLACFrame = 0;
|
|
ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
|
|
pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
|
|
if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) {
|
|
ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount;
|
|
if (!isMidFrame) {
|
|
ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
goto next_iteration;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
|
|
}
|
|
} else {
|
|
if (!isMidFrame) {
|
|
ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
runningPCMFrameCount += pcmFrameCountInThisFLACFrame;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
goto next_iteration;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining = 0;
|
|
isMidFrame = MA_FALSE;
|
|
}
|
|
if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
next_iteration:
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
typedef struct
|
|
{
|
|
ma_uint8 capturePattern[4];
|
|
ma_uint8 structureVersion;
|
|
ma_uint8 headerType;
|
|
ma_uint64 granulePosition;
|
|
ma_uint32 serialNumber;
|
|
ma_uint32 sequenceNumber;
|
|
ma_uint32 checksum;
|
|
ma_uint8 segmentCount;
|
|
ma_uint8 segmentTable[255];
|
|
} ma_dr_flac_ogg_page_header;
|
|
#endif
|
|
typedef struct
|
|
{
|
|
ma_dr_flac_read_proc onRead;
|
|
ma_dr_flac_seek_proc onSeek;
|
|
ma_dr_flac_tell_proc onTell;
|
|
ma_dr_flac_meta_proc onMeta;
|
|
ma_dr_flac_container container;
|
|
void* pUserData;
|
|
void* pUserDataMD;
|
|
ma_uint32 sampleRate;
|
|
ma_uint8 channels;
|
|
ma_uint8 bitsPerSample;
|
|
ma_uint64 totalPCMFrameCount;
|
|
ma_uint16 maxBlockSizeInPCMFrames;
|
|
ma_uint64 runningFilePos;
|
|
ma_bool32 hasStreamInfoBlock;
|
|
ma_bool32 hasMetadataBlocks;
|
|
ma_dr_flac_bs bs;
|
|
ma_dr_flac_frame_header firstFrameHeader;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
ma_uint32 oggSerial;
|
|
ma_uint64 oggFirstBytePos;
|
|
ma_dr_flac_ogg_page_header oggBosHeader;
|
|
#endif
|
|
} ma_dr_flac_init_info;
|
|
static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)
|
|
{
|
|
blockHeader = ma_dr_flac__be2host_32(blockHeader);
|
|
*isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31);
|
|
*blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24);
|
|
*blockSize = (blockHeader & 0x00FFFFFFUL);
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize)
|
|
{
|
|
ma_uint32 blockHeader;
|
|
*blockSize = 0;
|
|
if (onRead(pUserData, &blockHeader, 4) != 4) {
|
|
return MA_FALSE;
|
|
}
|
|
ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize);
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo)
|
|
{
|
|
ma_uint32 blockSizes;
|
|
ma_uint64 frameSizes = 0;
|
|
ma_uint64 importantProps;
|
|
ma_uint8 md5[16];
|
|
if (onRead(pUserData, &blockSizes, 4) != 4) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, &frameSizes, 6) != 6) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, &importantProps, 8) != 8) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) {
|
|
return MA_FALSE;
|
|
}
|
|
blockSizes = ma_dr_flac__be2host_32(blockSizes);
|
|
frameSizes = ma_dr_flac__be2host_64(frameSizes);
|
|
importantProps = ma_dr_flac__be2host_64(importantProps);
|
|
pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16);
|
|
pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF);
|
|
pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40);
|
|
pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16);
|
|
pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44);
|
|
pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1;
|
|
pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1;
|
|
pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF)));
|
|
MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5));
|
|
return MA_TRUE;
|
|
}
|
|
static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_DR_FLAC_MALLOC(sz);
|
|
}
|
|
static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_DR_FLAC_REALLOC(p, sz);
|
|
}
|
|
static void ma_dr_flac__free_default(void* p, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
MA_DR_FLAC_FREE(p);
|
|
}
|
|
static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks == NULL) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks->onMalloc != NULL) {
|
|
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
|
|
}
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
|
|
}
|
|
return NULL;
|
|
}
|
|
static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks == NULL) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
|
|
}
|
|
if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
|
|
void* p2;
|
|
p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
|
|
if (p2 == NULL) {
|
|
return NULL;
|
|
}
|
|
if (p != NULL) {
|
|
MA_DR_FLAC_COPY_MEMORY(p2, p, szOld);
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
}
|
|
return p2;
|
|
}
|
|
return NULL;
|
|
}
|
|
static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (p == NULL || pAllocationCallbacks == NULL) {
|
|
return;
|
|
}
|
|
if (pAllocationCallbacks->onFree != NULL) {
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_uint64 runningFilePos = 42;
|
|
ma_uint64 seektablePos = 0;
|
|
ma_uint32 seektableSize = 0;
|
|
(void)onTell;
|
|
for (;;) {
|
|
ma_dr_flac_metadata metadata;
|
|
ma_uint8 isLastBlock = 0;
|
|
ma_uint8 blockType = 0;
|
|
ma_uint32 blockSize;
|
|
if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
runningFilePos += 4;
|
|
metadata.type = blockType;
|
|
metadata.rawDataSize = 0;
|
|
metadata.rawDataOffset = runningFilePos;
|
|
metadata.pRawData = NULL;
|
|
switch (blockType)
|
|
{
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION:
|
|
{
|
|
if (blockSize < 4) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onMeta) {
|
|
void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
|
|
if (pRawData == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
metadata.pRawData = pRawData;
|
|
metadata.rawDataSize = blockSize;
|
|
metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData);
|
|
metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32));
|
|
metadata.data.application.dataSize = blockSize - sizeof(ma_uint32);
|
|
onMeta(pUserDataMD, &metadata);
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE:
|
|
{
|
|
seektablePos = runningFilePos;
|
|
seektableSize = blockSize;
|
|
if (onMeta) {
|
|
ma_uint32 seekpointCount;
|
|
ma_uint32 iSeekpoint;
|
|
void* pRawData;
|
|
seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;
|
|
pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks);
|
|
if (pRawData == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) {
|
|
ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint;
|
|
if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame);
|
|
pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset);
|
|
pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount);
|
|
}
|
|
metadata.pRawData = pRawData;
|
|
metadata.rawDataSize = blockSize;
|
|
metadata.data.seektable.seekpointCount = seekpointCount;
|
|
metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData;
|
|
onMeta(pUserDataMD, &metadata);
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT:
|
|
{
|
|
if (blockSize < 8) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onMeta) {
|
|
void* pRawData;
|
|
const char* pRunningData;
|
|
const char* pRunningDataEnd;
|
|
ma_uint32 i;
|
|
pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
|
|
if (pRawData == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
metadata.pRawData = pRawData;
|
|
metadata.rawDataSize = blockSize;
|
|
pRunningData = (const char*)pRawData;
|
|
pRunningDataEnd = (const char*)pRawData + blockSize;
|
|
metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
|
|
if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength;
|
|
metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
|
|
if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
metadata.data.vorbis_comment.pComments = pRunningData;
|
|
for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) {
|
|
ma_uint32 commentLength;
|
|
if (pRunningDataEnd - pRunningData < 4) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4;
|
|
if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pRunningData += commentLength;
|
|
}
|
|
onMeta(pUserDataMD, &metadata);
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET:
|
|
{
|
|
if (blockSize < 396) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onMeta) {
|
|
void* pRawData;
|
|
const char* pRunningData;
|
|
const char* pRunningDataEnd;
|
|
size_t bufferSize;
|
|
ma_uint8 iTrack;
|
|
ma_uint8 iIndex;
|
|
void* pTrackData;
|
|
pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
|
|
if (pRawData == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
metadata.pRawData = pRawData;
|
|
metadata.rawDataSize = blockSize;
|
|
pRunningData = (const char*)pRawData;
|
|
pRunningDataEnd = (const char*)pRawData + blockSize;
|
|
MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128;
|
|
metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8;
|
|
metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259;
|
|
metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1;
|
|
metadata.data.cuesheet.pTrackData = NULL;
|
|
{
|
|
const char* pRunningDataSaved = pRunningData;
|
|
bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES;
|
|
for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
|
|
ma_uint8 indexCount;
|
|
ma_uint32 indexPointSize;
|
|
if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pRunningData += 35;
|
|
indexCount = pRunningData[0];
|
|
pRunningData += 1;
|
|
bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index);
|
|
indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
|
|
if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pRunningData += indexPointSize;
|
|
}
|
|
pRunningData = pRunningDataSaved;
|
|
}
|
|
{
|
|
char* pRunningTrackData;
|
|
pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks);
|
|
if (pTrackData == NULL) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
pRunningTrackData = (char*)pTrackData;
|
|
for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) {
|
|
ma_uint8 indexCount;
|
|
MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES);
|
|
pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
|
|
pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1;
|
|
indexCount = pRunningData[0];
|
|
pRunningData += 1;
|
|
pRunningTrackData += 1;
|
|
for (iIndex = 0; iIndex < indexCount; ++iIndex) {
|
|
ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData;
|
|
MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES);
|
|
pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES;
|
|
pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index);
|
|
pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset);
|
|
}
|
|
}
|
|
metadata.data.cuesheet.pTrackData = pTrackData;
|
|
}
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
pRawData = NULL;
|
|
onMeta(pUserDataMD, &metadata);
|
|
ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks);
|
|
pTrackData = NULL;
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE:
|
|
{
|
|
if (blockSize < 32) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onMeta) {
|
|
ma_bool32 result = MA_TRUE;
|
|
ma_uint32 blockSizeRemaining = blockSize;
|
|
char* pMime = NULL;
|
|
char* pDescription = NULL;
|
|
void* pPictureData = NULL;
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.type, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.type = ma_dr_flac__be2host_32(metadata.data.picture.type);
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.mimeLength, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.mimeLength = ma_dr_flac__be2host_32(metadata.data.picture.mimeLength);
|
|
pMime = (char*)ma_dr_flac__malloc_from_callbacks(metadata.data.picture.mimeLength + 1, pAllocationCallbacks);
|
|
if (pMime == NULL) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
if (blockSizeRemaining < metadata.data.picture.mimeLength || onRead(pUserData, pMime, metadata.data.picture.mimeLength) != metadata.data.picture.mimeLength) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= metadata.data.picture.mimeLength;
|
|
pMime[metadata.data.picture.mimeLength] = '\0';
|
|
metadata.data.picture.mime = (const char*)pMime;
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.descriptionLength, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32(metadata.data.picture.descriptionLength);
|
|
pDescription = (char*)ma_dr_flac__malloc_from_callbacks(metadata.data.picture.descriptionLength + 1, pAllocationCallbacks);
|
|
if (pDescription == NULL) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
if (blockSizeRemaining < metadata.data.picture.descriptionLength || onRead(pUserData, pDescription, metadata.data.picture.descriptionLength) != metadata.data.picture.descriptionLength) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= metadata.data.picture.descriptionLength;
|
|
pDescription[metadata.data.picture.descriptionLength] = '\0';
|
|
metadata.data.picture.description = (const char*)pDescription;
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.width, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.width = ma_dr_flac__be2host_32(metadata.data.picture.width);
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.height, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.height = ma_dr_flac__be2host_32(metadata.data.picture.height);
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.colorDepth, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.colorDepth = ma_dr_flac__be2host_32(metadata.data.picture.colorDepth);
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.indexColorCount, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32(metadata.data.picture.indexColorCount);
|
|
if (blockSizeRemaining < 4 || onRead(pUserData, &metadata.data.picture.pictureDataSize, 4) != 4) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
blockSizeRemaining -= 4;
|
|
metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32(metadata.data.picture.pictureDataSize);
|
|
if (blockSizeRemaining < metadata.data.picture.pictureDataSize) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
metadata.data.picture.pictureDataOffset = runningFilePos + (blockSize - blockSizeRemaining);
|
|
#ifndef MA_DR_FLAC_NO_PICTURE_METADATA_MALLOC
|
|
pPictureData = ma_dr_flac__malloc_from_callbacks(metadata.data.picture.pictureDataSize, pAllocationCallbacks);
|
|
if (pPictureData != NULL) {
|
|
if (onRead(pUserData, pPictureData, metadata.data.picture.pictureDataSize) != metadata.data.picture.pictureDataSize) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
if (!onSeek(pUserData, metadata.data.picture.pictureDataSize, MA_DR_FLAC_SEEK_CUR)) {
|
|
result = MA_FALSE;
|
|
goto done_flac;
|
|
}
|
|
}
|
|
blockSizeRemaining -= metadata.data.picture.pictureDataSize;
|
|
(void)blockSizeRemaining;
|
|
metadata.data.picture.pPictureData = (const ma_uint8*)pPictureData;
|
|
if (metadata.data.picture.pictureDataOffset != 0 || metadata.data.picture.pPictureData != NULL) {
|
|
onMeta(pUserDataMD, &metadata);
|
|
} else {
|
|
}
|
|
done_flac:
|
|
ma_dr_flac__free_from_callbacks(pMime, pAllocationCallbacks);
|
|
ma_dr_flac__free_from_callbacks(pDescription, pAllocationCallbacks);
|
|
ma_dr_flac__free_from_callbacks(pPictureData, pAllocationCallbacks);
|
|
if (result != MA_TRUE) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING:
|
|
{
|
|
if (onMeta) {
|
|
metadata.data.padding.unused = 0;
|
|
if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
|
|
isLastBlock = MA_TRUE;
|
|
} else {
|
|
onMeta(pUserDataMD, &metadata);
|
|
}
|
|
}
|
|
} break;
|
|
case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID:
|
|
{
|
|
if (onMeta) {
|
|
if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
|
|
isLastBlock = MA_TRUE;
|
|
}
|
|
}
|
|
} break;
|
|
default:
|
|
{
|
|
if (onMeta) {
|
|
void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks);
|
|
if (pRawData != NULL) {
|
|
if (onRead(pUserData, pRawData, blockSize) != blockSize) {
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
metadata.pRawData = pRawData;
|
|
metadata.rawDataSize = blockSize;
|
|
onMeta(pUserDataMD, &metadata);
|
|
ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks);
|
|
}
|
|
} break;
|
|
}
|
|
if (onMeta == NULL && blockSize > 0) {
|
|
if (!onSeek(pUserData, blockSize, MA_DR_FLAC_SEEK_CUR)) {
|
|
isLastBlock = MA_TRUE;
|
|
}
|
|
}
|
|
runningFilePos += blockSize;
|
|
if (isLastBlock) {
|
|
break;
|
|
}
|
|
}
|
|
*pSeektablePos = seektablePos;
|
|
*pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES;
|
|
*pFirstFramePos = runningFilePos;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed)
|
|
{
|
|
ma_uint8 isLastBlock;
|
|
ma_uint8 blockType;
|
|
ma_uint32 blockSize;
|
|
(void)onSeek;
|
|
pInit->container = ma_dr_flac_container_native;
|
|
if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
|
|
if (!relaxed) {
|
|
return MA_FALSE;
|
|
} else {
|
|
pInit->hasStreamInfoBlock = MA_FALSE;
|
|
pInit->hasMetadataBlocks = MA_FALSE;
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pInit->firstFrameHeader.bitsPerSample == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->sampleRate = pInit->firstFrameHeader.sampleRate;
|
|
pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment);
|
|
pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample;
|
|
pInit->maxBlockSizeInPCMFrames = 65535;
|
|
return MA_TRUE;
|
|
}
|
|
} else {
|
|
ma_dr_flac_streaminfo streaminfo;
|
|
if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->hasStreamInfoBlock = MA_TRUE;
|
|
pInit->sampleRate = streaminfo.sampleRate;
|
|
pInit->channels = streaminfo.channels;
|
|
pInit->bitsPerSample = streaminfo.bitsPerSample;
|
|
pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
|
|
pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
|
|
pInit->hasMetadataBlocks = !isLastBlock;
|
|
if (onMeta) {
|
|
ma_dr_flac_metadata metadata;
|
|
metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;
|
|
metadata.pRawData = NULL;
|
|
metadata.rawDataSize = 0;
|
|
metadata.data.streaminfo = streaminfo;
|
|
onMeta(pUserDataMD, &metadata);
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307
|
|
#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199
|
|
typedef enum
|
|
{
|
|
ma_dr_flac_ogg_recover_on_crc_mismatch,
|
|
ma_dr_flac_ogg_fail_on_crc_mismatch
|
|
} ma_dr_flac_ogg_crc_mismatch_recovery;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
static ma_uint32 ma_dr_flac__crc32_table[] = {
|
|
0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L,
|
|
0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L,
|
|
0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L,
|
|
0x350C9B64L, 0x31CD86D3L, 0x3C8EA00AL, 0x384FBDBDL,
|
|
0x4C11DB70L, 0x48D0C6C7L, 0x4593E01EL, 0x4152FDA9L,
|
|
0x5F15ADACL, 0x5BD4B01BL, 0x569796C2L, 0x52568B75L,
|
|
0x6A1936C8L, 0x6ED82B7FL, 0x639B0DA6L, 0x675A1011L,
|
|
0x791D4014L, 0x7DDC5DA3L, 0x709F7B7AL, 0x745E66CDL,
|
|
0x9823B6E0L, 0x9CE2AB57L, 0x91A18D8EL, 0x95609039L,
|
|
0x8B27C03CL, 0x8FE6DD8BL, 0x82A5FB52L, 0x8664E6E5L,
|
|
0xBE2B5B58L, 0xBAEA46EFL, 0xB7A96036L, 0xB3687D81L,
|
|
0xAD2F2D84L, 0xA9EE3033L, 0xA4AD16EAL, 0xA06C0B5DL,
|
|
0xD4326D90L, 0xD0F37027L, 0xDDB056FEL, 0xD9714B49L,
|
|
0xC7361B4CL, 0xC3F706FBL, 0xCEB42022L, 0xCA753D95L,
|
|
0xF23A8028L, 0xF6FB9D9FL, 0xFBB8BB46L, 0xFF79A6F1L,
|
|
0xE13EF6F4L, 0xE5FFEB43L, 0xE8BCCD9AL, 0xEC7DD02DL,
|
|
0x34867077L, 0x30476DC0L, 0x3D044B19L, 0x39C556AEL,
|
|
0x278206ABL, 0x23431B1CL, 0x2E003DC5L, 0x2AC12072L,
|
|
0x128E9DCFL, 0x164F8078L, 0x1B0CA6A1L, 0x1FCDBB16L,
|
|
0x018AEB13L, 0x054BF6A4L, 0x0808D07DL, 0x0CC9CDCAL,
|
|
0x7897AB07L, 0x7C56B6B0L, 0x71159069L, 0x75D48DDEL,
|
|
0x6B93DDDBL, 0x6F52C06CL, 0x6211E6B5L, 0x66D0FB02L,
|
|
0x5E9F46BFL, 0x5A5E5B08L, 0x571D7DD1L, 0x53DC6066L,
|
|
0x4D9B3063L, 0x495A2DD4L, 0x44190B0DL, 0x40D816BAL,
|
|
0xACA5C697L, 0xA864DB20L, 0xA527FDF9L, 0xA1E6E04EL,
|
|
0xBFA1B04BL, 0xBB60ADFCL, 0xB6238B25L, 0xB2E29692L,
|
|
0x8AAD2B2FL, 0x8E6C3698L, 0x832F1041L, 0x87EE0DF6L,
|
|
0x99A95DF3L, 0x9D684044L, 0x902B669DL, 0x94EA7B2AL,
|
|
0xE0B41DE7L, 0xE4750050L, 0xE9362689L, 0xEDF73B3EL,
|
|
0xF3B06B3BL, 0xF771768CL, 0xFA325055L, 0xFEF34DE2L,
|
|
0xC6BCF05FL, 0xC27DEDE8L, 0xCF3ECB31L, 0xCBFFD686L,
|
|
0xD5B88683L, 0xD1799B34L, 0xDC3ABDEDL, 0xD8FBA05AL,
|
|
0x690CE0EEL, 0x6DCDFD59L, 0x608EDB80L, 0x644FC637L,
|
|
0x7A089632L, 0x7EC98B85L, 0x738AAD5CL, 0x774BB0EBL,
|
|
0x4F040D56L, 0x4BC510E1L, 0x46863638L, 0x42472B8FL,
|
|
0x5C007B8AL, 0x58C1663DL, 0x558240E4L, 0x51435D53L,
|
|
0x251D3B9EL, 0x21DC2629L, 0x2C9F00F0L, 0x285E1D47L,
|
|
0x36194D42L, 0x32D850F5L, 0x3F9B762CL, 0x3B5A6B9BL,
|
|
0x0315D626L, 0x07D4CB91L, 0x0A97ED48L, 0x0E56F0FFL,
|
|
0x1011A0FAL, 0x14D0BD4DL, 0x19939B94L, 0x1D528623L,
|
|
0xF12F560EL, 0xF5EE4BB9L, 0xF8AD6D60L, 0xFC6C70D7L,
|
|
0xE22B20D2L, 0xE6EA3D65L, 0xEBA91BBCL, 0xEF68060BL,
|
|
0xD727BBB6L, 0xD3E6A601L, 0xDEA580D8L, 0xDA649D6FL,
|
|
0xC423CD6AL, 0xC0E2D0DDL, 0xCDA1F604L, 0xC960EBB3L,
|
|
0xBD3E8D7EL, 0xB9FF90C9L, 0xB4BCB610L, 0xB07DABA7L,
|
|
0xAE3AFBA2L, 0xAAFBE615L, 0xA7B8C0CCL, 0xA379DD7BL,
|
|
0x9B3660C6L, 0x9FF77D71L, 0x92B45BA8L, 0x9675461FL,
|
|
0x8832161AL, 0x8CF30BADL, 0x81B02D74L, 0x857130C3L,
|
|
0x5D8A9099L, 0x594B8D2EL, 0x5408ABF7L, 0x50C9B640L,
|
|
0x4E8EE645L, 0x4A4FFBF2L, 0x470CDD2BL, 0x43CDC09CL,
|
|
0x7B827D21L, 0x7F436096L, 0x7200464FL, 0x76C15BF8L,
|
|
0x68860BFDL, 0x6C47164AL, 0x61043093L, 0x65C52D24L,
|
|
0x119B4BE9L, 0x155A565EL, 0x18197087L, 0x1CD86D30L,
|
|
0x029F3D35L, 0x065E2082L, 0x0B1D065BL, 0x0FDC1BECL,
|
|
0x3793A651L, 0x3352BBE6L, 0x3E119D3FL, 0x3AD08088L,
|
|
0x2497D08DL, 0x2056CD3AL, 0x2D15EBE3L, 0x29D4F654L,
|
|
0xC5A92679L, 0xC1683BCEL, 0xCC2B1D17L, 0xC8EA00A0L,
|
|
0xD6AD50A5L, 0xD26C4D12L, 0xDF2F6BCBL, 0xDBEE767CL,
|
|
0xE3A1CBC1L, 0xE760D676L, 0xEA23F0AFL, 0xEEE2ED18L,
|
|
0xF0A5BD1DL, 0xF464A0AAL, 0xF9278673L, 0xFDE69BC4L,
|
|
0x89B8FD09L, 0x8D79E0BEL, 0x803AC667L, 0x84FBDBD0L,
|
|
0x9ABC8BD5L, 0x9E7D9662L, 0x933EB0BBL, 0x97FFAD0CL,
|
|
0xAFB010B1L, 0xAB710D06L, 0xA6322BDFL, 0xA2F33668L,
|
|
0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L
|
|
};
|
|
#endif
|
|
static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data)
|
|
{
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data];
|
|
#else
|
|
(void)data;
|
|
return crc32;
|
|
#endif
|
|
}
|
|
#if 0
|
|
static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data)
|
|
{
|
|
crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF));
|
|
crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF));
|
|
crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF));
|
|
crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF));
|
|
return crc32;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data)
|
|
{
|
|
crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF));
|
|
crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF));
|
|
return crc32;
|
|
}
|
|
#endif
|
|
static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize)
|
|
{
|
|
ma_uint32 i;
|
|
for (i = 0; i < dataSize; ++i) {
|
|
crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]);
|
|
}
|
|
return crc32;
|
|
}
|
|
static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4])
|
|
{
|
|
return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S';
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader)
|
|
{
|
|
return 27 + pHeader->segmentCount;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader)
|
|
{
|
|
ma_uint32 pageBodySize = 0;
|
|
int i;
|
|
for (i = 0; i < pHeader->segmentCount; ++i) {
|
|
pageBodySize += pHeader->segmentTable[i];
|
|
}
|
|
return pageBodySize;
|
|
}
|
|
static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32)
|
|
{
|
|
ma_uint8 data[23];
|
|
ma_uint32 i;
|
|
MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32);
|
|
if (onRead(pUserData, data, 23) != 23) {
|
|
return MA_AT_END;
|
|
}
|
|
*pBytesRead += 23;
|
|
pHeader->capturePattern[0] = 'O';
|
|
pHeader->capturePattern[1] = 'g';
|
|
pHeader->capturePattern[2] = 'g';
|
|
pHeader->capturePattern[3] = 'S';
|
|
pHeader->structureVersion = data[0];
|
|
pHeader->headerType = data[1];
|
|
MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8);
|
|
MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4);
|
|
MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4);
|
|
MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4);
|
|
pHeader->segmentCount = data[22];
|
|
data[18] = 0;
|
|
data[19] = 0;
|
|
data[20] = 0;
|
|
data[21] = 0;
|
|
for (i = 0; i < 23; ++i) {
|
|
*pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]);
|
|
}
|
|
if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) {
|
|
return MA_AT_END;
|
|
}
|
|
*pBytesRead += pHeader->segmentCount;
|
|
for (i = 0; i < pHeader->segmentCount; ++i) {
|
|
*pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]);
|
|
}
|
|
return MA_SUCCESS;
|
|
}
|
|
static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32)
|
|
{
|
|
ma_uint8 id[4];
|
|
*pBytesRead = 0;
|
|
if (onRead(pUserData, id, 4) != 4) {
|
|
return MA_AT_END;
|
|
}
|
|
*pBytesRead += 4;
|
|
for (;;) {
|
|
if (ma_dr_flac_ogg__is_capture_pattern(id)) {
|
|
ma_result result;
|
|
*pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;
|
|
result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32);
|
|
if (result == MA_SUCCESS) {
|
|
return MA_SUCCESS;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
continue;
|
|
} else {
|
|
return result;
|
|
}
|
|
}
|
|
} else {
|
|
id[0] = id[1];
|
|
id[1] = id[2];
|
|
id[2] = id[3];
|
|
if (onRead(pUserData, &id[3], 1) != 1) {
|
|
return MA_AT_END;
|
|
}
|
|
*pBytesRead += 1;
|
|
}
|
|
}
|
|
}
|
|
typedef struct
|
|
{
|
|
ma_dr_flac_read_proc onRead;
|
|
ma_dr_flac_seek_proc onSeek;
|
|
ma_dr_flac_tell_proc onTell;
|
|
void* pUserData;
|
|
ma_uint64 currentBytePos;
|
|
ma_uint64 firstBytePos;
|
|
ma_uint32 serialNumber;
|
|
ma_dr_flac_ogg_page_header bosPageHeader;
|
|
ma_dr_flac_ogg_page_header currentPageHeader;
|
|
ma_uint32 bytesRemainingInPage;
|
|
ma_uint32 pageDataSize;
|
|
ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE];
|
|
} ma_dr_flac_oggbs;
|
|
static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead)
|
|
{
|
|
size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead);
|
|
oggbs->currentBytePos += bytesActuallyRead;
|
|
return bytesActuallyRead;
|
|
}
|
|
static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin)
|
|
{
|
|
if (origin == MA_DR_FLAC_SEEK_SET) {
|
|
if (offset <= 0x7FFFFFFF) {
|
|
if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->currentBytePos = offset;
|
|
return MA_TRUE;
|
|
} else {
|
|
if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->currentBytePos = offset;
|
|
return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR);
|
|
}
|
|
} else {
|
|
while (offset > 0x7FFFFFFF) {
|
|
if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->currentBytePos += 0x7FFFFFFF;
|
|
offset -= 0x7FFFFFFF;
|
|
}
|
|
if (!oggbs->onSeek(oggbs->pUserData, (int)offset, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->currentBytePos += offset;
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod)
|
|
{
|
|
ma_dr_flac_ogg_page_header header;
|
|
for (;;) {
|
|
ma_uint32 crc32 = 0;
|
|
ma_uint32 bytesRead;
|
|
ma_uint32 pageBodySize;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
ma_uint32 actualCRC32;
|
|
#endif
|
|
if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->currentBytePos += bytesRead;
|
|
pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);
|
|
if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) {
|
|
continue;
|
|
}
|
|
if (header.serialNumber != oggbs->serialNumber) {
|
|
if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->pageDataSize = pageBodySize;
|
|
#ifndef MA_DR_FLAC_NO_CRC
|
|
actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize);
|
|
if (actualCRC32 != header.checksum) {
|
|
if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) {
|
|
continue;
|
|
} else {
|
|
ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch);
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
#else
|
|
(void)recoveryMethod;
|
|
#endif
|
|
oggbs->currentPageHeader = header;
|
|
oggbs->bytesRemainingInPage = pageBodySize;
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
#if 0
|
|
static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg)
|
|
{
|
|
ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage;
|
|
ma_uint8 iSeg = 0;
|
|
ma_uint32 iByte = 0;
|
|
while (iByte < bytesConsumedInPage) {
|
|
ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
|
|
if (iByte + segmentSize > bytesConsumedInPage) {
|
|
break;
|
|
} else {
|
|
iSeg += 1;
|
|
iByte += segmentSize;
|
|
}
|
|
}
|
|
*pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte);
|
|
return iSeg;
|
|
}
|
|
static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs)
|
|
{
|
|
for (;;) {
|
|
ma_bool32 atEndOfPage = MA_FALSE;
|
|
ma_uint8 bytesRemainingInSeg;
|
|
ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg);
|
|
ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg;
|
|
for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) {
|
|
ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg];
|
|
if (segmentSize < 255) {
|
|
if (iSeg == oggbs->currentPageHeader.segmentCount-1) {
|
|
atEndOfPage = MA_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
bytesToEndOfPacketOrPage += segmentSize;
|
|
}
|
|
ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, MA_DR_FLAC_SEEK_CUR);
|
|
oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage;
|
|
if (atEndOfPage) {
|
|
if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
} else {
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs)
|
|
{
|
|
return ma_dr_flac_oggbs__seek_to_next_packet(oggbs);
|
|
}
|
|
#endif
|
|
static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead)
|
|
{
|
|
ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;
|
|
ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut;
|
|
size_t bytesRead = 0;
|
|
MA_DR_FLAC_ASSERT(oggbs != NULL);
|
|
MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL);
|
|
while (bytesRead < bytesToRead) {
|
|
size_t bytesRemainingToRead = bytesToRead - bytesRead;
|
|
if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) {
|
|
MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead);
|
|
bytesRead += bytesRemainingToRead;
|
|
oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead;
|
|
break;
|
|
}
|
|
if (oggbs->bytesRemainingInPage > 0) {
|
|
MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage);
|
|
bytesRead += oggbs->bytesRemainingInPage;
|
|
pRunningBufferOut += oggbs->bytesRemainingInPage;
|
|
oggbs->bytesRemainingInPage = 0;
|
|
}
|
|
MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0);
|
|
if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
|
|
break;
|
|
}
|
|
}
|
|
return bytesRead;
|
|
}
|
|
static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
|
|
{
|
|
ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData;
|
|
int bytesSeeked = 0;
|
|
MA_DR_FLAC_ASSERT(oggbs != NULL);
|
|
MA_DR_FLAC_ASSERT(offset >= 0);
|
|
if (origin == MA_DR_FLAC_SEEK_SET) {
|
|
if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, MA_DR_FLAC_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {
|
|
return MA_FALSE;
|
|
}
|
|
return ma_dr_flac__on_seek_ogg(pUserData, offset, MA_DR_FLAC_SEEK_CUR);
|
|
} else if (origin == MA_DR_FLAC_SEEK_CUR) {
|
|
while (bytesSeeked < offset) {
|
|
int bytesRemainingToSeek = offset - bytesSeeked;
|
|
MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0);
|
|
if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) {
|
|
bytesSeeked += bytesRemainingToSeek;
|
|
(void)bytesSeeked;
|
|
oggbs->bytesRemainingInPage -= bytesRemainingToSeek;
|
|
break;
|
|
}
|
|
if (oggbs->bytesRemainingInPage > 0) {
|
|
bytesSeeked += (int)oggbs->bytesRemainingInPage;
|
|
oggbs->bytesRemainingInPage = 0;
|
|
}
|
|
MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0);
|
|
if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else if (origin == MA_DR_FLAC_SEEK_END) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__on_tell_ogg(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
(void)pUserData;
|
|
(void)pCursor;
|
|
return MA_FALSE;
|
|
}
|
|
static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
|
|
{
|
|
ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
|
|
ma_uint64 originalBytePos;
|
|
ma_uint64 runningGranulePosition;
|
|
ma_uint64 runningFrameBytePos;
|
|
ma_uint64 runningPCMFrameCount;
|
|
MA_DR_FLAC_ASSERT(oggbs != NULL);
|
|
originalBytePos = oggbs->currentBytePos;
|
|
if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) {
|
|
return MA_FALSE;
|
|
}
|
|
oggbs->bytesRemainingInPage = 0;
|
|
runningGranulePosition = 0;
|
|
for (;;) {
|
|
if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
|
|
ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, MA_DR_FLAC_SEEK_SET);
|
|
return MA_FALSE;
|
|
}
|
|
runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize;
|
|
if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) {
|
|
break;
|
|
}
|
|
if ((oggbs->currentPageHeader.headerType & 0x01) == 0) {
|
|
if (oggbs->currentPageHeader.segmentTable[0] >= 2) {
|
|
ma_uint8 firstBytesInPage[2];
|
|
firstBytesInPage[0] = oggbs->pageData[0];
|
|
firstBytesInPage[1] = oggbs->pageData[1];
|
|
if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) {
|
|
runningGranulePosition = oggbs->currentPageHeader.granulePosition;
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, MA_DR_FLAC_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) {
|
|
return MA_FALSE;
|
|
}
|
|
runningPCMFrameCount = runningGranulePosition;
|
|
for (;;) {
|
|
ma_uint64 firstPCMFrameInFLACFrame = 0;
|
|
ma_uint64 lastPCMFrameInFLACFrame = 0;
|
|
ma_uint64 pcmFrameCountInThisFrame;
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
return MA_FALSE;
|
|
}
|
|
ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame);
|
|
pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1;
|
|
if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) {
|
|
ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
pFlac->currentPCMFrame = pcmFrameIndex;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining = 0;
|
|
return MA_TRUE;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) {
|
|
ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount);
|
|
if (pcmFramesToDecode == 0) {
|
|
return MA_TRUE;
|
|
}
|
|
pFlac->currentPCMFrame = runningPCMFrameCount;
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
continue;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
runningPCMFrameCount += pcmFrameCountInThisFrame;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
continue;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed)
|
|
{
|
|
ma_dr_flac_ogg_page_header header;
|
|
ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32;
|
|
ma_uint32 bytesRead = 0;
|
|
(void)relaxed;
|
|
pInit->container = ma_dr_flac_container_ogg;
|
|
pInit->oggFirstBytePos = 0;
|
|
if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->runningFilePos += bytesRead;
|
|
for (;;) {
|
|
int pageBodySize;
|
|
if ((header.headerType & 0x02) == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header);
|
|
if (pageBodySize == 51) {
|
|
ma_uint32 bytesRemainingInPage = pageBodySize;
|
|
ma_uint8 packetType;
|
|
if (onRead(pUserData, &packetType, 1) != 1) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingInPage -= 1;
|
|
if (packetType == 0x7F) {
|
|
ma_uint8 sig[4];
|
|
if (onRead(pUserData, sig, 4) != 4) {
|
|
return MA_FALSE;
|
|
}
|
|
bytesRemainingInPage -= 4;
|
|
if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') {
|
|
ma_uint8 mappingVersion[2];
|
|
if (onRead(pUserData, mappingVersion, 2) != 2) {
|
|
return MA_FALSE;
|
|
}
|
|
if (mappingVersion[0] != 1) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!onSeek(pUserData, 2, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (onRead(pUserData, sig, 4) != 4) {
|
|
return MA_FALSE;
|
|
}
|
|
if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') {
|
|
ma_dr_flac_streaminfo streaminfo;
|
|
ma_uint8 isLastBlock;
|
|
ma_uint8 blockType;
|
|
ma_uint32 blockSize;
|
|
if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) {
|
|
return MA_FALSE;
|
|
}
|
|
if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) {
|
|
pInit->hasStreamInfoBlock = MA_TRUE;
|
|
pInit->sampleRate = streaminfo.sampleRate;
|
|
pInit->channels = streaminfo.channels;
|
|
pInit->bitsPerSample = streaminfo.bitsPerSample;
|
|
pInit->totalPCMFrameCount = streaminfo.totalPCMFrameCount;
|
|
pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames;
|
|
pInit->hasMetadataBlocks = !isLastBlock;
|
|
if (onMeta) {
|
|
ma_dr_flac_metadata metadata;
|
|
metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO;
|
|
metadata.pRawData = NULL;
|
|
metadata.rawDataSize = 0;
|
|
metadata.data.streaminfo = streaminfo;
|
|
onMeta(pUserDataMD, &metadata);
|
|
}
|
|
pInit->runningFilePos += pageBodySize;
|
|
pInit->oggFirstBytePos = pInit->runningFilePos - 79;
|
|
pInit->oggSerial = header.serialNumber;
|
|
pInit->oggBosHeader = header;
|
|
break;
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
if (!onSeek(pUserData, bytesRemainingInPage, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
if (!onSeek(pUserData, pageBodySize, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
pInit->runningFilePos += pageBodySize;
|
|
if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->runningFilePos += bytesRead;
|
|
}
|
|
pInit->hasMetadataBlocks = MA_TRUE;
|
|
return MA_TRUE;
|
|
}
|
|
#endif
|
|
static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD)
|
|
{
|
|
ma_bool32 relaxed;
|
|
ma_uint8 id[4];
|
|
if (pInit == NULL || onRead == NULL || onSeek == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit));
|
|
pInit->onRead = onRead;
|
|
pInit->onSeek = onSeek;
|
|
pInit->onTell = onTell;
|
|
pInit->onMeta = onMeta;
|
|
pInit->container = container;
|
|
pInit->pUserData = pUserData;
|
|
pInit->pUserDataMD = pUserDataMD;
|
|
pInit->bs.onRead = onRead;
|
|
pInit->bs.onSeek = onSeek;
|
|
pInit->bs.onTell = onTell;
|
|
pInit->bs.pUserData = pUserData;
|
|
ma_dr_flac__reset_cache(&pInit->bs);
|
|
relaxed = container != ma_dr_flac_container_unknown;
|
|
for (;;) {
|
|
if (onRead(pUserData, id, 4) != 4) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->runningFilePos += 4;
|
|
if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') {
|
|
ma_uint8 header[6];
|
|
ma_uint8 flags;
|
|
ma_uint32 headerSize;
|
|
if (onRead(pUserData, header, 6) != 6) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->runningFilePos += 6;
|
|
flags = header[1];
|
|
MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4);
|
|
headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize));
|
|
if (flags & 0x10) {
|
|
headerSize += 10;
|
|
}
|
|
if (!onSeek(pUserData, headerSize, MA_DR_FLAC_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
pInit->runningFilePos += headerSize;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') {
|
|
return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') {
|
|
return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
|
|
}
|
|
#endif
|
|
if (relaxed) {
|
|
if (container == ma_dr_flac_container_native) {
|
|
return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (container == ma_dr_flac_container_ogg) {
|
|
return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed);
|
|
}
|
|
#endif
|
|
}
|
|
return MA_FALSE;
|
|
}
|
|
static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit)
|
|
{
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL);
|
|
MA_DR_FLAC_ASSERT(pInit != NULL);
|
|
MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac));
|
|
pFlac->bs = pInit->bs;
|
|
pFlac->onMeta = pInit->onMeta;
|
|
pFlac->pUserDataMD = pInit->pUserDataMD;
|
|
pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames;
|
|
pFlac->sampleRate = pInit->sampleRate;
|
|
pFlac->channels = (ma_uint8)pInit->channels;
|
|
pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample;
|
|
pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount;
|
|
pFlac->container = pInit->container;
|
|
}
|
|
static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac_init_info init;
|
|
ma_uint32 allocationSize;
|
|
ma_uint32 wholeSIMDVectorCountPerChannel;
|
|
ma_uint32 decodedSamplesAllocationSize;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
ma_dr_flac_oggbs* pOggbs = NULL;
|
|
#endif
|
|
ma_uint64 firstFramePos;
|
|
ma_uint64 seektablePos;
|
|
ma_uint32 seekpointCount;
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
ma_dr_flac* pFlac;
|
|
ma_dr_flac__init_cpu_caps();
|
|
if (!ma_dr_flac__init_private(&init, onRead, onSeek, onTell, onMeta, container, pUserData, pUserDataMD)) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks != NULL) {
|
|
allocationCallbacks = *pAllocationCallbacks;
|
|
if (allocationCallbacks.onFree == NULL || (allocationCallbacks.onMalloc == NULL && allocationCallbacks.onRealloc == NULL)) {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
allocationCallbacks.pUserData = NULL;
|
|
allocationCallbacks.onMalloc = ma_dr_flac__malloc_default;
|
|
allocationCallbacks.onRealloc = ma_dr_flac__realloc_default;
|
|
allocationCallbacks.onFree = ma_dr_flac__free_default;
|
|
}
|
|
allocationSize = sizeof(ma_dr_flac);
|
|
if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) {
|
|
wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32)));
|
|
} else {
|
|
wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1;
|
|
}
|
|
decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels;
|
|
allocationSize += decodedSamplesAllocationSize;
|
|
allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (init.container == ma_dr_flac_container_ogg) {
|
|
allocationSize += sizeof(ma_dr_flac_oggbs);
|
|
pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks);
|
|
if (pOggbs == NULL) {
|
|
return NULL;
|
|
}
|
|
MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs));
|
|
pOggbs->onRead = onRead;
|
|
pOggbs->onSeek = onSeek;
|
|
pOggbs->onTell = onTell;
|
|
pOggbs->pUserData = pUserData;
|
|
pOggbs->currentBytePos = init.oggFirstBytePos;
|
|
pOggbs->firstBytePos = init.oggFirstBytePos;
|
|
pOggbs->serialNumber = init.oggSerial;
|
|
pOggbs->bosPageHeader = init.oggBosHeader;
|
|
pOggbs->bytesRemainingInPage = 0;
|
|
}
|
|
#endif
|
|
firstFramePos = 42;
|
|
seektablePos = 0;
|
|
seekpointCount = 0;
|
|
if (init.hasMetadataBlocks) {
|
|
ma_dr_flac_read_proc onReadOverride = onRead;
|
|
ma_dr_flac_seek_proc onSeekOverride = onSeek;
|
|
ma_dr_flac_tell_proc onTellOverride = onTell;
|
|
void* pUserDataOverride = pUserData;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (init.container == ma_dr_flac_container_ogg) {
|
|
onReadOverride = ma_dr_flac__on_read_ogg;
|
|
onSeekOverride = ma_dr_flac__on_seek_ogg;
|
|
onTellOverride = ma_dr_flac__on_tell_ogg;
|
|
pUserDataOverride = (void*)pOggbs;
|
|
}
|
|
#endif
|
|
if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onTellOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) {
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint);
|
|
}
|
|
pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
|
|
#endif
|
|
return NULL;
|
|
}
|
|
ma_dr_flac__init_from_info(pFlac, &init);
|
|
pFlac->allocationCallbacks = allocationCallbacks;
|
|
pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE);
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (init.container == ma_dr_flac_container_ogg) {
|
|
ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint)));
|
|
MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs));
|
|
ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks);
|
|
pOggbs = NULL;
|
|
pFlac->bs.onRead = ma_dr_flac__on_read_ogg;
|
|
pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg;
|
|
pFlac->bs.onTell = ma_dr_flac__on_tell_ogg;
|
|
pFlac->bs.pUserData = (void*)pInternalOggbs;
|
|
pFlac->_oggbs = (void*)pInternalOggbs;
|
|
}
|
|
#endif
|
|
pFlac->firstFLACFramePosInBytes = firstFramePos;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (init.container == ma_dr_flac_container_ogg)
|
|
{
|
|
pFlac->pSeekpoints = NULL;
|
|
pFlac->seekpointCount = 0;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (seektablePos != 0) {
|
|
pFlac->seekpointCount = seekpointCount;
|
|
pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize);
|
|
MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL);
|
|
MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL);
|
|
if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, MA_DR_FLAC_SEEK_SET)) {
|
|
ma_uint32 iSeekpoint;
|
|
for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) {
|
|
if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) {
|
|
pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame);
|
|
pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset);
|
|
pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount);
|
|
} else {
|
|
pFlac->pSeekpoints = NULL;
|
|
pFlac->seekpointCount = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, MA_DR_FLAC_SEEK_SET)) {
|
|
ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
|
|
return NULL;
|
|
}
|
|
} else {
|
|
pFlac->pSeekpoints = NULL;
|
|
pFlac->seekpointCount = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!init.hasStreamInfoBlock) {
|
|
pFlac->currentFLACFrame.header = init.firstFrameHeader;
|
|
for (;;) {
|
|
ma_result result = ma_dr_flac__decode_flac_frame(pFlac);
|
|
if (result == MA_SUCCESS) {
|
|
break;
|
|
} else {
|
|
if (result == MA_CRC_MISMATCH) {
|
|
if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) {
|
|
ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
|
|
return NULL;
|
|
}
|
|
continue;
|
|
} else {
|
|
ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks);
|
|
return NULL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return pFlac;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_STDIO
|
|
#include <stdio.h>
|
|
#ifndef MA_DR_FLAC_NO_WCHAR
|
|
#include <wchar.h>
|
|
#endif
|
|
static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead)
|
|
{
|
|
return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData);
|
|
}
|
|
static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
|
|
{
|
|
int whence = SEEK_SET;
|
|
if (origin == MA_DR_FLAC_SEEK_CUR) {
|
|
whence = SEEK_CUR;
|
|
} else if (origin == MA_DR_FLAC_SEEK_END) {
|
|
whence = SEEK_END;
|
|
}
|
|
return fseek((FILE*)pUserData, offset, whence) == 0;
|
|
}
|
|
static ma_bool32 ma_dr_flac__on_tell_stdio(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
FILE* pFileStdio = (FILE*)pUserData;
|
|
ma_int64 result;
|
|
MA_DR_FLAC_ASSERT(pFileStdio != NULL);
|
|
MA_DR_FLAC_ASSERT(pCursor != NULL);
|
|
#if defined(_WIN32) && !defined(NXDK)
|
|
#if defined(_MSC_VER) && _MSC_VER > 1200
|
|
result = _ftelli64(pFileStdio);
|
|
#else
|
|
result = ftell(pFileStdio);
|
|
#endif
|
|
#else
|
|
result = ftell(pFileStdio);
|
|
#endif
|
|
*pCursor = result;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
FILE* pFile;
|
|
if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
fclose(pFile);
|
|
return NULL;
|
|
}
|
|
return pFlac;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_WCHAR
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
FILE* pFile;
|
|
if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, (void*)pFile, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
fclose(pFile);
|
|
return NULL;
|
|
}
|
|
return pFlac;
|
|
}
|
|
#endif
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
FILE* pFile;
|
|
if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
fclose(pFile);
|
|
return pFlac;
|
|
}
|
|
return pFlac;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_WCHAR
|
|
MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
FILE* pFile;
|
|
if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
|
|
return NULL;
|
|
}
|
|
pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, ma_dr_flac__on_tell_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
fclose(pFile);
|
|
return pFlac;
|
|
}
|
|
return pFlac;
|
|
}
|
|
#endif
|
|
#endif
|
|
static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead)
|
|
{
|
|
ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
|
|
size_t bytesRemaining;
|
|
MA_DR_FLAC_ASSERT(memoryStream != NULL);
|
|
MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos);
|
|
bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos;
|
|
if (bytesToRead > bytesRemaining) {
|
|
bytesToRead = bytesRemaining;
|
|
}
|
|
if (bytesToRead > 0) {
|
|
MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead);
|
|
memoryStream->currentReadPos += bytesToRead;
|
|
}
|
|
return bytesToRead;
|
|
}
|
|
static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin)
|
|
{
|
|
ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
|
|
ma_int64 newCursor;
|
|
MA_DR_FLAC_ASSERT(memoryStream != NULL);
|
|
if (origin == MA_DR_FLAC_SEEK_SET) {
|
|
newCursor = 0;
|
|
} else if (origin == MA_DR_FLAC_SEEK_CUR) {
|
|
newCursor = (ma_int64)memoryStream->currentReadPos;
|
|
} else if (origin == MA_DR_FLAC_SEEK_END) {
|
|
newCursor = (ma_int64)memoryStream->dataSize;
|
|
} else {
|
|
MA_DR_FLAC_ASSERT(!"Invalid seek origin");
|
|
return MA_FALSE;
|
|
}
|
|
newCursor += offset;
|
|
if (newCursor < 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((size_t)newCursor > memoryStream->dataSize) {
|
|
return MA_FALSE;
|
|
}
|
|
memoryStream->currentReadPos = (size_t)newCursor;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_flac__on_tell_memory(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData;
|
|
MA_DR_FLAC_ASSERT(memoryStream != NULL);
|
|
MA_DR_FLAC_ASSERT(pCursor != NULL);
|
|
*pCursor = (ma_int64)memoryStream->currentReadPos;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac__memory_stream memoryStream;
|
|
ma_dr_flac* pFlac;
|
|
memoryStream.data = (const ma_uint8*)pData;
|
|
memoryStream.dataSize = dataSize;
|
|
memoryStream.currentReadPos = 0;
|
|
pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, &memoryStream, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
pFlac->memoryStream = memoryStream;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (pFlac->container == ma_dr_flac_container_ogg)
|
|
{
|
|
ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
|
|
oggbs->pUserData = &pFlac->memoryStream;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pFlac->bs.pUserData = &pFlac->memoryStream;
|
|
}
|
|
return pFlac;
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac__memory_stream memoryStream;
|
|
ma_dr_flac* pFlac;
|
|
memoryStream.data = (const ma_uint8*)pData;
|
|
memoryStream.dataSize = dataSize;
|
|
memoryStream.currentReadPos = 0;
|
|
pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, ma_dr_flac__on_tell_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
pFlac->memoryStream = memoryStream;
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (pFlac->container == ma_dr_flac_container_ogg)
|
|
{
|
|
ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
|
|
oggbs->pUserData = &pFlac->memoryStream;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pFlac->bs.pUserData = &pFlac->memoryStream;
|
|
}
|
|
return pFlac;
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, NULL, container, pUserData, pUserData, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onTell, onMeta, container, pUserData, pUserData, pAllocationCallbacks);
|
|
}
|
|
MA_API void ma_dr_flac_close(ma_dr_flac* pFlac)
|
|
{
|
|
if (pFlac == NULL) {
|
|
return;
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_STDIO
|
|
if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) {
|
|
fclose((FILE*)pFlac->bs.pUserData);
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (pFlac->container == ma_dr_flac_container_ogg) {
|
|
ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs;
|
|
MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg);
|
|
if (oggbs->onRead == ma_dr_flac__on_read_stdio) {
|
|
fclose((FILE*)oggbs->pUserData);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks);
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCount; ++i) {
|
|
ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
|
|
ma_uint32 right0 = left0 - side0;
|
|
ma_uint32 right1 = left1 - side1;
|
|
ma_uint32 right2 = left2 - side2;
|
|
ma_uint32 right3 = left3 - side3;
|
|
pOutputSamples[i*8+0] = (ma_int32)left0;
|
|
pOutputSamples[i*8+1] = (ma_int32)right0;
|
|
pOutputSamples[i*8+2] = (ma_int32)left1;
|
|
pOutputSamples[i*8+3] = (ma_int32)right1;
|
|
pOutputSamples[i*8+4] = (ma_int32)left2;
|
|
pOutputSamples[i*8+5] = (ma_int32)right2;
|
|
pOutputSamples[i*8+6] = (ma_int32)left3;
|
|
pOutputSamples[i*8+7] = (ma_int32)right3;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
__m128i right = _mm_sub_epi32(left, side);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
int32x4_t shift0_4;
|
|
int32x4_t shift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
shift0_4 = vdupq_n_s32(shift0);
|
|
shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t left;
|
|
uint32x4_t side;
|
|
uint32x4_t right;
|
|
left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
|
right = vsubq_u32(left, side);
|
|
ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCount; ++i) {
|
|
ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
|
|
ma_uint32 left0 = right0 + side0;
|
|
ma_uint32 left1 = right1 + side1;
|
|
ma_uint32 left2 = right2 + side2;
|
|
ma_uint32 left3 = right3 + side3;
|
|
pOutputSamples[i*8+0] = (ma_int32)left0;
|
|
pOutputSamples[i*8+1] = (ma_int32)right0;
|
|
pOutputSamples[i*8+2] = (ma_int32)left1;
|
|
pOutputSamples[i*8+3] = (ma_int32)right1;
|
|
pOutputSamples[i*8+4] = (ma_int32)left2;
|
|
pOutputSamples[i*8+5] = (ma_int32)right2;
|
|
pOutputSamples[i*8+6] = (ma_int32)left3;
|
|
pOutputSamples[i*8+7] = (ma_int32)right3;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
__m128i left = _mm_add_epi32(right, side);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
int32x4_t shift0_4;
|
|
int32x4_t shift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
shift0_4 = vdupq_n_s32(shift0);
|
|
shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t side;
|
|
uint32x4_t right;
|
|
uint32x4_t left;
|
|
side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
|
|
right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
|
left = vaddq_u32(right, side);
|
|
ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left;
|
|
pOutputSamples[i*2+1] = (ma_int32)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
for (ma_uint64 i = 0; i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);
|
|
pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_int32 shift = unusedBitsPerSample;
|
|
if (shift > 0) {
|
|
shift -= 1;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 temp0L;
|
|
ma_uint32 temp1L;
|
|
ma_uint32 temp2L;
|
|
ma_uint32 temp3L;
|
|
ma_uint32 temp0R;
|
|
ma_uint32 temp1R;
|
|
ma_uint32 temp2R;
|
|
ma_uint32 temp3R;
|
|
ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid0 = (mid0 << 1) | (side0 & 0x01);
|
|
mid1 = (mid1 << 1) | (side1 & 0x01);
|
|
mid2 = (mid2 << 1) | (side2 & 0x01);
|
|
mid3 = (mid3 << 1) | (side3 & 0x01);
|
|
temp0L = (mid0 + side0) << shift;
|
|
temp1L = (mid1 + side1) << shift;
|
|
temp2L = (mid2 + side2) << shift;
|
|
temp3L = (mid3 + side3) << shift;
|
|
temp0R = (mid0 - side0) << shift;
|
|
temp1R = (mid1 - side1) << shift;
|
|
temp2R = (mid2 - side2) << shift;
|
|
temp3R = (mid3 - side3) << shift;
|
|
pOutputSamples[i*8+0] = (ma_int32)temp0L;
|
|
pOutputSamples[i*8+1] = (ma_int32)temp0R;
|
|
pOutputSamples[i*8+2] = (ma_int32)temp1L;
|
|
pOutputSamples[i*8+3] = (ma_int32)temp1R;
|
|
pOutputSamples[i*8+4] = (ma_int32)temp2L;
|
|
pOutputSamples[i*8+5] = (ma_int32)temp2R;
|
|
pOutputSamples[i*8+6] = (ma_int32)temp3L;
|
|
pOutputSamples[i*8+7] = (ma_int32)temp3R;
|
|
}
|
|
} else {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 temp0L;
|
|
ma_uint32 temp1L;
|
|
ma_uint32 temp2L;
|
|
ma_uint32 temp3L;
|
|
ma_uint32 temp0R;
|
|
ma_uint32 temp1R;
|
|
ma_uint32 temp2R;
|
|
ma_uint32 temp3R;
|
|
ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid0 = (mid0 << 1) | (side0 & 0x01);
|
|
mid1 = (mid1 << 1) | (side1 & 0x01);
|
|
mid2 = (mid2 << 1) | (side2 & 0x01);
|
|
mid3 = (mid3 << 1) | (side3 & 0x01);
|
|
temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);
|
|
temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);
|
|
temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);
|
|
temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);
|
|
temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);
|
|
temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);
|
|
temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);
|
|
temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);
|
|
pOutputSamples[i*8+0] = (ma_int32)temp0L;
|
|
pOutputSamples[i*8+1] = (ma_int32)temp0R;
|
|
pOutputSamples[i*8+2] = (ma_int32)temp1L;
|
|
pOutputSamples[i*8+3] = (ma_int32)temp1R;
|
|
pOutputSamples[i*8+4] = (ma_int32)temp2L;
|
|
pOutputSamples[i*8+5] = (ma_int32)temp2R;
|
|
pOutputSamples[i*8+6] = (ma_int32)temp3L;
|
|
pOutputSamples[i*8+7] = (ma_int32)temp3R;
|
|
}
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample);
|
|
pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample);
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_int32 shift = unusedBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
if (shift == 0) {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i mid;
|
|
__m128i side;
|
|
__m128i left;
|
|
__m128i right;
|
|
mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
|
|
left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
|
|
right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;
|
|
pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;
|
|
}
|
|
} else {
|
|
shift -= 1;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i mid;
|
|
__m128i side;
|
|
__m128i left;
|
|
__m128i right;
|
|
mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
|
|
left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
|
|
right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);
|
|
pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_int32 shift = unusedBitsPerSample;
|
|
int32x4_t wbpsShift0_4;
|
|
int32x4_t wbpsShift1_4;
|
|
uint32x4_t one4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
one4 = vdupq_n_u32(1);
|
|
if (shift == 0) {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t mid;
|
|
uint32x4_t side;
|
|
int32x4_t left;
|
|
int32x4_t right;
|
|
mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
|
|
mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
|
|
left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
|
|
right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
|
|
ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1;
|
|
pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1;
|
|
}
|
|
} else {
|
|
int32x4_t shift4;
|
|
shift -= 1;
|
|
shift4 = vdupq_n_s32(shift);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t mid;
|
|
uint32x4_t side;
|
|
int32x4_t left;
|
|
int32x4_t right;
|
|
mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
|
|
mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4));
|
|
left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
|
|
right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
|
|
ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift);
|
|
pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
for (ma_uint64 i = 0; i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample));
|
|
pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample));
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
|
|
pOutputSamples[i*8+0] = (ma_int32)tempL0;
|
|
pOutputSamples[i*8+1] = (ma_int32)tempR0;
|
|
pOutputSamples[i*8+2] = (ma_int32)tempL1;
|
|
pOutputSamples[i*8+3] = (ma_int32)tempR1;
|
|
pOutputSamples[i*8+4] = (ma_int32)tempL2;
|
|
pOutputSamples[i*8+5] = (ma_int32)tempR2;
|
|
pOutputSamples[i*8+6] = (ma_int32)tempL3;
|
|
pOutputSamples[i*8+7] = (ma_int32)tempR3;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
|
|
pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 0), _mm_unpacklo_epi32(left, right));
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
|
|
pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
int32x4_t shift4_0 = vdupq_n_s32(shift0);
|
|
int32x4_t shift4_1 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
int32x4_t left;
|
|
int32x4_t right;
|
|
left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0));
|
|
right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1));
|
|
ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0);
|
|
pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead;
|
|
ma_uint32 unusedBitsPerSample;
|
|
if (pFlac == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
|
|
}
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
|
|
unusedBitsPerSample = 32 - pFlac->bitsPerSample;
|
|
framesRead = 0;
|
|
while (framesToRead > 0) {
|
|
if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
|
|
if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
|
|
break;
|
|
}
|
|
} else {
|
|
unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
|
|
ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
ma_uint64 frameCountThisIteration = framesToRead;
|
|
if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
|
|
frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
}
|
|
if (channelCount == 2) {
|
|
const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
|
|
const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
|
|
switch (pFlac->currentFLACFrame.header.channelAssignment)
|
|
{
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
|
|
default:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCountThisIteration; ++i) {
|
|
unsigned int j;
|
|
for (j = 0; j < channelCount; ++j) {
|
|
pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
|
|
}
|
|
}
|
|
}
|
|
framesRead += frameCountThisIteration;
|
|
pBufferOut += frameCountThisIteration * channelCount;
|
|
framesToRead -= frameCountThisIteration;
|
|
pFlac->currentPCMFrame += frameCountThisIteration;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;
|
|
}
|
|
}
|
|
return framesRead;
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCount; ++i) {
|
|
ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
ma_uint32 right = left - side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
|
|
ma_uint32 right0 = left0 - side0;
|
|
ma_uint32 right1 = left1 - side1;
|
|
ma_uint32 right2 = left2 - side2;
|
|
ma_uint32 right3 = left3 - side3;
|
|
left0 >>= 16;
|
|
left1 >>= 16;
|
|
left2 >>= 16;
|
|
left3 >>= 16;
|
|
right0 >>= 16;
|
|
right1 >>= 16;
|
|
right2 >>= 16;
|
|
right3 >>= 16;
|
|
pOutputSamples[i*8+0] = (ma_int16)left0;
|
|
pOutputSamples[i*8+1] = (ma_int16)right0;
|
|
pOutputSamples[i*8+2] = (ma_int16)left1;
|
|
pOutputSamples[i*8+3] = (ma_int16)right1;
|
|
pOutputSamples[i*8+4] = (ma_int16)left2;
|
|
pOutputSamples[i*8+5] = (ma_int16)right2;
|
|
pOutputSamples[i*8+6] = (ma_int16)left3;
|
|
pOutputSamples[i*8+7] = (ma_int16)right3;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
__m128i right = _mm_sub_epi32(left, side);
|
|
left = _mm_srai_epi32(left, 16);
|
|
right = _mm_srai_epi32(right, 16);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
int32x4_t shift0_4;
|
|
int32x4_t shift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
shift0_4 = vdupq_n_s32(shift0);
|
|
shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t left;
|
|
uint32x4_t side;
|
|
uint32x4_t right;
|
|
left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
|
right = vsubq_u32(left, side);
|
|
left = vshrq_n_u32(left, 16);
|
|
right = vshrq_n_u32(right, 16);
|
|
ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCount; ++i) {
|
|
ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
ma_uint32 left = right + side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
|
|
ma_uint32 left0 = right0 + side0;
|
|
ma_uint32 left1 = right1 + side1;
|
|
ma_uint32 left2 = right2 + side2;
|
|
ma_uint32 left3 = right3 + side3;
|
|
left0 >>= 16;
|
|
left1 >>= 16;
|
|
left2 >>= 16;
|
|
left3 >>= 16;
|
|
right0 >>= 16;
|
|
right1 >>= 16;
|
|
right2 >>= 16;
|
|
right3 >>= 16;
|
|
pOutputSamples[i*8+0] = (ma_int16)left0;
|
|
pOutputSamples[i*8+1] = (ma_int16)right0;
|
|
pOutputSamples[i*8+2] = (ma_int16)left1;
|
|
pOutputSamples[i*8+3] = (ma_int16)right1;
|
|
pOutputSamples[i*8+4] = (ma_int16)left2;
|
|
pOutputSamples[i*8+5] = (ma_int16)right2;
|
|
pOutputSamples[i*8+6] = (ma_int16)left3;
|
|
pOutputSamples[i*8+7] = (ma_int16)right3;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
__m128i left = _mm_add_epi32(right, side);
|
|
left = _mm_srai_epi32(left, 16);
|
|
right = _mm_srai_epi32(right, 16);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
int32x4_t shift0_4;
|
|
int32x4_t shift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
shift0_4 = vdupq_n_s32(shift0);
|
|
shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t side;
|
|
uint32x4_t right;
|
|
uint32x4_t left;
|
|
side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
|
|
right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
|
left = vaddq_u32(right, side);
|
|
left = vshrq_n_u32(left, 16);
|
|
right = vshrq_n_u32(right, 16);
|
|
ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right)));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
left >>= 16;
|
|
right >>= 16;
|
|
pOutputSamples[i*2+0] = (ma_int16)left;
|
|
pOutputSamples[i*2+1] = (ma_int16)right;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
for (ma_uint64 i = 0; i < frameCount; ++i) {
|
|
ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift = unusedBitsPerSample;
|
|
if (shift > 0) {
|
|
shift -= 1;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 temp0L;
|
|
ma_uint32 temp1L;
|
|
ma_uint32 temp2L;
|
|
ma_uint32 temp3L;
|
|
ma_uint32 temp0R;
|
|
ma_uint32 temp1R;
|
|
ma_uint32 temp2R;
|
|
ma_uint32 temp3R;
|
|
ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid0 = (mid0 << 1) | (side0 & 0x01);
|
|
mid1 = (mid1 << 1) | (side1 & 0x01);
|
|
mid2 = (mid2 << 1) | (side2 & 0x01);
|
|
mid3 = (mid3 << 1) | (side3 & 0x01);
|
|
temp0L = (mid0 + side0) << shift;
|
|
temp1L = (mid1 + side1) << shift;
|
|
temp2L = (mid2 + side2) << shift;
|
|
temp3L = (mid3 + side3) << shift;
|
|
temp0R = (mid0 - side0) << shift;
|
|
temp1R = (mid1 - side1) << shift;
|
|
temp2R = (mid2 - side2) << shift;
|
|
temp3R = (mid3 - side3) << shift;
|
|
temp0L >>= 16;
|
|
temp1L >>= 16;
|
|
temp2L >>= 16;
|
|
temp3L >>= 16;
|
|
temp0R >>= 16;
|
|
temp1R >>= 16;
|
|
temp2R >>= 16;
|
|
temp3R >>= 16;
|
|
pOutputSamples[i*8+0] = (ma_int16)temp0L;
|
|
pOutputSamples[i*8+1] = (ma_int16)temp0R;
|
|
pOutputSamples[i*8+2] = (ma_int16)temp1L;
|
|
pOutputSamples[i*8+3] = (ma_int16)temp1R;
|
|
pOutputSamples[i*8+4] = (ma_int16)temp2L;
|
|
pOutputSamples[i*8+5] = (ma_int16)temp2R;
|
|
pOutputSamples[i*8+6] = (ma_int16)temp3L;
|
|
pOutputSamples[i*8+7] = (ma_int16)temp3R;
|
|
}
|
|
} else {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 temp0L;
|
|
ma_uint32 temp1L;
|
|
ma_uint32 temp2L;
|
|
ma_uint32 temp3L;
|
|
ma_uint32 temp0R;
|
|
ma_uint32 temp1R;
|
|
ma_uint32 temp2R;
|
|
ma_uint32 temp3R;
|
|
ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid0 = (mid0 << 1) | (side0 & 0x01);
|
|
mid1 = (mid1 << 1) | (side1 & 0x01);
|
|
mid2 = (mid2 << 1) | (side2 & 0x01);
|
|
mid3 = (mid3 << 1) | (side3 & 0x01);
|
|
temp0L = ((ma_int32)(mid0 + side0) >> 1);
|
|
temp1L = ((ma_int32)(mid1 + side1) >> 1);
|
|
temp2L = ((ma_int32)(mid2 + side2) >> 1);
|
|
temp3L = ((ma_int32)(mid3 + side3) >> 1);
|
|
temp0R = ((ma_int32)(mid0 - side0) >> 1);
|
|
temp1R = ((ma_int32)(mid1 - side1) >> 1);
|
|
temp2R = ((ma_int32)(mid2 - side2) >> 1);
|
|
temp3R = ((ma_int32)(mid3 - side3) >> 1);
|
|
temp0L >>= 16;
|
|
temp1L >>= 16;
|
|
temp2L >>= 16;
|
|
temp3L >>= 16;
|
|
temp0R >>= 16;
|
|
temp1R >>= 16;
|
|
temp2R >>= 16;
|
|
temp3R >>= 16;
|
|
pOutputSamples[i*8+0] = (ma_int16)temp0L;
|
|
pOutputSamples[i*8+1] = (ma_int16)temp0R;
|
|
pOutputSamples[i*8+2] = (ma_int16)temp1L;
|
|
pOutputSamples[i*8+3] = (ma_int16)temp1R;
|
|
pOutputSamples[i*8+4] = (ma_int16)temp2L;
|
|
pOutputSamples[i*8+5] = (ma_int16)temp2R;
|
|
pOutputSamples[i*8+6] = (ma_int16)temp3L;
|
|
pOutputSamples[i*8+7] = (ma_int16)temp3R;
|
|
}
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16);
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift = unusedBitsPerSample;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
if (shift == 0) {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i mid;
|
|
__m128i side;
|
|
__m128i left;
|
|
__m128i right;
|
|
mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
|
|
left = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
|
|
right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
|
|
left = _mm_srai_epi32(left, 16);
|
|
right = _mm_srai_epi32(right, 16);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);
|
|
}
|
|
} else {
|
|
shift -= 1;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i mid;
|
|
__m128i side;
|
|
__m128i left;
|
|
__m128i right;
|
|
mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
|
|
left = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
|
|
right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
|
|
left = _mm_srai_epi32(left, 16);
|
|
right = _mm_srai_epi32(right, 16);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift = unusedBitsPerSample;
|
|
int32x4_t wbpsShift0_4;
|
|
int32x4_t wbpsShift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
if (shift == 0) {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t mid;
|
|
uint32x4_t side;
|
|
int32x4_t left;
|
|
int32x4_t right;
|
|
mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
|
|
mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
|
|
left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
|
|
right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
|
|
left = vshrq_n_s32(left, 16);
|
|
right = vshrq_n_s32(right, 16);
|
|
ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16);
|
|
}
|
|
} else {
|
|
int32x4_t shift4;
|
|
shift -= 1;
|
|
shift4 = vdupq_n_s32(shift);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t mid;
|
|
uint32x4_t side;
|
|
int32x4_t left;
|
|
int32x4_t right;
|
|
mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbpsShift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbpsShift1_4);
|
|
mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
|
|
left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
|
|
right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
|
|
left = vshrq_n_s32(left, 16);
|
|
right = vshrq_n_s32(right, 16);
|
|
ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
for (ma_uint64 i = 0; i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
|
|
tempL0 >>= 16;
|
|
tempL1 >>= 16;
|
|
tempL2 >>= 16;
|
|
tempL3 >>= 16;
|
|
tempR0 >>= 16;
|
|
tempR1 >>= 16;
|
|
tempR2 >>= 16;
|
|
tempR3 >>= 16;
|
|
pOutputSamples[i*8+0] = (ma_int16)tempL0;
|
|
pOutputSamples[i*8+1] = (ma_int16)tempR0;
|
|
pOutputSamples[i*8+2] = (ma_int16)tempL1;
|
|
pOutputSamples[i*8+3] = (ma_int16)tempR1;
|
|
pOutputSamples[i*8+4] = (ma_int16)tempL2;
|
|
pOutputSamples[i*8+5] = (ma_int16)tempR2;
|
|
pOutputSamples[i*8+6] = (ma_int16)tempL3;
|
|
pOutputSamples[i*8+7] = (ma_int16)tempR3;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
left = _mm_srai_epi32(left, 16);
|
|
right = _mm_srai_epi32(right, 16);
|
|
_mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
int32x4_t shift0_4 = vdupq_n_s32(shift0);
|
|
int32x4_t shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
int32x4_t left;
|
|
int32x4_t right;
|
|
left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
|
|
right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
|
|
left = vshrq_n_s32(left, 16);
|
|
right = vshrq_n_s32(right, 16);
|
|
ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right)));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16);
|
|
pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead;
|
|
ma_uint32 unusedBitsPerSample;
|
|
if (pFlac == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
|
|
}
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
|
|
unusedBitsPerSample = 32 - pFlac->bitsPerSample;
|
|
framesRead = 0;
|
|
while (framesToRead > 0) {
|
|
if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
|
|
if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
|
|
break;
|
|
}
|
|
} else {
|
|
unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
|
|
ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
ma_uint64 frameCountThisIteration = framesToRead;
|
|
if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
|
|
frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
}
|
|
if (channelCount == 2) {
|
|
const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
|
|
const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
|
|
switch (pFlac->currentFLACFrame.header.channelAssignment)
|
|
{
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
|
|
default:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCountThisIteration; ++i) {
|
|
unsigned int j;
|
|
for (j = 0; j < channelCount; ++j) {
|
|
ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
|
|
pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16);
|
|
}
|
|
}
|
|
}
|
|
framesRead += frameCountThisIteration;
|
|
pBufferOut += frameCountThisIteration * channelCount;
|
|
framesToRead -= frameCountThisIteration;
|
|
pFlac->currentPCMFrame += frameCountThisIteration;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration;
|
|
}
|
|
}
|
|
return framesRead;
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCount; ++i) {
|
|
ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0);
|
|
pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
float factor = 1 / 2147483648.0;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1;
|
|
ma_uint32 right0 = left0 - side0;
|
|
ma_uint32 right1 = left1 - side1;
|
|
ma_uint32 right2 = left2 - side2;
|
|
ma_uint32 right3 = left3 - side3;
|
|
pOutputSamples[i*8+0] = (ma_int32)left0 * factor;
|
|
pOutputSamples[i*8+1] = (ma_int32)right0 * factor;
|
|
pOutputSamples[i*8+2] = (ma_int32)left1 * factor;
|
|
pOutputSamples[i*8+3] = (ma_int32)right1 * factor;
|
|
pOutputSamples[i*8+4] = (ma_int32)left2 * factor;
|
|
pOutputSamples[i*8+5] = (ma_int32)right2 * factor;
|
|
pOutputSamples[i*8+6] = (ma_int32)left3 * factor;
|
|
pOutputSamples[i*8+7] = (ma_int32)right3 * factor;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)right * factor;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
|
|
ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
|
|
__m128 factor;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
factor = _mm_set1_ps(1.0f / 8388608.0f);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
__m128i right = _mm_sub_epi32(left, side);
|
|
__m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
|
|
__m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
|
|
pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
|
|
ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
|
|
float32x4_t factor4;
|
|
int32x4_t shift0_4;
|
|
int32x4_t shift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
factor4 = vdupq_n_f32(1.0f / 8388608.0f);
|
|
shift0_4 = vdupq_n_s32(shift0);
|
|
shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t left;
|
|
uint32x4_t side;
|
|
uint32x4_t right;
|
|
float32x4_t leftf;
|
|
float32x4_t rightf;
|
|
left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
|
right = vsubq_u32(left, side);
|
|
leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
|
|
rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
|
|
ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 left = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 side = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 right = left - side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
|
|
pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCount; ++i) {
|
|
ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0);
|
|
pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
float factor = 1 / 2147483648.0;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1;
|
|
ma_uint32 left0 = right0 + side0;
|
|
ma_uint32 left1 = right1 + side1;
|
|
ma_uint32 left2 = right2 + side2;
|
|
ma_uint32 left3 = right3 + side3;
|
|
pOutputSamples[i*8+0] = (ma_int32)left0 * factor;
|
|
pOutputSamples[i*8+1] = (ma_int32)right0 * factor;
|
|
pOutputSamples[i*8+2] = (ma_int32)left1 * factor;
|
|
pOutputSamples[i*8+3] = (ma_int32)right1 * factor;
|
|
pOutputSamples[i*8+4] = (ma_int32)left2 * factor;
|
|
pOutputSamples[i*8+5] = (ma_int32)right2 * factor;
|
|
pOutputSamples[i*8+6] = (ma_int32)left3 * factor;
|
|
pOutputSamples[i*8+7] = (ma_int32)right3 * factor;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)right * factor;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
|
|
ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
|
|
__m128 factor;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
factor = _mm_set1_ps(1.0f / 8388608.0f);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
__m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
__m128i left = _mm_add_epi32(right, side);
|
|
__m128 leftf = _mm_mul_ps(_mm_cvtepi32_ps(left), factor);
|
|
__m128 rightf = _mm_mul_ps(_mm_cvtepi32_ps(right), factor);
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
|
|
pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
|
|
ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
|
|
float32x4_t factor4;
|
|
int32x4_t shift0_4;
|
|
int32x4_t shift1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
factor4 = vdupq_n_f32(1.0f / 8388608.0f);
|
|
shift0_4 = vdupq_n_s32(shift0);
|
|
shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t side;
|
|
uint32x4_t right;
|
|
uint32x4_t left;
|
|
float32x4_t leftf;
|
|
float32x4_t rightf;
|
|
side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4);
|
|
right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4);
|
|
left = vaddq_u32(right, side);
|
|
leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4);
|
|
rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4);
|
|
ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 side = pInputSamples0U32[i] << shift0;
|
|
ma_uint32 right = pInputSamples1U32[i] << shift1;
|
|
ma_uint32 left = right + side;
|
|
pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f;
|
|
pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
for (ma_uint64 i = 0; i < frameCount; ++i) {
|
|
ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
|
|
pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift = unusedBitsPerSample;
|
|
float factor = 1 / 2147483648.0;
|
|
if (shift > 0) {
|
|
shift -= 1;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 temp0L;
|
|
ma_uint32 temp1L;
|
|
ma_uint32 temp2L;
|
|
ma_uint32 temp3L;
|
|
ma_uint32 temp0R;
|
|
ma_uint32 temp1R;
|
|
ma_uint32 temp2R;
|
|
ma_uint32 temp3R;
|
|
ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid0 = (mid0 << 1) | (side0 & 0x01);
|
|
mid1 = (mid1 << 1) | (side1 & 0x01);
|
|
mid2 = (mid2 << 1) | (side2 & 0x01);
|
|
mid3 = (mid3 << 1) | (side3 & 0x01);
|
|
temp0L = (mid0 + side0) << shift;
|
|
temp1L = (mid1 + side1) << shift;
|
|
temp2L = (mid2 + side2) << shift;
|
|
temp3L = (mid3 + side3) << shift;
|
|
temp0R = (mid0 - side0) << shift;
|
|
temp1R = (mid1 - side1) << shift;
|
|
temp2R = (mid2 - side2) << shift;
|
|
temp3R = (mid3 - side3) << shift;
|
|
pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;
|
|
pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;
|
|
pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;
|
|
pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;
|
|
pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;
|
|
pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;
|
|
pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;
|
|
pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;
|
|
}
|
|
} else {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 temp0L;
|
|
ma_uint32 temp1L;
|
|
ma_uint32 temp2L;
|
|
ma_uint32 temp3L;
|
|
ma_uint32 temp0R;
|
|
ma_uint32 temp1R;
|
|
ma_uint32 temp2R;
|
|
ma_uint32 temp3R;
|
|
ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid0 = (mid0 << 1) | (side0 & 0x01);
|
|
mid1 = (mid1 << 1) | (side1 & 0x01);
|
|
mid2 = (mid2 << 1) | (side2 & 0x01);
|
|
mid3 = (mid3 << 1) | (side3 & 0x01);
|
|
temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1);
|
|
temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1);
|
|
temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1);
|
|
temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1);
|
|
temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1);
|
|
temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1);
|
|
temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1);
|
|
temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1);
|
|
pOutputSamples[i*8+0] = (ma_int32)temp0L * factor;
|
|
pOutputSamples[i*8+1] = (ma_int32)temp0R * factor;
|
|
pOutputSamples[i*8+2] = (ma_int32)temp1L * factor;
|
|
pOutputSamples[i*8+3] = (ma_int32)temp1R * factor;
|
|
pOutputSamples[i*8+4] = (ma_int32)temp2L * factor;
|
|
pOutputSamples[i*8+5] = (ma_int32)temp2R * factor;
|
|
pOutputSamples[i*8+6] = (ma_int32)temp3L * factor;
|
|
pOutputSamples[i*8+7] = (ma_int32)temp3R * factor;
|
|
}
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift = unusedBitsPerSample - 8;
|
|
float factor;
|
|
__m128 factor128;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
factor = 1.0f / 8388608.0f;
|
|
factor128 = _mm_set1_ps(factor);
|
|
if (shift == 0) {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i mid;
|
|
__m128i side;
|
|
__m128i tempL;
|
|
__m128i tempR;
|
|
__m128 leftf;
|
|
__m128 rightf;
|
|
mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
|
|
tempL = _mm_srai_epi32(_mm_add_epi32(mid, side), 1);
|
|
tempR = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1);
|
|
leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
|
|
rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;
|
|
pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;
|
|
}
|
|
} else {
|
|
shift -= 1;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i mid;
|
|
__m128i side;
|
|
__m128i tempL;
|
|
__m128i tempR;
|
|
__m128 leftf;
|
|
__m128 rightf;
|
|
mid = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
mid = _mm_or_si128(_mm_slli_epi32(mid, 1), _mm_and_si128(side, _mm_set1_epi32(0x01)));
|
|
tempL = _mm_slli_epi32(_mm_add_epi32(mid, side), shift);
|
|
tempR = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift);
|
|
leftf = _mm_mul_ps(_mm_cvtepi32_ps(tempL), factor128);
|
|
rightf = _mm_mul_ps(_mm_cvtepi32_ps(tempR), factor128);
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift = unusedBitsPerSample - 8;
|
|
float factor;
|
|
float32x4_t factor4;
|
|
int32x4_t shift4;
|
|
int32x4_t wbps0_4;
|
|
int32x4_t wbps1_4;
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24);
|
|
factor = 1.0f / 8388608.0f;
|
|
factor4 = vdupq_n_f32(factor);
|
|
wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample);
|
|
wbps1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample);
|
|
if (shift == 0) {
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
int32x4_t lefti;
|
|
int32x4_t righti;
|
|
float32x4_t leftf;
|
|
float32x4_t rightf;
|
|
uint32x4_t mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
|
|
uint32x4_t side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
|
|
mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
|
|
lefti = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1);
|
|
righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1);
|
|
leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
|
|
rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
|
|
ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor;
|
|
pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor;
|
|
}
|
|
} else {
|
|
shift -= 1;
|
|
shift4 = vdupq_n_s32(shift);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
uint32x4_t mid;
|
|
uint32x4_t side;
|
|
int32x4_t lefti;
|
|
int32x4_t righti;
|
|
float32x4_t leftf;
|
|
float32x4_t rightf;
|
|
mid = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), wbps0_4);
|
|
side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), wbps1_4);
|
|
mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, vdupq_n_u32(1)));
|
|
lefti = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4));
|
|
righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4));
|
|
leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
|
|
rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
|
|
ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
mid = (mid << 1) | (side & 0x01);
|
|
pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
#if 0
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
for (ma_uint64 i = 0; i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0);
|
|
pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0);
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample;
|
|
ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample;
|
|
float factor = 1 / 2147483648.0;
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0;
|
|
ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0;
|
|
ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0;
|
|
ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0;
|
|
ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1;
|
|
ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1;
|
|
ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1;
|
|
ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1;
|
|
pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor;
|
|
pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor;
|
|
pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor;
|
|
pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor;
|
|
pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor;
|
|
pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor;
|
|
pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor;
|
|
pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor;
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
|
|
}
|
|
}
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
|
|
ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
|
|
float factor = 1.0f / 8388608.0f;
|
|
__m128 factor128 = _mm_set1_ps(factor);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
__m128i lefti;
|
|
__m128i righti;
|
|
__m128 leftf;
|
|
__m128 rightf;
|
|
lefti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0);
|
|
righti = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1);
|
|
leftf = _mm_mul_ps(_mm_cvtepi32_ps(lefti), factor128);
|
|
rightf = _mm_mul_ps(_mm_cvtepi32_ps(righti), factor128);
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 0, _mm_unpacklo_ps(leftf, rightf));
|
|
_mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
|
|
}
|
|
}
|
|
#endif
|
|
#if defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 frameCount4 = frameCount >> 2;
|
|
const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0;
|
|
const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1;
|
|
ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8;
|
|
ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8;
|
|
float factor = 1.0f / 8388608.0f;
|
|
float32x4_t factor4 = vdupq_n_f32(factor);
|
|
int32x4_t shift0_4 = vdupq_n_s32(shift0);
|
|
int32x4_t shift1_4 = vdupq_n_s32(shift1);
|
|
for (i = 0; i < frameCount4; ++i) {
|
|
int32x4_t lefti;
|
|
int32x4_t righti;
|
|
float32x4_t leftf;
|
|
float32x4_t rightf;
|
|
lefti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4));
|
|
righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4));
|
|
leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4);
|
|
rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4);
|
|
ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf));
|
|
}
|
|
for (i = (frameCount4 << 2); i < frameCount; ++i) {
|
|
pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor;
|
|
pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor;
|
|
}
|
|
}
|
|
#endif
|
|
static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples)
|
|
{
|
|
#if defined(MA_DR_FLAC_SUPPORT_SSE2)
|
|
if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#elif defined(MA_DR_FLAC_SUPPORT_NEON)
|
|
if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) {
|
|
ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
} else
|
|
#endif
|
|
{
|
|
#if 0
|
|
ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#else
|
|
ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples);
|
|
#endif
|
|
}
|
|
}
|
|
MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
ma_uint64 framesRead;
|
|
ma_uint32 unusedBitsPerSample;
|
|
if (pFlac == NULL || framesToRead == 0) {
|
|
return 0;
|
|
}
|
|
if (pBufferOut == NULL) {
|
|
return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead);
|
|
}
|
|
MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32);
|
|
unusedBitsPerSample = 32 - pFlac->bitsPerSample;
|
|
framesRead = 0;
|
|
while (framesToRead > 0) {
|
|
if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) {
|
|
if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) {
|
|
break;
|
|
}
|
|
} else {
|
|
unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment);
|
|
ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
ma_uint64 frameCountThisIteration = framesToRead;
|
|
if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) {
|
|
frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
}
|
|
if (channelCount == 2) {
|
|
const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame;
|
|
const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame;
|
|
switch (pFlac->currentFLACFrame.header.channelAssignment)
|
|
{
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT:
|
|
default:
|
|
{
|
|
ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut);
|
|
} break;
|
|
}
|
|
} else {
|
|
ma_uint64 i;
|
|
for (i = 0; i < frameCountThisIteration; ++i) {
|
|
unsigned int j;
|
|
for (j = 0; j < channelCount; ++j) {
|
|
ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample));
|
|
pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0);
|
|
}
|
|
}
|
|
}
|
|
framesRead += frameCountThisIteration;
|
|
pBufferOut += frameCountThisIteration * channelCount;
|
|
framesToRead -= frameCountThisIteration;
|
|
pFlac->currentPCMFrame += frameCountThisIteration;
|
|
pFlac->currentFLACFrame.pcmFramesRemaining -= (unsigned int)frameCountThisIteration;
|
|
}
|
|
}
|
|
return framesRead;
|
|
}
|
|
MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex)
|
|
{
|
|
if (pFlac == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pFlac->currentPCMFrame == pcmFrameIndex) {
|
|
return MA_TRUE;
|
|
}
|
|
if (pFlac->firstFLACFramePosInBytes == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pcmFrameIndex == 0) {
|
|
pFlac->currentPCMFrame = 0;
|
|
return ma_dr_flac__seek_to_first_frame(pFlac);
|
|
} else {
|
|
ma_bool32 wasSuccessful = MA_FALSE;
|
|
ma_uint64 originalPCMFrame = pFlac->currentPCMFrame;
|
|
if (pcmFrameIndex > pFlac->totalPCMFrameCount) {
|
|
pcmFrameIndex = pFlac->totalPCMFrameCount;
|
|
}
|
|
if (pcmFrameIndex > pFlac->currentPCMFrame) {
|
|
ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame);
|
|
if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) {
|
|
pFlac->currentFLACFrame.pcmFramesRemaining -= offset;
|
|
pFlac->currentPCMFrame = pcmFrameIndex;
|
|
return MA_TRUE;
|
|
}
|
|
} else {
|
|
ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex);
|
|
ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames;
|
|
ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining;
|
|
if (currentFLACFramePCMFramesConsumed > offsetAbs) {
|
|
pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs;
|
|
pFlac->currentPCMFrame = pcmFrameIndex;
|
|
return MA_TRUE;
|
|
}
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_OGG
|
|
if (pFlac->container == ma_dr_flac_container_ogg)
|
|
{
|
|
wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (!pFlac->_noSeekTableSeek) {
|
|
wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex);
|
|
}
|
|
#if !defined(MA_DR_FLAC_NO_CRC)
|
|
if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) {
|
|
wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex);
|
|
}
|
|
#endif
|
|
if (!wasSuccessful && !pFlac->_noBruteForceSeek) {
|
|
wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex);
|
|
}
|
|
}
|
|
if (wasSuccessful) {
|
|
pFlac->currentPCMFrame = pcmFrameIndex;
|
|
} else {
|
|
if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) {
|
|
ma_dr_flac_seek_to_pcm_frame(pFlac, 0);
|
|
}
|
|
}
|
|
return wasSuccessful;
|
|
}
|
|
}
|
|
#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \
|
|
static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\
|
|
{ \
|
|
type* pSampleData = NULL; \
|
|
ma_uint64 totalPCMFrameCount; \
|
|
type buffer[4096]; \
|
|
ma_uint64 pcmFramesRead; \
|
|
size_t sampleDataBufferSize = sizeof(buffer); \
|
|
\
|
|
MA_DR_FLAC_ASSERT(pFlac != NULL); \
|
|
\
|
|
totalPCMFrameCount = 0; \
|
|
\
|
|
pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \
|
|
if (pSampleData == NULL) { \
|
|
goto on_error; \
|
|
} \
|
|
\
|
|
while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \
|
|
if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \
|
|
type* pNewSampleData; \
|
|
size_t newSampleDataBufferSize; \
|
|
\
|
|
newSampleDataBufferSize = sampleDataBufferSize * 2; \
|
|
pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \
|
|
if (pNewSampleData == NULL) { \
|
|
ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \
|
|
goto on_error; \
|
|
} \
|
|
\
|
|
sampleDataBufferSize = newSampleDataBufferSize; \
|
|
pSampleData = pNewSampleData; \
|
|
} \
|
|
\
|
|
MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \
|
|
totalPCMFrameCount += pcmFramesRead; \
|
|
} \
|
|
\
|
|
\
|
|
MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \
|
|
\
|
|
if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \
|
|
if (channelsOut) *channelsOut = pFlac->channels; \
|
|
if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \
|
|
\
|
|
ma_dr_flac_close(pFlac); \
|
|
return pSampleData; \
|
|
\
|
|
on_error: \
|
|
ma_dr_flac_close(pFlac); \
|
|
return NULL; \
|
|
}
|
|
MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32)
|
|
MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16)
|
|
MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float)
|
|
MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalPCMFrameCountOut) {
|
|
*totalPCMFrameCountOut = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
|
|
}
|
|
MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalPCMFrameCountOut) {
|
|
*totalPCMFrameCountOut = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
|
|
}
|
|
MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_tell_proc onTell, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (channelsOut) {
|
|
*channelsOut = 0;
|
|
}
|
|
if (sampleRateOut) {
|
|
*sampleRateOut = 0;
|
|
}
|
|
if (totalPCMFrameCountOut) {
|
|
*totalPCMFrameCountOut = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open(onRead, onSeek, onTell, pUserData, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut);
|
|
}
|
|
#ifndef MA_DR_FLAC_NO_STDIO
|
|
MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (sampleRate) {
|
|
*sampleRate = 0;
|
|
}
|
|
if (channels) {
|
|
*channels = 0;
|
|
}
|
|
if (totalPCMFrameCount) {
|
|
*totalPCMFrameCount = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
|
|
}
|
|
MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (sampleRate) {
|
|
*sampleRate = 0;
|
|
}
|
|
if (channels) {
|
|
*channels = 0;
|
|
}
|
|
if (totalPCMFrameCount) {
|
|
*totalPCMFrameCount = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
|
|
}
|
|
MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (sampleRate) {
|
|
*sampleRate = 0;
|
|
}
|
|
if (channels) {
|
|
*channels = 0;
|
|
}
|
|
if (totalPCMFrameCount) {
|
|
*totalPCMFrameCount = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
|
|
}
|
|
#endif
|
|
MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (sampleRate) {
|
|
*sampleRate = 0;
|
|
}
|
|
if (channels) {
|
|
*channels = 0;
|
|
}
|
|
if (totalPCMFrameCount) {
|
|
*totalPCMFrameCount = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount);
|
|
}
|
|
MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (sampleRate) {
|
|
*sampleRate = 0;
|
|
}
|
|
if (channels) {
|
|
*channels = 0;
|
|
}
|
|
if (totalPCMFrameCount) {
|
|
*totalPCMFrameCount = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount);
|
|
}
|
|
MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_flac* pFlac;
|
|
if (sampleRate) {
|
|
*sampleRate = 0;
|
|
}
|
|
if (channels) {
|
|
*channels = 0;
|
|
}
|
|
if (totalPCMFrameCount) {
|
|
*totalPCMFrameCount = 0;
|
|
}
|
|
pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks);
|
|
if (pFlac == NULL) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount);
|
|
}
|
|
MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks);
|
|
} else {
|
|
ma_dr_flac__free_default(p, NULL);
|
|
}
|
|
}
|
|
MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments)
|
|
{
|
|
if (pIter == NULL) {
|
|
return;
|
|
}
|
|
pIter->countRemaining = commentCount;
|
|
pIter->pRunningData = (const char*)pComments;
|
|
}
|
|
MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut)
|
|
{
|
|
ma_int32 length;
|
|
const char* pComment;
|
|
if (pCommentLengthOut) {
|
|
*pCommentLengthOut = 0;
|
|
}
|
|
if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
|
|
return NULL;
|
|
}
|
|
length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData);
|
|
pIter->pRunningData += 4;
|
|
pComment = pIter->pRunningData;
|
|
pIter->pRunningData += length;
|
|
pIter->countRemaining -= 1;
|
|
if (pCommentLengthOut) {
|
|
*pCommentLengthOut = length;
|
|
}
|
|
return pComment;
|
|
}
|
|
MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData)
|
|
{
|
|
if (pIter == NULL) {
|
|
return;
|
|
}
|
|
pIter->countRemaining = trackCount;
|
|
pIter->pRunningData = (const char*)pTrackData;
|
|
}
|
|
MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack)
|
|
{
|
|
ma_dr_flac_cuesheet_track cuesheetTrack;
|
|
const char* pRunningData;
|
|
ma_uint64 offsetHi;
|
|
ma_uint64 offsetLo;
|
|
if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
pRunningData = pIter->pRunningData;
|
|
offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;
|
|
offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4;
|
|
cuesheetTrack.offset = offsetLo | (offsetHi << 32);
|
|
cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1;
|
|
MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12;
|
|
cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0;
|
|
cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14;
|
|
cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1;
|
|
cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index);
|
|
pIter->pRunningData = pRunningData;
|
|
pIter->countRemaining -= 1;
|
|
if (pCuesheetTrack) {
|
|
*pCuesheetTrack = cuesheetTrack;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING)
|
|
#if !defined(MA_DR_MP3_IMPLEMENTATION)
|
|
#ifndef ma_dr_mp3_c
|
|
#define ma_dr_mp3_c
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision)
|
|
{
|
|
if (pMajor) {
|
|
*pMajor = MA_DR_MP3_VERSION_MAJOR;
|
|
}
|
|
if (pMinor) {
|
|
*pMinor = MA_DR_MP3_VERSION_MINOR;
|
|
}
|
|
if (pRevision) {
|
|
*pRevision = MA_DR_MP3_VERSION_REVISION;
|
|
}
|
|
}
|
|
MA_API const char* ma_dr_mp3_version_string(void)
|
|
{
|
|
return MA_DR_MP3_VERSION_STRING;
|
|
}
|
|
#if defined(__TINYC__)
|
|
#define MA_DR_MP3_NO_SIMD
|
|
#endif
|
|
#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset)))
|
|
#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES
|
|
#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10
|
|
#endif
|
|
#define MA_DR_MP3_SHORT_BLOCK_TYPE 2
|
|
#define MA_DR_MP3_STOP_BLOCK_TYPE 3
|
|
#define MA_DR_MP3_MODE_MONO 3
|
|
#define MA_DR_MP3_MODE_JOINT_STEREO 1
|
|
#define MA_DR_MP3_HDR_SIZE 4
|
|
#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0)
|
|
#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60)
|
|
#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0)
|
|
#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1))
|
|
#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2)
|
|
#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8)
|
|
#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10)
|
|
#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10)
|
|
#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20)
|
|
#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3)
|
|
#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3)
|
|
#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3)
|
|
#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4)
|
|
#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3)
|
|
#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3)
|
|
#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2)
|
|
#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6)
|
|
#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1
|
|
#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210)
|
|
#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3)
|
|
#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a))
|
|
#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a))
|
|
#if !defined(MA_DR_MP3_NO_SIMD)
|
|
#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC))
|
|
#define MA_DR_MP3_ONLY_SIMD
|
|
#endif
|
|
#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__)))
|
|
#if defined(_MSC_VER)
|
|
#include <intrin.h>
|
|
#endif
|
|
#include <emmintrin.h>
|
|
#define MA_DR_MP3_HAVE_SSE 1
|
|
#define MA_DR_MP3_HAVE_SIMD 1
|
|
#define MA_DR_MP3_VSTORE _mm_storeu_ps
|
|
#define MA_DR_MP3_VLD _mm_loadu_ps
|
|
#define MA_DR_MP3_VSET _mm_set1_ps
|
|
#define MA_DR_MP3_VADD _mm_add_ps
|
|
#define MA_DR_MP3_VSUB _mm_sub_ps
|
|
#define MA_DR_MP3_VMUL _mm_mul_ps
|
|
#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y))
|
|
#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y))
|
|
#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s))
|
|
#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3))
|
|
typedef __m128 ma_dr_mp3_f4;
|
|
#if (defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD)) && !defined(__clang__)
|
|
#define ma_dr_mp3_cpuid __cpuid
|
|
#else
|
|
static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType)
|
|
{
|
|
#if defined(__PIC__)
|
|
__asm__ __volatile__(
|
|
#if defined(__x86_64__)
|
|
"push %%rbx\n"
|
|
"cpuid\n"
|
|
"xchgl %%ebx, %1\n"
|
|
"pop %%rbx\n"
|
|
#else
|
|
"xchgl %%ebx, %1\n"
|
|
"cpuid\n"
|
|
"xchgl %%ebx, %1\n"
|
|
#endif
|
|
: "=a" (CPUInfo[0]), "=r" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
|
|
: "a" (InfoType));
|
|
#else
|
|
__asm__ __volatile__(
|
|
"cpuid"
|
|
: "=a" (CPUInfo[0]), "=b" (CPUInfo[1]), "=c" (CPUInfo[2]), "=d" (CPUInfo[3])
|
|
: "a" (InfoType));
|
|
#endif
|
|
}
|
|
#endif
|
|
static int ma_dr_mp3_have_simd(void)
|
|
{
|
|
#ifdef MA_DR_MP3_ONLY_SIMD
|
|
return 1;
|
|
#else
|
|
static int g_have_simd;
|
|
int CPUInfo[4];
|
|
#ifdef MINIMP3_TEST
|
|
static int g_counter;
|
|
if (g_counter++ > 100)
|
|
return 0;
|
|
#endif
|
|
if (g_have_simd)
|
|
goto end;
|
|
ma_dr_mp3_cpuid(CPUInfo, 0);
|
|
if (CPUInfo[0] > 0)
|
|
{
|
|
ma_dr_mp3_cpuid(CPUInfo, 1);
|
|
g_have_simd = (CPUInfo[3] & (1 << 26)) + 1;
|
|
return g_have_simd - 1;
|
|
}
|
|
end:
|
|
return g_have_simd - 1;
|
|
#endif
|
|
}
|
|
#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
|
|
#include <arm_neon.h>
|
|
#define MA_DR_MP3_HAVE_SSE 0
|
|
#define MA_DR_MP3_HAVE_SIMD 1
|
|
#define MA_DR_MP3_VSTORE vst1q_f32
|
|
#define MA_DR_MP3_VLD vld1q_f32
|
|
#define MA_DR_MP3_VSET vmovq_n_f32
|
|
#define MA_DR_MP3_VADD vaddq_f32
|
|
#define MA_DR_MP3_VSUB vsubq_f32
|
|
#define MA_DR_MP3_VMUL vmulq_f32
|
|
#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y)
|
|
#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y)
|
|
#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
|
|
#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
|
|
typedef float32x4_t ma_dr_mp3_f4;
|
|
static int ma_dr_mp3_have_simd(void)
|
|
{
|
|
return 1;
|
|
}
|
|
#else
|
|
#define MA_DR_MP3_HAVE_SSE 0
|
|
#define MA_DR_MP3_HAVE_SIMD 0
|
|
#ifdef MA_DR_MP3_ONLY_SIMD
|
|
#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
|
|
#endif
|
|
#endif
|
|
#else
|
|
#define MA_DR_MP3_HAVE_SIMD 0
|
|
#endif
|
|
#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) && !defined(_M_ARM64EC) && !defined(__ARM_ARCH_6M__)
|
|
#define MA_DR_MP3_HAVE_ARMV6 1
|
|
static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a)
|
|
{
|
|
ma_int32 x = 0;
|
|
__asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
|
|
return x;
|
|
}
|
|
#else
|
|
#define MA_DR_MP3_HAVE_ARMV6 0
|
|
#endif
|
|
#ifndef MA_DR_MP3_ASSERT
|
|
#include <assert.h>
|
|
#define MA_DR_MP3_ASSERT(expression) assert(expression)
|
|
#endif
|
|
#ifndef MA_DR_MP3_COPY_MEMORY
|
|
#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
|
|
#endif
|
|
#ifndef MA_DR_MP3_MOVE_MEMORY
|
|
#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz))
|
|
#endif
|
|
#ifndef MA_DR_MP3_ZERO_MEMORY
|
|
#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz))
|
|
#endif
|
|
#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p)))
|
|
#ifndef MA_DR_MP3_MALLOC
|
|
#define MA_DR_MP3_MALLOC(sz) malloc((sz))
|
|
#endif
|
|
#ifndef MA_DR_MP3_REALLOC
|
|
#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz))
|
|
#endif
|
|
#ifndef MA_DR_MP3_FREE
|
|
#define MA_DR_MP3_FREE(p) free((p))
|
|
#endif
|
|
typedef struct
|
|
{
|
|
float scf[3*64];
|
|
ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64];
|
|
} ma_dr_mp3_L12_scale_info;
|
|
typedef struct
|
|
{
|
|
ma_uint8 tab_offset, code_tab_width, band_count;
|
|
} ma_dr_mp3_L12_subband_alloc;
|
|
static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes)
|
|
{
|
|
bs->buf = data;
|
|
bs->pos = 0;
|
|
bs->limit = bytes*8;
|
|
}
|
|
static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n)
|
|
{
|
|
ma_uint32 next, cache = 0, s = bs->pos & 7;
|
|
int shl = n + s;
|
|
const ma_uint8 *p = bs->buf + (bs->pos >> 3);
|
|
if ((bs->pos += n) > bs->limit)
|
|
return 0;
|
|
next = *p++ & (255 >> s);
|
|
while ((shl -= 8) > 0)
|
|
{
|
|
cache |= next << shl;
|
|
next = *p++;
|
|
}
|
|
return cache | (next >> -shl);
|
|
}
|
|
static int ma_dr_mp3_hdr_valid(const ma_uint8 *h)
|
|
{
|
|
return h[0] == 0xff &&
|
|
((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) &&
|
|
(MA_DR_MP3_HDR_GET_LAYER(h) != 0) &&
|
|
(MA_DR_MP3_HDR_GET_BITRATE(h) != 15) &&
|
|
(MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3);
|
|
}
|
|
static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2)
|
|
{
|
|
return ma_dr_mp3_hdr_valid(h2) &&
|
|
((h1[1] ^ h2[1]) & 0xFE) == 0 &&
|
|
((h1[2] ^ h2[2]) & 0x0C) == 0 &&
|
|
!(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2));
|
|
}
|
|
static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h)
|
|
{
|
|
static const ma_uint8 halfrate[2][3][15] = {
|
|
{ { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } },
|
|
{ { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } },
|
|
};
|
|
return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)];
|
|
}
|
|
static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h)
|
|
{
|
|
static const unsigned g_hz[3] = { 44100, 48000, 32000 };
|
|
return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h);
|
|
}
|
|
static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h)
|
|
{
|
|
return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h));
|
|
}
|
|
static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size)
|
|
{
|
|
int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h);
|
|
if (MA_DR_MP3_HDR_IS_LAYER_1(h))
|
|
{
|
|
frame_bytes &= ~3;
|
|
}
|
|
return frame_bytes ? frame_bytes : free_format_size;
|
|
}
|
|
static int ma_dr_mp3_hdr_padding(const ma_uint8 *h)
|
|
{
|
|
return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0;
|
|
}
|
|
#ifndef MA_DR_MP3_ONLY_MP3
|
|
static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci)
|
|
{
|
|
const ma_dr_mp3_L12_subband_alloc *alloc;
|
|
int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr);
|
|
int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32;
|
|
if (MA_DR_MP3_HDR_IS_LAYER_1(hdr))
|
|
{
|
|
static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } };
|
|
alloc = g_alloc_L1;
|
|
nbands = 32;
|
|
} else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr))
|
|
{
|
|
static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } };
|
|
alloc = g_alloc_L2M2;
|
|
nbands = 30;
|
|
} else
|
|
{
|
|
static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } };
|
|
int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr);
|
|
unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO);
|
|
if (!kbps)
|
|
{
|
|
kbps = 192;
|
|
}
|
|
alloc = g_alloc_L2M1;
|
|
nbands = 27;
|
|
if (kbps < 56)
|
|
{
|
|
static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } };
|
|
alloc = g_alloc_L2M1_lowrate;
|
|
nbands = sample_rate_idx == 2 ? 12 : 8;
|
|
} else if (kbps >= 96 && sample_rate_idx != 1)
|
|
{
|
|
nbands = 30;
|
|
}
|
|
}
|
|
sci->total_bands = (ma_uint8)nbands;
|
|
sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands);
|
|
return alloc;
|
|
}
|
|
static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf)
|
|
{
|
|
static const float g_deq_L12[18*3] = {
|
|
#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x
|
|
MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9)
|
|
};
|
|
int i, m;
|
|
for (i = 0; i < bands; i++)
|
|
{
|
|
float s = 0;
|
|
int ba = *pba++;
|
|
int mask = ba ? 4 + ((19 >> scfcod[i]) & 3) : 0;
|
|
for (m = 4; m; m >>= 1)
|
|
{
|
|
if (mask & m)
|
|
{
|
|
int b = ma_dr_mp3_bs_get_bits(bs, 6);
|
|
s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
|
|
}
|
|
*scf++ = s;
|
|
}
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci)
|
|
{
|
|
static const ma_uint8 g_bitalloc_code_tab[] = {
|
|
0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16,
|
|
0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16,
|
|
0,17,18, 3,19,4,5,16,
|
|
0,17,18,16,
|
|
0,17,18,19, 4,5,6, 7,8, 9,10,11,12,13,14,15,
|
|
0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14,
|
|
0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16
|
|
};
|
|
const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci);
|
|
int i, k = 0, ba_bits = 0;
|
|
const ma_uint8 *ba_code_tab = g_bitalloc_code_tab;
|
|
for (i = 0; i < sci->total_bands; i++)
|
|
{
|
|
ma_uint8 ba;
|
|
if (i == k)
|
|
{
|
|
k += subband_alloc->band_count;
|
|
ba_bits = subband_alloc->code_tab_width;
|
|
ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset;
|
|
subband_alloc++;
|
|
}
|
|
ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];
|
|
sci->bitalloc[2*i] = ba;
|
|
if (i < sci->stereo_bands)
|
|
{
|
|
ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)];
|
|
}
|
|
sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0;
|
|
}
|
|
for (i = 0; i < 2*sci->total_bands; i++)
|
|
{
|
|
sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6);
|
|
}
|
|
ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf);
|
|
for (i = sci->stereo_bands; i < sci->total_bands; i++)
|
|
{
|
|
sci->bitalloc[2*i + 1] = 0;
|
|
}
|
|
}
|
|
static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size)
|
|
{
|
|
int i, j, k, choff = 576;
|
|
for (j = 0; j < 4; j++)
|
|
{
|
|
float *dst = grbuf + group_size*j;
|
|
for (i = 0; i < 2*sci->total_bands; i++)
|
|
{
|
|
int ba = sci->bitalloc[i];
|
|
if (ba != 0)
|
|
{
|
|
if (ba < 17)
|
|
{
|
|
int half = (1 << (ba - 1)) - 1;
|
|
for (k = 0; k < group_size; k++)
|
|
{
|
|
dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half);
|
|
}
|
|
} else
|
|
{
|
|
unsigned mod = (2 << (ba - 17)) + 1;
|
|
unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3));
|
|
for (k = 0; k < group_size; k++, code /= mod)
|
|
{
|
|
dst[k] = (float)((int)(code % mod - mod/2));
|
|
}
|
|
}
|
|
}
|
|
dst += choff;
|
|
choff = 18 - choff;
|
|
}
|
|
}
|
|
return group_size*4;
|
|
}
|
|
static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst)
|
|
{
|
|
int i, k;
|
|
MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
|
|
for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
|
|
{
|
|
for (k = 0; k < 12; k++)
|
|
{
|
|
dst[k + 0] *= scf[0];
|
|
dst[k + 576] *= scf[3];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr)
|
|
{
|
|
static const ma_uint8 g_scf_long[8][23] = {
|
|
{ 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
|
|
{ 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 },
|
|
{ 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
|
|
{ 6,6,6,6,6,6,8,10,12,14,16,18,22,26,32,38,46,54,62,70,76,36,0 },
|
|
{ 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 },
|
|
{ 4,4,4,4,4,4,6,6,8,8,10,12,16,20,24,28,34,42,50,54,76,158,0 },
|
|
{ 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 },
|
|
{ 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 }
|
|
};
|
|
static const ma_uint8 g_scf_short[8][40] = {
|
|
{ 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
|
|
{ 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 },
|
|
{ 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 },
|
|
{ 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 },
|
|
{ 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
|
|
{ 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 },
|
|
{ 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 },
|
|
{ 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 }
|
|
};
|
|
static const ma_uint8 g_scf_mixed[8][40] = {
|
|
{ 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
|
|
{ 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 },
|
|
{ 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 },
|
|
{ 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,32,32,32,44,44,44,12,12,12,0 },
|
|
{ 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 },
|
|
{ 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,22,22,22,30,30,30,56,56,56,0 },
|
|
{ 4,4,4,4,4,4,6,6,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 },
|
|
{ 4,4,4,4,4,4,6,6,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 }
|
|
};
|
|
unsigned tables, scfsi = 0;
|
|
int main_data_begin, part_23_sum = 0;
|
|
int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;
|
|
int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
|
|
if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
|
|
{
|
|
gr_count *= 2;
|
|
main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9);
|
|
scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count);
|
|
} else
|
|
{
|
|
main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count;
|
|
}
|
|
do
|
|
{
|
|
if (MA_DR_MP3_HDR_IS_MONO(hdr))
|
|
{
|
|
scfsi <<= 4;
|
|
}
|
|
gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12);
|
|
part_23_sum += gr->part_23_length;
|
|
gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9);
|
|
if (gr->big_values > 288)
|
|
{
|
|
return -1;
|
|
}
|
|
gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8);
|
|
gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9);
|
|
gr->sfbtab = g_scf_long[sr_idx];
|
|
gr->n_long_sfb = 22;
|
|
gr->n_short_sfb = 0;
|
|
if (ma_dr_mp3_bs_get_bits(bs, 1))
|
|
{
|
|
gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2);
|
|
if (!gr->block_type)
|
|
{
|
|
return -1;
|
|
}
|
|
gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
|
|
gr->region_count[0] = 7;
|
|
gr->region_count[1] = 255;
|
|
if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)
|
|
{
|
|
scfsi &= 0x0F0F;
|
|
if (!gr->mixed_block_flag)
|
|
{
|
|
gr->region_count[0] = 8;
|
|
gr->sfbtab = g_scf_short[sr_idx];
|
|
gr->n_long_sfb = 0;
|
|
gr->n_short_sfb = 39;
|
|
} else
|
|
{
|
|
gr->sfbtab = g_scf_mixed[sr_idx];
|
|
gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6;
|
|
gr->n_short_sfb = 30;
|
|
}
|
|
}
|
|
tables = ma_dr_mp3_bs_get_bits(bs, 10);
|
|
tables <<= 5;
|
|
gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
|
|
gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
|
|
gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
|
|
} else
|
|
{
|
|
gr->block_type = 0;
|
|
gr->mixed_block_flag = 0;
|
|
tables = ma_dr_mp3_bs_get_bits(bs, 15);
|
|
gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4);
|
|
gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3);
|
|
gr->region_count[2] = 255;
|
|
}
|
|
gr->table_select[0] = (ma_uint8)(tables >> 10);
|
|
gr->table_select[1] = (ma_uint8)((tables >> 5) & 31);
|
|
gr->table_select[2] = (ma_uint8)((tables) & 31);
|
|
gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500));
|
|
gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
|
|
gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1);
|
|
gr->scfsi = (ma_uint8)((scfsi >> 12) & 15);
|
|
scfsi <<= 4;
|
|
gr++;
|
|
} while(--gr_count);
|
|
if (part_23_sum + bs->pos > bs->limit + main_data_begin*8)
|
|
{
|
|
return -1;
|
|
}
|
|
return main_data_begin;
|
|
}
|
|
static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi)
|
|
{
|
|
int i, k;
|
|
for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2)
|
|
{
|
|
int cnt = scf_count[i];
|
|
if (scfsi & 8)
|
|
{
|
|
MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt);
|
|
} else
|
|
{
|
|
int bits = scf_size[i];
|
|
if (!bits)
|
|
{
|
|
MA_DR_MP3_ZERO_MEMORY(scf, cnt);
|
|
MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt);
|
|
} else
|
|
{
|
|
int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
|
|
for (k = 0; k < cnt; k++)
|
|
{
|
|
int s = ma_dr_mp3_bs_get_bits(bitbuf, bits);
|
|
ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s);
|
|
scf[k] = (ma_uint8)s;
|
|
}
|
|
}
|
|
}
|
|
ist_pos += cnt;
|
|
scf += cnt;
|
|
}
|
|
scf[0] = scf[1] = scf[2] = 0;
|
|
}
|
|
static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2)
|
|
{
|
|
static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f };
|
|
int e;
|
|
do
|
|
{
|
|
e = MA_DR_MP3_MIN(30*4, exp_q2);
|
|
y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2));
|
|
} while ((exp_q2 -= e) > 0);
|
|
return y;
|
|
}
|
|
#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__)
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wstringop-overflow"
|
|
#endif
|
|
static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch)
|
|
{
|
|
static const ma_uint8 g_scf_partitions[3][28] = {
|
|
{ 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 },
|
|
{ 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 },
|
|
{ 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 }
|
|
};
|
|
const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb];
|
|
ma_uint8 scf_size[4], iscf[40];
|
|
int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi;
|
|
float gain;
|
|
if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
|
|
{
|
|
static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 };
|
|
int part = g_scfc_decode[gr->scalefac_compress];
|
|
scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2);
|
|
scf_size[3] = scf_size[2] = (ma_uint8)(part & 3);
|
|
} else
|
|
{
|
|
static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 };
|
|
int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch;
|
|
sfc = gr->scalefac_compress >> ist;
|
|
for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4)
|
|
{
|
|
for (modprod = 1, i = 3; i >= 0; i--)
|
|
{
|
|
scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]);
|
|
modprod *= g_mod[k + i];
|
|
}
|
|
}
|
|
scf_partition += k;
|
|
scfsi = -16;
|
|
}
|
|
ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi);
|
|
if (gr->n_short_sfb)
|
|
{
|
|
int sh = 3 - scf_shift;
|
|
for (i = 0; i < gr->n_short_sfb; i += 3)
|
|
{
|
|
iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
|
|
iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
|
|
iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
|
|
}
|
|
} else if (gr->preflag)
|
|
{
|
|
static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]);
|
|
}
|
|
}
|
|
gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0);
|
|
gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp);
|
|
for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++)
|
|
{
|
|
scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift);
|
|
}
|
|
}
|
|
#if (defined(__GNUC__) && (__GNUC__ >= 13)) && !defined(__clang__)
|
|
#pragma GCC diagnostic pop
|
|
#endif
|
|
static const float ma_dr_mp3_g_pow43[129 + 16] = {
|
|
0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f,
|
|
0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f
|
|
};
|
|
static float ma_dr_mp3_L3_pow_43(int x)
|
|
{
|
|
float frac;
|
|
int sign, mult = 256;
|
|
if (x < 129)
|
|
{
|
|
return ma_dr_mp3_g_pow43[16 + x];
|
|
}
|
|
if (x < 1024)
|
|
{
|
|
mult = 16;
|
|
x <<= 3;
|
|
}
|
|
sign = 2*x & 64;
|
|
frac = (float)((x & 63) - sign) / ((x & ~63) + sign);
|
|
return ma_dr_mp3_g_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult;
|
|
}
|
|
static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit)
|
|
{
|
|
static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,
|
|
-255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288,
|
|
-255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288,
|
|
-253,-318,-351,-367,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,819,818,547,547,275,275,275,275,561,560,515,546,289,274,288,258,
|
|
-254,-287,1329,1299,1314,1312,1057,1057,1042,1042,1026,1026,784,784,784,784,529,529,529,529,529,529,529,529,769,769,769,769,768,768,768,768,563,560,306,306,291,259,
|
|
-252,-413,-477,-542,1298,-575,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-383,-399,1107,1092,1106,1061,849,849,789,789,1104,1091,773,773,1076,1075,341,340,325,309,834,804,577,577,532,532,516,516,832,818,803,816,561,561,531,531,515,546,289,289,288,258,
|
|
-252,-429,-493,-559,1057,1057,1042,1042,529,529,529,529,529,529,529,529,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,-382,1077,-415,1106,1061,1104,849,849,789,789,1091,1076,1029,1075,834,834,597,581,340,340,339,324,804,833,532,532,832,772,818,803,817,787,816,771,290,290,290,290,288,258,
|
|
-253,-349,-414,-447,-463,1329,1299,-479,1314,1312,1057,1057,1042,1042,1026,1026,785,785,785,785,784,784,784,784,769,769,769,769,768,768,768,768,-319,851,821,-335,836,850,805,849,341,340,325,336,533,533,579,579,564,564,773,832,578,548,563,516,321,276,306,291,304,259,
|
|
-251,-572,-733,-830,-863,-879,1041,1041,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,1396,1351,1381,1366,1395,1335,1380,-559,1334,1138,1138,1063,1063,1350,1392,1031,1031,1062,1062,1364,1363,1120,1120,1333,1348,881,881,881,881,375,374,359,373,343,358,341,325,791,791,1123,1122,-703,1105,1045,-719,865,865,790,790,774,774,1104,1029,338,293,323,308,-799,-815,833,788,772,818,803,816,322,292,307,320,561,531,515,546,289,274,288,258,
|
|
-251,-525,-605,-685,-765,-831,-846,1298,1057,1057,1312,1282,785,785,785,785,784,784,784,784,769,769,769,769,512,512,512,512,512,512,512,512,1399,1398,1383,1367,1382,1396,1351,-511,1381,1366,1139,1139,1079,1079,1124,1124,1364,1349,1363,1333,882,882,882,882,807,807,807,807,1094,1094,1136,1136,373,341,535,535,881,775,867,822,774,-591,324,338,-671,849,550,550,866,864,609,609,293,336,534,534,789,835,773,-751,834,804,308,307,833,788,832,772,562,562,547,547,305,275,560,515,290,290,
|
|
-252,-397,-477,-557,-622,-653,-719,-735,-750,1329,1299,1314,1057,1057,1042,1042,1312,1282,1024,1024,785,785,785,785,784,784,784,784,769,769,769,769,-383,1127,1141,1111,1126,1140,1095,1110,869,869,883,883,1079,1109,882,882,375,374,807,868,838,881,791,-463,867,822,368,263,852,837,836,-543,610,610,550,550,352,336,534,534,865,774,851,821,850,805,593,533,579,564,773,832,578,578,548,548,577,577,307,276,306,291,516,560,259,259,
|
|
-250,-2107,-2507,-2764,-2909,-2974,-3007,-3023,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-767,-1052,-1213,-1277,-1358,-1405,-1469,-1535,-1550,-1582,-1614,-1647,-1662,-1694,-1726,-1759,-1774,-1807,-1822,-1854,-1886,1565,-1919,-1935,-1951,-1967,1731,1730,1580,1717,-1983,1729,1564,-1999,1548,-2015,-2031,1715,1595,-2047,1714,-2063,1610,-2079,1609,-2095,1323,1323,1457,1457,1307,1307,1712,1547,1641,1700,1699,1594,1685,1625,1442,1442,1322,1322,-780,-973,-910,1279,1278,1277,1262,1276,1261,1275,1215,1260,1229,-959,974,974,989,989,-943,735,478,478,495,463,506,414,-1039,1003,958,1017,927,942,987,957,431,476,1272,1167,1228,-1183,1256,-1199,895,895,941,941,1242,1227,1212,1135,1014,1014,490,489,503,487,910,1013,985,925,863,894,970,955,1012,847,-1343,831,755,755,984,909,428,366,754,559,-1391,752,486,457,924,997,698,698,983,893,740,740,908,877,739,739,667,667,953,938,497,287,271,271,683,606,590,712,726,574,302,302,738,736,481,286,526,725,605,711,636,724,696,651,589,681,666,710,364,467,573,695,466,466,301,465,379,379,709,604,665,679,316,316,634,633,436,436,464,269,424,394,452,332,438,363,347,408,393,448,331,422,362,407,392,421,346,406,391,376,375,359,1441,1306,-2367,1290,-2383,1337,-2399,-2415,1426,1321,-2431,1411,1336,-2447,-2463,-2479,1169,1169,1049,1049,1424,1289,1412,1352,1319,-2495,1154,1154,1064,1064,1153,1153,416,390,360,404,403,389,344,374,373,343,358,372,327,357,342,311,356,326,1395,1394,1137,1137,1047,1047,1365,1392,1287,1379,1334,1364,1349,1378,1318,1363,792,792,792,792,1152,1152,1032,1032,1121,1121,1046,1046,1120,1120,1030,1030,-2895,1106,1061,1104,849,849,789,789,1091,1076,1029,1090,1060,1075,833,833,309,324,532,532,832,772,818,803,561,561,531,560,515,546,289,274,288,258,
|
|
-250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259,
|
|
-251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258,
|
|
-253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 };
|
|
static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205};
|
|
static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 };
|
|
static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 };
|
|
static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 };
|
|
#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n)))
|
|
#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); }
|
|
#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; }
|
|
#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh)
|
|
float one = 0.0f;
|
|
int ireg = 0, big_val_cnt = gr_info->big_values;
|
|
const ma_uint8 *sfb = gr_info->sfbtab;
|
|
const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8;
|
|
ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7);
|
|
int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8;
|
|
bs_next_ptr += 4;
|
|
while (big_val_cnt > 0)
|
|
{
|
|
int tab_num = gr_info->table_select[ireg];
|
|
int sfb_cnt = gr_info->region_count[ireg++];
|
|
const ma_int16 *codebook = tabs + tabindex[tab_num];
|
|
int linbits = g_linbits[tab_num];
|
|
if (linbits)
|
|
{
|
|
do
|
|
{
|
|
np = *sfb++ / 2;
|
|
pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);
|
|
one = *scf++;
|
|
do
|
|
{
|
|
int j, w = 5;
|
|
int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];
|
|
while (leaf < 0)
|
|
{
|
|
MA_DR_MP3_FLUSH_BITS(w);
|
|
w = leaf & 7;
|
|
leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];
|
|
}
|
|
MA_DR_MP3_FLUSH_BITS(leaf >> 8);
|
|
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
|
{
|
|
int lsb = leaf & 0x0F;
|
|
if (lsb == 15)
|
|
{
|
|
lsb += MA_DR_MP3_PEEK_BITS(linbits);
|
|
MA_DR_MP3_FLUSH_BITS(linbits);
|
|
MA_DR_MP3_CHECK_BITS;
|
|
*dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1);
|
|
} else
|
|
{
|
|
*dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
|
}
|
|
MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);
|
|
}
|
|
MA_DR_MP3_CHECK_BITS;
|
|
} while (--pairs_to_decode);
|
|
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
|
} else
|
|
{
|
|
do
|
|
{
|
|
np = *sfb++ / 2;
|
|
pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np);
|
|
one = *scf++;
|
|
do
|
|
{
|
|
int j, w = 5;
|
|
int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)];
|
|
while (leaf < 0)
|
|
{
|
|
MA_DR_MP3_FLUSH_BITS(w);
|
|
w = leaf & 7;
|
|
leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)];
|
|
}
|
|
MA_DR_MP3_FLUSH_BITS(leaf >> 8);
|
|
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
|
{
|
|
int lsb = leaf & 0x0F;
|
|
*dst = ma_dr_mp3_g_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
|
MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0);
|
|
}
|
|
MA_DR_MP3_CHECK_BITS;
|
|
} while (--pairs_to_decode);
|
|
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
|
}
|
|
}
|
|
for (np = 1 - big_val_cnt;; dst += 4)
|
|
{
|
|
const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32;
|
|
int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)];
|
|
if (!(leaf & 8))
|
|
{
|
|
leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))];
|
|
}
|
|
MA_DR_MP3_FLUSH_BITS(leaf & 7);
|
|
if (MA_DR_MP3_BSPOS > layer3gr_limit)
|
|
{
|
|
break;
|
|
}
|
|
#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; }
|
|
#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) }
|
|
MA_DR_MP3_RELOAD_SCALEFACTOR;
|
|
MA_DR_MP3_DEQ_COUNT1(0);
|
|
MA_DR_MP3_DEQ_COUNT1(1);
|
|
MA_DR_MP3_RELOAD_SCALEFACTOR;
|
|
MA_DR_MP3_DEQ_COUNT1(2);
|
|
MA_DR_MP3_DEQ_COUNT1(3);
|
|
MA_DR_MP3_CHECK_BITS;
|
|
}
|
|
bs->pos = layer3gr_limit;
|
|
}
|
|
static void ma_dr_mp3_L3_midside_stereo(float *left, int n)
|
|
{
|
|
int i = 0;
|
|
float *right = left + 576;
|
|
#if MA_DR_MP3_HAVE_SIMD
|
|
if (ma_dr_mp3_have_simd())
|
|
{
|
|
for (; i < n - 3; i += 4)
|
|
{
|
|
ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i);
|
|
ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i);
|
|
MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr));
|
|
MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr));
|
|
}
|
|
#ifdef __GNUC__
|
|
if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
|
|
return;
|
|
#endif
|
|
}
|
|
#endif
|
|
for (; i < n; i++)
|
|
{
|
|
float a = left[i];
|
|
float b = right[i];
|
|
left[i] = a + b;
|
|
right[i] = a - b;
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr)
|
|
{
|
|
int i;
|
|
for (i = 0; i < n; i++)
|
|
{
|
|
left[i + 576] = left[i]*kr;
|
|
left[i] = left[i]*kl;
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3])
|
|
{
|
|
int i, k;
|
|
max_band[0] = max_band[1] = max_band[2] = -1;
|
|
for (i = 0; i < nbands; i++)
|
|
{
|
|
for (k = 0; k < sfb[i]; k += 2)
|
|
{
|
|
if (right[k] != 0 || right[k + 1] != 0)
|
|
{
|
|
max_band[i % 3] = i;
|
|
break;
|
|
}
|
|
}
|
|
right += sfb[i];
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh)
|
|
{
|
|
static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 };
|
|
unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64;
|
|
for (i = 0; sfb[i]; i++)
|
|
{
|
|
unsigned ipos = ist_pos[i];
|
|
if ((int)i > max_band[i % 3] && ipos < max_pos)
|
|
{
|
|
float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1;
|
|
if (MA_DR_MP3_HDR_TEST_MPEG1(hdr))
|
|
{
|
|
kl = g_pan[2*ipos];
|
|
kr = g_pan[2*ipos + 1];
|
|
} else
|
|
{
|
|
kl = 1;
|
|
kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh);
|
|
if (ipos & 1)
|
|
{
|
|
kl = kr;
|
|
kr = 1;
|
|
}
|
|
}
|
|
ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s);
|
|
} else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr))
|
|
{
|
|
ma_dr_mp3_L3_midside_stereo(left, sfb[i]);
|
|
}
|
|
left += sfb[i];
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr)
|
|
{
|
|
int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb;
|
|
int i, max_blocks = gr->n_short_sfb ? 3 : 1;
|
|
ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band);
|
|
if (gr->n_long_sfb)
|
|
{
|
|
max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]);
|
|
}
|
|
for (i = 0; i < max_blocks; i++)
|
|
{
|
|
int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0;
|
|
int itop = n_sfb - max_blocks + i;
|
|
int prev = itop - max_blocks;
|
|
ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]);
|
|
}
|
|
ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1);
|
|
}
|
|
static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb)
|
|
{
|
|
int i, len;
|
|
float *src = grbuf, *dst = scratch;
|
|
for (;0 != (len = *sfb); sfb += 3, src += 2*len)
|
|
{
|
|
for (i = 0; i < len; i++, src++)
|
|
{
|
|
*dst++ = src[0*len];
|
|
*dst++ = src[1*len];
|
|
*dst++ = src[2*len];
|
|
}
|
|
}
|
|
MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));
|
|
}
|
|
static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands)
|
|
{
|
|
static const float g_aa[2][8] = {
|
|
{0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f},
|
|
{0.51449576f,0.47173197f,0.31337745f,0.18191320f,0.09457419f,0.04096558f,0.01419856f,0.00369997f}
|
|
};
|
|
for (; nbands > 0; nbands--, grbuf += 18)
|
|
{
|
|
int i = 0;
|
|
#if MA_DR_MP3_HAVE_SIMD
|
|
if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)
|
|
{
|
|
ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i);
|
|
ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i);
|
|
ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i);
|
|
ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i);
|
|
vd = MA_DR_MP3_VREV(vd);
|
|
MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1)));
|
|
vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0));
|
|
MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd));
|
|
}
|
|
#endif
|
|
#ifndef MA_DR_MP3_ONLY_SIMD
|
|
for(; i < 8; i++)
|
|
{
|
|
float u = grbuf[18 + i];
|
|
float d = grbuf[17 - i];
|
|
grbuf[18 + i] = u*g_aa[0][i] - d*g_aa[1][i];
|
|
grbuf[17 - i] = u*g_aa[1][i] + d*g_aa[0][i];
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_dct3_9(float *y)
|
|
{
|
|
float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4;
|
|
s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8];
|
|
t0 = s0 + s6*0.5f;
|
|
s0 -= s6;
|
|
t4 = (s4 + s2)*0.93969262f;
|
|
t2 = (s8 + s2)*0.76604444f;
|
|
s6 = (s4 - s8)*0.17364818f;
|
|
s4 += s8 - s2;
|
|
s2 = s0 - s4*0.5f;
|
|
y[4] = s4 + s0;
|
|
s8 = t0 - t2 + s6;
|
|
s0 = t0 - t4 + t2;
|
|
s4 = t0 + t4 - s6;
|
|
s1 = y[1]; s3 = y[3]; s5 = y[5]; s7 = y[7];
|
|
s3 *= 0.86602540f;
|
|
t0 = (s5 + s1)*0.98480775f;
|
|
t4 = (s5 - s7)*0.34202014f;
|
|
t2 = (s1 + s7)*0.64278761f;
|
|
s1 = (s1 - s5 - s7)*0.86602540f;
|
|
s5 = t0 - s3 - t2;
|
|
s7 = t4 - s3 - t0;
|
|
s3 = t4 + s3 - t2;
|
|
y[0] = s4 - s7;
|
|
y[1] = s2 + s1;
|
|
y[2] = s0 - s3;
|
|
y[3] = s8 + s5;
|
|
y[5] = s8 - s5;
|
|
y[6] = s0 + s3;
|
|
y[7] = s2 - s1;
|
|
y[8] = s4 + s7;
|
|
}
|
|
static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands)
|
|
{
|
|
int i, j;
|
|
static const float g_twid9[18] = {
|
|
0.73727734f,0.79335334f,0.84339145f,0.88701083f,0.92387953f,0.95371695f,0.97629601f,0.99144486f,0.99904822f,0.67559021f,0.60876143f,0.53729961f,0.46174861f,0.38268343f,0.30070580f,0.21643961f,0.13052619f,0.04361938f
|
|
};
|
|
for (j = 0; j < nbands; j++, grbuf += 18, overlap += 9)
|
|
{
|
|
float co[9], si[9];
|
|
co[0] = -grbuf[0];
|
|
si[0] = grbuf[17];
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
si[8 - 2*i] = grbuf[4*i + 1] - grbuf[4*i + 2];
|
|
co[1 + 2*i] = grbuf[4*i + 1] + grbuf[4*i + 2];
|
|
si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3];
|
|
co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]);
|
|
}
|
|
ma_dr_mp3_L3_dct3_9(co);
|
|
ma_dr_mp3_L3_dct3_9(si);
|
|
si[1] = -si[1];
|
|
si[3] = -si[3];
|
|
si[5] = -si[5];
|
|
si[7] = -si[7];
|
|
i = 0;
|
|
#if MA_DR_MP3_HAVE_SIMD
|
|
if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4)
|
|
{
|
|
ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i);
|
|
ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i);
|
|
ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i);
|
|
ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i);
|
|
ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i);
|
|
ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i);
|
|
ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i);
|
|
ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0));
|
|
MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1)));
|
|
MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1)));
|
|
vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0));
|
|
MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum));
|
|
}
|
|
#endif
|
|
for (; i < 9; i++)
|
|
{
|
|
float ovl = overlap[i];
|
|
float sum = co[i]*g_twid9[9 + i] + si[i]*g_twid9[0 + i];
|
|
overlap[i] = co[i]*g_twid9[0 + i] - si[i]*g_twid9[9 + i];
|
|
grbuf[i] = ovl*window[0 + i] - sum*window[9 + i];
|
|
grbuf[17 - i] = ovl*window[9 + i] + sum*window[0 + i];
|
|
}
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst)
|
|
{
|
|
float m1 = x1*0.86602540f;
|
|
float a1 = x0 - x2*0.5f;
|
|
dst[1] = x0 + x2;
|
|
dst[0] = a1 + m1;
|
|
dst[2] = a1 - m1;
|
|
}
|
|
static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap)
|
|
{
|
|
static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f };
|
|
float co[3], si[3];
|
|
int i;
|
|
ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co);
|
|
ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si);
|
|
si[1] = -si[1];
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
float ovl = overlap[i];
|
|
float sum = co[i]*g_twid3[3 + i] + si[i]*g_twid3[0 + i];
|
|
overlap[i] = co[i]*g_twid3[0 + i] - si[i]*g_twid3[3 + i];
|
|
dst[i] = ovl*g_twid3[2 - i] - sum*g_twid3[5 - i];
|
|
dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i];
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
|
|
{
|
|
for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
|
|
{
|
|
float tmp[18];
|
|
MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));
|
|
MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));
|
|
ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
|
|
ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
|
|
ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
|
|
}
|
|
}
|
|
static void ma_dr_mp3_L3_change_sign(float *grbuf)
|
|
{
|
|
int b, i;
|
|
for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36)
|
|
for (i = 1; i < 18; i += 2)
|
|
grbuf[i] = -grbuf[i];
|
|
}
|
|
static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands)
|
|
{
|
|
static const float g_mdct_window[2][18] = {
|
|
{ 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f },
|
|
{ 1,1,1,1,1,1,0.99144486f,0.92387953f,0.79335334f,0,0,0,0,0,0,0.13052619f,0.38268343f,0.60876143f }
|
|
};
|
|
if (n_long_bands)
|
|
{
|
|
ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands);
|
|
grbuf += 18*n_long_bands;
|
|
overlap += 9*n_long_bands;
|
|
}
|
|
if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE)
|
|
ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands);
|
|
else
|
|
ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands);
|
|
}
|
|
static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s)
|
|
{
|
|
int pos = (s->bs.pos + 7)/8u;
|
|
int remains = s->bs.limit/8u - pos;
|
|
if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES)
|
|
{
|
|
pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES;
|
|
remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES;
|
|
}
|
|
if (remains > 0)
|
|
{
|
|
MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);
|
|
}
|
|
h->reserv = remains;
|
|
}
|
|
static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin)
|
|
{
|
|
int frame_bytes = (bs->limit - bs->pos)/8;
|
|
int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin);
|
|
MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin));
|
|
MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
|
|
ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
|
|
return h->reserv >= main_data_begin;
|
|
}
|
|
static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch)
|
|
{
|
|
int ch;
|
|
for (ch = 0; ch < nch; ch++)
|
|
{
|
|
int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length;
|
|
ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch);
|
|
ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit);
|
|
}
|
|
if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header))
|
|
{
|
|
ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header);
|
|
} else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header))
|
|
{
|
|
ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576);
|
|
}
|
|
for (ch = 0; ch < nch; ch++, gr_info++)
|
|
{
|
|
int aa_bands = 31;
|
|
int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2);
|
|
if (gr_info->n_short_sfb)
|
|
{
|
|
aa_bands = n_long_bands - 1;
|
|
ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb);
|
|
}
|
|
ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands);
|
|
ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands);
|
|
ma_dr_mp3_L3_change_sign(s->grbuf[ch]);
|
|
}
|
|
}
|
|
static void ma_dr_mp3d_DCT_II(float *grbuf, int n)
|
|
{
|
|
static const float g_sec[24] = {
|
|
10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f
|
|
};
|
|
int i, k = 0;
|
|
#if MA_DR_MP3_HAVE_SIMD
|
|
if (ma_dr_mp3_have_simd()) for (; k < n; k += 4)
|
|
{
|
|
ma_dr_mp3_f4 t[4][8], *x;
|
|
float *y = grbuf + k;
|
|
for (x = t[0], i = 0; i < 8; i++, x++)
|
|
{
|
|
ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]);
|
|
ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]);
|
|
ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]);
|
|
ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]);
|
|
ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3);
|
|
ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2);
|
|
ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]);
|
|
ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]);
|
|
x[0] = MA_DR_MP3_VADD(t0, t1);
|
|
x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]);
|
|
x[16] = MA_DR_MP3_VADD(t3, t2);
|
|
x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]);
|
|
}
|
|
for (x = t[0], i = 0; i < 4; i++, x += 8)
|
|
{
|
|
ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
|
|
xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7);
|
|
x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6);
|
|
x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5);
|
|
x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4);
|
|
x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3);
|
|
x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2);
|
|
x[0] = MA_DR_MP3_VADD(x0, x1);
|
|
x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f);
|
|
x5 = MA_DR_MP3_VADD(x5, x6);
|
|
x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f);
|
|
x7 = MA_DR_MP3_VADD(x7, xt);
|
|
x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f);
|
|
x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));
|
|
x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f));
|
|
x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f));
|
|
x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6);
|
|
x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f);
|
|
x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f);
|
|
x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f);
|
|
x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f);
|
|
x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f);
|
|
x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f);
|
|
}
|
|
if (k > n - 3)
|
|
{
|
|
#if MA_DR_MP3_HAVE_SSE
|
|
#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v)
|
|
#else
|
|
#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v))
|
|
#endif
|
|
for (i = 0; i < 7; i++, y += 4*18)
|
|
{
|
|
ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);
|
|
MA_DR_MP3_VSAVE2(0, t[0][i]);
|
|
MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s));
|
|
MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));
|
|
MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s));
|
|
}
|
|
MA_DR_MP3_VSAVE2(0, t[0][7]);
|
|
MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));
|
|
MA_DR_MP3_VSAVE2(2, t[1][7]);
|
|
MA_DR_MP3_VSAVE2(3, t[3][7]);
|
|
} else
|
|
{
|
|
#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v)
|
|
for (i = 0; i < 7; i++, y += 4*18)
|
|
{
|
|
ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]);
|
|
MA_DR_MP3_VSAVE4(0, t[0][i]);
|
|
MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s));
|
|
MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1]));
|
|
MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s));
|
|
}
|
|
MA_DR_MP3_VSAVE4(0, t[0][7]);
|
|
MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7]));
|
|
MA_DR_MP3_VSAVE4(2, t[1][7]);
|
|
MA_DR_MP3_VSAVE4(3, t[3][7]);
|
|
}
|
|
} else
|
|
#endif
|
|
#ifdef MA_DR_MP3_ONLY_SIMD
|
|
{}
|
|
#else
|
|
for (; k < n; k++)
|
|
{
|
|
float t[4][8], *x, *y = grbuf + k;
|
|
for (x = t[0], i = 0; i < 8; i++, x++)
|
|
{
|
|
float x0 = y[i*18];
|
|
float x1 = y[(15 - i)*18];
|
|
float x2 = y[(16 + i)*18];
|
|
float x3 = y[(31 - i)*18];
|
|
float t0 = x0 + x3;
|
|
float t1 = x1 + x2;
|
|
float t2 = (x1 - x2)*g_sec[3*i + 0];
|
|
float t3 = (x0 - x3)*g_sec[3*i + 1];
|
|
x[0] = t0 + t1;
|
|
x[8] = (t0 - t1)*g_sec[3*i + 2];
|
|
x[16] = t3 + t2;
|
|
x[24] = (t3 - t2)*g_sec[3*i + 2];
|
|
}
|
|
for (x = t[0], i = 0; i < 4; i++, x += 8)
|
|
{
|
|
float x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt;
|
|
xt = x0 - x7; x0 += x7;
|
|
x7 = x1 - x6; x1 += x6;
|
|
x6 = x2 - x5; x2 += x5;
|
|
x5 = x3 - x4; x3 += x4;
|
|
x4 = x0 - x3; x0 += x3;
|
|
x3 = x1 - x2; x1 += x2;
|
|
x[0] = x0 + x1;
|
|
x[4] = (x0 - x1)*0.70710677f;
|
|
x5 = x5 + x6;
|
|
x6 = (x6 + x7)*0.70710677f;
|
|
x7 = x7 + xt;
|
|
x3 = (x3 + x4)*0.70710677f;
|
|
x5 -= x7*0.198912367f;
|
|
x7 += x5*0.382683432f;
|
|
x5 -= x7*0.198912367f;
|
|
x0 = xt - x6; xt += x6;
|
|
x[1] = (xt + x7)*0.50979561f;
|
|
x[2] = (x4 + x3)*0.54119611f;
|
|
x[3] = (x0 - x5)*0.60134488f;
|
|
x[5] = (x0 + x5)*0.89997619f;
|
|
x[6] = (x4 - x3)*1.30656302f;
|
|
x[7] = (xt - x7)*2.56291556f;
|
|
}
|
|
for (i = 0; i < 7; i++, y += 4*18)
|
|
{
|
|
y[0*18] = t[0][i];
|
|
y[1*18] = t[2][i] + t[3][i] + t[3][i + 1];
|
|
y[2*18] = t[1][i] + t[1][i + 1];
|
|
y[3*18] = t[2][i + 1] + t[3][i] + t[3][i + 1];
|
|
}
|
|
y[0*18] = t[0][7];
|
|
y[1*18] = t[2][7] + t[3][7];
|
|
y[2*18] = t[1][7];
|
|
y[3*18] = t[3][7];
|
|
}
|
|
#endif
|
|
}
|
|
#ifndef MA_DR_MP3_FLOAT_OUTPUT
|
|
typedef ma_int16 ma_dr_mp3d_sample_t;
|
|
static ma_int16 ma_dr_mp3d_scale_pcm(float sample)
|
|
{
|
|
ma_int16 s;
|
|
#if MA_DR_MP3_HAVE_ARMV6
|
|
ma_int32 s32 = (ma_int32)(sample + .5f);
|
|
s32 -= (s32 < 0);
|
|
s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32);
|
|
#else
|
|
if (sample >= 32766.5f) return (ma_int16) 32767;
|
|
if (sample <= -32767.5f) return (ma_int16)-32768;
|
|
s = (ma_int16)(sample + .5f);
|
|
s -= (s < 0);
|
|
#endif
|
|
return s;
|
|
}
|
|
#else
|
|
typedef float ma_dr_mp3d_sample_t;
|
|
static float ma_dr_mp3d_scale_pcm(float sample)
|
|
{
|
|
return sample*(1.f/32768.f);
|
|
}
|
|
#endif
|
|
static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z)
|
|
{
|
|
float a;
|
|
a = (z[14*64] - z[ 0]) * 29;
|
|
a += (z[ 1*64] + z[13*64]) * 213;
|
|
a += (z[12*64] - z[ 2*64]) * 459;
|
|
a += (z[ 3*64] + z[11*64]) * 2037;
|
|
a += (z[10*64] - z[ 4*64]) * 5153;
|
|
a += (z[ 5*64] + z[ 9*64]) * 6574;
|
|
a += (z[ 8*64] - z[ 6*64]) * 37489;
|
|
a += z[ 7*64] * 75038;
|
|
pcm[0] = ma_dr_mp3d_scale_pcm(a);
|
|
z += 2;
|
|
a = z[14*64] * 104;
|
|
a += z[12*64] * 1567;
|
|
a += z[10*64] * 9727;
|
|
a += z[ 8*64] * 64019;
|
|
a += z[ 6*64] * -9975;
|
|
a += z[ 4*64] * -45;
|
|
a += z[ 2*64] * 146;
|
|
a += z[ 0*64] * -5;
|
|
pcm[16*nch] = ma_dr_mp3d_scale_pcm(a);
|
|
}
|
|
static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins)
|
|
{
|
|
int i;
|
|
float *xr = xl + 576*(nch - 1);
|
|
ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1);
|
|
static const float g_win[] = {
|
|
-1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992,
|
|
-1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856,
|
|
-1,21,-38,196,225,294,-645,2087,1893,4063,-6237,8092,4561,31947,-43006,74630,
|
|
-1,19,-41,190,227,244,-711,2085,1822,3705,-6589,8492,3776,30112,-44821,74313,
|
|
-1,17,-45,183,228,197,-779,2075,1739,3351,-6935,8840,2935,28289,-46617,73908,
|
|
-1,16,-49,176,228,153,-848,2057,1644,3004,-7271,9139,2037,26482,-48390,73415,
|
|
-2,14,-53,169,227,111,-919,2032,1535,2663,-7597,9389,1082,24694,-50137,72835,
|
|
-2,13,-58,161,224,72,-991,2001,1414,2330,-7910,9592,70,22929,-51853,72169,
|
|
-2,11,-63,154,221,36,-1064,1962,1280,2006,-8209,9750,-998,21189,-53534,71420,
|
|
-2,10,-68,147,215,2,-1137,1919,1131,1692,-8491,9863,-2122,19478,-55178,70590,
|
|
-3,9,-73,139,208,-29,-1210,1870,970,1388,-8755,9935,-3300,17799,-56778,69679,
|
|
-3,8,-79,132,200,-57,-1283,1817,794,1095,-8998,9966,-4533,16155,-58333,68692,
|
|
-4,7,-85,125,189,-83,-1356,1759,605,814,-9219,9959,-5818,14548,-59838,67629,
|
|
-4,7,-91,117,177,-106,-1428,1698,402,545,-9416,9916,-7154,12980,-61289,66494,
|
|
-5,6,-97,111,163,-127,-1498,1634,185,288,-9585,9838,-8540,11455,-62684,65290
|
|
};
|
|
float *zlin = lins + 15*64;
|
|
const float *w = g_win;
|
|
zlin[4*15] = xl[18*16];
|
|
zlin[4*15 + 1] = xr[18*16];
|
|
zlin[4*15 + 2] = xl[0];
|
|
zlin[4*15 + 3] = xr[0];
|
|
zlin[4*31] = xl[1 + 18*16];
|
|
zlin[4*31 + 1] = xr[1 + 18*16];
|
|
zlin[4*31 + 2] = xl[1];
|
|
zlin[4*31 + 3] = xr[1];
|
|
ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1);
|
|
ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1);
|
|
ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15);
|
|
ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64);
|
|
#if MA_DR_MP3_HAVE_SIMD
|
|
if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--)
|
|
{
|
|
#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]);
|
|
#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); }
|
|
#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); }
|
|
#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); }
|
|
ma_dr_mp3_f4 a, b;
|
|
zlin[4*i] = xl[18*(31 - i)];
|
|
zlin[4*i + 1] = xr[18*(31 - i)];
|
|
zlin[4*i + 2] = xl[1 + 18*(31 - i)];
|
|
zlin[4*i + 3] = xr[1 + 18*(31 - i)];
|
|
zlin[4*i + 64] = xl[1 + 18*(1 + i)];
|
|
zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)];
|
|
zlin[4*i - 64 + 2] = xl[18*(1 + i)];
|
|
zlin[4*i - 64 + 3] = xr[18*(1 + i)];
|
|
MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7)
|
|
{
|
|
#ifndef MA_DR_MP3_FLOAT_OUTPUT
|
|
#if MA_DR_MP3_HAVE_SSE
|
|
static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f };
|
|
static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f };
|
|
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)),
|
|
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min)));
|
|
dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1);
|
|
dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5);
|
|
dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0);
|
|
dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4);
|
|
dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3);
|
|
dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7);
|
|
dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2);
|
|
dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6);
|
|
#else
|
|
int16x4_t pcma, pcmb;
|
|
a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));
|
|
b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));
|
|
pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));
|
|
pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));
|
|
vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1);
|
|
vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1);
|
|
vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0);
|
|
vst1_lane_s16(dstl + (17 + i)*nch, pcmb, 0);
|
|
vst1_lane_s16(dstr + (47 - i)*nch, pcma, 3);
|
|
vst1_lane_s16(dstr + (49 + i)*nch, pcmb, 3);
|
|
vst1_lane_s16(dstl + (47 - i)*nch, pcma, 2);
|
|
vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2);
|
|
#endif
|
|
#else
|
|
#if MA_DR_MP3_HAVE_SSE
|
|
static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f };
|
|
#else
|
|
const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f);
|
|
#endif
|
|
a = MA_DR_MP3_VMUL(a, g_scale);
|
|
b = MA_DR_MP3_VMUL(b, g_scale);
|
|
#if MA_DR_MP3_HAVE_SSE
|
|
_mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1)));
|
|
_mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1)));
|
|
_mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0)));
|
|
_mm_store_ss(dstl + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(0, 0, 0, 0)));
|
|
_mm_store_ss(dstr + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 3, 3, 3)));
|
|
_mm_store_ss(dstr + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(3, 3, 3, 3)));
|
|
_mm_store_ss(dstl + (47 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(2, 2, 2, 2)));
|
|
_mm_store_ss(dstl + (49 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(2, 2, 2, 2)));
|
|
#else
|
|
vst1q_lane_f32(dstr + (15 - i)*nch, a, 1);
|
|
vst1q_lane_f32(dstr + (17 + i)*nch, b, 1);
|
|
vst1q_lane_f32(dstl + (15 - i)*nch, a, 0);
|
|
vst1q_lane_f32(dstl + (17 + i)*nch, b, 0);
|
|
vst1q_lane_f32(dstr + (47 - i)*nch, a, 3);
|
|
vst1q_lane_f32(dstr + (49 + i)*nch, b, 3);
|
|
vst1q_lane_f32(dstl + (47 - i)*nch, a, 2);
|
|
vst1q_lane_f32(dstl + (49 + i)*nch, b, 2);
|
|
#endif
|
|
#endif
|
|
}
|
|
} else
|
|
#endif
|
|
#ifdef MA_DR_MP3_ONLY_SIMD
|
|
{}
|
|
#else
|
|
for (i = 14; i >= 0; i--)
|
|
{
|
|
#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64];
|
|
#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; }
|
|
#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; }
|
|
#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; }
|
|
float a[4], b[4];
|
|
zlin[4*i] = xl[18*(31 - i)];
|
|
zlin[4*i + 1] = xr[18*(31 - i)];
|
|
zlin[4*i + 2] = xl[1 + 18*(31 - i)];
|
|
zlin[4*i + 3] = xr[1 + 18*(31 - i)];
|
|
zlin[4*(i + 16)] = xl[1 + 18*(1 + i)];
|
|
zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)];
|
|
zlin[4*(i - 16) + 2] = xl[18*(1 + i)];
|
|
zlin[4*(i - 16) + 3] = xr[18*(1 + i)];
|
|
MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7)
|
|
dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]);
|
|
dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]);
|
|
dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]);
|
|
dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]);
|
|
dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]);
|
|
dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]);
|
|
dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]);
|
|
dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]);
|
|
}
|
|
#endif
|
|
}
|
|
static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins)
|
|
{
|
|
int i;
|
|
for (i = 0; i < nch; i++)
|
|
{
|
|
ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands);
|
|
}
|
|
MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);
|
|
for (i = 0; i < nbands; i += 2)
|
|
{
|
|
ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64);
|
|
}
|
|
#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL
|
|
if (nch == 1)
|
|
{
|
|
for (i = 0; i < 15*64; i += 2)
|
|
{
|
|
qmf_state[i] = lins[nbands*64 + i];
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);
|
|
}
|
|
}
|
|
static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes)
|
|
{
|
|
int i, nmatch;
|
|
for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++)
|
|
{
|
|
i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i);
|
|
if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes)
|
|
return nmatch > 0;
|
|
if (!ma_dr_mp3_hdr_compare(hdr, hdr + i))
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes)
|
|
{
|
|
int i, k;
|
|
for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++)
|
|
{
|
|
if (ma_dr_mp3_hdr_valid(mp3))
|
|
{
|
|
int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes);
|
|
int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3);
|
|
for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++)
|
|
{
|
|
if (ma_dr_mp3_hdr_compare(mp3, mp3 + k))
|
|
{
|
|
int fb = k - ma_dr_mp3_hdr_padding(mp3);
|
|
int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k);
|
|
if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb))
|
|
continue;
|
|
frame_and_padding = k;
|
|
frame_bytes = fb;
|
|
*free_format_bytes = fb;
|
|
}
|
|
}
|
|
if ((frame_bytes && i + frame_and_padding <= mp3_bytes &&
|
|
ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) ||
|
|
(!i && frame_and_padding == mp3_bytes))
|
|
{
|
|
*ptr_frame_bytes = frame_and_padding;
|
|
return i;
|
|
}
|
|
*free_format_bytes = 0;
|
|
}
|
|
}
|
|
*ptr_frame_bytes = 0;
|
|
return mp3_bytes;
|
|
}
|
|
MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec)
|
|
{
|
|
dec->header[0] = 0;
|
|
}
|
|
MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info)
|
|
{
|
|
int i = 0, igr, frame_size = 0, success = 1;
|
|
const ma_uint8 *hdr;
|
|
ma_dr_mp3_bs bs_frame[1];
|
|
if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3))
|
|
{
|
|
frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3);
|
|
if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size)))
|
|
{
|
|
frame_size = 0;
|
|
}
|
|
}
|
|
if (!frame_size)
|
|
{
|
|
MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec));
|
|
i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
|
|
if (!frame_size || i + frame_size > mp3_bytes)
|
|
{
|
|
info->frame_bytes = i;
|
|
return 0;
|
|
}
|
|
}
|
|
hdr = mp3 + i;
|
|
MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE);
|
|
info->frame_bytes = i + frame_size;
|
|
info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2;
|
|
info->sample_rate = ma_dr_mp3_hdr_sample_rate_hz(hdr);
|
|
info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr);
|
|
info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr);
|
|
ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE);
|
|
if (MA_DR_MP3_HDR_IS_CRC(hdr))
|
|
{
|
|
ma_dr_mp3_bs_get_bits(bs_frame, 16);
|
|
}
|
|
if (info->layer == 3)
|
|
{
|
|
int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, dec->scratch.gr_info, hdr);
|
|
if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit)
|
|
{
|
|
ma_dr_mp3dec_init(dec);
|
|
return 0;
|
|
}
|
|
success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &dec->scratch, main_data_begin);
|
|
if (success && pcm != NULL)
|
|
{
|
|
for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels))
|
|
{
|
|
MA_DR_MP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
|
|
ma_dr_mp3_L3_decode(dec, &dec->scratch, dec->scratch.gr_info + igr*info->channels, info->channels);
|
|
ma_dr_mp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, dec->scratch.syn[0]);
|
|
}
|
|
}
|
|
ma_dr_mp3_L3_save_reservoir(dec, &dec->scratch);
|
|
} else
|
|
{
|
|
#ifdef MA_DR_MP3_ONLY_MP3
|
|
return 0;
|
|
#else
|
|
ma_dr_mp3_L12_scale_info sci[1];
|
|
if (pcm == NULL) {
|
|
return ma_dr_mp3_hdr_frame_samples(hdr);
|
|
}
|
|
ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci);
|
|
MA_DR_MP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
|
|
for (i = 0, igr = 0; igr < 3; igr++)
|
|
{
|
|
if (12 == (i += ma_dr_mp3_L12_dequantize_granule(dec->scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
|
|
{
|
|
i = 0;
|
|
ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, dec->scratch.grbuf[0]);
|
|
ma_dr_mp3d_synth_granule(dec->qmf_state, dec->scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, dec->scratch.syn[0]);
|
|
MA_DR_MP3_ZERO_MEMORY(dec->scratch.grbuf[0], 576*2*sizeof(float));
|
|
pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels);
|
|
}
|
|
if (bs_frame->pos > bs_frame->limit)
|
|
{
|
|
ma_dr_mp3dec_init(dec);
|
|
return 0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
return success*ma_dr_mp3_hdr_frame_samples(dec->header);
|
|
}
|
|
MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples)
|
|
{
|
|
size_t i = 0;
|
|
#if MA_DR_MP3_HAVE_SIMD
|
|
size_t aligned_count = num_samples & ~7;
|
|
for(; i < aligned_count; i+=8)
|
|
{
|
|
ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f);
|
|
ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale);
|
|
ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale);
|
|
#if MA_DR_MP3_HAVE_SSE
|
|
ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f);
|
|
ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f);
|
|
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
|
|
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
|
|
out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0);
|
|
out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1);
|
|
out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2);
|
|
out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3);
|
|
out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4);
|
|
out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5);
|
|
out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6);
|
|
out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7);
|
|
#else
|
|
int16x4_t pcma, pcmb;
|
|
a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f));
|
|
b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f));
|
|
pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0)))));
|
|
pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0)))));
|
|
vst1_lane_s16(out+i , pcma, 0);
|
|
vst1_lane_s16(out+i+1, pcma, 1);
|
|
vst1_lane_s16(out+i+2, pcma, 2);
|
|
vst1_lane_s16(out+i+3, pcma, 3);
|
|
vst1_lane_s16(out+i+4, pcmb, 0);
|
|
vst1_lane_s16(out+i+5, pcmb, 1);
|
|
vst1_lane_s16(out+i+6, pcmb, 2);
|
|
vst1_lane_s16(out+i+7, pcmb, 3);
|
|
#endif
|
|
}
|
|
#endif
|
|
for(; i < num_samples; i++)
|
|
{
|
|
float sample = in[i] * 32768.0f;
|
|
if (sample >= 32766.5f)
|
|
out[i] = (ma_int16) 32767;
|
|
else if (sample <= -32767.5f)
|
|
out[i] = (ma_int16)-32768;
|
|
else
|
|
{
|
|
short s = (ma_int16)(sample + .5f);
|
|
s -= (s < 0);
|
|
out[i] = s;
|
|
}
|
|
}
|
|
}
|
|
#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES
|
|
#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2
|
|
#endif
|
|
#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384
|
|
#ifndef MA_DR_MP3_DATA_CHUNK_SIZE
|
|
#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4)
|
|
#endif
|
|
#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
|
|
#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi)))
|
|
#ifndef MA_DR_MP3_PI_D
|
|
#define MA_DR_MP3_PI_D 3.14159265358979323846264
|
|
#endif
|
|
#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2
|
|
static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a)
|
|
{
|
|
return x*(1-a) + y*a;
|
|
}
|
|
static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a)
|
|
{
|
|
float r0 = (y - x);
|
|
float r1 = r0*a;
|
|
return x + r1;
|
|
}
|
|
static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b)
|
|
{
|
|
for (;;) {
|
|
if (b == 0) {
|
|
break;
|
|
} else {
|
|
ma_uint32 t = a;
|
|
a = b;
|
|
b = t % a;
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_DR_MP3_MALLOC(sz);
|
|
}
|
|
static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
return MA_DR_MP3_REALLOC(p, sz);
|
|
}
|
|
static void ma_dr_mp3__free_default(void* p, void* pUserData)
|
|
{
|
|
(void)pUserData;
|
|
MA_DR_MP3_FREE(p);
|
|
}
|
|
static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks == NULL) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks->onMalloc != NULL) {
|
|
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
|
|
}
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
|
|
}
|
|
return NULL;
|
|
}
|
|
static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks == NULL) {
|
|
return NULL;
|
|
}
|
|
if (pAllocationCallbacks->onRealloc != NULL) {
|
|
return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
|
|
}
|
|
if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
|
|
void* p2;
|
|
p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
|
|
if (p2 == NULL) {
|
|
return NULL;
|
|
}
|
|
if (p != NULL) {
|
|
MA_DR_MP3_COPY_MEMORY(p2, p, szOld);
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
}
|
|
return p2;
|
|
}
|
|
return NULL;
|
|
}
|
|
static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (p == NULL || pAllocationCallbacks == NULL) {
|
|
return;
|
|
}
|
|
if (pAllocationCallbacks->onFree != NULL) {
|
|
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
|
}
|
|
}
|
|
static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
return *pAllocationCallbacks;
|
|
} else {
|
|
ma_allocation_callbacks allocationCallbacks;
|
|
allocationCallbacks.pUserData = NULL;
|
|
allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default;
|
|
allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default;
|
|
allocationCallbacks.onFree = ma_dr_mp3__free_default;
|
|
return allocationCallbacks;
|
|
}
|
|
}
|
|
static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
size_t bytesRead;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
|
|
if (bytesToRead == 0) {
|
|
return 0;
|
|
}
|
|
bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
|
|
pMP3->streamCursor += bytesRead;
|
|
return bytesRead;
|
|
}
|
|
static size_t ma_dr_mp3__on_read_clamped(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
|
|
if (pMP3->streamLength == MA_UINT64_MAX) {
|
|
return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead);
|
|
} else {
|
|
ma_uint64 bytesRemaining;
|
|
bytesRemaining = (pMP3->streamLength - pMP3->streamCursor);
|
|
if (bytesToRead > bytesRemaining) {
|
|
bytesToRead = (size_t)bytesRemaining;
|
|
}
|
|
return ma_dr_mp3__on_read(pMP3, pBufferOut, bytesToRead);
|
|
}
|
|
}
|
|
static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin)
|
|
{
|
|
MA_DR_MP3_ASSERT(offset >= 0);
|
|
MA_DR_MP3_ASSERT(origin == MA_DR_MP3_SEEK_SET || origin == MA_DR_MP3_SEEK_CUR);
|
|
if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (origin == MA_DR_MP3_SEEK_SET) {
|
|
pMP3->streamCursor = (ma_uint64)offset;
|
|
} else{
|
|
pMP3->streamCursor += offset;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin)
|
|
{
|
|
if (offset <= 0x7FFFFFFF) {
|
|
return ma_dr_mp3__on_seek(pMP3, (int)offset, origin);
|
|
}
|
|
if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset -= 0x7FFFFFFF;
|
|
while (offset > 0) {
|
|
if (offset <= 0x7FFFFFFF) {
|
|
if (!ma_dr_mp3__on_seek(pMP3, (int)offset, MA_DR_MP3_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset = 0;
|
|
} else {
|
|
if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, MA_DR_MP3_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
offset -= 0x7FFFFFFF;
|
|
}
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static void ma_dr_mp3__on_meta(ma_dr_mp3* pMP3, ma_dr_mp3_metadata_type type, const void* pRawData, size_t rawDataSize)
|
|
{
|
|
if (pMP3->onMeta) {
|
|
ma_dr_mp3_metadata metadata;
|
|
MA_DR_MP3_ZERO_OBJECT(&metadata);
|
|
metadata.type = type;
|
|
metadata.pRawData = pRawData;
|
|
metadata.rawDataSize = rawDataSize;
|
|
pMP3->onMeta(pMP3->pUserDataMeta, &metadata);
|
|
}
|
|
}
|
|
static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData)
|
|
{
|
|
ma_uint32 pcmFramesRead = 0;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
|
|
if (pMP3->atEnd) {
|
|
return 0;
|
|
}
|
|
for (;;) {
|
|
ma_dr_mp3dec_frame_info info;
|
|
if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) {
|
|
size_t bytesRead;
|
|
if (pMP3->pData != NULL) {
|
|
MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
|
|
}
|
|
pMP3->dataConsumed = 0;
|
|
if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) {
|
|
ma_uint8* pNewData;
|
|
size_t newDataCap;
|
|
newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE;
|
|
pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
|
|
if (pNewData == NULL) {
|
|
return 0;
|
|
}
|
|
pMP3->pData = pNewData;
|
|
pMP3->dataCapacity = newDataCap;
|
|
}
|
|
bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
|
|
if (bytesRead == 0) {
|
|
if (pMP3->dataSize == 0) {
|
|
pMP3->atEnd = MA_TRUE;
|
|
return 0;
|
|
}
|
|
}
|
|
pMP3->dataSize += bytesRead;
|
|
}
|
|
if (pMP3->dataSize > INT_MAX) {
|
|
pMP3->atEnd = MA_TRUE;
|
|
return 0;
|
|
}
|
|
MA_DR_MP3_ASSERT(pMP3->pData != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0);
|
|
if (pMP3->pData == NULL) {
|
|
return 0;
|
|
}
|
|
pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info);
|
|
pMP3->dataConsumed += (size_t)info.frame_bytes;
|
|
pMP3->dataSize -= (size_t)info.frame_bytes;
|
|
if (pcmFramesRead > 0) {
|
|
pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);
|
|
pMP3->pcmFramesConsumedInMP3Frame = 0;
|
|
pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
|
|
pMP3->mp3FrameChannels = info.channels;
|
|
pMP3->mp3FrameSampleRate = info.sample_rate;
|
|
if (pMP3FrameInfo != NULL) {
|
|
*pMP3FrameInfo = info;
|
|
}
|
|
if (ppMP3FrameData != NULL) {
|
|
*ppMP3FrameData = pMP3->pData + pMP3->dataConsumed - (size_t)info.frame_bytes;
|
|
}
|
|
break;
|
|
} else if (info.frame_bytes == 0) {
|
|
size_t bytesRead;
|
|
MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
|
|
pMP3->dataConsumed = 0;
|
|
if (pMP3->dataCapacity == pMP3->dataSize) {
|
|
ma_uint8* pNewData;
|
|
size_t newDataCap;
|
|
newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE;
|
|
pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
|
|
if (pNewData == NULL) {
|
|
return 0;
|
|
}
|
|
pMP3->pData = pNewData;
|
|
pMP3->dataCapacity = newDataCap;
|
|
}
|
|
bytesRead = ma_dr_mp3__on_read_clamped(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
|
|
if (bytesRead == 0) {
|
|
pMP3->atEnd = MA_TRUE;
|
|
return 0;
|
|
}
|
|
pMP3->dataSize += bytesRead;
|
|
}
|
|
};
|
|
return pcmFramesRead;
|
|
}
|
|
static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData)
|
|
{
|
|
ma_uint32 pcmFramesRead = 0;
|
|
ma_dr_mp3dec_frame_info info;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL);
|
|
if (pMP3->atEnd) {
|
|
return 0;
|
|
}
|
|
for (;;) {
|
|
pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
|
|
if (pcmFramesRead > 0) {
|
|
pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header);
|
|
pMP3->pcmFramesConsumedInMP3Frame = 0;
|
|
pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
|
|
pMP3->mp3FrameChannels = info.channels;
|
|
pMP3->mp3FrameSampleRate = info.sample_rate;
|
|
if (pMP3FrameInfo != NULL) {
|
|
*pMP3FrameInfo = info;
|
|
}
|
|
if (ppMP3FrameData != NULL) {
|
|
*ppMP3FrameData = pMP3->memory.pData + pMP3->memory.currentReadPos;
|
|
}
|
|
break;
|
|
} else if (info.frame_bytes > 0) {
|
|
pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
|
|
pMP3->streamCursor += (size_t)info.frame_bytes;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
|
|
pMP3->streamCursor += (size_t)info.frame_bytes;
|
|
return pcmFramesRead;
|
|
}
|
|
static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames, ma_dr_mp3dec_frame_info* pMP3FrameInfo, const ma_uint8** ppMP3FrameData)
|
|
{
|
|
if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
|
|
return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData);
|
|
} else {
|
|
return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames, pMP3FrameInfo, ppMP3FrameData);
|
|
}
|
|
}
|
|
static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3)
|
|
{
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, NULL, NULL);
|
|
}
|
|
#if 0
|
|
static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3)
|
|
{
|
|
ma_uint32 pcmFrameCount;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
|
|
if (pcmFrameCount == 0) {
|
|
return 0;
|
|
}
|
|
pMP3->currentPCMFrame += pcmFrameCount;
|
|
pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
|
|
pMP3->pcmFramesRemainingInMP3Frame = 0;
|
|
return pcmFrameCount;
|
|
}
|
|
#endif
|
|
static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3dec_frame_info firstFrameInfo;
|
|
const ma_uint8* pFirstFrameData;
|
|
ma_uint32 firstFramePCMFrameCount;
|
|
ma_uint32 detectedMP3FrameCount = 0xFFFFFFFF;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(onRead != NULL);
|
|
ma_dr_mp3dec_init(&pMP3->decoder);
|
|
pMP3->onRead = onRead;
|
|
pMP3->onSeek = onSeek;
|
|
pMP3->onMeta = onMeta;
|
|
pMP3->pUserData = pUserData;
|
|
pMP3->pUserDataMeta = pUserDataMeta;
|
|
pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
|
|
if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
|
|
return MA_FALSE;
|
|
}
|
|
pMP3->streamCursor = 0;
|
|
pMP3->streamLength = MA_UINT64_MAX;
|
|
pMP3->streamStartOffset = 0;
|
|
pMP3->delayInPCMFrames = 0;
|
|
pMP3->paddingInPCMFrames = 0;
|
|
pMP3->totalPCMFrameCount = MA_UINT64_MAX;
|
|
#if 1
|
|
if (onSeek != NULL && onTell != NULL) {
|
|
if (onSeek(pUserData, 0, MA_DR_MP3_SEEK_END)) {
|
|
ma_int64 streamLen;
|
|
int streamEndOffset = 0;
|
|
if (onTell(pUserData, &streamLen)) {
|
|
if (streamLen > 128) {
|
|
char id3[3];
|
|
if (onSeek(pUserData, streamEndOffset - 128, MA_DR_MP3_SEEK_END)) {
|
|
if (onRead(pUserData, id3, 3) == 3 && id3[0] == 'T' && id3[1] == 'A' && id3[2] == 'G') {
|
|
streamEndOffset -= 128;
|
|
streamLen -= 128;
|
|
if (onMeta != NULL) {
|
|
ma_uint8 tag[128];
|
|
tag[0] = 'T'; tag[1] = 'A'; tag[2] = 'G';
|
|
if (onRead(pUserData, tag + 3, 125) == 125) {
|
|
ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V1, tag, 128);
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else {
|
|
}
|
|
} else {
|
|
}
|
|
if (streamLen > 32) {
|
|
char ape[32];
|
|
if (onSeek(pUserData, streamEndOffset - 32, MA_DR_MP3_SEEK_END)) {
|
|
if (onRead(pUserData, ape, 32) == 32 && ape[0] == 'A' && ape[1] == 'P' && ape[2] == 'E' && ape[3] == 'T' && ape[4] == 'A' && ape[5] == 'G' && ape[6] == 'E' && ape[7] == 'X') {
|
|
ma_uint32 tagSize =
|
|
((ma_uint32)ape[24] << 0) |
|
|
((ma_uint32)ape[25] << 8) |
|
|
((ma_uint32)ape[26] << 16) |
|
|
((ma_uint32)ape[27] << 24);
|
|
if (32 + tagSize < streamLen) {
|
|
streamEndOffset -= 32 + tagSize;
|
|
streamLen -= 32 + tagSize;
|
|
if (onMeta != NULL) {
|
|
if (onSeek(pUserData, streamEndOffset, MA_DR_MP3_SEEK_END)) {
|
|
size_t apeTagSize = (size_t)tagSize + 32;
|
|
ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(apeTagSize, pAllocationCallbacks);
|
|
if (pTagData != NULL) {
|
|
if (onRead(pUserData, pTagData, apeTagSize) == apeTagSize) {
|
|
ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_APE, pTagData, apeTagSize);
|
|
}
|
|
ma_dr_mp3_free(pTagData, pAllocationCallbacks);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
pMP3->streamLength = (ma_uint64)streamLen;
|
|
if (pMP3->memory.pData != NULL) {
|
|
pMP3->memory.dataSize = (size_t)pMP3->streamLength;
|
|
}
|
|
} else {
|
|
if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
} else {
|
|
}
|
|
#endif
|
|
#if 1
|
|
{
|
|
char header[10];
|
|
if (onRead(pUserData, header, 10) == 10) {
|
|
if (header[0] == 'I' && header[1] == 'D' && header[2] == '3') {
|
|
ma_uint32 tagSize =
|
|
(((ma_uint32)header[6] & 0x7F) << 21) |
|
|
(((ma_uint32)header[7] & 0x7F) << 14) |
|
|
(((ma_uint32)header[8] & 0x7F) << 7) |
|
|
(((ma_uint32)header[9] & 0x7F) << 0);
|
|
if (header[5] & 0x10) {
|
|
tagSize += 10;
|
|
}
|
|
if (onMeta != NULL) {
|
|
size_t tagSizeWithHeader = 10 + tagSize;
|
|
ma_uint8* pTagData = (ma_uint8*)ma_dr_mp3_malloc(tagSizeWithHeader, pAllocationCallbacks);
|
|
if (pTagData != NULL) {
|
|
MA_DR_MP3_COPY_MEMORY(pTagData, header, 10);
|
|
if (onRead(pUserData, pTagData + 10, tagSize) == tagSize) {
|
|
ma_dr_mp3__on_meta(pMP3, MA_DR_MP3_METADATA_TYPE_ID3V2, pTagData, tagSizeWithHeader);
|
|
}
|
|
ma_dr_mp3_free(pTagData, pAllocationCallbacks);
|
|
}
|
|
} else {
|
|
if (onSeek != NULL) {
|
|
if (!onSeek(pUserData, tagSize, MA_DR_MP3_SEEK_CUR)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
char discard[1024];
|
|
while (tagSize > 0) {
|
|
size_t bytesToRead = tagSize;
|
|
if (bytesToRead > sizeof(discard)) {
|
|
bytesToRead = sizeof(discard);
|
|
}
|
|
if (onRead(pUserData, discard, bytesToRead) != bytesToRead) {
|
|
return MA_FALSE;
|
|
}
|
|
tagSize -= (ma_uint32)bytesToRead;
|
|
}
|
|
}
|
|
}
|
|
pMP3->streamStartOffset += 10 + tagSize;
|
|
pMP3->streamCursor = pMP3->streamStartOffset;
|
|
} else {
|
|
if (onSeek != NULL) {
|
|
if (!onSeek(pUserData, 0, MA_DR_MP3_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
} else {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
firstFramePCMFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames, &firstFrameInfo, &pFirstFrameData);
|
|
if (firstFramePCMFrameCount > 0) {
|
|
MA_DR_MP3_ASSERT(pFirstFrameData != NULL);
|
|
#if 1
|
|
MA_DR_MP3_ASSERT(firstFrameInfo.frame_bytes > 0);
|
|
{
|
|
ma_dr_mp3_bs bs;
|
|
ma_dr_mp3_L3_gr_info grInfo[4];
|
|
ma_dr_mp3_bs_init(&bs, pFirstFrameData + MA_DR_MP3_HDR_SIZE, firstFrameInfo.frame_bytes - MA_DR_MP3_HDR_SIZE);
|
|
if (MA_DR_MP3_HDR_IS_CRC(pFirstFrameData)) {
|
|
ma_dr_mp3_bs_get_bits(&bs, 16);
|
|
}
|
|
if (ma_dr_mp3_L3_read_side_info(&bs, grInfo, pFirstFrameData) >= 0) {
|
|
ma_bool32 isXing = MA_FALSE;
|
|
ma_bool32 isInfo = MA_FALSE;
|
|
const ma_uint8* pTagData;
|
|
const ma_uint8* pTagDataBeg;
|
|
pTagDataBeg = pFirstFrameData + MA_DR_MP3_HDR_SIZE + (bs.pos/8);
|
|
pTagData = pTagDataBeg;
|
|
isXing = (pTagData[0] == 'X' && pTagData[1] == 'i' && pTagData[2] == 'n' && pTagData[3] == 'g');
|
|
isInfo = (pTagData[0] == 'I' && pTagData[1] == 'n' && pTagData[2] == 'f' && pTagData[3] == 'o');
|
|
if (isXing || isInfo) {
|
|
ma_uint32 bytes = 0;
|
|
ma_uint32 flags = pTagData[7];
|
|
pTagData += 8;
|
|
if (flags & 0x01) {
|
|
detectedMP3FrameCount = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3];
|
|
pTagData += 4;
|
|
}
|
|
if (flags & 0x02) {
|
|
bytes = (ma_uint32)pTagData[0] << 24 | (ma_uint32)pTagData[1] << 16 | (ma_uint32)pTagData[2] << 8 | (ma_uint32)pTagData[3];
|
|
(void)bytes;
|
|
pTagData += 4;
|
|
}
|
|
if (flags & 0x04) {
|
|
pTagData += 100;
|
|
}
|
|
if (flags & 0x08) {
|
|
pTagData += 4;
|
|
}
|
|
if (pTagData[0]) {
|
|
pTagData += 21;
|
|
if (pTagData - pFirstFrameData + 14 < firstFrameInfo.frame_bytes) {
|
|
int delayInPCMFrames;
|
|
int paddingInPCMFrames;
|
|
delayInPCMFrames = (( (ma_uint32)pTagData[0] << 4) | ((ma_uint32)pTagData[1] >> 4)) + (528 + 1);
|
|
paddingInPCMFrames = ((((ma_uint32)pTagData[1] & 0xF) << 8) | ((ma_uint32)pTagData[2] )) - (528 + 1);
|
|
if (paddingInPCMFrames < 0) {
|
|
paddingInPCMFrames = 0;
|
|
}
|
|
pMP3->delayInPCMFrames = (ma_uint32)delayInPCMFrames;
|
|
pMP3->paddingInPCMFrames = (ma_uint32)paddingInPCMFrames;
|
|
}
|
|
}
|
|
if (isXing) {
|
|
pMP3->isVBR = MA_TRUE;
|
|
} else if (isInfo) {
|
|
pMP3->isCBR = MA_TRUE;
|
|
}
|
|
if (onMeta != NULL) {
|
|
ma_dr_mp3_metadata_type metadataType = isXing ? MA_DR_MP3_METADATA_TYPE_XING : MA_DR_MP3_METADATA_TYPE_VBRI;
|
|
size_t tagDataSize;
|
|
tagDataSize = (size_t)firstFrameInfo.frame_bytes;
|
|
tagDataSize -= (size_t)(pTagDataBeg - pFirstFrameData);
|
|
ma_dr_mp3__on_meta(pMP3, metadataType, pTagDataBeg, tagDataSize);
|
|
}
|
|
pMP3->pcmFramesRemainingInMP3Frame = 0;
|
|
pMP3->streamStartOffset += (ma_uint32)(firstFrameInfo.frame_bytes);
|
|
pMP3->streamCursor = pMP3->streamStartOffset;
|
|
ma_dr_mp3dec_init(&pMP3->decoder);
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
#endif
|
|
} else {
|
|
ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
|
|
return MA_FALSE;
|
|
}
|
|
if (detectedMP3FrameCount != 0xFFFFFFFF) {
|
|
pMP3->totalPCMFrameCount = detectedMP3FrameCount * firstFramePCMFrameCount;
|
|
}
|
|
pMP3->channels = pMP3->mp3FrameChannels;
|
|
pMP3->sampleRate = pMP3->mp3FrameSampleRate;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, ma_dr_mp3_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pMP3 == NULL || onRead == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_MP3_ZERO_OBJECT(pMP3);
|
|
return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, onTell, onMeta, pUserData, pUserData, pAllocationCallbacks);
|
|
}
|
|
static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
|
|
size_t bytesRemaining;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
|
|
bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
|
|
if (bytesToRead > bytesRemaining) {
|
|
bytesToRead = bytesRemaining;
|
|
}
|
|
if (bytesToRead > 0) {
|
|
MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
|
|
pMP3->memory.currentReadPos += bytesToRead;
|
|
}
|
|
return bytesToRead;
|
|
}
|
|
static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin)
|
|
{
|
|
ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
|
|
ma_int64 newCursor;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
if (origin == MA_DR_MP3_SEEK_SET) {
|
|
newCursor = 0;
|
|
} else if (origin == MA_DR_MP3_SEEK_CUR) {
|
|
newCursor = (ma_int64)pMP3->memory.currentReadPos;
|
|
} else if (origin == MA_DR_MP3_SEEK_END) {
|
|
newCursor = (ma_int64)pMP3->memory.dataSize;
|
|
} else {
|
|
MA_DR_MP3_ASSERT(!"Invalid seek origin");
|
|
return MA_FALSE;
|
|
}
|
|
newCursor += byteOffset;
|
|
if (newCursor < 0) {
|
|
return MA_FALSE;
|
|
}
|
|
if ((size_t)newCursor > pMP3->memory.dataSize) {
|
|
return MA_FALSE;
|
|
}
|
|
pMP3->memory.currentReadPos = (size_t)newCursor;
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_mp3__on_tell_memory(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pCursor != NULL);
|
|
*pCursor = (ma_int64)pMP3->memory.currentReadPos;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init_memory_with_metadata(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bool32 result;
|
|
if (pMP3 == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_MP3_ZERO_OBJECT(pMP3);
|
|
if (pData == NULL || dataSize == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
pMP3->memory.pData = (const ma_uint8*)pData;
|
|
pMP3->memory.dataSize = dataSize;
|
|
pMP3->memory.currentReadPos = 0;
|
|
result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, ma_dr_mp3__on_tell_memory, onMeta, pMP3, pUserDataMeta, pAllocationCallbacks);
|
|
if (result == MA_FALSE) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pMP3->streamLength <= (ma_uint64)MA_SIZE_MAX) {
|
|
pMP3->memory.dataSize = (size_t)pMP3->streamLength;
|
|
}
|
|
if (pMP3->streamStartOffset > (ma_uint64)MA_SIZE_MAX) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_mp3_init_memory_with_metadata(pMP3, pData, dataSize, NULL, NULL, pAllocationCallbacks);
|
|
}
|
|
#ifndef MA_DR_MP3_NO_STDIO
|
|
#include <stdio.h>
|
|
#include <wchar.h>
|
|
static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
|
{
|
|
return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
|
|
}
|
|
static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin)
|
|
{
|
|
int whence = SEEK_SET;
|
|
if (origin == MA_DR_MP3_SEEK_CUR) {
|
|
whence = SEEK_CUR;
|
|
} else if (origin == MA_DR_MP3_SEEK_END) {
|
|
whence = SEEK_END;
|
|
}
|
|
return fseek((FILE*)pUserData, offset, whence) == 0;
|
|
}
|
|
static ma_bool32 ma_dr_mp3__on_tell_stdio(void* pUserData, ma_int64* pCursor)
|
|
{
|
|
FILE* pFileStdio = (FILE*)pUserData;
|
|
ma_int64 result;
|
|
MA_DR_MP3_ASSERT(pFileStdio != NULL);
|
|
MA_DR_MP3_ASSERT(pCursor != NULL);
|
|
#if defined(_WIN32) && !defined(NXDK)
|
|
#if defined(_MSC_VER) && _MSC_VER > 1200
|
|
result = _ftelli64(pFileStdio);
|
|
#else
|
|
result = ftell(pFileStdio);
|
|
#endif
|
|
#else
|
|
result = ftell(pFileStdio);
|
|
#endif
|
|
*pCursor = result;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata(ma_dr_mp3* pMP3, const char* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bool32 result;
|
|
FILE* pFile;
|
|
if (pMP3 == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_MP3_ZERO_OBJECT(pMP3);
|
|
if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks);
|
|
if (result != MA_TRUE) {
|
|
fclose(pFile);
|
|
return result;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init_file_with_metadata_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, ma_dr_mp3_meta_proc onMeta, void* pUserDataMeta, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_bool32 result;
|
|
FILE* pFile;
|
|
if (pMP3 == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
MA_DR_MP3_ZERO_OBJECT(pMP3);
|
|
if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) {
|
|
return MA_FALSE;
|
|
}
|
|
result = ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, ma_dr_mp3__on_tell_stdio, onMeta, (void*)pFile, pUserDataMeta, pAllocationCallbacks);
|
|
if (result != MA_TRUE) {
|
|
fclose(pFile);
|
|
return result;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_mp3_init_file_with_metadata(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks);
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
return ma_dr_mp3_init_file_with_metadata_w(pMP3, pFilePath, NULL, NULL, pAllocationCallbacks);
|
|
}
|
|
#endif
|
|
MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3)
|
|
{
|
|
if (pMP3 == NULL) {
|
|
return;
|
|
}
|
|
#ifndef MA_DR_MP3_NO_STDIO
|
|
if (pMP3->onRead == ma_dr_mp3__on_read_stdio) {
|
|
FILE* pFile = (FILE*)pMP3->pUserData;
|
|
if (pFile != NULL) {
|
|
fclose(pFile);
|
|
pMP3->pUserData = NULL;
|
|
}
|
|
}
|
|
#endif
|
|
ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
|
|
}
|
|
#if defined(MA_DR_MP3_FLOAT_OUTPUT)
|
|
static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 i;
|
|
ma_uint64 i4;
|
|
ma_uint64 sampleCount4;
|
|
i = 0;
|
|
sampleCount4 = sampleCount >> 2;
|
|
for (i4 = 0; i4 < sampleCount4; i4 += 1) {
|
|
float x0 = src[i+0];
|
|
float x1 = src[i+1];
|
|
float x2 = src[i+2];
|
|
float x3 = src[i+3];
|
|
x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
|
|
x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
|
|
x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
|
|
x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
|
|
x0 = x0 * 32767.0f;
|
|
x1 = x1 * 32767.0f;
|
|
x2 = x2 * 32767.0f;
|
|
x3 = x3 * 32767.0f;
|
|
dst[i+0] = (ma_int16)x0;
|
|
dst[i+1] = (ma_int16)x1;
|
|
dst[i+2] = (ma_int16)x2;
|
|
dst[i+3] = (ma_int16)x3;
|
|
i += 4;
|
|
}
|
|
for (; i < sampleCount; i += 1) {
|
|
float x = src[i];
|
|
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x));
|
|
x = x * 32767.0f;
|
|
dst[i] = (ma_int16)x;
|
|
}
|
|
}
|
|
#endif
|
|
#if !defined(MA_DR_MP3_FLOAT_OUTPUT)
|
|
static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount)
|
|
{
|
|
ma_uint64 i;
|
|
for (i = 0; i < sampleCount; i += 1) {
|
|
float x = (float)src[i];
|
|
x = x * 0.000030517578125f;
|
|
dst[i] = x;
|
|
}
|
|
}
|
|
#endif
|
|
static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->onRead != NULL);
|
|
while (framesToRead > 0) {
|
|
ma_uint32 framesToConsume;
|
|
if (pMP3->currentPCMFrame < pMP3->delayInPCMFrames) {
|
|
ma_uint32 framesToSkip = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, pMP3->delayInPCMFrames - pMP3->currentPCMFrame);
|
|
pMP3->currentPCMFrame += framesToSkip;
|
|
pMP3->pcmFramesConsumedInMP3Frame += framesToSkip;
|
|
pMP3->pcmFramesRemainingInMP3Frame -= framesToSkip;
|
|
}
|
|
framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
|
|
if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames) {
|
|
if (pMP3->currentPCMFrame < (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) {
|
|
ma_uint64 framesRemainigToPadding = (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames) - pMP3->currentPCMFrame;
|
|
if (framesToConsume > framesRemainigToPadding) {
|
|
framesToConsume = (ma_uint32)framesRemainigToPadding;
|
|
}
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
if (pBufferOut != NULL) {
|
|
#if defined(MA_DR_MP3_FLOAT_OUTPUT)
|
|
{
|
|
float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
|
|
float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
|
|
MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
|
|
}
|
|
#else
|
|
{
|
|
ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels);
|
|
ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
|
|
MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels);
|
|
}
|
|
#endif
|
|
}
|
|
pMP3->currentPCMFrame += framesToConsume;
|
|
pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
|
|
pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
|
|
totalFramesRead += framesToConsume;
|
|
framesToRead -= framesToConsume;
|
|
if (framesToRead == 0) {
|
|
break;
|
|
}
|
|
if (pMP3->totalPCMFrameCount != MA_UINT64_MAX && pMP3->totalPCMFrameCount > pMP3->paddingInPCMFrames && pMP3->currentPCMFrame >= (pMP3->totalPCMFrameCount - pMP3->paddingInPCMFrames)) {
|
|
break;
|
|
}
|
|
MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
|
|
if (ma_dr_mp3_decode_next_frame(pMP3) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
return totalFramesRead;
|
|
}
|
|
MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut)
|
|
{
|
|
if (pMP3 == NULL || pMP3->onRead == NULL) {
|
|
return 0;
|
|
}
|
|
#if defined(MA_DR_MP3_FLOAT_OUTPUT)
|
|
return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
|
|
#else
|
|
{
|
|
ma_int16 pTempS16[8192];
|
|
ma_uint64 totalPCMFramesRead = 0;
|
|
while (totalPCMFramesRead < framesToRead) {
|
|
ma_uint64 framesJustRead;
|
|
ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
|
|
ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels;
|
|
if (framesToReadNow > framesRemaining) {
|
|
framesToReadNow = framesRemaining;
|
|
}
|
|
framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
|
|
if (framesJustRead == 0) {
|
|
break;
|
|
}
|
|
ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
|
|
totalPCMFramesRead += framesJustRead;
|
|
}
|
|
return totalPCMFramesRead;
|
|
}
|
|
#endif
|
|
}
|
|
MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut)
|
|
{
|
|
if (pMP3 == NULL || pMP3->onRead == NULL) {
|
|
return 0;
|
|
}
|
|
#if !defined(MA_DR_MP3_FLOAT_OUTPUT)
|
|
return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
|
|
#else
|
|
{
|
|
float pTempF32[4096];
|
|
ma_uint64 totalPCMFramesRead = 0;
|
|
while (totalPCMFramesRead < framesToRead) {
|
|
ma_uint64 framesJustRead;
|
|
ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
|
|
ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels;
|
|
if (framesToReadNow > framesRemaining) {
|
|
framesToReadNow = framesRemaining;
|
|
}
|
|
framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
|
|
if (framesJustRead == 0) {
|
|
break;
|
|
}
|
|
ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
|
|
totalPCMFramesRead += framesJustRead;
|
|
}
|
|
return totalPCMFramesRead;
|
|
}
|
|
#endif
|
|
}
|
|
static void ma_dr_mp3_reset(ma_dr_mp3* pMP3)
|
|
{
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
pMP3->pcmFramesConsumedInMP3Frame = 0;
|
|
pMP3->pcmFramesRemainingInMP3Frame = 0;
|
|
pMP3->currentPCMFrame = 0;
|
|
pMP3->dataSize = 0;
|
|
pMP3->atEnd = MA_FALSE;
|
|
ma_dr_mp3dec_init(&pMP3->decoder);
|
|
}
|
|
static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3)
|
|
{
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->onSeek != NULL);
|
|
if (!ma_dr_mp3__on_seek_64(pMP3, pMP3->streamStartOffset, MA_DR_MP3_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
ma_dr_mp3_reset(pMP3);
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset)
|
|
{
|
|
ma_uint64 framesRead;
|
|
#if defined(MA_DR_MP3_FLOAT_OUTPUT)
|
|
framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
|
|
#else
|
|
framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
|
|
#endif
|
|
if (framesRead != frameOffset) {
|
|
return MA_FALSE;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
|
|
{
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
if (frameIndex == pMP3->currentPCMFrame) {
|
|
return MA_TRUE;
|
|
}
|
|
if (frameIndex < pMP3->currentPCMFrame) {
|
|
if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
|
|
return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
|
|
}
|
|
static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex)
|
|
{
|
|
ma_uint32 iSeekPoint;
|
|
MA_DR_MP3_ASSERT(pSeekPointIndex != NULL);
|
|
*pSeekPointIndex = 0;
|
|
if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
|
|
return MA_FALSE;
|
|
}
|
|
for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
|
|
if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
|
|
break;
|
|
}
|
|
*pSeekPointIndex = iSeekPoint;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
|
|
{
|
|
ma_dr_mp3_seek_point seekPoint;
|
|
ma_uint32 priorSeekPointIndex;
|
|
ma_uint16 iMP3Frame;
|
|
ma_uint64 leftoverFrames;
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL);
|
|
MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0);
|
|
if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
|
|
seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
|
|
} else {
|
|
seekPoint.seekPosInBytes = 0;
|
|
seekPoint.pcmFrameIndex = 0;
|
|
seekPoint.mp3FramesToDiscard = 0;
|
|
seekPoint.pcmFramesToDiscard = 0;
|
|
}
|
|
if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, MA_DR_MP3_SEEK_SET)) {
|
|
return MA_FALSE;
|
|
}
|
|
ma_dr_mp3_reset(pMP3);
|
|
for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
|
|
ma_uint32 pcmFramesRead;
|
|
ma_dr_mp3d_sample_t* pPCMFrames;
|
|
pPCMFrames = NULL;
|
|
if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
|
|
pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames;
|
|
}
|
|
pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames, NULL, NULL);
|
|
if (pcmFramesRead == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
|
|
leftoverFrames = frameIndex - pMP3->currentPCMFrame;
|
|
return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex)
|
|
{
|
|
if (pMP3 == NULL || pMP3->onSeek == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (frameIndex == 0) {
|
|
return ma_dr_mp3_seek_to_start_of_stream(pMP3);
|
|
}
|
|
if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
|
|
return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
|
|
} else {
|
|
return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
|
|
}
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount)
|
|
{
|
|
ma_uint64 currentPCMFrame;
|
|
ma_uint64 totalPCMFrameCount;
|
|
ma_uint64 totalMP3FrameCount;
|
|
if (pMP3 == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pMP3->onSeek == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
currentPCMFrame = pMP3->currentPCMFrame;
|
|
if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
|
|
return MA_FALSE;
|
|
}
|
|
totalPCMFrameCount = 0;
|
|
totalMP3FrameCount = 0;
|
|
for (;;) {
|
|
ma_uint32 pcmFramesInCurrentMP3Frame;
|
|
pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
|
|
if (pcmFramesInCurrentMP3Frame == 0) {
|
|
break;
|
|
}
|
|
totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
|
|
totalMP3FrameCount += 1;
|
|
}
|
|
if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (pMP3FrameCount != NULL) {
|
|
*pMP3FrameCount = totalMP3FrameCount;
|
|
}
|
|
if (pPCMFrameCount != NULL) {
|
|
*pPCMFrameCount = totalPCMFrameCount;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3)
|
|
{
|
|
ma_uint64 totalPCMFrameCount;
|
|
if (pMP3 == NULL) {
|
|
return 0;
|
|
}
|
|
if (pMP3->totalPCMFrameCount != MA_UINT64_MAX) {
|
|
totalPCMFrameCount = pMP3->totalPCMFrameCount;
|
|
if (totalPCMFrameCount >= pMP3->delayInPCMFrames) {
|
|
totalPCMFrameCount -= pMP3->delayInPCMFrames;
|
|
} else {
|
|
}
|
|
if (totalPCMFrameCount >= pMP3->paddingInPCMFrames) {
|
|
totalPCMFrameCount -= pMP3->paddingInPCMFrames;
|
|
} else {
|
|
}
|
|
return totalPCMFrameCount;
|
|
} else {
|
|
if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
|
|
return 0;
|
|
}
|
|
return totalPCMFrameCount;
|
|
}
|
|
}
|
|
MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3)
|
|
{
|
|
ma_uint64 totalMP3FrameCount;
|
|
if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
|
|
return 0;
|
|
}
|
|
return totalMP3FrameCount;
|
|
}
|
|
static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
|
|
{
|
|
float srcRatio;
|
|
float pcmFrameCountOutF;
|
|
ma_uint32 pcmFrameCountOut;
|
|
srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
|
|
MA_DR_MP3_ASSERT(srcRatio > 0);
|
|
pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
|
|
pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF;
|
|
*pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
|
|
*pRunningPCMFrameCount += pcmFrameCountOut;
|
|
}
|
|
typedef struct
|
|
{
|
|
ma_uint64 bytePos;
|
|
ma_uint64 pcmFrameIndex;
|
|
} ma_dr_mp3__seeking_mp3_frame_info;
|
|
MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints)
|
|
{
|
|
ma_uint32 seekPointCount;
|
|
ma_uint64 currentPCMFrame;
|
|
ma_uint64 totalMP3FrameCount;
|
|
ma_uint64 totalPCMFrameCount;
|
|
if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
seekPointCount = *pSeekPointCount;
|
|
if (seekPointCount == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
currentPCMFrame = pMP3->currentPCMFrame;
|
|
if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) {
|
|
seekPointCount = 1;
|
|
pSeekPoints[0].seekPosInBytes = 0;
|
|
pSeekPoints[0].pcmFrameIndex = 0;
|
|
pSeekPoints[0].mp3FramesToDiscard = 0;
|
|
pSeekPoints[0].pcmFramesToDiscard = 0;
|
|
} else {
|
|
ma_uint64 pcmFramesBetweenSeekPoints;
|
|
ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1];
|
|
ma_uint64 runningPCMFrameCount = 0;
|
|
float runningPCMFrameCountFractionalPart = 0;
|
|
ma_uint64 nextTargetPCMFrame;
|
|
ma_uint32 iMP3Frame;
|
|
ma_uint32 iSeekPoint;
|
|
if (seekPointCount > totalMP3FrameCount-1) {
|
|
seekPointCount = (ma_uint32)totalMP3FrameCount-1;
|
|
}
|
|
pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
|
|
if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
|
|
return MA_FALSE;
|
|
}
|
|
for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
|
|
ma_uint32 pcmFramesInCurrentMP3FrameIn;
|
|
MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
|
|
mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
|
|
mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
|
|
pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
|
|
if (pcmFramesInCurrentMP3FrameIn == 0) {
|
|
return MA_FALSE;
|
|
}
|
|
ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
|
|
}
|
|
nextTargetPCMFrame = 0;
|
|
for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
|
|
nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
|
|
for (;;) {
|
|
if (nextTargetPCMFrame < runningPCMFrameCount) {
|
|
pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
|
|
pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
|
|
pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;
|
|
pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
|
|
break;
|
|
} else {
|
|
size_t i;
|
|
ma_uint32 pcmFramesInCurrentMP3FrameIn;
|
|
for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) {
|
|
mp3FrameInfo[i] = mp3FrameInfo[i+1];
|
|
}
|
|
mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
|
|
mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
|
|
pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL, NULL, NULL);
|
|
if (pcmFramesInCurrentMP3FrameIn == 0) {
|
|
pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
|
|
pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
|
|
pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES;
|
|
pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
|
|
break;
|
|
}
|
|
ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
|
|
}
|
|
}
|
|
}
|
|
if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) {
|
|
return MA_FALSE;
|
|
}
|
|
if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
|
|
return MA_FALSE;
|
|
}
|
|
}
|
|
*pSeekPointCount = seekPointCount;
|
|
return MA_TRUE;
|
|
}
|
|
MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints)
|
|
{
|
|
if (pMP3 == NULL) {
|
|
return MA_FALSE;
|
|
}
|
|
if (seekPointCount == 0 || pSeekPoints == NULL) {
|
|
pMP3->seekPointCount = 0;
|
|
pMP3->pSeekPoints = NULL;
|
|
} else {
|
|
pMP3->seekPointCount = seekPointCount;
|
|
pMP3->pSeekPoints = pSeekPoints;
|
|
}
|
|
return MA_TRUE;
|
|
}
|
|
static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_uint64 framesCapacity = 0;
|
|
float* pFrames = NULL;
|
|
float temp[4096];
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
for (;;) {
|
|
ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;
|
|
ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
|
|
if (framesJustRead == 0) {
|
|
break;
|
|
}
|
|
if (framesCapacity < totalFramesRead + framesJustRead) {
|
|
ma_uint64 oldFramesBufferSize;
|
|
ma_uint64 newFramesBufferSize;
|
|
ma_uint64 newFramesCap;
|
|
float* pNewFrames;
|
|
newFramesCap = framesCapacity * 2;
|
|
if (newFramesCap < totalFramesRead + framesJustRead) {
|
|
newFramesCap = totalFramesRead + framesJustRead;
|
|
}
|
|
oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
|
|
newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
|
|
if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {
|
|
break;
|
|
}
|
|
pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
|
|
if (pNewFrames == NULL) {
|
|
ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
|
|
pFrames = NULL;
|
|
totalFramesRead = 0;
|
|
break;
|
|
}
|
|
pFrames = pNewFrames;
|
|
framesCapacity = newFramesCap;
|
|
}
|
|
MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
|
|
totalFramesRead += framesJustRead;
|
|
if (framesJustRead != framesToReadRightNow) {
|
|
break;
|
|
}
|
|
}
|
|
if (pConfig != NULL) {
|
|
pConfig->channels = pMP3->channels;
|
|
pConfig->sampleRate = pMP3->sampleRate;
|
|
}
|
|
ma_dr_mp3_uninit(pMP3);
|
|
if (pTotalFrameCount) {
|
|
*pTotalFrameCount = totalFramesRead;
|
|
}
|
|
return pFrames;
|
|
}
|
|
static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount)
|
|
{
|
|
ma_uint64 totalFramesRead = 0;
|
|
ma_uint64 framesCapacity = 0;
|
|
ma_int16* pFrames = NULL;
|
|
ma_int16 temp[4096];
|
|
MA_DR_MP3_ASSERT(pMP3 != NULL);
|
|
for (;;) {
|
|
ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels;
|
|
ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
|
|
if (framesJustRead == 0) {
|
|
break;
|
|
}
|
|
if (framesCapacity < totalFramesRead + framesJustRead) {
|
|
ma_uint64 newFramesBufferSize;
|
|
ma_uint64 oldFramesBufferSize;
|
|
ma_uint64 newFramesCap;
|
|
ma_int16* pNewFrames;
|
|
newFramesCap = framesCapacity * 2;
|
|
if (newFramesCap < totalFramesRead + framesJustRead) {
|
|
newFramesCap = totalFramesRead + framesJustRead;
|
|
}
|
|
oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16);
|
|
newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16);
|
|
if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) {
|
|
break;
|
|
}
|
|
pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
|
|
if (pNewFrames == NULL) {
|
|
ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
|
|
pFrames = NULL;
|
|
totalFramesRead = 0;
|
|
break;
|
|
}
|
|
pFrames = pNewFrames;
|
|
framesCapacity = newFramesCap;
|
|
}
|
|
MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16)));
|
|
totalFramesRead += framesJustRead;
|
|
if (framesJustRead != framesToReadRightNow) {
|
|
break;
|
|
}
|
|
}
|
|
if (pConfig != NULL) {
|
|
pConfig->channels = pMP3->channels;
|
|
pConfig->sampleRate = pMP3->sampleRate;
|
|
}
|
|
ma_dr_mp3_uninit(pMP3);
|
|
if (pTotalFrameCount) {
|
|
*pTotalFrameCount = totalFramesRead;
|
|
}
|
|
return pFrames;
|
|
}
|
|
MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3 mp3;
|
|
if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
|
|
}
|
|
MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, ma_dr_mp3_tell_proc onTell, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3 mp3;
|
|
if (!ma_dr_mp3_init(&mp3, onRead, onSeek, onTell, NULL, pUserData, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
|
|
}
|
|
MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3 mp3;
|
|
if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
|
|
}
|
|
MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3 mp3;
|
|
if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
|
|
}
|
|
#ifndef MA_DR_MP3_NO_STDIO
|
|
MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3 mp3;
|
|
if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
|
|
}
|
|
MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
ma_dr_mp3 mp3;
|
|
if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
|
|
return NULL;
|
|
}
|
|
return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
|
|
}
|
|
#endif
|
|
MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks);
|
|
} else {
|
|
return ma_dr_mp3__malloc_default(sz, NULL);
|
|
}
|
|
}
|
|
MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks)
|
|
{
|
|
if (pAllocationCallbacks != NULL) {
|
|
ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks);
|
|
} else {
|
|
ma_dr_mp3__free_default(p, NULL);
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma warning(pop)
|
|
#endif
|
|
|
|
#endif
|
|
#endif |