From bcbc7ad4a219427a5745585feabeb49861bf208a Mon Sep 17 00:00:00 2001 From: David Reid Date: Sun, 25 Mar 2018 08:50:03 +1000 Subject: [PATCH] Early work on improving format conversion. --- mini_al.h | 1803 ++++++++++++++++- tests/mal_test_0.c | 622 ++++++ .../benchmarks/pcm_f32_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_f32_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_f32_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_f32_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_f32_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_s16_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s16_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_s16_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_s16_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s16_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_s24_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s24_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_s24_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_s24_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s24_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_s32_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s32_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_s32_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_s32_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_s32_to_u8__mono_8000.raw | 1 + .../benchmarks/pcm_u8_to_f32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_u8_to_s16__mono_8000.raw | Bin 0 -> 160 bytes .../benchmarks/pcm_u8_to_s24__mono_8000.raw | Bin 0 -> 240 bytes .../benchmarks/pcm_u8_to_s32__mono_8000.raw | Bin 0 -> 320 bytes .../benchmarks/pcm_u8_to_u8__mono_8000.raw | 1 + 27 files changed, 2361 insertions(+), 69 deletions(-) create mode 100644 tests/res/benchmarks/pcm_f32_to_f32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_f32_to_s16__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_f32_to_s24__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_f32_to_s32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_f32_to_u8__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s16_to_f32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s16_to_s16__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s16_to_s24__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s16_to_s32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s16_to_u8__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s24_to_f32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s24_to_s16__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s24_to_s24__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s24_to_s32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s24_to_u8__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s32_to_f32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s32_to_s16__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s32_to_s24__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s32_to_s32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_s32_to_u8__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_u8_to_f32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_u8_to_s16__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_u8_to_s24__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_u8_to_s32__mono_8000.raw create mode 100644 tests/res/benchmarks/pcm_u8_to_u8__mono_8000.raw diff --git a/mini_al.h b/mini_al.h index fd8e8a08..6f1c76af 100644 --- a/mini_al.h +++ b/mini_al.h @@ -593,6 +593,18 @@ typedef enum mal_stream_format_pcm = 0, } mal_stream_format; +typedef enum +{ + mal_stream_layout_interleaved = 0, + mal_stream_layout_deinterleaved +} mal_stream_layout; + +typedef enum +{ + mal_dither_mode_none = 0, + //mal_dither_mode_triangle +} mal_dither_mode; + typedef enum { // I like to keep these explicitly defined because they're used as a key into a lookup table. When items are @@ -1728,6 +1740,53 @@ static inline mal_device_config mal_device_config_init_playback(mal_format forma void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, mal_uint32 channels, mal_channel channelMap[MAL_MAX_CHANNELS]); + +/////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion. +// +/////////////////////////////////////////////////////////////////////////////// + +typedef struct mal_format_converter mal_format_converter; + +// Callback for reading input data for the format converter. +typedef mal_uint32 (* mal_format_converter_read_proc) (mal_format_converter* pConverter, mal_uint32 frameCount, void* pFramesOut, void* pUserData); +typedef mal_uint32 (* mal_format_converter_read_separated_proc)(mal_format_converter* pConverter, mal_uint32 frameCount, void** ppSamplesOut, void* pUserData); + +typedef struct +{ + mal_format formatIn; + mal_format formatOut; + mal_uint32 channels; + mal_stream_format streamFormatIn; + mal_stream_format streamFormatOut; + mal_dither_mode ditherMode; +} mal_format_converter_config; + +struct mal_format_converter +{ + mal_format_converter_config config; + mal_format_converter_read_proc onRead; + mal_format_converter_read_separated_proc onReadSeparated; + void* pUserData; + void (* onConvertPCM)(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode); + void (* onInterleavePCM)(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels); + void (* onDeinterleavePCM)(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels); +}; + +// Initializes a format converter. +mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter_read_proc onRead, void* pUserData, mal_format_converter* pConverter); + +// Initializes a format converter when the input data is non-interleaved. +mal_result mal_format_converter_init_separated(const mal_format_converter_config* pConfig, mal_format_converter_read_separated_proc onRead, void* pUserData, mal_format_converter* pConverter); + +// Reads data from the format converter as interleaved channels. +mal_uint64 mal_format_converter_read_frames(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut); + +// Reads data from the format converter as separated channels. +mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut); + + /////////////////////////////////////////////////////////////////////////////// // // SRC @@ -1848,27 +1907,27 @@ void mal_blend_f32(float* pOut, float* pInA, float* pInB, float factor, mal_uint // Format Conversion // /////////////////////////////////////////////////////////////////////////////// -void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count); -void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count); -void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count); -void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); -void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount); +void mal_pcm_u8_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_u8_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s16_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s24_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_s32_to_f32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_u8(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s16(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s24(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_f32_to_s32(void* pOut, const void* pIn, mal_uint64 count, mal_dither_mode ditherMode); +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode); @@ -15214,6 +15273,1632 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// +// Format Conversion. +// +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//#define MAL_USE_REFERENCE_CONVERSION_APIS 1 +#define MAL_USE_SSE + +// u8 +void mal_pcm_u8_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + mal_copy_memory(dst, src, count * sizeof(mal_uint8)); +} + + +void mal_pcm_u8_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_u8[i]; + x = x - 128; + x = x << 8; + dst_s16[i] = x; + } +} + +void mal_pcm_u8_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_u8_to_s16__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_s16__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_u8_to_s16__sse(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_s16__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_u8_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_u8[i]; + x = x - 128; + + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = 0; + dst_s24[i*3+2] = (mal_uint8)((mal_int8)x); + } +} + +void mal_pcm_u8_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_u8_to_s24__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_s24__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_u8_to_s24__sse(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_s24__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_u8_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_u8[i]; + x = x - 128; + x = x << 24; + dst_s32[i] = x; + } +} + +void mal_pcm_u8_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_u8_to_s32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_s32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_u8_to_s32__sse(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_s32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_u8_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_u8[i]; + x = x * 0.00784313725490196078f; // 0..255 to 0..2 + x = x - 1; // 0..2 to -1..1 + + dst_f32[i] = x; + } +} + +void mal_pcm_u8_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_u8_to_f32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_u8_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_u8_to_f32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_u8_to_f32__sse(dst, src, count, ditherMode); +#else + mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + + +void mal_pcm_interleave_u8__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_uint8** src_u8 = (const mal_uint8**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_u8__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_uint8** src_u8 = (const mal_uint8**)src; + + if (channels == 1) { + mal_copy_memory(dst, src[0], frameCount * sizeof(mal_uint8)); + } else if (channels == 2) { + mal_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 { + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iFrame*channels + iChannel] = src_u8[iChannel][iFrame]; + } + } + } +} + +void mal_pcm_interleave_u8(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_u8__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_u8__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8** dst_u8 = (mal_uint8**)dst; + const mal_uint8* src_u8 = (const mal_uint8*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_u8[iChannel][iFrame] = src_u8[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_u8__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_u8(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_u8__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_u8__optimized(dst, src, frameCount, channels); +#endif +} + + +// s16 +void mal_pcm_s16_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int16 x = src_s16[i]; + x = x >> 8; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } +} + +void mal_pcm_s16_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s16_to_u8__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_u8__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s16_to_u8__sse(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_u8__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s16_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + mal_copy_memory(dst, src, count * sizeof(mal_int16)); +} + + +void mal_pcm_s16_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s24[i*3+0] = 0; + dst_s24[i*3+1] = (mal_uint8)(src_s16[i] & 0xFF); + dst_s24[i*3+2] = (mal_uint8)(src_s16[i] >> 8); + } +} + +void mal_pcm_s16_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s16_to_s24__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_s24__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s16_to_s24__sse(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_s24__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s16_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = src_s16[i] << 16; + } +} + +void mal_pcm_s16_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s16_to_s32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_s32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s16_to_s32__sse(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_s32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s16_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)src_s16[i]; + +#if 0 + // The accurate way. + x = x + 32768.0f; // -32768..32767 to 0..65535 + x = x * 0.00003051804379339284f; // 0..65536 to 0..2 + x = x - 1; // 0..2 to -1..1 +#else + // The fast way. + x = x * 0.000030517578125f; // -32768..32767 to -1..0.999969482421875 +#endif + + dst_f32[i] = x; + } +} + +void mal_pcm_s16_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s16_to_f32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s16_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s16_to_f32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s16_to_f32__sse(dst, src, count, ditherMode); +#else + mal_pcm_s16_to_f32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_interleave_s16__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_int16** src_s16 = (const mal_int16**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iFrame*channels + iChannel] = src_s16[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_s16__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_s16__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_s16(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_s16__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_s16__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int16** dst_s16 = (mal_int16**)dst; + const mal_int16* src_s16 = (const mal_int16*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s16[iChannel][iFrame] = src_s16[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_s16__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_s16(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_s16__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_s16__optimized(dst, src, frameCount, channels); +#endif +} + + +// s24 +void mal_pcm_s24_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int8 x = (mal_int8)src_s24[i*3 + 2] + 128; + dst_u8[i] = (mal_uint8)x; + } +} + +void mal_pcm_s24_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s24_to_u8__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_u8__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s24_to_u8__sse(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_u8__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s24_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_uint16 dst_lo = ((mal_uint16)src_s24[i*3 + 1]); + mal_uint16 dst_hi = ((mal_uint16)src_s24[i*3 + 2]) << 8; + dst_s16[i] = (mal_int16)dst_lo | dst_hi; + } +} + +void mal_pcm_s24_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s24_to_s16__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_s16__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s24_to_s16__sse(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_s16__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s24_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_copy_memory(dst, src, count * 3); +} + + +void mal_pcm_s24_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + dst_s32[i] = (mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24); + } +} + +void mal_pcm_s24_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s24_to_s32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_s32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s24_to_s32__sse(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_s32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s24_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_uint8* src_s24 = (const mal_uint8*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = (float)(((mal_int32)(((mal_uint32)(src_s24[i*3+0]) << 8) | ((mal_uint32)(src_s24[i*3+1]) << 16) | ((mal_uint32)(src_s24[i*3+2])) << 24)) >> 8); + +#if 0 + // The accurate way. + x = x + 8388608.0f; // -8388608..8388607 to 0..16777215 + x = x * 0.00000011920929665621f; // 0..16777215 to 0..2 + x = x - 1; // 0..2 to -1..1 +#else + // The fast way. + x = x * 0.00000011920928955078125f; // -8388608..8388607 to -1..0.999969482421875 +#endif + + dst_f32[i] = x; + } +} + +void mal_pcm_s24_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s24_to_f32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s24_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s24_to_f32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s24_to_f32__sse(dst, src, count, ditherMode); +#else + mal_pcm_s24_to_f32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_interleave_s24__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8* dst8 = (mal_uint8*)dst; + const mal_uint8** src8 = (const mal_uint8**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iFrame*3*channels + iChannel + 0] = src8[iChannel][iFrame*3 + 0]; + dst8[iFrame*3*channels + iChannel + 1] = src8[iChannel][iFrame*3 + 1]; + dst8[iFrame*3*channels + iChannel + 2] = src8[iChannel][iFrame*3 + 2]; + } + } +} + +void mal_pcm_interleave_s24__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_s24__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_s24(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_s24__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_s24__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_uint8** dst8 = (mal_uint8**)dst; + const mal_uint8* src8 = (const mal_uint8*)src; + + mal_uint32 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst8[iChannel][iFrame*3 + 0] = src8[iFrame*3*channels + iChannel + 0]; + dst8[iChannel][iFrame*3 + 1] = src8[iFrame*3*channels + iChannel + 1]; + dst8[iChannel][iFrame*3 + 2] = src8[iFrame*3*channels + iChannel + 2]; + } + } +} + +void mal_pcm_deinterleave_s24__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_s24(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_s24__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_s24__optimized(dst, src, frameCount, channels); +#endif +} + + + +// s32 +void mal_pcm_s32_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_u8 = (mal_uint8*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_s32[i]; + x = x >> 24; + x = x + 128; + dst_u8[i] = (mal_uint8)x; + } +} + +void mal_pcm_s32_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s32_to_u8__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_u8__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s32_to_u8__sse(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_u8__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s32_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int16* dst_s16 = (mal_int16*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_int32 x = src_s32[i]; + x = x >> 16; + dst_s16[i] = (mal_int16)x; + } +} + +void mal_pcm_s32_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s32_to_s16__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_s16__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s32_to_s16__sse(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_s16__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s32_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + mal_uint32 x = (mal_uint32)src_s32[i]; + dst_s24[i*3+0] = (mal_uint8)((x & 0x0000FF00) >> 8); + dst_s24[i*3+1] = (mal_uint8)((x & 0x00FF0000) >> 16); + dst_s24[i*3+2] = (mal_uint8)((x & 0xFF000000) >> 24); + } +} + +void mal_pcm_s32_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s32_to_s24__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_s24__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s32_to_s24__sse(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_s24__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_s32_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_copy_memory(dst, src, count * sizeof(mal_int32)); +} + + +void mal_pcm_s32_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + float* dst_f32 = (float*)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_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 mal_pcm_s32_to_f32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_s32_to_f32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_s32_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_s32_to_f32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_s32_to_f32__sse(dst, src, count, ditherMode); +#else + mal_pcm_s32_to_f32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_interleave_s32__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int32* dst_s32 = (mal_int32*)dst; + const mal_int32** src_s32 = (const mal_int32**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iFrame*channels + iChannel] = src_s32[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_s32__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_s32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_s32(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_s32__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_s32__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_int32** dst_s32 = (mal_int32**)dst; + const mal_int32* src_s32 = (const mal_int32*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_s32[iChannel][iFrame] = src_s32[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_s32__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_s32(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_s32__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_s32__optimized(dst, src, frameCount, channels); +#endif +} + + +// f32 +void mal_pcm_f32_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_u8 = (mal_uint8*)dst; + const float* src_f32 = (const float*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + x = x + 1; // -1..1 to 0..2 + x = x * 127.5f; // 0..2 to 0..255 + + dst_u8[i] = (mal_uint8)x; + } +} + +void mal_pcm_f32_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_f32_to_u8__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_u8(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_u8__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_f32_to_u8__sse(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_u8__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_f32_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int16* dst_s16 = (mal_int16*)dst; + const float* src_f32 = (const float*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + +#if 0 + // The accurate way. + x = x + 1; // -1..1 to 0..2 + x = x * 32767.5f; // 0..2 to 0..65535 + x = x - 32768.0f; // 0...65535 to -32768..32767 +#else + // The fast way. + x = x * 32767.0f; // -1..1 to -32767..32767 +#endif + + dst_s16[i] = (mal_int16)x; + } +} + +void mal_pcm_f32_to_s16__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s16__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_f32_to_s16__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_s16(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_s16__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_f32_to_s16__sse(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_s16__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_f32_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_uint8* dst_s24 = (mal_uint8*)dst; + const float* src_f32 = (const float*)src; + + mal_uint64 i; + for (i = 0; i < count; i += 1) { + float x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + +#if 0 + // The accurate way. + x = x + 1; // -1..1 to 0..2 + x = x * 8388607.5f; // 0..2 to 0..16777215 + x = x - 8388608.0f; // 0..16777215 to -8388608..8388607 +#else + // The fast way. + x = x * 8388607.0f; // -1..1 to -8388607..8388607 +#endif + + mal_int32 r = (mal_int32)x; + dst_s24[(i*3)+0] = (mal_uint8)((r & 0x0000FF) >> 0); + dst_s24[(i*3)+1] = (mal_uint8)((r & 0x00FF00) >> 8); + dst_s24[(i*3)+2] = (mal_uint8)((r & 0xFF0000) >> 16); + } +} + +void mal_pcm_f32_to_s24__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_f32_to_s24__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_s24(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_s24__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_f32_to_s24__sse(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_s24__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_f32_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_int32* dst_s32 = (mal_int32*)dst; + const float* src_f32 = (const float*)src; + + mal_uint32 i; + for (i = 0; i < count; i += 1) { + double x = src_f32[i]; + x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); // clip + +#if 0 + // The accurate way. + x = x + 1; // -1..1 to 0..2 + x = x * 2147483647.5; // 0..2 to 0..4294967295 + x = x - 2147483648.0; // 0...4294967295 to -2147483648..2147483647 +#else + // The fast way. + x = x * 2147483647.0; // -1..1 to -2147483647..2147483647 +#endif + + dst_s32[i] = (mal_int32)x; + } +} + +void mal_pcm_f32_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +} + +#ifdef MAL_USE_SSE +void mal_pcm_f32_to_s32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + mal_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +} +#endif + +void mal_pcm_f32_to_s32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_f32_to_s32__reference(dst, src, count, ditherMode); +#else +#ifdef MAL_USE_SSE + mal_pcm_f32_to_s32__sse(dst, src, count, ditherMode); +#else + mal_pcm_f32_to_s32__optimized(dst, src, count, ditherMode); +#endif +#endif +} + + +void mal_pcm_f32_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) +{ + (void)ditherMode; + + mal_copy_memory(dst, src, count * sizeof(float)); +} + + +void mal_pcm_interleave_f32__reference(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + float* dst_f32 = (float*)dst; + const float** src_f32 = (const float**)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iFrame*channels + iChannel] = src_f32[iChannel][iFrame]; + } + } +} + +void mal_pcm_interleave_f32__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_interleave_f32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_interleave_f32(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_interleave_f32__reference(dst, src, frameCount, channels); +#else + mal_pcm_interleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + +void mal_pcm_deinterleave_f32__reference(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + float** dst_f32 = (float**)dst; + const float* src_f32 = (const float*)src; + + mal_uint64 iFrame; + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + mal_uint32 iChannel; + for (iChannel = 0; iChannel < channels; iChannel += 1) { + dst_f32[iChannel][iFrame] = src_f32[iFrame*channels + iChannel]; + } + } +} + +void mal_pcm_deinterleave_f32__optimized(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ + mal_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +} + +void mal_pcm_deinterleave_f32(void** dst, const void* src, mal_uint64 frameCount, mal_uint32 channels) +{ +#ifdef MAL_USE_REFERENCE_CONVERSION_APIS + mal_pcm_deinterleave_f32__reference(dst, src, frameCount, channels); +#else + mal_pcm_deinterleave_f32__optimized(dst, src, frameCount, channels); +#endif +} + + + +mal_result mal_format_converter_init__common(const mal_format_converter_config* pConfig, void* pUserData, mal_format_converter* pConverter) +{ + if (pConverter == NULL) { + return MAL_INVALID_ARGS; + } + mal_zero_object(pConverter); + + if (pConfig == NULL) { + return MAL_INVALID_ARGS; + } + + pConverter->config = *pConfig; + pConverter->pUserData = pUserData; + + switch (pConfig->formatIn) + { + case mal_format_u8: + { + if (pConfig->formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_u8_to_u8; + } else if (pConfig->formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_u8_to_s16; + } else if (pConfig->formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_u8_to_s24; + } else if (pConfig->formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_u8_to_s32; + } else if (pConfig->formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_u8_to_f32; + } + } break; + + case mal_format_s16: + { + if (pConfig->formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s16_to_u8; + } else if (pConfig->formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s16_to_s16; + } else if (pConfig->formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s16_to_s24; + } else if (pConfig->formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s16_to_s32; + } else if (pConfig->formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s16_to_f32; + } + } break; + + case mal_format_s24: + { + if (pConfig->formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s24_to_u8; + } else if (pConfig->formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s24_to_s16; + } else if (pConfig->formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s24_to_s24; + } else if (pConfig->formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s24_to_s32; + } else if (pConfig->formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s24_to_f32; + } + } break; + + case mal_format_s32: + { + if (pConfig->formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_s32_to_u8; + } else if (pConfig->formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_s32_to_s16; + } else if (pConfig->formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_s32_to_s24; + } else if (pConfig->formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_s32_to_s32; + } else if (pConfig->formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_s32_to_f32; + } + } break; + + case mal_format_f32: + default: + { + if (pConfig->formatOut == mal_format_u8) { + pConverter->onConvertPCM = mal_pcm_f32_to_u8; + } else if (pConfig->formatOut == mal_format_s16) { + pConverter->onConvertPCM = mal_pcm_f32_to_s16; + } else if (pConfig->formatOut == mal_format_s24) { + pConverter->onConvertPCM = mal_pcm_f32_to_s24; + } else if (pConfig->formatOut == mal_format_s32) { + pConverter->onConvertPCM = mal_pcm_f32_to_s32; + } else if (pConfig->formatOut == mal_format_f32) { + pConverter->onConvertPCM = mal_pcm_f32_to_f32; + } + } break; + } + + + switch (pConfig->formatOut) + { + case mal_format_u8: + { + pConverter->onInterleavePCM = mal_pcm_interleave_u8; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_u8; + } break; + case mal_format_s16: + { + pConverter->onInterleavePCM = mal_pcm_interleave_s16; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_s16; + } break; + case mal_format_s24: + { + pConverter->onInterleavePCM = mal_pcm_interleave_s24; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_s24; + } break; + case mal_format_s32: + { + pConverter->onInterleavePCM = mal_pcm_interleave_s32; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_s32; + } break; + case mal_format_f32: + default: + { + pConverter->onInterleavePCM = mal_pcm_interleave_f32; + pConverter->onDeinterleavePCM = mal_pcm_deinterleave_f32; + } break; + } + + return MAL_SUCCESS; +} + + +mal_result mal_format_converter_init(const mal_format_converter_config* pConfig, mal_format_converter_read_proc onRead, void* pUserData, mal_format_converter* pConverter) +{ + mal_result result = mal_format_converter_init__common(pConfig, pUserData, pConverter); + if (result != MAL_SUCCESS) { + return result; + } + + pConverter->onRead = onRead; + + return MAL_SUCCESS; +} + +mal_result mal_format_converter_init_separated(const mal_format_converter_config* pConfig, mal_format_converter_read_separated_proc onRead, void* pUserData, mal_format_converter* pConverter) +{ + mal_result result = mal_format_converter_init__common(pConfig, pUserData, pConverter); + if (result != MAL_SUCCESS) { + return result; + } + + pConverter->onReadSeparated = onRead; + + return MAL_SUCCESS; +} + +mal_uint64 mal_format_converter_read_frames(mal_format_converter* pConverter, mal_uint64 frameCount, void* pFramesOut) +{ + if (pConverter == NULL || pFramesOut == NULL) { + return 0; + } + + mal_uint64 totalFramesRead = 0; + mal_uint32 sampleSizeIn = mal_get_sample_size_in_bytes(pConverter->config.formatIn); + //mal_uint32 sampleSizeOut = mal_get_sample_size_in_bytes(pConverter->config.formatOut); + mal_uint32 frameSizeIn = sampleSizeIn * pConverter->config.channels; + //mal_uint32 frameSizeOut = sampleSizeOut * pConverter->config.channels; + mal_uint8* pNextFramesOut = (mal_uint8*)pFramesOut; + + if (pConverter->onRead != NULL) { + // Input data is interleaved. + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Pass through. + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->onRead(pConverter, (mal_uint32)framesToReadRightNow, pNextFramesOut, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + + totalFramesRead += framesJustRead; + pNextFramesOut += framesJustRead * frameSizeIn; + } + } else { + // Conversion required. + mal_uint8 temp[MAL_MAX_CHANNELS * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(temp) <= 0xFFFFFFFF); + + mal_uint32 maxFramesToReadAtATime = sizeof(temp) / sampleSizeIn / pConverter->config.channels; + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->onRead(pConverter, (mal_uint32)framesToReadRightNow, temp, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + + pConverter->onConvertPCM(pNextFramesOut, temp, framesJustRead*pConverter->config.channels, pConverter->config.ditherMode); + + totalFramesRead += framesJustRead; + pNextFramesOut += framesJustRead * frameSizeIn; + } + } + } else { + // Input data is separated. If a conversion is required we need to do an intermediary step. + mal_uint8 tempSamplesOfOutFormat[MAL_MAX_CHANNELS][MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(tempSamplesOfOutFormat[0]) <= 0xFFFFFFFFF); + + mal_uint32 maxFramesToReadAtATime = sizeof(tempSamplesOfOutFormat[0]) / sampleSizeIn; + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = 0; + + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Only interleaving. + framesJustRead = (mal_uint32)pConverter->onReadSeparated(pConverter, (mal_uint32)framesToReadRightNow, (void**)tempSamplesOfOutFormat, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + } else { + // Interleaving + Conversion. Convert first, then interleave. + mal_uint8 tempSamplesOfInFormat[MAL_MAX_CHANNELS][MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + + framesJustRead = (mal_uint32)pConverter->onReadSeparated(pConverter, (mal_uint32)framesToReadRightNow, (void**)tempSamplesOfInFormat, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) { + pConverter->onConvertPCM(tempSamplesOfOutFormat[iChannel], tempSamplesOfInFormat[iChannel], framesJustRead, pConverter->config.ditherMode); + } + } + + pConverter->onInterleavePCM(pNextFramesOut, (void**)tempSamplesOfOutFormat, framesJustRead, pConverter->config.channels); + + totalFramesRead += framesJustRead; + pNextFramesOut += framesJustRead * frameSizeIn; + } + } + + return totalFramesRead; +} + +mal_uint64 mal_format_converter_read_frames_separated(mal_format_converter* pConverter, mal_uint64 frameCount, void** ppSamplesOut) +{ + if (pConverter == NULL || ppSamplesOut == NULL) { + return 0; + } + + mal_uint64 totalFramesRead = 0; + mal_uint32 sampleSizeIn = mal_get_sample_size_in_bytes(pConverter->config.formatIn); + //mal_uint32 sampleSizeOut = mal_get_sample_size_in_bytes(pConverter->config.formatOut); + + mal_uint8* ppNextSamplesOut[MAL_MAX_CHANNELS]; + mal_copy_memory(ppNextSamplesOut, ppSamplesOut, sizeof(void*) * pConverter->config.channels); + + if (pConverter->onRead != NULL) { + // Input data is interleaved. + mal_uint8 tempSamplesOfOutFormat[MAL_MAX_CHANNELS * MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(tempSamplesOfOutFormat) <= 0xFFFFFFFF); + + mal_uint32 maxFramesToReadAtATime = sizeof(tempSamplesOfOutFormat) / sampleSizeIn / pConverter->config.channels; + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = 0; + + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Only de-interleaving. + framesJustRead = (mal_uint32)pConverter->onRead(pConverter, (mal_uint32)framesToReadRightNow, tempSamplesOfOutFormat, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + } else { + // De-interleaving + Conversion. Convert first, then de-interleave. + mal_uint8 tempSamplesOfInFormat[sizeof(tempSamplesOfOutFormat)]; + + framesJustRead = (mal_uint32)pConverter->onRead(pConverter, (mal_uint32)framesToReadRightNow, tempSamplesOfInFormat, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + + pConverter->onConvertPCM(tempSamplesOfOutFormat, tempSamplesOfInFormat, framesJustRead * pConverter->config.channels, pConverter->config.ditherMode); + } + + pConverter->onDeinterleavePCM((void**)ppNextSamplesOut, tempSamplesOfOutFormat, framesJustRead, pConverter->config.channels); + + totalFramesRead += framesJustRead; + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) { + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeIn; + } + } + } else { + // Input data is separated. + if (pConverter->config.formatIn == pConverter->config.formatOut) { + // Pass through. + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > 0xFFFFFFFF) { + framesToReadRightNow = 0xFFFFFFFF; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->onReadSeparated(pConverter, (mal_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + + totalFramesRead += framesJustRead; + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; ++iChannel) { + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeIn; + } + } + } else { + // Conversion required. + mal_uint8 temp[MAL_MAX_CHANNELS][MAL_MAX_PCM_SAMPLE_SIZE_IN_BYTES * 128]; + mal_assert(sizeof(temp[0]) <= 0xFFFFFFFF); + + mal_uint32 maxFramesToReadAtATime = sizeof(temp[0]) / sampleSizeIn; + + while (totalFramesRead < frameCount) { + mal_uint64 framesRemaining = (frameCount - totalFramesRead); + mal_uint64 framesToReadRightNow = framesRemaining; + if (framesToReadRightNow > maxFramesToReadAtATime) { + framesToReadRightNow = maxFramesToReadAtATime; + } + + mal_uint32 framesJustRead = (mal_uint32)pConverter->onReadSeparated(pConverter, (mal_uint32)framesToReadRightNow, (void**)ppNextSamplesOut, pConverter->pUserData); + if (framesJustRead == 0) { + break; + } + + for (mal_uint32 iChannel = 0; iChannel < pConverter->config.channels; iChannel += 1) { + pConverter->onConvertPCM(ppNextSamplesOut[iChannel], temp[iChannel], framesJustRead, pConverter->config.ditherMode); + ppNextSamplesOut[iChannel] += framesJustRead * sampleSizeIn; + } + + totalFramesRead += framesJustRead; + } + } + } + + return totalFramesRead; +} + + + + ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SRC @@ -15284,7 +16969,7 @@ mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCoun pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pIntermediaryBuffer, pCache->pSRC->pUserData); // Convert to f32. - mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels); + mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels, mal_dither_mode_none); } @@ -15421,7 +17106,7 @@ mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, break; } - mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pStagingBuffer, pSRC->config.formatIn, framesRead * pSRC->config.channels); + mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pStagingBuffer, pSRC->config.formatIn, framesRead * pSRC->config.channels, mal_dither_mode_none); pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut)); frameCount -= framesRead; @@ -15495,7 +17180,7 @@ mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void } } - mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pFrame, mal_format_f32, 1 * pSRC->config.channels); + mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pFrame, mal_format_f32, 1 * pSRC->config.channels, mal_dither_mode_none); pFramesOut = (mal_uint8*)pFramesOut + (1 * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut)); frameCount -= 1; @@ -15519,28 +17204,7 @@ mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void // ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -void mal_pcm_u8_to_s16(short* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s24(void* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_s32(int* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_u8_to_f32(float* pOut, const unsigned char* pIn, unsigned int count); -void mal_pcm_s16_to_u8(unsigned char* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s24(void* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_s32(int* pOut, const short* pIn, unsigned int count); -void mal_pcm_s16_to_f32(float* pOut, const short* pIn, unsigned int count); -void mal_pcm_s24_to_u8(unsigned char* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s16(short* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_s32(int* pOut, const void* pIn, unsigned int count); -void mal_pcm_s24_to_f32(float* pOut, const void* pIn, unsigned int count); -void mal_pcm_s32_to_u8(unsigned char* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s16(short* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_s24(void* pOut, const int* pIn, unsigned int count); -void mal_pcm_s32_to_f32(float* pOut, const int* pIn, unsigned int count); -void mal_pcm_f32_to_u8(unsigned char* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s16(short* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s24(void* pOut, const float* pIn, unsigned int count); -void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); - -void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount) +void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, mal_uint64 sampleCount, mal_dither_mode ditherMode) { if (formatOut == formatIn) { mal_copy_memory(pOut, pIn, sampleCount * mal_get_sample_size_in_bytes(formatOut)); @@ -15553,10 +17217,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_s16: mal_pcm_u8_to_s16((short*)pOut, (const unsigned char*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_u8_to_s24( pOut, (const unsigned char*)pIn, sampleCount); return; - case mal_format_s32: mal_pcm_u8_to_s32( (int*)pOut, (const unsigned char*)pIn, sampleCount); return; - case mal_format_f32: mal_pcm_u8_to_f32((float*)pOut, (const unsigned char*)pIn, sampleCount); return; + case mal_format_s16: mal_pcm_u8_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_u8_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_u8_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_u8_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -15565,10 +17229,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_s16_to_u8( (unsigned char*)pOut, (const short*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_s16_to_s24( pOut, (const short*)pIn, sampleCount); return; - case mal_format_s32: mal_pcm_s16_to_s32( (int*)pOut, (const short*)pIn, sampleCount); return; - case mal_format_f32: mal_pcm_s16_to_f32( (float*)pOut, (const short*)pIn, sampleCount); return; + case mal_format_u8: mal_pcm_s16_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_s16_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_s16_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_s16_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -15577,10 +17241,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_s24_to_u8( (unsigned char*)pOut, pIn, sampleCount); return; - case mal_format_s16: mal_pcm_s24_to_s16( (short*)pOut, pIn, sampleCount); return; - case mal_format_s32: mal_pcm_s24_to_s32( (int*)pOut, pIn, sampleCount); return; - case mal_format_f32: mal_pcm_s24_to_f32( (float*)pOut, pIn, sampleCount); return; + case mal_format_u8: mal_pcm_s24_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s16: mal_pcm_s24_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_s24_to_s32(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_s24_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -15589,10 +17253,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_s32_to_u8( (unsigned char*)pOut, (const int*)pIn, sampleCount); return; - case mal_format_s16: mal_pcm_s32_to_s16( (short*)pOut, (const int*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_s32_to_s24( pOut, (const int*)pIn, sampleCount); return; - case mal_format_f32: mal_pcm_s32_to_f32( (float*)pOut, (const int*)pIn, sampleCount); return; + case mal_format_u8: mal_pcm_s32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s16: mal_pcm_s32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_s32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_f32: mal_pcm_s32_to_f32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -15601,10 +17265,10 @@ void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_form { switch (formatOut) { - case mal_format_u8: mal_pcm_f32_to_u8( (unsigned char*)pOut, (const float*)pIn, sampleCount); return; - case mal_format_s16: mal_pcm_f32_to_s16( (short*)pOut, (const float*)pIn, sampleCount); return; - case mal_format_s24: mal_pcm_f32_to_s24( pOut, (const float*)pIn, sampleCount); return; - case mal_format_s32: mal_pcm_f32_to_s32( (int*)pOut, (const float*)pIn, sampleCount); return; + case mal_format_u8: mal_pcm_f32_to_u8( pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s16: mal_pcm_f32_to_s16(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s24: mal_pcm_f32_to_s24(pOut, pIn, sampleCount, ditherMode); return; + case mal_format_s32: mal_pcm_f32_to_s32(pOut, pIn, sampleCount, ditherMode); return; default: break; } } break; @@ -16322,7 +17986,7 @@ mal_uint64 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pF // Channel mixing. The input format must be in f32 which may require a conversion. if (pDSP->config.channelsIn != pDSP->config.channelsOut) { if (pFramesFormat[iFrames] != mal_format_f32) { - mal_pcm_convert(pFrames[(iFrames + 1) % 2], mal_format_f32, pFrames[iFrames], pDSP->config.formatIn, framesRead * pDSP->config.channelsIn); + mal_pcm_convert(pFrames[(iFrames + 1) % 2], mal_format_f32, pFrames[iFrames], pDSP->config.formatIn, framesRead * pDSP->config.channelsIn, mal_dither_mode_none); iFrames = (iFrames + 1) % 2; pFramesFormat[iFrames] = mal_format_f32; } @@ -16342,7 +18006,7 @@ mal_uint64 mal_dsp_read_frames_ex(mal_dsp* pDSP, mal_uint64 frameCount, void* pF // Final conversion to output format. - mal_pcm_convert(pFramesOut, pDSP->config.formatOut, pFrames[iFrames], pFramesFormat[iFrames], framesRead * pDSP->config.channelsOut); + mal_pcm_convert(pFramesOut, pDSP->config.formatOut, pFrames[iFrames], pFramesFormat[iFrames], framesRead * pDSP->config.channelsOut, mal_dither_mode_none); pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pDSP->config.channelsOut * mal_get_sample_size_in_bytes(pDSP->config.formatOut)); frameCount -= framesRead; @@ -17647,7 +19311,7 @@ mal_result mal_decoder_seek_to_frame(mal_decoder* pDecoder, mal_uint64 frameInde - +#if 0 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -17902,6 +19566,7 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count) pOut[i] = (int)r; } } +#endif #endif diff --git a/tests/mal_test_0.c b/tests/mal_test_0.c index 85bd83d6..9242e876 100644 --- a/tests/mal_test_0.c +++ b/tests/mal_test_0.c @@ -35,6 +35,619 @@ void on_log(mal_context* pContext, mal_device* pDevice, const char* message) } +void* open_and_read_file_data(const char* filePath, size_t* pSizeOut) +{ + // Safety. + if (pSizeOut) *pSizeOut = 0; + + if (filePath == NULL) { + return NULL; + } + + FILE* pFile; +#if _MSC_VER + if (fopen_s(&pFile, filePath, "rb") != 0) { + return NULL; + } +#else + pFile = fopen(filePath, "rb"); + if (pFile == NULL) { + return NULL; + } +#endif + + fseek(pFile, 0, SEEK_END); + mal_uint64 fileSize = ftell(pFile); + fseek(pFile, 0, SEEK_SET); + + if (fileSize > SIZE_MAX) { + fclose(pFile); + return NULL; + } + + void* pFileData = mal_malloc((size_t)fileSize); // <-- Safe cast due to the check above. + if (pFileData == NULL) { + fclose(pFile); + return NULL; + } + + size_t bytesRead = fread(pFileData, 1, (size_t)fileSize, pFile); + if (bytesRead != fileSize) { + mal_free(pFileData); + fclose(pFile); + return NULL; + } + + fclose(pFile); + + if (pSizeOut) { + *pSizeOut = (size_t)fileSize; + } + + return pFileData; +} + +void* load_raw_audio_data(const char* filePath, mal_format format, mal_uint64* pBenchmarkFrameCount) +{ + mal_assert(pBenchmarkFrameCount != NULL); + *pBenchmarkFrameCount = 0; + + size_t fileSize; + void* pFileData = open_and_read_file_data(filePath, &fileSize); + if (pFileData == NULL) { + printf("Cound not open file %s\n", filePath); + return NULL; + } + + *pBenchmarkFrameCount = fileSize / mal_get_sample_size_in_bytes(format); + return pFileData; +} + +void* load_benchmark_base_data(mal_format format, mal_uint32* pChannelsOut, mal_uint32* pSampleRateOut, mal_uint64* pBenchmarkFrameCount) +{ + mal_assert(pChannelsOut != NULL); + mal_assert(pSampleRateOut != NULL); + mal_assert(pBenchmarkFrameCount != NULL); + + *pChannelsOut = 1; + *pSampleRateOut = 8000; + *pBenchmarkFrameCount = 0; + + const char* filePath = NULL; + switch (format) { + case mal_format_u8: filePath = "res/benchmarks/pcm_u8_to_u8__mono_8000.raw"; break; + case mal_format_s16: filePath = "res/benchmarks/pcm_s16_to_s16__mono_8000.raw"; break; + case mal_format_s24: filePath = "res/benchmarks/pcm_s24_to_s24__mono_8000.raw"; break; + case mal_format_s32: filePath = "res/benchmarks/pcm_s32_to_s32__mono_8000.raw"; break; + case mal_format_f32: filePath = "res/benchmarks/pcm_f32_to_f32__mono_8000.raw"; break; + default: return NULL; + } + + return load_raw_audio_data(filePath, format, pBenchmarkFrameCount); +} + +int mal_pcm_compare(const void* a, const void* b, mal_uint64 count, mal_format format, float allowedDifference) +{ + int result = 0; + + const mal_uint8* a_u8 = (const mal_uint8*)a; + const mal_uint8* b_u8 = (const mal_uint8*)b; + const mal_int16* a_s16 = (const mal_int16*)a; + const mal_int16* b_s16 = (const mal_int16*)b; + const mal_int32* a_s32 = (const mal_int32*)a; + const mal_int32* b_s32 = (const mal_int32*)b; + const float* a_f32 = (const float* )a; + const float* b_f32 = (const float* )b; + + for (mal_uint64 i = 0; i < count; ++i) { + switch (format) { + case mal_format_u8: + { + mal_uint8 sampleA = a_u8[i]; + mal_uint8 sampleB = b_u8[i]; + if (sampleA != sampleB) { + if (abs(sampleA - sampleB) > allowedDifference) { // Allow a difference of 1. + printf("Sample %I64u not equal. %d != %d (diff: %d)\n", i, sampleA, sampleB, sampleA - sampleB); + result = -1; + } + } + } break; + + case mal_format_s16: + { + mal_int16 sampleA = a_s16[i]; + mal_int16 sampleB = b_s16[i]; + if (sampleA != sampleB) { + if (abs(sampleA - sampleB) > allowedDifference) { // Allow a difference of 1. + printf("Sample %I64u not equal. %d != %d (diff: %d)\n", i, sampleA, sampleB, sampleA - sampleB); + result = -1; + } + } + } break; + + case mal_format_s24: + { + mal_int32 sampleA = ((mal_int32)(((mal_uint32)(a_u8[i*3+0]) << 8) | ((mal_uint32)(a_u8[i*3+1]) << 16) | ((mal_uint32)(a_u8[i*3+2])) << 24)) >> 8; + mal_int32 sampleB = ((mal_int32)(((mal_uint32)(b_u8[i*3+0]) << 8) | ((mal_uint32)(b_u8[i*3+1]) << 16) | ((mal_uint32)(b_u8[i*3+2])) << 24)) >> 8;; + if (sampleA != sampleB) { + if (abs(sampleA - sampleB) > allowedDifference) { // Allow a difference of 1. + printf("Sample %I64u not equal. %d != %d (diff: %d)\n", i, sampleA, sampleB, sampleA - sampleB); + result = -1; + } + } + } break; + + case mal_format_s32: + { + mal_int32 sampleA = a_s32[i]; + mal_int32 sampleB = b_s32[i]; + if (sampleA != sampleB) { + if (abs(sampleA - sampleB) > allowedDifference) { // Allow a difference of 1. + printf("Sample %I64u not equal. %d != %d (diff: %d)\n", i, sampleA, sampleB, sampleA - sampleB); + result = -1; + } + } + } break; + + case mal_format_f32: + { + float sampleA = a_f32[i]; + float sampleB = b_f32[i]; + if (sampleA != sampleB) { + float difference = sampleA - sampleB; + difference = (difference < 0) ? -difference : difference; + + if (difference > allowedDifference) { + printf("Sample %I64u not equal. %.8f != %.8f (diff: %.8f)\n", i, sampleA, sampleB, sampleA - sampleB); + result = -1; + } + } + } break; + + default: return -1; + } + } + + return result; +} + +int do_format_conversion_test(mal_format formatIn, mal_format formatOut) +{ + int result = 0; + + mal_uint32 channels; + mal_uint32 sampleRate; + mal_uint64 baseFrameCount; + mal_int16* pBaseData = (mal_int16*)load_benchmark_base_data(formatIn, &channels, &sampleRate, &baseFrameCount); + if (pBaseData == NULL) { + return -1; // Failed to load file. + } + + void (* onConvertPCM)(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode) = NULL; + const char* pBenchmarkFilePath = NULL; + + switch (formatIn) { + case mal_format_u8: + { + switch (formatOut) { + case mal_format_u8: + { + onConvertPCM = mal_pcm_u8_to_u8; + pBenchmarkFilePath = "res/benchmarks/pcm_u8_to_u8__mono_8000.raw"; + } break; + case mal_format_s16: + { + onConvertPCM = mal_pcm_u8_to_s16__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_u8_to_s16__mono_8000.raw"; + } break; + case mal_format_s24: + { + onConvertPCM = mal_pcm_u8_to_s24__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_u8_to_s24__mono_8000.raw"; + } break; + case mal_format_s32: + { + onConvertPCM = mal_pcm_u8_to_s32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_u8_to_s32__mono_8000.raw"; + } break; + case mal_format_f32: + { + onConvertPCM = mal_pcm_u8_to_f32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_u8_to_f32__mono_8000.raw"; + } break; + default: + { + result = -1; + } break; + } + } break; + + case mal_format_s16: + { + switch (formatOut) { + case mal_format_u8: + { + onConvertPCM = mal_pcm_s16_to_u8__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s16_to_u8__mono_8000.raw"; + } break; + case mal_format_s16: + { + onConvertPCM = mal_pcm_s16_to_s16; + pBenchmarkFilePath = "res/benchmarks/pcm_s16_to_s16__mono_8000.raw"; + } break; + case mal_format_s24: + { + onConvertPCM = mal_pcm_s16_to_s24__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s16_to_s24__mono_8000.raw"; + } break; + case mal_format_s32: + { + onConvertPCM = mal_pcm_s16_to_s32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s16_to_s32__mono_8000.raw"; + } break; + case mal_format_f32: + { + onConvertPCM = mal_pcm_s16_to_f32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s16_to_f32__mono_8000.raw"; + } break; + default: + { + result = -1; + } break; + } + } break; + + case mal_format_s24: + { + switch (formatOut) { + case mal_format_u8: + { + onConvertPCM = mal_pcm_s24_to_u8__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s24_to_u8__mono_8000.raw"; + } break; + case mal_format_s16: + { + onConvertPCM = mal_pcm_s24_to_s16__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s24_to_s16__mono_8000.raw"; + } break; + case mal_format_s24: + { + onConvertPCM = mal_pcm_s24_to_s24; + pBenchmarkFilePath = "res/benchmarks/pcm_s24_to_s24__mono_8000.raw"; + } break; + case mal_format_s32: + { + onConvertPCM = mal_pcm_s24_to_s32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s24_to_s32__mono_8000.raw"; + } break; + case mal_format_f32: + { + onConvertPCM = mal_pcm_s24_to_f32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s24_to_f32__mono_8000.raw"; + } break; + default: + { + result = -1; + } break; + } + } break; + + case mal_format_s32: + { + switch (formatOut) { + case mal_format_u8: + { + onConvertPCM = mal_pcm_s32_to_u8__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s32_to_u8__mono_8000.raw"; + } break; + case mal_format_s16: + { + onConvertPCM = mal_pcm_s32_to_s16__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s32_to_s16__mono_8000.raw"; + } break; + case mal_format_s24: + { + onConvertPCM = mal_pcm_s32_to_s24__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s32_to_s24__mono_8000.raw"; + } break; + case mal_format_s32: + { + onConvertPCM = mal_pcm_s32_to_s32; + pBenchmarkFilePath = "res/benchmarks/pcm_s32_to_s32__mono_8000.raw"; + } break; + case mal_format_f32: + { + onConvertPCM = mal_pcm_s32_to_f32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_s32_to_f32__mono_8000.raw"; + } break; + default: + { + result = -1; + } break; + } + } break; + + case mal_format_f32: + { + switch (formatOut) { + case mal_format_u8: + { + onConvertPCM = mal_pcm_f32_to_u8__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_f32_to_u8__mono_8000.raw"; + } break; + case mal_format_s16: + { + onConvertPCM = mal_pcm_f32_to_s16__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_f32_to_s16__mono_8000.raw"; + } break; + case mal_format_s24: + { + onConvertPCM = mal_pcm_f32_to_s24__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_f32_to_s24__mono_8000.raw"; + } break; + case mal_format_s32: + { + onConvertPCM = mal_pcm_f32_to_s32__reference; + pBenchmarkFilePath = "res/benchmarks/pcm_f32_to_s32__mono_8000.raw"; + } break; + case mal_format_f32: + { + onConvertPCM = mal_pcm_f32_to_f32; + pBenchmarkFilePath = "res/benchmarks/pcm_f32_to_f32__mono_8000.raw"; + } break; + default: + { + result = -1; + } break; + } + } break; + + default: + { + result = -1; + } break; + } + + + if (result != 0) { + mal_free(pBaseData); + return result; + } + + // We need to allow a very small amount of difference to each sample because the software that generated our testing benchmarks can use slightly + // different (but still correct) algorithms which produce slightly different results. I'm allowing for this variability in my basic comparison + // tests, but testing things like dithering will require more detailed testing which I'll probably do separate to this test project. + mal_bool32 allowSmallDifference = MAL_TRUE; + float allowedDifference = 0; + if (allowSmallDifference) { + if (formatOut == mal_format_f32) { + switch (formatIn) { + case mal_format_u8: allowedDifference = 1.0f / 255 * 2; break; + case mal_format_s16: allowedDifference = 1.0f / 32767 * 2; break; + case mal_format_s24: allowedDifference = 1.0f / 8388608 * 2; break; + case mal_format_s32: allowedDifference = 1.0f / 2147483647 * 2; break; + case mal_format_f32: allowedDifference = 0; break; + default: break; + } + } else { + allowedDifference = 1; + } + } + + mal_uint64 benchmarkFrameCount; + void* pBenchmarkData = load_raw_audio_data(pBenchmarkFilePath, formatOut, &benchmarkFrameCount); + if (pBenchmarkData != NULL) { + if (benchmarkFrameCount == baseFrameCount) { + void* pConvertedData = (void*)mal_malloc(benchmarkFrameCount * mal_get_sample_size_in_bytes(formatOut)); + if (pConvertedData != NULL) { + onConvertPCM(pConvertedData, pBaseData, (mal_uint32)benchmarkFrameCount, mal_dither_mode_none); + result = mal_pcm_compare(pBenchmarkData, pConvertedData, benchmarkFrameCount, formatOut, allowedDifference); + if (result == 0) { + printf("PASSED.\n"); + } + } else { + printf("FAILED. Out of memory.\n"); + result = -3; + } + } else { + printf("FAILED. Frame count mismatch.\n"); + result = -2; + } + } else { + printf("FAILED."); + result = -1; + } + + + mal_free(pBaseData); + mal_free(pBenchmarkData); + return result; +} + +int do_format_conversion_tests_u8() +{ + int result = 0; + + printf("PCM u8 -> u8... "); + if (do_format_conversion_test(mal_format_u8, mal_format_u8) != 0) { + result = -1; + } + + printf("PCM u8 -> s16... "); + if (do_format_conversion_test(mal_format_u8, mal_format_s16) != 0) { + result = -1; + } + + printf("PCM u8 -> s24... "); + if (do_format_conversion_test(mal_format_u8, mal_format_s24) != 0) { + result = -1; + } + + printf("PCM u8 -> s32... "); + if (do_format_conversion_test(mal_format_u8, mal_format_s32) != 0) { + result = -1; + } + + printf("PCM u8 -> f32... "); + if (do_format_conversion_test(mal_format_u8, mal_format_f32) != 0) { + result = -1; + } + + return result; +} + +int do_format_conversion_tests_s16() +{ + int result = 0; + + printf("PCM s16 -> u8... "); + if (do_format_conversion_test(mal_format_s16, mal_format_u8) != 0) { + result = -1; + } + + printf("PCM s16 -> s16... "); + if (do_format_conversion_test(mal_format_s16, mal_format_s16) != 0) { + result = -1; + } + + printf("PCM s16 -> s24... "); + if (do_format_conversion_test(mal_format_s16, mal_format_s24) != 0) { + result = -1; + } + + printf("PCM s16 -> s32... "); + if (do_format_conversion_test(mal_format_s16, mal_format_s32) != 0) { + result = -1; + } + + printf("PCM s16 -> f32... "); + if (do_format_conversion_test(mal_format_s16, mal_format_f32) != 0) { + result = -1; + } + + return result; +} + +int do_format_conversion_tests_s24() +{ + int result = 0; + + printf("PCM s24 -> u8... "); + if (do_format_conversion_test(mal_format_s24, mal_format_u8) != 0) { + result = -1; + } + + printf("PCM s24 -> s16... "); + if (do_format_conversion_test(mal_format_s24, mal_format_s16) != 0) { + result = -1; + } + + printf("PCM s24 -> s24... "); + if (do_format_conversion_test(mal_format_s24, mal_format_s24) != 0) { + result = -1; + } + + printf("PCM s24 -> s32... "); + if (do_format_conversion_test(mal_format_s24, mal_format_s32) != 0) { + result = -1; + } + + printf("PCM s24 -> f32... "); + if (do_format_conversion_test(mal_format_s24, mal_format_f32) != 0) { + result = -1; + } + + return result; +} + +int do_format_conversion_tests_s32() +{ + int result = 0; + + printf("PCM s32 -> u8... "); + if (do_format_conversion_test(mal_format_s32, mal_format_u8) != 0) { + result = -1; + } + + printf("PCM s32 -> s16... "); + if (do_format_conversion_test(mal_format_s32, mal_format_s16) != 0) { + result = -1; + } + + printf("PCM s32 -> s24... "); + if (do_format_conversion_test(mal_format_s32, mal_format_s24) != 0) { + result = -1; + } + + printf("PCM s32 -> s32... "); + if (do_format_conversion_test(mal_format_s32, mal_format_s32) != 0) { + result = -1; + } + + printf("PCM s32 -> f32... "); + if (do_format_conversion_test(mal_format_s32, mal_format_f32) != 0) { + result = -1; + } + + return result; +} + +int do_format_conversion_tests_f32() +{ + int result = 0; + + printf("PCM f32 -> u8... "); + if (do_format_conversion_test(mal_format_f32, mal_format_u8) != 0) { + result = -1; + } + + printf("PCM f32 -> s16... "); + if (do_format_conversion_test(mal_format_f32, mal_format_s16) != 0) { + result = -1; + } + + printf("PCM f32 -> s24... "); + if (do_format_conversion_test(mal_format_f32, mal_format_s24) != 0) { + result = -1; + } + + printf("PCM f32 -> s32... "); + if (do_format_conversion_test(mal_format_f32, mal_format_s32) != 0) { + result = -1; + } + + printf("PCM f32 -> f32... "); + if (do_format_conversion_test(mal_format_f32, mal_format_f32) != 0) { + result = -1; + } + + return result; +} + +int do_format_conversion_tests() +{ + int result = 0; + + if (do_format_conversion_tests_u8() != 0) { + result = -1; + } + if (do_format_conversion_tests_s16() != 0) { + result = -1; + } + if (do_format_conversion_tests_s24() != 0) { + result = -1; + } + if (do_format_conversion_tests_s32() != 0) { + result = -1; + } + if (do_format_conversion_tests_f32() != 0) { + result = -1; + } + + return result; +} + + int do_backend_test(mal_backend backend) { mal_result result = MAL_SUCCESS; @@ -264,6 +877,15 @@ int main(int argc, char** argv) mal_bool32 hasErrorOccurred = MAL_FALSE; int result = 0; + + printf("=== TESTING FORMAT CONVERSION ===\n"); + result = do_format_conversion_tests(); + if (result < 0) { + hasErrorOccurred = MAL_TRUE; + } + printf("=== END TESTING FORMAT CONVERSION ===\n"); + + printf("\n"); printf("=== TESTING BACKENDS ===\n"); result = do_backend_tests(); diff --git a/tests/res/benchmarks/pcm_f32_to_f32__mono_8000.raw b/tests/res/benchmarks/pcm_f32_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd5ceeb79a44152c1da630b4104e9a4df23724f8 GIT binary patch literal 320 zcmXYry^6w65Jta8NV#Pidn>tBu81rl_%s%>2!c-}A|kQ5*CI#=BDb*E(!vKwvE>$? z*-bUf%=dF7Ib|wTZVElmg}(bj*UYOPd95a1?aZsJyt;)~#k{hwx$pTWbPf6*pTloC z+B%IDly8E5&OvP-bjzYnqp0O1D$Sy%Rn%kgKX2?S?nnN2bPam*nctCPc{#Z``M>nk z^gZa^(Qh0bJaf1bd^U~O?g##NbnU=re)q>$>x10!z;~f9(R-l(3yuz+IeZDePiD)$ Q=Dr{J68aZ>C4MQq0S(1&BLDyZ literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_f32_to_s16__mono_8000.raw b/tests/res/benchmarks/pcm_f32_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..9a918a5354d79442052db00b459b7feced48a2f6 GIT binary patch literal 160 zcmV;R0AK$B00I!HA9yY{JYz`QQaf8@VI*Z`W+i1~VLe;eQfNpqJbW&v9|8~p0Q&D- z;Dgc5!3!BL_w+LO F`UhfTOxyqf literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_f32_to_s32__mono_8000.raw b/tests/res/benchmarks/pcm_f32_to_s32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd29023573451ff42f920a2314aec94cf168e040 GIT binary patch literal 320 zcmXYrze+*@6o}NV_n};Hv#UGxrDY>;Bv7p4Eph{4;YRZ_(VI z`RDeO?K`n|Y=3Yh@~p_!k}qcP#{JFzvAV6CFZ@g9{QSb)+(yp#Vqa?ShW+<)T+8z* XUn<{+v*SMSUds8N)L+V1*U$712bE0R literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_f32_to_u8__mono_8000.raw b/tests/res/benchmarks/pcm_f32_to_u8__mono_8000.raw new file mode 100644 index 00000000..7babb8eb --- /dev/null +++ b/tests/res/benchmarks/pcm_f32_to_u8__mono_8000.raw @@ -0,0 +1 @@ +€گں®¼بزغلهوهلغزب¼®ںگ€o`QC7-$$-7CQ`o€گں®¼بزغلهوهلغزب¼®ںگo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/tests/res/benchmarks/pcm_s16_to_f32__mono_8000.raw b/tests/res/benchmarks/pcm_s16_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..7ddfa62ca40378ece49bf9ef6796f04b480cff9e GIT binary patch literal 320 zcmX|*v1$TA6h&`oxuriK>=y*Fmguhc4K?$HzWpWHpZ z1O5xSKKbxRTB@yF@if;p%e8KD9U`aqj^m$Gl{h5>r!sZwGk542`w#9OUyc7v?vZ@V zA8r5mV*WSuc=}3uL;o0#Z+Loe)$k?E0zGHH;C>7~{H>MI_04GX%a{H0-RN1;ccgbg ef8Z$K30xI?PfU+~WgqxUd=>r+xgmVbee(kj!Ee+6 literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s16_to_s16__mono_8000.raw b/tests/res/benchmarks/pcm_s16_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..574838c35bd20086f1ce80d01c3061e98af5de09 GIT binary patch literal 160 zcmV;R0AK$B00I!IA9gM`JYz`RQaW2?VIyT`W+r7~VLV&fQe;RqJb5mu9|92n|NZY< z;DXW7!>Y!-&yX;QQ|b00I!DAABw{JZDJSQaM{` zVI5^`W+7!`VL)5eQfo*pJc2Ht9}5ut|NZY<;DOQ7!~F1-`6IS5*E(JzpAhn>qc6x@8X zyx<@$Z{QM@_YHKfFy5dK@ce%d1vn`2Y;kRKCg?bPxLioO9u=P;;MnKVLrC6Sx(=s; z8=EJ~toD0;w#b~JMk^*6)s$tzG-eYKhP+42V-^V~DYJ}~VytQBd~G@Wa~~~kZ5o1} zWBTWkzQ>WzRY2LN-5QzISI&J#-)VV|~y1PtEPim-zYx;L%Rn literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s16_to_u8__mono_8000.raw b/tests/res/benchmarks/pcm_s16_to_u8__mono_8000.raw new file mode 100644 index 00000000..fa7bb23a --- /dev/null +++ b/tests/res/benchmarks/pcm_s16_to_u8__mono_8000.raw @@ -0,0 +1 @@ +€گں®¼بزغلهوهلغزب¼®ںگo`QC7-$$-7CQ`o€گں®¼بزغلهوهلغزب¼®ںگo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/tests/res/benchmarks/pcm_s24_to_f32__mono_8000.raw b/tests/res/benchmarks/pcm_s24_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd5ceeb79a44152c1da630b4104e9a4df23724f8 GIT binary patch literal 320 zcmXYry^6w65Jta8NV#Pidn>tBu81rl_%s%>2!c-}A|kQ5*CI#=BDb*E(!vKwvE>$? z*-bUf%=dF7Ib|wTZVElmg}(bj*UYOPd95a1?aZsJyt;)~#k{hwx$pTWbPf6*pTloC z+B%IDly8E5&OvP-bjzYnqp0O1D$Sy%Rn%kgKX2?S?nnN2bPam*nctCPc{#Z``M>nk z^gZa^(Qh0bJaf1bd^U~O?g##NbnU=re)q>$>x10!z;~f9(R-l(3yuz+IeZDePiD)$ Q=Dr{J68aZ>C4MQq0S(1&BLDyZ literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s24_to_s16__mono_8000.raw b/tests/res/benchmarks/pcm_s24_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..9a918a5354d79442052db00b459b7feced48a2f6 GIT binary patch literal 160 zcmV;R0AK$B00I!HA9yY{JYz`QQaf8@VI*Z`W+i1~VLe;eQfNpqJbW&v9|8~p0Q&D- z;Dgc5!3!BL_w+LO F`UhfTOxyqf literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s24_to_s32__mono_8000.raw b/tests/res/benchmarks/pcm_s24_to_s32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd29023573451ff42f920a2314aec94cf168e040 GIT binary patch literal 320 zcmXYrze+*@6o}NV_n};Hv#UGxrDY>;Bv7p4Eph{4;YRZ_(VI z`RDeO?K`n|Y=3Yh@~p_!k}qcP#{JFzvAV6CFZ@g9{QSb)+(yp#Vqa?ShW+<)T+8z* XUn<{+v*SMSUds8N)L+V1*U$712bE0R literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s24_to_u8__mono_8000.raw b/tests/res/benchmarks/pcm_s24_to_u8__mono_8000.raw new file mode 100644 index 00000000..7babb8eb --- /dev/null +++ b/tests/res/benchmarks/pcm_s24_to_u8__mono_8000.raw @@ -0,0 +1 @@ +€گں®¼بزغلهوهلغزب¼®ںگ€o`QC7-$$-7CQ`o€گں®¼بزغلهوهلغزب¼®ںگo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/tests/res/benchmarks/pcm_s32_to_f32__mono_8000.raw b/tests/res/benchmarks/pcm_s32_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd5ceeb79a44152c1da630b4104e9a4df23724f8 GIT binary patch literal 320 zcmXYry^6w65Jta8NV#Pidn>tBu81rl_%s%>2!c-}A|kQ5*CI#=BDb*E(!vKwvE>$? z*-bUf%=dF7Ib|wTZVElmg}(bj*UYOPd95a1?aZsJyt;)~#k{hwx$pTWbPf6*pTloC z+B%IDly8E5&OvP-bjzYnqp0O1D$Sy%Rn%kgKX2?S?nnN2bPam*nctCPc{#Z``M>nk z^gZa^(Qh0bJaf1bd^U~O?g##NbnU=re)q>$>x10!z;~f9(R-l(3yuz+IeZDePiD)$ Q=Dr{J68aZ>C4MQq0S(1&BLDyZ literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s32_to_s16__mono_8000.raw b/tests/res/benchmarks/pcm_s32_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..9a918a5354d79442052db00b459b7feced48a2f6 GIT binary patch literal 160 zcmV;R0AK$B00I!HA9yY{JYz`QQaf8@VI*Z`W+i1~VLe;eQfNpqJbW&v9|8~p0Q&D- z;Dgc5!3!BL_w+LO F`UhfTOxyqf literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s32_to_s32__mono_8000.raw b/tests/res/benchmarks/pcm_s32_to_s32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..dd29023573451ff42f920a2314aec94cf168e040 GIT binary patch literal 320 zcmXYrze+*@6o}NV_n};Hv#UGxrDY>;Bv7p4Eph{4;YRZ_(VI z`RDeO?K`n|Y=3Yh@~p_!k}qcP#{JFzvAV6CFZ@g9{QSb)+(yp#Vqa?ShW+<)T+8z* XUn<{+v*SMSUds8N)L+V1*U$712bE0R literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_s32_to_u8__mono_8000.raw b/tests/res/benchmarks/pcm_s32_to_u8__mono_8000.raw new file mode 100644 index 00000000..7babb8eb --- /dev/null +++ b/tests/res/benchmarks/pcm_s32_to_u8__mono_8000.raw @@ -0,0 +1 @@ +€گں®¼بزغلهوهلغزب¼®ںگ€o`QC7-$$-7CQ`o€گں®¼بزغلهوهلغزب¼®ںگo`QC7-$$-7CQ`p \ No newline at end of file diff --git a/tests/res/benchmarks/pcm_u8_to_f32__mono_8000.raw b/tests/res/benchmarks/pcm_u8_to_f32__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..a39db0eb08eabd44ba7ac0e70f9a26a3e64421ed GIT binary patch literal 320 zcmd7My9t0W6ouhaT8cA>BZyT5v6A3CmX2i+;z||{;Q2#_@WRLCzCxrDK|1WR$J$xy zZM4NUi#1czXU-n`+;J}?smao5$zJPjX|Tywi?-KHO`kb?>~qIG@01At{Gaj%Hh)2J literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_u8_to_s16__mono_8000.raw b/tests/res/benchmarks/pcm_u8_to_s16__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..293a723e42f1c6f7c5f154625a72ef8292fc0b59 GIT binary patch literal 160 zcmZQzU=U!CXV7D?VenuGVu)r)WJqO51L8y=I|wLd1C)~os`=0Gp5Xz*MTWx++Zom} dEMb_(FpFU(5YGd$*8;^31LYn7)lk>G4*={pC)xl2 literal 0 HcmV?d00001 diff --git a/tests/res/benchmarks/pcm_u8_to_s24__mono_8000.raw b/tests/res/benchmarks/pcm_u8_to_s24__mono_8000.raw new file mode 100644 index 0000000000000000000000000000000000000000..5d321211f2cebfd2985c86954f14d62f8998f677 GIT binary patch literal 240 zcmci5F%iN55Jb@@0S7oh0RmE|rJ@L)ph%UL8UZN40SW