Commit 79cbe52e authored by David Reid's avatar David Reid

Merge branch 'dev' of https://github.com/dr-soft/mini_al into dev

parents ac0e5592 54ee4879
// Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file. // Audio playback and capture library. Public domain. See "unlicense" statement at the end of this file.
// mini_al - v0.8.12 - 2018-11-27 // mini_al - v0.8.13 - 2018-12-04
// //
// David Reid - davidreidsoftware@gmail.com // David Reid - davidreidsoftware@gmail.com
...@@ -24688,8 +24688,8 @@ float g_malChannelPlaneRatios[MAL_CHANNEL_POSITION_COUNT][6] = { ...@@ -24688,8 +24688,8 @@ float g_malChannelPlaneRatios[MAL_CHANNEL_POSITION_COUNT][6] = {
{ 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT { 0.0f, 0.5f, 0.5f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT
{ 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_CENTER { 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_CENTER
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_LFE { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_LFE
{ 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_LEFT { 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_LEFT
{ 0.0f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT { 0.0f, 0.5f, 0.0f, 0.5f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_RIGHT
{ 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_LEFT_CENTER { 0.25f, 0.0f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_LEFT_CENTER
{ 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT_CENTER { 0.0f, 0.25f, 0.75f, 0.0f, 0.0f, 0.0f}, // MAL_CHANNEL_FRONT_RIGHT_CENTER
{ 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_CENTER { 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f}, // MAL_CHANNEL_BACK_CENTER
...@@ -28485,6 +28485,10 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount ...@@ -28485,6 +28485,10 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
// REVISION HISTORY // REVISION HISTORY
// ================ // ================
// //
// v0.8.13 - 2018-12-04
// - Core Audio: Fix a bug with channel mapping.
// - Fix a bug with channel routing where the back/left and back/right channels have the wrong weight.
//
// v0.8.12 - 2018-11-27 // v0.8.12 - 2018-11-27
// - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2". // - Drop support for SDL 1.2. The Emscripten build now requires "-s USE_SDL=2".
// - Fix a linking error with ALSA. // - Fix a linking error with ALSA.
...@@ -25,7 +25,6 @@ Requirements: ...@@ -25,7 +25,6 @@ Requirements:
- Must have different modes on how to handle the last of the input samples. Certain situations (streaming) requires - Must have different modes on how to handle the last of the input samples. Certain situations (streaming) requires
the last input samples to be cached in the internal structure for the windowing algorithm. Other situations require the last input samples to be cached in the internal structure for the windowing algorithm. Other situations require
all of the input samples to be consumed in order to output the correct total sample count. all of the input samples to be consumed in order to output the correct total sample count.
- Pointers passed into the onRead() callback must be guaranteed to be aligned to MAL_SIMD_ALIGNMENT.
Other Notes: Other Notes:
...@@ -38,8 +37,9 @@ Other Notes: ...@@ -38,8 +37,9 @@ Other Notes:
Random Notes: Random Notes:
- You cannot change the algorithm after initialization. - You cannot change the algorithm after initialization.
- It is recommended to keep the mal_resampler object aligned to MAL_SIMD_ALIGNMENT, though it is not necessary. - It is recommended to keep the mal_resampler object aligned to MAL_SIMD_ALIGNMENT, though it is not necessary.
- Ratios need to be in the range of MAL_RESAMPLER_MIN_RATIO and MAL_RESAMPLER_MAX_RATIO. If you need extreme ratios - Ratios need to be in the range of MAL_RESAMPLER_MIN_RATIO and MAL_RESAMPLER_MAX_RATIO. This is enough to convert
then you will need to chain resamplers together. to and from 8000 and 384000, which is the smallest and largest standard rates supported by mini_al. If you need
extreme ratios then you will need to chain resamplers together.
*/ */
#ifndef mal_resampler_h #ifndef mal_resampler_h
#define mal_resampler_h #define mal_resampler_h
...@@ -47,7 +47,9 @@ Random Notes: ...@@ -47,7 +47,9 @@ Random Notes:
#define MAL_RESAMPLER_SEEK_NO_CLIENT_READ (1 << 0) /* When set, does not read anything from the client when seeking. This does _not_ call onRead(). */ #define MAL_RESAMPLER_SEEK_NO_CLIENT_READ (1 << 0) /* When set, does not read anything from the client when seeking. This does _not_ call onRead(). */
#define MAL_RESAMPLER_SEEK_INPUT_RATE (1 << 1) /* When set, treats the specified frame count based on the input sample rate rather than the output sample rate. */ #define MAL_RESAMPLER_SEEK_INPUT_RATE (1 << 1) /* When set, treats the specified frame count based on the input sample rate rather than the output sample rate. */
#ifndef MAL_RESAMPLER_CACHE_SIZE_IN_BYTES
#define MAL_RESAMPLER_CACHE_SIZE_IN_BYTES 4096 #define MAL_RESAMPLER_CACHE_SIZE_IN_BYTES 4096
#endif
typedef struct mal_resampler mal_resampler; typedef struct mal_resampler mal_resampler;
...@@ -55,9 +57,10 @@ typedef struct mal_resampler mal_resampler; ...@@ -55,9 +57,10 @@ typedef struct mal_resampler mal_resampler;
typedef mal_uint32 (* mal_resampler_read_from_client_proc)(mal_resampler* pResampler, mal_uint32 frameCount, void** ppFrames); typedef mal_uint32 (* mal_resampler_read_from_client_proc)(mal_resampler* pResampler, mal_uint32 frameCount, void** ppFrames);
/* Backend functions. */ /* Backend functions. */
typedef mal_result (* mal_resampler_init_proc)(mal_resampler* pResampler); typedef mal_result (* mal_resampler_init_proc) (mal_resampler* pResampler);
typedef mal_uint64 (* mal_resampler_read_proc)(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames); typedef mal_uint64 (* mal_resampler_read_f32_proc)(mal_resampler* pResampler, mal_uint64 frameCount, float** ppFrames);
typedef mal_uint64 (* mal_resampler_seek_proc)(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); typedef mal_uint64 (* mal_resampler_read_s16_proc)(mal_resampler* pResampler, mal_uint64 frameCount, mal_int16** ppFrames);
typedef mal_uint64 (* mal_resampler_seek_proc) (mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
typedef enum typedef enum
{ {
...@@ -90,14 +93,15 @@ struct mal_resampler ...@@ -90,14 +93,15 @@ struct mal_resampler
{ {
float f32[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(float)]; float f32[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(float)];
mal_int16 s16[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(mal_int16)]; mal_int16 s16[MAL_RESAMPLER_CACHE_SIZE_IN_BYTES/sizeof(mal_int16)];
} cache; /* Do not use directly. Keep this as the first member of this structure for SIMD alignment purposes. */ } cache; /* Keep this as the first member of this structure for SIMD alignment purposes. */
mal_uint16 firstCachedFrameOffset; mal_uint32 cacheStrideInFrames; /* The number of the samples between channels in the cache. The first sample for channel 0 is cacheStrideInFrames*0. The first sample for channel 1 is cacheStrideInFrames*1, etc. */
mal_uint16 cacheLengthInFrames; /* The number of valid frames sitting in the cache. May be less than the cache's capacity. */ mal_uint16 cacheLengthInFrames; /* The number of valid frames sitting in the cache, including the filter window. May be less than the cache's capacity. */
mal_uint16 windowLength; mal_uint16 windowLength;
double windowTime; /* By input rate. Relative to the start of the cache. */ double windowTime; /* By input rate. Relative to the start of the cache. */
mal_resampler_config config; mal_resampler_config config;
mal_resampler_init_proc init; mal_resampler_init_proc init;
mal_resampler_read_proc read; mal_resampler_read_f32_proc readF32;
mal_resampler_read_s16_proc readS16;
mal_resampler_seek_proc seek; mal_resampler_seek_proc seek;
}; };
...@@ -118,6 +122,8 @@ mal_result mal_resampler_set_rate(mal_resampler* pResampler, mal_uint32 sampleRa ...@@ -118,6 +122,8 @@ mal_result mal_resampler_set_rate(mal_resampler* pResampler, mal_uint32 sampleRa
/* /*
Dynamically adjusts the sample rate by a ratio. Dynamically adjusts the sample rate by a ratio.
The ratio is in/out.
*/ */
mal_result mal_resampler_set_rate_ratio(mal_resampler* pResampler, double ratio); mal_result mal_resampler_set_rate_ratio(mal_resampler* pResampler, double ratio);
...@@ -159,6 +165,10 @@ of time in input rate making up the cached input. ...@@ -159,6 +165,10 @@ of time in input rate making up the cached input.
When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the
window are not included in the calculation. window are not included in the calculation.
This can return a negative value if nothing has yet been loaded into the internal cache. This will happen if this is called
immediately after initialization, before the first read has been performed. It may also happen if only a few samples have
been read from the client.
*/ */
double mal_resampler_get_cached_input_time(mal_resampler* pResampler); double mal_resampler_get_cached_input_time(mal_resampler* pResampler);
...@@ -168,6 +178,8 @@ of time in output rate making up the cached output. ...@@ -168,6 +178,8 @@ of time in output rate making up the cached output.
When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the When the end of input mode is set to mal_resampler_end_of_input_mode_no_consume, the input frames currently sitting in the
window are not included in the calculation. window are not included in the calculation.
This can return a negative value. See mal_resampler_get_cached_input_time() for details.
*/ */
double mal_resampler_get_cached_output_time(mal_resampler* pResampler); double mal_resampler_get_cached_output_time(mal_resampler* pResampler);
...@@ -202,20 +214,33 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl ...@@ -202,20 +214,33 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl
#ifdef MINI_AL_IMPLEMENTATION #ifdef MINI_AL_IMPLEMENTATION
#ifndef MAL_RESAMPLER_MIN_RATIO #ifndef MAL_RESAMPLER_MIN_RATIO
#define MAL_RESAMPLER_MIN_RATIO 0.001 #define MAL_RESAMPLER_MIN_RATIO 0.02083333
#endif #endif
#ifndef MAL_RESAMPLER_MAX_RATIO #ifndef MAL_RESAMPLER_MAX_RATIO
#define MAL_RESAMPLER_MAX_RATIO 100.0 #define MAL_RESAMPLER_MAX_RATIO 48.0
#endif #endif
mal_uint64 mal_resampler_read__linear(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames); mal_result mal_resampler_init__linear(mal_resampler* pResampler);
mal_uint64 mal_resampler_read_f32__linear(mal_resampler* pResampler, mal_uint64 frameCount, float** ppFrames);
mal_uint64 mal_resampler_read_s16__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_int16** ppFrames);
mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); mal_uint64 mal_resampler_seek__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
mal_result mal_resampler_init__sinc(mal_resampler* pResampler); mal_result mal_resampler_init__sinc(mal_resampler* pResampler);
mal_uint64 mal_resampler_read__sinc(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames); mal_uint64 mal_resampler_read_f32__sinc(mal_resampler* pResampler, mal_uint64 frameCount, float** ppFrames);
mal_uint64 mal_resampler_read_s16__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_int16** ppFrames);
mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options); mal_uint64 mal_resampler_seek__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options);
/* TODO: Add this to mini_al.h */ static MAL_INLINE float mal_fractional_part_f32(float x)
{
return x - ((mal_int32)x);
}
static MAL_INLINE double mal_fractional_part_f64(double x)
{
return x - ((mal_int64)x);
}
#if 0
#define MAL_ALIGN_INT(val, alignment) (((val) + ((alignment)-1)) & ~((alignment)-1)) #define MAL_ALIGN_INT(val, alignment) (((val) + ((alignment)-1)) & ~((alignment)-1))
#define MAL_ALIGN_PTR(ptr, alignment) (void*)MAL_ALIGN_INT(((mal_uintptr)(ptr)), (alignment)) #define MAL_ALIGN_PTR(ptr, alignment) (void*)MAL_ALIGN_INT(((mal_uintptr)(ptr)), (alignment))
...@@ -237,7 +262,21 @@ This does not work for formats that do not have a clean mapping to a primitive C ...@@ -237,7 +262,21 @@ This does not work for formats that do not have a clean mapping to a primitive C
name[iChannel] = (type*)((mal_uint8*)MAL_ALIGN_PTR(name##Unaligned, MAL_SIMD_ALIGNMENT) + (iChannel*((size) & ~((MAL_SIMD_ALIGNMENT)-1)))); \ name[iChannel] = (type*)((mal_uint8*)MAL_ALIGN_PTR(name##Unaligned, MAL_SIMD_ALIGNMENT) + (iChannel*((size) & ~((MAL_SIMD_ALIGNMENT)-1)))); \
} \ } \
} while (0) } while (0)
#endif
#define mal_filter_window_length_left(length) ((length) >> 1)
#define mal_filter_window_length_right(length) ((length) - mal_filter_window_length_left(length))
static MAL_INLINE mal_uint16 mal_resampler_window_length_left(const mal_resampler* pResampler)
{
return mal_filter_window_length_left(pResampler->windowLength);
}
static MAL_INLINE mal_uint16 mal_resampler_window_length_right(const mal_resampler* pResampler)
{
return mal_filter_window_length_right(pResampler->windowLength);
}
mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler* pResampler) mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler* pResampler)
{ {
...@@ -270,19 +309,27 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler ...@@ -270,19 +309,27 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler
switch (pResampler->config.algorithm) { switch (pResampler->config.algorithm) {
case mal_resampler_algorithm_linear: case mal_resampler_algorithm_linear:
{ {
pResampler->init = NULL; pResampler->init = mal_resampler_init__linear;
pResampler->read = mal_resampler_read__linear; pResampler->readF32 = mal_resampler_read_f32__linear;
pResampler->seek = mal_resampler_seek__linear; pResampler->readS16 = mal_resampler_read_s16__linear;
pResampler->seek = mal_resampler_seek__linear;
} break; } break;
case mal_resampler_algorithm_sinc: case mal_resampler_algorithm_sinc:
{ {
pResampler->init = mal_resampler_init__sinc; pResampler->init = mal_resampler_init__sinc;
pResampler->read = mal_resampler_read__sinc; pResampler->readF32 = mal_resampler_read_f32__sinc;
pResampler->seek = mal_resampler_seek__sinc; pResampler->readS16 = mal_resampler_read_s16__sinc;
pResampler->seek = mal_resampler_seek__sinc;
} break; } break;
} }
if (pResampler->config.format == mal_format_f32) {
pResampler->cacheStrideInFrames = mal_countof(pResampler->cache.f32) / pResampler->config.channels;
} else {
pResampler->cacheStrideInFrames = mal_countof(pResampler->cache.s16) / pResampler->config.channels;
}
if (pResampler->init != NULL) { if (pResampler->init != NULL) {
mal_result result = pResampler->init(pResampler); mal_result result = pResampler->init(pResampler);
if (result != MAL_SUCCESS) { if (result != MAL_SUCCESS) {
...@@ -290,6 +337,12 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler ...@@ -290,6 +337,12 @@ mal_result mal_resampler_init(const mal_resampler_config* pConfig, mal_resampler
} }
} }
/*
After initializing the backend, we'll need to pre-fill the filter with zeroes. This has already been half done via
the call to mal_zero_object() at the top of this function, but we need to increment the frame counter to complete it.
*/
pResampler->cacheLengthInFrames = mal_resampler_window_length_left(pResampler);
return MAL_SUCCESS; return MAL_SUCCESS;
} }
...@@ -335,9 +388,19 @@ mal_result mal_resampler_set_rate_ratio(mal_resampler* pResampler, double ratio) ...@@ -335,9 +388,19 @@ mal_result mal_resampler_set_rate_ratio(mal_resampler* pResampler, double ratio)
return MAL_SUCCESS; return MAL_SUCCESS;
} }
typedef union
{
float* f32[MAL_MAX_CHANNELS];
mal_int16* s16[MAL_MAX_CHANNELS];
} mal_resampler_deinterleaved_pointers;
mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames) mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames)
{ {
if (pResampler == NULL || pResampler->read == NULL) { mal_uint64 totalFramesRead;
mal_resampler_deinterleaved_pointers runningFramesOut;
mal_bool32 atEnd = MAL_FALSE;
if (pResampler == NULL) {
return 0; /* Invalid arguments. */ return 0; /* Invalid arguments. */
} }
...@@ -351,7 +414,158 @@ mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount, ...@@ -351,7 +414,158 @@ mal_uint64 mal_resampler_read(mal_resampler* pResampler, mal_uint64 frameCount,
} }
return pResampler->read(pResampler, frameCount, ppFrames); if (pResampler->config.format == mal_format_f32) {
mal_assert(pResampler->readF32 != NULL);
} else {
mal_assert(pResampler->readS16 != NULL);
}
/* Initialization of the running frame pointers. */
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
runningFramesOut.f32[iChannel] = (float*)ppFrames[iChannel];
}
/*
The backend read callbacks are only called for ranges that can be read entirely from cache. This simplifies each backend
because they do not need to worry about cache reloading logic. Instead we do all of the cache reloading stuff from here.
*/
totalFramesRead = 0;
while (totalFramesRead < frameCount) {
double cachedOutputTime;
mal_uint64 framesRemaining = frameCount - totalFramesRead;
mal_uint64 framesToReadRightNow = framesRemaining;
/* We need to make sure we don't read more than what's already in the buffer at a time. */
cachedOutputTime = mal_resampler_get_cached_output_time(pResampler);
if (cachedOutputTime > 0) {
if (framesRemaining > cachedOutputTime) {
framesToReadRightNow = (mal_uint64)floor(cachedOutputTime);
}
/*
At this point we should know how many frames can be read this iteration. We need an optimization for when the ratio=1
and the current time is a whole number. In this case we need to do a direct copy without any processing.
*/
if (pResampler->config.ratio == 1 && mal_fractional_part_f64(pResampler->windowTime) == 0) {
/*
No need to read from the backend - just copy the input straight over without any processing. We start reading from
the right side of the filter window.
*/
mal_uint16 iFirstSample = (mal_uint16)pResampler->windowTime + mal_resampler_window_length_left(pResampler);
if (pResampler->config.format == mal_format_f32) {
for (mal_uint16 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
for (mal_uint16 iFrame = 0; iFrame < framesToReadRightNow; ++iFrame) {
runningFramesOut.f32[iChannel][iFrame] = pResampler->cache.f32[pResampler->cacheStrideInFrames*iChannel + iFirstSample + iFrame];
}
}
} else {
for (mal_uint16 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
for (mal_uint16 iFrame = 0; iFrame < framesToReadRightNow; ++iFrame) {
runningFramesOut.s16[iChannel][iFrame] = pResampler->cache.s16[pResampler->cacheStrideInFrames*iChannel + iFirstSample + iFrame];
}
}
}
} else {
/* Need to read from the backend. */
mal_uint64 framesJustRead;
if (pResampler->config.format == mal_format_f32) {
framesJustRead = pResampler->readF32(pResampler, framesToReadRightNow, runningFramesOut.f32);
} else {
framesJustRead = pResampler->readS16(pResampler, framesToReadRightNow, runningFramesOut.s16);
}
if (framesJustRead != framesToReadRightNow) {
mal_assert(MAL_FALSE);
break; /* Should never hit this. */
}
}
/* Move time forward. */
pResampler->windowTime += (framesToReadRightNow * pResampler->config.ratio);
if (pResampler->config.format == mal_format_f32) {
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
runningFramesOut.f32[iChannel] += framesToReadRightNow;
}
} else {
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
runningFramesOut.s16[iChannel] += framesToReadRightNow;
}
}
/* We don't want to reload the buffer if we've finished reading. */
totalFramesRead += framesToReadRightNow;
if (totalFramesRead == frameCount) {
break;
}
}
/*
We need to exit if we've reached the end of the input buffer. We do not want to attempt to read more data, nor
do we want to read in zeroes to fill out the requested frame count (frameCount).
*/
if (atEnd) {
break;
}
/*
If we get here it means we need to reload the buffer from the client and keep iterating. To reload the buffer we
need to move the remaining data down to the front of the buffer, adjust the window time, then read more from the
client. If we have already reached the end of the client's data, we don't want to attempt to read more.
*/
{
mal_uint32 framesToReadFromClient;
mal_uint32 framesReadFromClient;
mal_uint16 framesToConsume;
mal_assert(pResampler->windowTime < 65536);
mal_assert(pResampler->windowTime <= pResampler->cacheLengthInFrames);
framesToConsume = (mal_uint16)pResampler->windowTime;
pResampler->windowTime -= framesToConsume;
pResampler->cacheLengthInFrames -= framesToConsume;
if (pResampler->config.format == mal_format_f32) {
for (mal_int32 i = 0; i < pResampler->cacheLengthInFrames; ++i) {
pResampler->cache.f32[i] = pResampler->cache.f32[i + framesToConsume];
}
framesToReadFromClient = mal_countof(pResampler->cache.f32) - pResampler->cacheLengthInFrames;
} else {
for (mal_int32 i = 0; i < pResampler->cacheLengthInFrames; ++i) {
pResampler->cache.s16[i] = pResampler->cache.s16[i + framesToConsume];
}
framesToReadFromClient = mal_countof(pResampler->cache.s16) - pResampler->cacheLengthInFrames;
}
/* Here is where we need to read more data from the client. We need to construct some deinterleaved buffers first, though. */
mal_resampler_deinterleaved_pointers clientDst;
if (pResampler->config.format == mal_format_f32) {
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
clientDst.f32[iChannel] = pResampler->cache.f32 + (pResampler->cacheStrideInFrames*iChannel + pResampler->cacheLengthInFrames);
}
framesReadFromClient = pResampler->config.onRead(pResampler, framesToReadFromClient, clientDst.f32);
} else {
for (mal_uint32 iChannel = 0; iChannel < pResampler->config.channels; ++iChannel) {
clientDst.s16[iChannel] = pResampler->cache.s16 + (pResampler->cacheStrideInFrames*iChannel + pResampler->cacheLengthInFrames);
}
framesReadFromClient = pResampler->config.onRead(pResampler, framesToReadFromClient, clientDst.s16);
}
mal_assert(framesReadFromClient <= framesToReadFromClient);
if (framesReadFromClient < framesToReadFromClient) {
/* We have reached the end of the input buffer. We do _not_ want to attempt to read any more data from the client in this case. */
atEnd = MAL_TRUE;
}
mal_assert(framesReadFromClient <= 65535);
pResampler->cacheLengthInFrames += (mal_uint16)framesReadFromClient;
}
}
return totalFramesRead;
} }
mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options) mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount, mal_uint32 options)
...@@ -364,8 +578,10 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount, ...@@ -364,8 +578,10 @@ mal_uint64 mal_resampler_seek(mal_resampler* pResampler, mal_uint64 frameCount,
return 0; /* Nothing to do, so return early. */ return 0; /* Nothing to do, so return early. */
} }
/* TODO: Do seeking. */
(void)options;
return pResampler->seek(pResampler, frameCount, options); return 0;
} }
...@@ -379,7 +595,7 @@ mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler ...@@ -379,7 +595,7 @@ mal_uint64 mal_resampler_get_cached_output_frame_count(mal_resampler* pResampler
return (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler)); return (mal_uint64)floor(mal_resampler_get_cached_output_time(pResampler));
} }
double mal_resampler__calculate_cached_input_time(mal_resampler* pResampler) static MAL_INLINE double mal_resampler__calculate_cached_input_time(mal_resampler* pResampler)
{ {
/* /*
The cached input time depends on whether or not the end of the input is being consumed. If so, it's the difference between the The cached input time depends on whether or not the end of the input is being consumed. If so, it's the difference between the
...@@ -388,10 +604,10 @@ double mal_resampler__calculate_cached_input_time(mal_resampler* pResampler) ...@@ -388,10 +604,10 @@ double mal_resampler__calculate_cached_input_time(mal_resampler* pResampler)
*/ */
double cachedInputTime = pResampler->cacheLengthInFrames; double cachedInputTime = pResampler->cacheLengthInFrames;
if (pResampler->config.endOfInputMode == mal_resampler_end_of_input_mode_consume) { if (pResampler->config.endOfInputMode == mal_resampler_end_of_input_mode_consume) {
cachedInputTime -= (pResampler->windowTime + (pResampler->windowLength >> 1)); cachedInputTime -= (pResampler->windowTime + mal_resampler_window_length_left(pResampler));
} else { } else {
cachedInputTime -= (pResampler->windowTime + pResampler->windowLength); cachedInputTime -= (pResampler->windowTime + pResampler->windowLength);
} }
return cachedInputTime; return cachedInputTime;
} }
...@@ -405,7 +621,7 @@ double mal_resampler_get_cached_input_time(mal_resampler* pResampler) ...@@ -405,7 +621,7 @@ double mal_resampler_get_cached_input_time(mal_resampler* pResampler)
return mal_resampler__calculate_cached_input_time(pResampler); return mal_resampler__calculate_cached_input_time(pResampler);
} }
double mal_resampler__calculate_cached_output_time(mal_resampler* pResampler) static MAL_INLINE double mal_resampler__calculate_cached_output_time(mal_resampler* pResampler)
{ {
return mal_resampler__calculate_cached_input_time(pResampler) / pResampler->config.ratio; return mal_resampler__calculate_cached_input_time(pResampler) / pResampler->config.ratio;
} }
...@@ -472,7 +688,31 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl ...@@ -472,7 +688,31 @@ mal_uint64 mal_resampler_get_expected_output_frame_count(mal_resampler* pResampl
/* /*
Linear Linear
*/ */
mal_uint64 mal_resampler_read__linear(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames) mal_result mal_resampler_init__linear(mal_resampler* pResampler)
{
mal_assert(pResampler != NULL);
/* The linear implementation always has a window length of 2. */
pResampler->windowLength = 2;
return MAL_SUCCESS;
}
mal_uint64 mal_resampler_read_f32__linear(mal_resampler* pResampler, mal_uint64 frameCount, float** ppFrames)
{
mal_assert(pResampler != NULL);
mal_assert(pResampler->config.onRead != NULL);
mal_assert(frameCount > 0);
mal_assert(ppFrames != NULL);
/* TODO: Implement me. */
(void)pResampler;
(void)frameCount;
(void)ppFrames;
return 0;
}
mal_uint64 mal_resampler_read_s16__linear(mal_resampler* pResampler, mal_uint64 frameCount, mal_int16** ppFrames)
{ {
mal_assert(pResampler != NULL); mal_assert(pResampler != NULL);
mal_assert(pResampler->config.onRead != NULL); mal_assert(pResampler->config.onRead != NULL);
...@@ -511,7 +751,21 @@ mal_result mal_resampler_init__sinc(mal_resampler* pResampler) ...@@ -511,7 +751,21 @@ mal_result mal_resampler_init__sinc(mal_resampler* pResampler)
return MAL_SUCCESS; return MAL_SUCCESS;
} }
mal_uint64 mal_resampler_read__sinc(mal_resampler* pResampler, mal_uint64 frameCount, void** ppFrames) mal_uint64 mal_resampler_read_f32__sinc(mal_resampler* pResampler, mal_uint64 frameCount, float** ppFrames)
{
mal_assert(pResampler != NULL);
mal_assert(pResampler->config.onRead != NULL);
mal_assert(frameCount > 0);
mal_assert(ppFrames != NULL);
/* TODO: Implement me. */
(void)pResampler;
(void)frameCount;
(void)ppFrames;
return 0;
}
mal_uint64 mal_resampler_read_s16__sinc(mal_resampler* pResampler, mal_uint64 frameCount, mal_int16** ppFrames)
{ {
mal_assert(pResampler != NULL); mal_assert(pResampler != NULL);
mal_assert(pResampler->config.onRead != NULL); mal_assert(pResampler->config.onRead != NULL);
......
...@@ -262,7 +262,30 @@ ...@@ -262,7 +262,30 @@
</Link> </Link>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\research\tests\mal_resampler_test_0.c" /> <ClCompile Include="..\debugging\source\mal_router_1.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\examples\simple_playback.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\research\tests\mal_resampler_test_0.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="mal_dithering.c"> <ClCompile Include="mal_dithering.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
......
...@@ -39,6 +39,12 @@ ...@@ -39,6 +39,12 @@
<ClCompile Include="..\research\tests\mal_resampler_test_0.c"> <ClCompile Include="..\research\tests\mal_resampler_test_0.c">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\examples\simple_playback.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\debugging\source\mal_router_1.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\mini_al.h"> <ClInclude Include="..\mini_al.h">
......
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