Commit bcbc7ad4 authored by David Reid's avatar David Reid

Early work on improving format conversion.

parent afa279a2
......@@ -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,230 +15273,1856 @@ void mal_get_standard_channel_map(mal_standard_channel_map standardChannelMap, m
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// SRC
// Format Conversion.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mal_src_cache_init(mal_src* pSRC, mal_src_cache* pCache)
{
mal_assert(pSRC != NULL);
mal_assert(pCache != NULL);
//#define MAL_USE_REFERENCE_CONVERSION_APIS 1
#define MAL_USE_SSE
pCache->pSRC = pSRC;
pCache->cachedFrameCount = 0;
pCache->iNextFrame = 0;
// 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));
}
mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCount, float* pFramesOut)
void mal_pcm_u8_to_s16__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
mal_assert(pCache != NULL);
mal_assert(pCache->pSRC != NULL);
mal_assert(pCache->pSRC->onRead != NULL);
mal_assert(frameCount > 0);
mal_assert(pFramesOut != NULL);
(void)ditherMode;
mal_uint32 channels = pCache->pSRC->config.channels;
mal_int16* dst_s16 = (mal_int16*)dst;
const mal_uint8* src_u8 = (const mal_uint8*)src;
mal_uint32 totalFramesRead = 0;
while (frameCount > 0) {
// If there's anything in memory go ahead and copy that over first.
mal_uint32 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame;
mal_uint32 framesToReadFromMemory = frameCount;
if (framesToReadFromMemory > framesRemainingInMemory) {
framesToReadFromMemory = framesRemainingInMemory;
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;
}
}
mal_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, framesToReadFromMemory * channels * sizeof(float));
pCache->iNextFrame += framesToReadFromMemory;
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);
}
totalFramesRead += framesToReadFromMemory;
frameCount -= framesToReadFromMemory;
if (frameCount == 0) {
break;
}
#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
}
// At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data.
mal_assert(frameCount > 0);
pFramesOut += framesToReadFromMemory * channels;
pCache->iNextFrame = 0;
pCache->cachedFrameCount = 0;
if (pCache->pSRC->config.formatIn == mal_format_f32) {
// No need for a conversion - read straight into the cache.
mal_uint32 framesToReadFromClient = mal_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) {
framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames;
}
void mal_pcm_u8_to_s24__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
(void)ditherMode;
pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData);
} else {
// A format conversion is required which means we need to use an intermediary buffer.
mal_uint8 pIntermediaryBuffer[sizeof(pCache->pCachedFrames)];
mal_uint32 framesToReadFromClient = mal_min(mal_buffer_frame_capacity(pIntermediaryBuffer, channels, pCache->pSRC->config.formatIn), mal_buffer_frame_capacity(pCache->pCachedFrames, channels, mal_format_f32));
if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) {
framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames;
}
mal_uint8* dst_s24 = (mal_uint8*)dst;
const mal_uint8* src_u8 = (const mal_uint8*)src;
pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pIntermediaryBuffer, pCache->pSRC->pUserData);
mal_uint64 i;
for (i = 0; i < count; i += 1) {
mal_int16 x = src_u8[i];
x = x - 128;
// Convert to f32.
mal_pcm_convert(pCache->pCachedFrames, mal_format_f32, pIntermediaryBuffer, pCache->pSRC->config.formatIn, pCache->cachedFrameCount * channels);
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);
}
// Get out of this loop if nothing was able to be retrieved.
if (pCache->cachedFrameCount == 0) {
break;
}
}
return totalFramesRead;
#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
}
mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC)
void mal_pcm_u8_to_s32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
if (pSRC == NULL) return MAL_INVALID_ARGS;
mal_zero_object(pSRC);
if (pConfig == NULL || onRead == NULL) return MAL_INVALID_ARGS;
if (pConfig->channels == 0 || pConfig->channels > MAL_MAX_CHANNELS) return MAL_INVALID_ARGS;
(void)ditherMode;
pSRC->config = *pConfig;
pSRC->onRead = onRead;
pSRC->pUserData = pUserData;
mal_int32* dst_s32 = (mal_int32*)dst;
const mal_uint8* src_u8 = (const mal_uint8*)src;
if (pSRC->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) {
pSRC->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES;
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;
}
mal_src_cache_init(pSRC, &pSRC->cache);
return MAL_SUCCESS;
}
mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn)
void mal_pcm_u8_to_s32__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
if (pSRC == NULL) return MAL_INVALID_ARGS;
mal_pcm_u8_to_s32__reference(dst, src, count, ditherMode);
}
// Must have a sample rate of > 0.
if (sampleRateIn == 0) {
return MAL_INVALID_ARGS;
}
#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
pSRC->config.sampleRateIn = sampleRateIn;
return MAL_SUCCESS;
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
}
mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut)
void mal_pcm_u8_to_f32__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
if (pSRC == NULL) return MAL_INVALID_ARGS;
(void)ditherMode;
// Must have a sample rate of > 0.
if (sampleRateOut == 0) {
return MAL_INVALID_ARGS;
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;
}
}
pSRC->config.sampleRateOut = sampleRateOut;
return MAL_SUCCESS;
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);
}
mal_uint64 mal_src_read_frames(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut)
#ifdef MAL_USE_SSE
void mal_pcm_u8_to_f32__sse(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE);
mal_pcm_u8_to_f32__optimized(dst, src, count, ditherMode);
}
#endif
mal_uint64 mal_src_read_frames_ex(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
void mal_pcm_u8_to_f32(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0;
#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
}
mal_src_algorithm algorithm = pSRC->config.algorithm;
// Always use passthrough if the sample rates are the same.
if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) {
algorithm = mal_src_algorithm_none;
}
// Could just use a function pointer instead of a switch for this...
switch (algorithm)
{
case mal_src_algorithm_none: return mal_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush);
case mal_src_algorithm_linear: return mal_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush);
default: return 0;
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];
}
}
}
mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
void mal_pcm_interleave_u8__optimized(void* dst, const void** src, mal_uint64 frameCount, mal_uint32 channels)
{
mal_assert(pSRC != NULL);
mal_assert(frameCount > 0);
mal_assert(pFramesOut != NULL);
(void)flush; // Passthrough need not care about flushing.
mal_uint8* dst_u8 = (mal_uint8*)dst;
const mal_uint8** src_u8 = (const mal_uint8**)src;
// Fast path. No need for data conversion - just pass right through.
if (pSRC->config.formatIn == pSRC->config.formatOut) {
if (frameCount <= UINT32_MAX) {
return pSRC->onRead(pSRC, (mal_uint32)frameCount, pFramesOut, pSRC->pUserData);
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 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToReadRightNow = UINT32_MAX;
if (framesToReadRightNow > frameCount) {
framesToReadRightNow = (mal_uint32)frameCount;
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];
}
mal_uint32 framesRead = pSRC->onRead(pSRC, framesToReadRightNow, pFramesOut, pSRC->pUserData);
if (framesRead == 0) {
break;
}
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
}
}
return totalFramesRead;
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];
}
}
}
// Slower path. Need to do a format conversion.
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048];
mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_sample_size_in_bytes(pSRC->config.formatIn) / pSRC->config.channels;
mal_uint32 framesToRead = stagingBufferSizeInFrames;
if (framesToRead > frameCount) {
framesToRead = (mal_uint32)frameCount;
}
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);
}
mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData);
if (framesRead == 0) {
break;
}
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
}
mal_pcm_convert(pFramesOut, pSRC->config.formatOut, pStagingBuffer, pSRC->config.formatIn, framesRead * pSRC->config.channels);
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
}
// s16
void mal_pcm_s16_to_u8__reference(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
(void)ditherMode;
return totalFramesRead;
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;
}
}
mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
void mal_pcm_s16_to_u8__optimized(void* dst, const void* src, mal_uint64 count, mal_dither_mode ditherMode)
{
mal_assert(pSRC != NULL);
mal_assert(frameCount > 0);
mal_assert(pFramesOut != NULL);
mal_pcm_s16_to_u8__reference(dst, src, count, ditherMode);
}
// For linear SRC, the bin is only 2 frames: 1 prior, 1 future.
#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
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void mal_src_cache_init(mal_src* pSRC, mal_src_cache* pCache)
{
mal_assert(pSRC != NULL);
mal_assert(pCache != NULL);
pCache->pSRC = pSRC;
pCache->cachedFrameCount = 0;
pCache->iNextFrame = 0;
}
mal_uint32 mal_src_cache_read_frames(mal_src_cache* pCache, mal_uint32 frameCount, float* pFramesOut)
{
mal_assert(pCache != NULL);
mal_assert(pCache->pSRC != NULL);
mal_assert(pCache->pSRC->onRead != NULL);
mal_assert(frameCount > 0);
mal_assert(pFramesOut != NULL);
mal_uint32 channels = pCache->pSRC->config.channels;
mal_uint32 totalFramesRead = 0;
while (frameCount > 0) {
// If there's anything in memory go ahead and copy that over first.
mal_uint32 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame;
mal_uint32 framesToReadFromMemory = frameCount;
if (framesToReadFromMemory > framesRemainingInMemory) {
framesToReadFromMemory = framesRemainingInMemory;
}
mal_copy_memory(pFramesOut, pCache->pCachedFrames + pCache->iNextFrame*channels, framesToReadFromMemory * channels * sizeof(float));
pCache->iNextFrame += framesToReadFromMemory;
totalFramesRead += framesToReadFromMemory;
frameCount -= framesToReadFromMemory;
if (frameCount == 0) {
break;
}
// At this point there are still more frames to read from the client, so we'll need to reload the cache with fresh data.
mal_assert(frameCount > 0);
pFramesOut += framesToReadFromMemory * channels;
pCache->iNextFrame = 0;
pCache->cachedFrameCount = 0;
if (pCache->pSRC->config.formatIn == mal_format_f32) {
// No need for a conversion - read straight into the cache.
mal_uint32 framesToReadFromClient = mal_countof(pCache->pCachedFrames) / pCache->pSRC->config.channels;
if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) {
framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames;
}
pCache->cachedFrameCount = pCache->pSRC->onRead(pCache->pSRC, framesToReadFromClient, pCache->pCachedFrames, pCache->pSRC->pUserData);
} else {
// A format conversion is required which means we need to use an intermediary buffer.
mal_uint8 pIntermediaryBuffer[sizeof(pCache->pCachedFrames)];
mal_uint32 framesToReadFromClient = mal_min(mal_buffer_frame_capacity(pIntermediaryBuffer, channels, pCache->pSRC->config.formatIn), mal_buffer_frame_capacity(pCache->pCachedFrames, channels, mal_format_f32));
if (framesToReadFromClient > pCache->pSRC->config.cacheSizeInFrames) {
framesToReadFromClient = pCache->pSRC->config.cacheSizeInFrames;
}
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_dither_mode_none);
}
// Get out of this loop if nothing was able to be retrieved.
if (pCache->cachedFrameCount == 0) {
break;
}
}
return totalFramesRead;
}
mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush);
mal_result mal_src_init(mal_src_config* pConfig, mal_src_read_proc onRead, void* pUserData, mal_src* pSRC)
{
if (pSRC == NULL) return MAL_INVALID_ARGS;
mal_zero_object(pSRC);
if (pConfig == NULL || onRead == NULL) return MAL_INVALID_ARGS;
if (pConfig->channels == 0 || pConfig->channels > MAL_MAX_CHANNELS) return MAL_INVALID_ARGS;
pSRC->config = *pConfig;
pSRC->onRead = onRead;
pSRC->pUserData = pUserData;
if (pSRC->config.cacheSizeInFrames > MAL_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) {
pSRC->config.cacheSizeInFrames = MAL_SRC_CACHE_SIZE_IN_FRAMES;
}
mal_src_cache_init(pSRC, &pSRC->cache);
return MAL_SUCCESS;
}
mal_result mal_src_set_input_sample_rate(mal_src* pSRC, mal_uint32 sampleRateIn)
{
if (pSRC == NULL) return MAL_INVALID_ARGS;
// Must have a sample rate of > 0.
if (sampleRateIn == 0) {
return MAL_INVALID_ARGS;
}
pSRC->config.sampleRateIn = sampleRateIn;
return MAL_SUCCESS;
}
mal_result mal_src_set_output_sample_rate(mal_src* pSRC, mal_uint32 sampleRateOut)
{
if (pSRC == NULL) return MAL_INVALID_ARGS;
// Must have a sample rate of > 0.
if (sampleRateOut == 0) {
return MAL_INVALID_ARGS;
}
pSRC->config.sampleRateOut = sampleRateOut;
return MAL_SUCCESS;
}
mal_uint64 mal_src_read_frames(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut)
{
return mal_src_read_frames_ex(pSRC, frameCount, pFramesOut, MAL_FALSE);
}
mal_uint64 mal_src_read_frames_ex(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{
if (pSRC == NULL || frameCount == 0 || pFramesOut == NULL) return 0;
mal_src_algorithm algorithm = pSRC->config.algorithm;
// Always use passthrough if the sample rates are the same.
if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) {
algorithm = mal_src_algorithm_none;
}
// Could just use a function pointer instead of a switch for this...
switch (algorithm)
{
case mal_src_algorithm_none: return mal_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush);
case mal_src_algorithm_linear: return mal_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush);
default: return 0;
}
}
mal_uint64 mal_src_read_frames_passthrough(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{
mal_assert(pSRC != NULL);
mal_assert(frameCount > 0);
mal_assert(pFramesOut != NULL);
(void)flush; // Passthrough need not care about flushing.
// Fast path. No need for data conversion - just pass right through.
if (pSRC->config.formatIn == pSRC->config.formatOut) {
if (frameCount <= UINT32_MAX) {
return pSRC->onRead(pSRC, (mal_uint32)frameCount, pFramesOut, pSRC->pUserData);
} else {
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint32 framesToReadRightNow = UINT32_MAX;
if (framesToReadRightNow > frameCount) {
framesToReadRightNow = (mal_uint32)frameCount;
}
mal_uint32 framesRead = pSRC->onRead(pSRC, framesToReadRightNow, pFramesOut, pSRC->pUserData);
if (framesRead == 0) {
break;
}
pFramesOut = (mal_uint8*)pFramesOut + (framesRead * pSRC->config.channels * mal_get_sample_size_in_bytes(pSRC->config.formatOut));
frameCount -= framesRead;
totalFramesRead += framesRead;
}
return totalFramesRead;
}
}
// Slower path. Need to do a format conversion.
mal_uint64 totalFramesRead = 0;
while (frameCount > 0) {
mal_uint8 pStagingBuffer[MAL_MAX_CHANNELS * 2048];
mal_uint32 stagingBufferSizeInFrames = sizeof(pStagingBuffer) / mal_get_sample_size_in_bytes(pSRC->config.formatIn) / pSRC->config.channels;
mal_uint32 framesToRead = stagingBufferSizeInFrames;
if (framesToRead > frameCount) {
framesToRead = (mal_uint32)frameCount;
}
mal_uint32 framesRead = pSRC->onRead(pSRC, framesToRead, pStagingBuffer, pSRC->pUserData);
if (framesRead == 0) {
break;
}
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;
totalFramesRead += framesRead;
}
return totalFramesRead;
}
mal_uint64 mal_src_read_frames_linear(mal_src* pSRC, mal_uint64 frameCount, void* pFramesOut, mal_bool32 flush)
{
mal_assert(pSRC != NULL);
mal_assert(frameCount > 0);
mal_assert(pFramesOut != NULL);
// For linear SRC, the bin is only 2 frames: 1 prior, 1 future.
// Load the bin if necessary.
if (!pSRC->linear.isPrevFramesLoaded) {
......@@ -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
......
......@@ -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;
......@@ -265,6 +878,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();
if (result < 0) {
......
ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p
\ No newline at end of file
ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p
\ No newline at end of file
ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p
\ No newline at end of file
ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p
\ No newline at end of file
ȼo`QC7-$$-7CQ`oȼo`QC7-$$-7CQ`p
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment