Commit 3a62d449 authored by David Reid's avatar David Reid

Work on changes to the effect API.

parent 105d016e
...@@ -239,7 +239,7 @@ MA_API ma_result ma_engine_sound_set_position(ma_engine* pEngine, ma_sound* pSou ...@@ -239,7 +239,7 @@ MA_API ma_result ma_engine_sound_set_position(ma_engine* pEngine, ma_sound* pSou
MA_API ma_result ma_engine_sound_set_rotation(ma_engine* pEngine, ma_sound* pSound, ma_quat rotation); MA_API ma_result ma_engine_sound_set_rotation(ma_engine* pEngine, ma_sound* pSound, ma_quat rotation);
MA_API ma_result ma_engine_sound_set_looping(ma_engine* pEngine, ma_sound* pSound, ma_bool32 isLooping); MA_API ma_result ma_engine_sound_set_looping(ma_engine* pEngine, ma_sound* pSound, ma_bool32 isLooping);
MA_API ma_bool32 ma_engine_sound_at_end(ma_engine* pEngine, const ma_sound* pSound); MA_API ma_bool32 ma_engine_sound_at_end(ma_engine* pEngine, const ma_sound* pSound);
MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. Not yet implemented. */ MA_API ma_result ma_engine_play_sound(ma_engine* pEngine, const char* pFilePath, ma_sound_group* pGroup); /* Fire and forget. */
MA_API ma_result ma_engine_sound_group_init(ma_engine* pEngine, ma_sound_group* pParentGroup, ma_sound_group* pGroup); /* Parent must be set at initialization time and cannot be changed. Not thread-safe. */ MA_API ma_result ma_engine_sound_group_init(ma_engine* pEngine, ma_sound_group* pParentGroup, ma_sound_group* pGroup); /* Parent must be set at initialization time and cannot be changed. Not thread-safe. */
MA_API void ma_engine_sound_group_uninit(ma_engine* pEngine, ma_sound_group* pGroup); /* Not thread-safe. */ MA_API void ma_engine_sound_group_uninit(ma_engine* pEngine, ma_sound_group* pGroup); /* Not thread-safe. */
......
...@@ -76,8 +76,7 @@ int main(int argc, char** argv) ...@@ -76,8 +76,7 @@ int main(int argc, char** argv)
ma_uint64 inputFileDataSize2; ma_uint64 inputFileDataSize2;
const char* pInputFilePath1 = NULL; const char* pInputFilePath1 = NULL;
const char* pInputFilePath2 = NULL; const char* pInputFilePath2 = NULL;
ma_effect_config effectConfig; /*ma_effect_config effectConfig;*/
ma_effect effect;
if (argc > 1) { if (argc > 1) {
pInputFilePath1 = argv[1]; pInputFilePath1 = argv[1];
...@@ -98,6 +97,7 @@ int main(int argc, char** argv) ...@@ -98,6 +97,7 @@ int main(int argc, char** argv)
} }
#if 0
/* Effect. */ /* Effect. */
effectConfig = ma_effect_config_init(ma_effect_type_lpf, /*device.playback.format*/ ma_format_f32, device.playback.channels, device.sampleRate); effectConfig = ma_effect_config_init(ma_effect_type_lpf, /*device.playback.format*/ ma_format_f32, device.playback.channels, device.sampleRate);
effectConfig.lpf.cutoffFrequency = device.sampleRate / 16; effectConfig.lpf.cutoffFrequency = device.sampleRate / 16;
...@@ -106,6 +106,7 @@ int main(int argc, char** argv) ...@@ -106,6 +106,7 @@ int main(int argc, char** argv)
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; return result;
} }
#endif
/* Mixers. */ /* Mixers. */
...@@ -126,7 +127,10 @@ int main(int argc, char** argv) ...@@ -126,7 +127,10 @@ int main(int argc, char** argv)
} }
ma_mixer_set_volume(&g_mixerEffects, 1.0f); ma_mixer_set_volume(&g_mixerEffects, 1.0f);
#if 0
ma_mixer_set_effect(&g_mixerEffects, &effect); ma_mixer_set_effect(&g_mixerEffects, &effect);
#endif
......
...@@ -23,23 +23,6 @@ works it's way down. ...@@ -23,23 +23,6 @@ works it's way down.
Usage Usage
----- -----
Initialize an effect like the following:
```c
ma_effect_config config = ma_effect_config_init(ma_effect_type_lpf, ma_format_f32, 2, 48000);
config.lpf.cutoffFrequency = 8000;
ma_effect effect;
ma_result result = ma_effect_init(&config, &effect);
if (result != MA_SUCCESS) {
// Error.
}
```
Initializing an effect uses the same config system as all other objects in miniaudio. Initialize this with `ma_effect_config_init()`. This takes the effect
type, sample format, channel count and sample rate. Note that this alone is not enough to configure the config - you will need to set some effect type-specific
properties.
To apply the effect to some audio data, do something like the following: To apply the effect to some audio data, do something like the following:
```c ```c
...@@ -59,75 +42,28 @@ need to be specified when processing a chunk of audio data. ...@@ -59,75 +42,28 @@ need to be specified when processing a chunk of audio data.
*/ */
typedef struct ma_effect ma_effect; typedef void ma_effect;
typedef struct typedef struct ma_effect_base ma_effect_base;
{ struct ma_effect_base
void* pUserData; {
ma_result (* onProcess)(void* pUserData, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); ma_result (* onProcessPCMFrames)(ma_effect* pEffect, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
ma_uint64 (* onGetRequiredInputFrameCount)(void* pUserData, ma_uint64 outputFrameCount); ma_uint64 (* onGetRequiredInputFrameCount)(ma_effect* pEffect, ma_uint64 outputFrameCount);
ma_uint64 (* onGetExpectedOutputFrameCount)(void* pUserData, ma_uint64 inputFrameCount); ma_uint64 (* onGetExpectedOutputFrameCount)(ma_effect* pEffect, ma_uint64 inputFrameCount);
} ma_effect_callbacks; ma_result (* onGetInputDataFormat)(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
ma_result (* onGetOutputDataFormat)(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
typedef enum ma_effect_base* pPrev;
{ ma_effect_base* pNext;
ma_effect_type_custom,
ma_effect_type_converter,
ma_effect_type_biquad,
ma_effect_type_lpf,
ma_effect_type_hpf,
ma_effect_type_bpf
} ma_effect_type;
typedef struct
{
ma_effect_type type;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
ma_effect_callbacks custom;
ma_data_converter_config converter;
ma_biquad_config biquad;
ma_lpf_config lpf;
ma_hpf_config hpf;
ma_bpf_config bpf;
} ma_effect_config;
MA_API ma_effect_config ma_effect_config_init(ma_effect_type type, ma_format format, ma_uint32 channels, ma_uint32 sampleRate);
struct ma_effect
{
ma_effect_type type;
ma_format formatIn;
ma_uint32 channelsIn;
ma_uint32 sampleRateIn;
ma_format formatOut;
ma_uint32 channelsOut;
ma_uint32 sampleRateOut;
ma_effect_callbacks callbacks;
ma_effect* pPrev;
ma_effect* pNext;
union
{
ma_data_converter converter;
ma_biquad biquad;
ma_lpf lpf;
ma_hpf hpf;
ma_bpf bpf;
} state;
}; };
MA_API ma_result ma_effect_init(const ma_effect_config* pConfig, ma_effect* pEffect);
MA_API void ma_effect_uninit(ma_effect* pEffect);
MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut); MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut);
MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma_uint64 outputFrameCount); MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma_uint64 outputFrameCount);
MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, ma_uint64 inputFrameCount); MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, ma_uint64 inputFrameCount);
MA_API ma_result ma_effect_append(ma_effect* pEffect, ma_effect* pParent); MA_API ma_result ma_effect_append(ma_effect* pEffect, ma_effect* pParent);
MA_API ma_result ma_effect_prepend(ma_effect* pEffect, ma_effect* pChild); MA_API ma_result ma_effect_prepend(ma_effect* pEffect, ma_effect* pChild);
MA_API ma_result ma_effect_detach(ma_effect* pEffect); MA_API ma_result ma_effect_detach(ma_effect* pEffect);
MA_API ma_result ma_effect_get_output_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels); MA_API ma_result ma_effect_get_output_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
MA_API ma_result ma_effect_get_input_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels); MA_API ma_result ma_effect_get_input_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate);
...@@ -787,60 +723,15 @@ static void ma_convert_pcm_frames_format_and_channels(void* pDst, ma_format form ...@@ -787,60 +723,15 @@ static void ma_convert_pcm_frames_format_and_channels(void* pDst, ma_format form
} }
static ma_effect_base* ma_effect_get_root(ma_effect* pEffect)
MA_API ma_effect_config ma_effect_config_init(ma_effect_type type, ma_format format, ma_uint32 channels, ma_uint32 sampleRate)
{ {
ma_effect_config config; ma_effect_base* pRootEffect;
MA_ZERO_OBJECT(&config);
config.type = type;
config.format = format;
config.channels = channels;
config.sampleRate = sampleRate;
switch (type)
{
case ma_effect_type_converter:
{
config.converter = ma_data_converter_config_init(format, format, channels, channels, sampleRate, sampleRate);
} break;
case ma_effect_type_biquad:
{
config.biquad = ma_biquad_config_init(format, channels, 1, 0, 0, 1, 0, 0);
} break;
case ma_effect_type_lpf:
{
config.lpf = ma_lpf_config_init(format, channels, sampleRate, (double)sampleRate, 2);
} break;
case ma_effect_type_hpf:
{
config.hpf = ma_hpf_config_init(format, channels, sampleRate, (double)sampleRate, 2);
} break;
case ma_effect_type_bpf:
{
config.bpf = ma_bpf_config_init(format, channels, sampleRate, (double)sampleRate, 2);
} break;
default: break;
}
return config;
}
static ma_effect* ma_effect_get_root(ma_effect* pEffect)
{
ma_effect* pRootEffect;
if (pEffect == NULL) { if (pEffect == NULL) {
return NULL; return NULL;
} }
pRootEffect = pEffect; pRootEffect = (ma_effect_base*)pEffect;
for (;;) { for (;;) {
if (pRootEffect->pPrev == NULL) { if (pRootEffect->pPrev == NULL) {
return pRootEffect; return pRootEffect;
...@@ -853,316 +744,12 @@ static ma_effect* ma_effect_get_root(ma_effect* pEffect) ...@@ -853,316 +744,12 @@ static ma_effect* ma_effect_get_root(ma_effect* pEffect)
/*return NULL;*/ /*return NULL;*/
} }
static ma_result ma_effect_init__custom(const ma_effect_config* pConfig, ma_effect* pEffect)
{
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pEffect != NULL);
pEffect->callbacks = pConfig->custom;
return MA_SUCCESS;
}
static ma_result ma_effect__on_process__converter(void* pUserData, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
ma_data_converter* pConverter = (ma_data_converter*)pUserData;
/* The input and output buffers must not be equal for a converter effect. */
if (pFramesIn == pFramesOut) {
return MA_INVALID_OPERATION;
}
return ma_data_converter_process_pcm_frames(pConverter, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
}
static ma_uint64 ma_effect__on_get_required_input_frame_count__converter(void* pUserData, ma_uint64 outputFrameCount)
{
return ma_data_converter_get_required_input_frame_count((ma_data_converter*)pUserData, outputFrameCount);
}
static ma_uint64 ma_effect__on_get_expected_output_frame_count__converter(void* pUserData, ma_uint64 inputFrameCount)
{
return ma_data_converter_get_expected_output_frame_count((ma_data_converter*)pUserData, inputFrameCount);
}
static ma_result ma_effect_init__converter(const ma_effect_config* pConfig, ma_effect* pEffect)
{
ma_result result;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pEffect != NULL);
result = ma_data_converter_init(&pConfig->converter, &pEffect->state.converter);
if (result != MA_SUCCESS) {
return result;
}
pEffect->callbacks.pUserData = &pEffect->state.converter;
pEffect->callbacks.onProcess = ma_effect__on_process__converter;
pEffect->callbacks.onGetRequiredInputFrameCount = ma_effect__on_get_required_input_frame_count__converter;
pEffect->callbacks.onGetExpectedOutputFrameCount = ma_effect__on_get_expected_output_frame_count__converter;
return MA_SUCCESS;
}
static void ma_effect_uninit__converter(ma_effect* pEffect)
{
ma_data_converter_uninit(&pEffect->state.converter);
}
static ma_result ma_effect__on_process__biquad(void* pUserData, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
ma_biquad* pLPF = (ma_biquad*)pUserData;
ma_uint64 frameCountIn = *pFrameCountIn;
ma_uint64 frameCountOut = *pFrameCountOut;
ma_uint64 frameCount = ma_min(frameCountIn, frameCountOut);
ma_result result;
result = ma_biquad_process_pcm_frames(pLPF, pFramesOut, pFramesIn, frameCount);
if (result != MA_SUCCESS) {
return result;
}
*pFrameCountIn = frameCount;
*pFrameCountOut = frameCount;
return MA_SUCCESS;
}
static ma_result ma_effect_init__biquad(const ma_effect_config* pConfig, ma_effect* pEffect)
{
ma_result result;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pEffect != NULL);
result = ma_biquad_init(&pConfig->biquad, &pEffect->state.biquad);
if (result != MA_SUCCESS) {
return result;
}
pEffect->callbacks.pUserData = &pEffect->state.biquad;
pEffect->callbacks.onProcess = ma_effect__on_process__biquad;
pEffect->callbacks.onGetRequiredInputFrameCount = NULL; /* 1:1 */
pEffect->callbacks.onGetExpectedOutputFrameCount = NULL; /* 1:1 */
return MA_SUCCESS;
}
static ma_result ma_effect__on_process__lpf(void* pUserData, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
ma_lpf* pLPF = (ma_lpf*)pUserData;
ma_uint64 frameCountIn = *pFrameCountIn;
ma_uint64 frameCountOut = *pFrameCountOut;
ma_uint64 frameCount = ma_min(frameCountIn, frameCountOut);
ma_result result;
result = ma_lpf_process_pcm_frames(pLPF, pFramesOut, pFramesIn, frameCount);
if (result != MA_SUCCESS) {
return result;
}
*pFrameCountIn = frameCount;
*pFrameCountOut = frameCount;
return MA_SUCCESS;
}
static ma_result ma_effect_init__lpf(const ma_effect_config* pConfig, ma_effect* pEffect)
{
ma_result result;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pEffect != NULL);
result = ma_lpf_init(&pConfig->lpf, &pEffect->state.lpf);
if (result != MA_SUCCESS) {
return result;
}
pEffect->callbacks.pUserData = &pEffect->state.lpf;
pEffect->callbacks.onProcess = ma_effect__on_process__lpf;
pEffect->callbacks.onGetRequiredInputFrameCount = NULL; /* 1:1 */
pEffect->callbacks.onGetExpectedOutputFrameCount = NULL; /* 1:1 */
return MA_SUCCESS;
}
static ma_result ma_effect__on_process__hpf(void* pUserData, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
ma_hpf* pLPF = (ma_hpf*)pUserData;
ma_uint64 frameCountIn = *pFrameCountIn;
ma_uint64 frameCountOut = *pFrameCountOut;
ma_uint64 frameCount = ma_min(frameCountIn, frameCountOut);
ma_result result;
result = ma_hpf_process_pcm_frames(pLPF, pFramesOut, pFramesIn, frameCount);
if (result != MA_SUCCESS) {
return result;
}
*pFrameCountIn = frameCount;
*pFrameCountOut = frameCount;
return MA_SUCCESS;
}
static ma_result ma_effect_init__hpf(const ma_effect_config* pConfig, ma_effect* pEffect)
{
ma_result result;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pEffect != NULL);
result = ma_hpf_init(&pConfig->hpf, &pEffect->state.hpf);
if (result != MA_SUCCESS) {
return result;
}
pEffect->callbacks.pUserData = &pEffect->state.hpf;
pEffect->callbacks.onProcess = ma_effect__on_process__hpf;
pEffect->callbacks.onGetRequiredInputFrameCount = NULL; /* 1:1 */
pEffect->callbacks.onGetExpectedOutputFrameCount = NULL; /* 1:1 */
return MA_SUCCESS;
}
static ma_result ma_effect__on_process__bpf(void* pUserData, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{
ma_bpf* pLPF = (ma_bpf*)pUserData;
ma_uint64 frameCountIn = *pFrameCountIn;
ma_uint64 frameCountOut = *pFrameCountOut;
ma_uint64 frameCount = ma_min(frameCountIn, frameCountOut);
ma_result result;
result = ma_bpf_process_pcm_frames(pLPF, pFramesOut, pFramesIn, frameCount);
if (result != MA_SUCCESS) {
return result;
}
*pFrameCountIn = frameCount;
*pFrameCountOut = frameCount;
return MA_SUCCESS;
}
static ma_result ma_effect_init__bpf(const ma_effect_config* pConfig, ma_effect* pEffect)
{
ma_result result;
MA_ASSERT(pConfig != NULL);
MA_ASSERT(pEffect != NULL);
result = ma_bpf_init(&pConfig->bpf, &pEffect->state.bpf);
if (result != MA_SUCCESS) {
return result;
}
pEffect->callbacks.pUserData = &pEffect->state.bpf;
pEffect->callbacks.onProcess = ma_effect__on_process__bpf;
pEffect->callbacks.onGetRequiredInputFrameCount = NULL; /* 1:1 */
pEffect->callbacks.onGetExpectedOutputFrameCount = NULL; /* 1:1 */
return MA_SUCCESS;
}
MA_API ma_result ma_effect_init(const ma_effect_config* pConfig, ma_effect* pEffect)
{
ma_result result;
if (pEffect == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pEffect);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
pEffect->type = pConfig->type;
pEffect->formatIn = pConfig->format;
pEffect->channelsIn = pConfig->channels;
pEffect->sampleRateIn = pConfig->sampleRate;
pEffect->formatOut = pConfig->format;
pEffect->channelsOut = pConfig->channels;
pEffect->sampleRateOut = pConfig->sampleRate;
switch (pConfig->type)
{
case ma_effect_type_custom:
{
result = ma_effect_init__custom(pConfig, pEffect);
} break;
case ma_effect_type_converter:
{
/* If the input format is inconsistent we need to return an error. */
if (pConfig->format != pConfig->converter.formatIn || pConfig->channels != pConfig->converter.channelsIn || pConfig->sampleRate != pConfig->converter.sampleRateIn) {
return MA_INVALID_ARGS;
}
/* The output format is defined by the converter. */
pEffect->formatOut = pConfig->converter.formatOut;
pEffect->channelsOut = pConfig->converter.channelsOut;
pEffect->sampleRateOut = pConfig->converter.sampleRateOut;
result = ma_effect_init__converter(pConfig, pEffect);
} break;
case ma_effect_type_biquad:
{
result = ma_effect_init__biquad(pConfig, pEffect);
} break;
case ma_effect_type_lpf:
{
result = ma_effect_init__lpf(pConfig, pEffect);
} break;
case ma_effect_type_hpf:
{
result = ma_effect_init__hpf(pConfig, pEffect);
} break;
case ma_effect_type_bpf:
{
result = ma_effect_init__bpf(pConfig, pEffect);
} break;
default:
{
result = MA_INVALID_ARGS; /* Unknown effect type. */
} break;
}
return result;
}
MA_API void ma_effect_uninit(ma_effect* pEffect)
{
if (pEffect == NULL) {
return;
}
if (pEffect->type == ma_effect_type_converter) {
ma_effect_uninit__converter(pEffect);
}
}
MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{ {
ma_result result = MA_SUCCESS; ma_result result = MA_SUCCESS;
ma_effect* pFirstEffect; ma_effect_base* pBase = (ma_effect_base*)pEffect;
ma_effect* pRunningEffect; ma_effect_base* pFirstEffect;
ma_effect_base* pRunningEffect;
ma_uint32 iTempBuffer = 0; ma_uint32 iTempBuffer = 0;
ma_uint8 tempFrames[2][MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 tempFrames[2][MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint64 tempFrameCount[2]; ma_uint64 tempFrameCount[2];
...@@ -1171,7 +758,7 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF ...@@ -1171,7 +758,7 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF
ma_uint64 frameCountOut; ma_uint64 frameCountOut;
ma_uint64 frameCountOutConsumed; ma_uint64 frameCountOutConsumed;
if (pEffect == NULL || pEffect->callbacks.onProcess == NULL) { if (pEffect == NULL || pBase->onProcessPCMFrames == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
...@@ -1184,8 +771,8 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF ...@@ -1184,8 +771,8 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF
pRunningEffect = pFirstEffect; pRunningEffect = pFirstEffect;
/* Optimized path if this is the only effect in the chain. */ /* Optimized path if this is the only effect in the chain. */
if (pFirstEffect == pEffect) { if (pFirstEffect == pBase) {
return pEffect->callbacks.onProcess(pRunningEffect->callbacks.pUserData, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut); return pBase->onProcessPCMFrames(pRunningEffect, pFramesIn, pFrameCountIn, pFramesOut, pFrameCountOut);
} }
frameCountIn = *pFrameCountIn; frameCountIn = *pFrameCountIn;
...@@ -1198,16 +785,25 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF ...@@ -1198,16 +785,25 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF
several iterations to process as much data as possible available in the input buffer, or can fit in the output buffer. several iterations to process as much data as possible available in the input buffer, or can fit in the output buffer.
*/ */
while (frameCountIn < frameCountInConsumed && frameCountOut < frameCountOutConsumed) { while (frameCountIn < frameCountInConsumed && frameCountOut < frameCountOutConsumed) {
do for (;;) {
{ MA_ASSERT(pRunningEffect != NULL);
const void* pRunningFramesIn; const void* pRunningFramesIn;
/* */ void* pRunningFramesOut; /* */ void* pRunningFramesOut;
ma_uint64 frameCountInThisIteration; ma_uint64 frameCountInThisIteration;
ma_uint64 frameCountOutThisIteration; ma_uint64 frameCountOutThisIteration;
ma_format runningEffectFormatIn;
ma_uint32 runningEffectChannelsIn;
result = ma_effect_get_input_data_format(pRunningEffect, &runningEffectFormatIn, &runningEffectChannelsIn, NULL);
if (result != MA_SUCCESS) {
return result; /* Failed to retrieve the input data format. Abort. */
}
if (pRunningEffect == pFirstEffect) { if (pRunningEffect == pFirstEffect) {
/* It's the first effect which means we need to read directly from the input buffer. */ /* It's the first effect which means we need to read directly from the input buffer. */
pRunningFramesIn = ma_offset_ptr(pFramesIn, frameCountInConsumed * ma_get_bytes_per_frame(pRunningEffect->formatIn, pRunningEffect->channelsIn)); pRunningFramesIn = ma_offset_ptr(pFramesIn, frameCountInConsumed * ma_get_bytes_per_frame(runningEffectFormatIn, runningEffectChannelsIn));
frameCountInThisIteration = frameCountIn - frameCountInConsumed; frameCountInThisIteration = frameCountIn - frameCountInConsumed;
} else { } else {
/* It's not the first item. We need to read from a temp buffer. */ /* It's not the first item. We need to read from a temp buffer. */
...@@ -1218,15 +814,15 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF ...@@ -1218,15 +814,15 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF
if (pRunningEffect == pEffect) { if (pRunningEffect == pEffect) {
/* It's the last item in the chain so we need to output directly to the output buffer. */ /* It's the last item in the chain so we need to output directly to the output buffer. */
pRunningFramesOut = ma_offset_ptr(pFramesOut, frameCountOutConsumed * ma_get_bytes_per_frame(pRunningEffect->formatIn, pRunningEffect->channelsIn)); pRunningFramesOut = ma_offset_ptr(pFramesOut, frameCountOutConsumed * ma_get_bytes_per_frame(runningEffectFormatIn, runningEffectChannelsIn));
frameCountOutThisIteration = frameCountOut - frameCountOutConsumed; frameCountOutThisIteration = frameCountOut - frameCountOutConsumed;
} else { } else {
/* It's not the last item in the chain. We need to output to a temp buffer so that it becomes our input buffer in the next iteration. */ /* It's not the last item in the chain. We need to output to a temp buffer so that it becomes our input buffer in the next iteration. */
pRunningFramesOut = tempFrames[iTempBuffer]; pRunningFramesOut = tempFrames[iTempBuffer];
frameCountOutThisIteration = sizeof(tempFrames[iTempBuffer]) / ma_get_bytes_per_frame(pRunningEffect->formatIn, pRunningEffect->channelsIn); frameCountOutThisIteration = sizeof(tempFrames[iTempBuffer]) / ma_get_bytes_per_frame(runningEffectFormatIn, runningEffectChannelsIn);
} }
result = pEffect->callbacks.onProcess(pRunningEffect->callbacks.pUserData, pRunningFramesIn, &frameCountInThisIteration, pRunningFramesOut, &frameCountOutThisIteration); result = pRunningEffect->onProcessPCMFrames(pRunningEffect, pRunningFramesIn, &frameCountInThisIteration, pRunningFramesOut, &frameCountOutThisIteration);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
break; break;
} }
...@@ -1238,13 +834,20 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF ...@@ -1238,13 +834,20 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF
if (pRunningEffect == pFirstEffect) { if (pRunningEffect == pFirstEffect) {
frameCountInConsumed += frameCountInThisIteration; frameCountInConsumed += frameCountInThisIteration;
} }
if (pRunningEffect == pEffect) { if (pRunningEffect == pBase) {
frameCountOutConsumed += frameCountOutThisIteration; frameCountOutConsumed += frameCountOutThisIteration;
} }
tempFrameCount[iTempBuffer] = frameCountOutThisIteration; tempFrameCount[iTempBuffer] = frameCountOutThisIteration;
pRunningEffect = pEffect->pNext;
} while (pRunningEffect != pEffect);
/* If we just processed the input effect we need to abort. */
if (pRunningEffect == pBase) {
break;
}
pRunningEffect = pRunningEffect->pNext;
}
} }
...@@ -1260,10 +863,12 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF ...@@ -1260,10 +863,12 @@ MA_API ma_result ma_effect_process_pcm_frames(ma_effect* pEffect, const void* pF
static ma_uint64 ma_effect_get_required_input_frame_count_local(ma_effect* pEffect, ma_uint64 outputFrameCount) static ma_uint64 ma_effect_get_required_input_frame_count_local(ma_effect* pEffect, ma_uint64 outputFrameCount)
{ {
ma_effect_base* pBase = (ma_effect_base*)pEffect;
MA_ASSERT(pEffect != NULL); MA_ASSERT(pEffect != NULL);
if (pEffect->callbacks.onGetRequiredInputFrameCount) { if (pBase->onGetRequiredInputFrameCount) {
return pEffect->callbacks.onGetRequiredInputFrameCount(pEffect->callbacks.pUserData, outputFrameCount); return pBase->onGetRequiredInputFrameCount(pEffect, outputFrameCount);
} else { } else {
/* If there is no callback, assume a 1:1 mapping. */ /* If there is no callback, assume a 1:1 mapping. */
return outputFrameCount; return outputFrameCount;
...@@ -1272,6 +877,7 @@ static ma_uint64 ma_effect_get_required_input_frame_count_local(ma_effect* pEffe ...@@ -1272,6 +877,7 @@ static ma_uint64 ma_effect_get_required_input_frame_count_local(ma_effect* pEffe
MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma_uint64 outputFrameCount) MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma_uint64 outputFrameCount)
{ {
ma_effect_base* pBase = (ma_effect_base*)pEffect;
ma_uint64 localInputFrameCount; ma_uint64 localInputFrameCount;
if (pEffect == NULL) { if (pEffect == NULL) {
...@@ -1280,10 +886,10 @@ MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma ...@@ -1280,10 +886,10 @@ MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma
localInputFrameCount = ma_effect_get_required_input_frame_count_local(pEffect, outputFrameCount); localInputFrameCount = ma_effect_get_required_input_frame_count_local(pEffect, outputFrameCount);
if (pEffect->pPrev == NULL) { if (pBase->pPrev == NULL) {
return localInputFrameCount; return localInputFrameCount;
} else { } else {
ma_uint64 parentInputFrameCount = ma_effect_get_required_input_frame_count(pEffect->pPrev, outputFrameCount); ma_uint64 parentInputFrameCount = ma_effect_get_required_input_frame_count(pBase->pPrev, outputFrameCount);
if (parentInputFrameCount > localInputFrameCount) { if (parentInputFrameCount > localInputFrameCount) {
return parentInputFrameCount; return parentInputFrameCount;
} else { } else {
...@@ -1294,10 +900,12 @@ MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma ...@@ -1294,10 +900,12 @@ MA_API ma_uint64 ma_effect_get_required_input_frame_count(ma_effect* pEffect, ma
static ma_uint64 ma_effect_get_expected_output_frame_count_local(ma_effect* pEffect, ma_uint64 inputFrameCount) static ma_uint64 ma_effect_get_expected_output_frame_count_local(ma_effect* pEffect, ma_uint64 inputFrameCount)
{ {
ma_effect_base* pBase = (ma_effect_base*)pEffect;
MA_ASSERT(pEffect != NULL); MA_ASSERT(pEffect != NULL);
if (pEffect->callbacks.onGetExpectedOutputFrameCount) { if (pBase->onGetExpectedOutputFrameCount) {
return pEffect->callbacks.onGetExpectedOutputFrameCount(pEffect->callbacks.pUserData, inputFrameCount); return pBase->onGetExpectedOutputFrameCount(pEffect, inputFrameCount);
} else { } else {
/* If there is no callback, assume a 1:1 mapping. */ /* If there is no callback, assume a 1:1 mapping. */
return inputFrameCount; return inputFrameCount;
...@@ -1306,6 +914,7 @@ static ma_uint64 ma_effect_get_expected_output_frame_count_local(ma_effect* pEff ...@@ -1306,6 +914,7 @@ static ma_uint64 ma_effect_get_expected_output_frame_count_local(ma_effect* pEff
MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, ma_uint64 inputFrameCount) MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, ma_uint64 inputFrameCount)
{ {
ma_effect_base* pBase = (ma_effect_base*)pEffect;
ma_uint64 localOutputFrameCount; ma_uint64 localOutputFrameCount;
if (pEffect == NULL) { if (pEffect == NULL) {
...@@ -1314,10 +923,10 @@ MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, m ...@@ -1314,10 +923,10 @@ MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, m
localOutputFrameCount = ma_effect_get_expected_output_frame_count_local(pEffect, inputFrameCount); localOutputFrameCount = ma_effect_get_expected_output_frame_count_local(pEffect, inputFrameCount);
if (pEffect->pPrev == NULL) { if (pBase->pPrev == NULL) {
return localOutputFrameCount; return localOutputFrameCount;
} else { } else {
ma_uint64 parentOutputFrameCount = ma_effect_get_expected_output_frame_count(pEffect->pPrev, inputFrameCount); ma_uint64 parentOutputFrameCount = ma_effect_get_expected_output_frame_count(pBase->pPrev, inputFrameCount);
if (parentOutputFrameCount < localOutputFrameCount) { if (parentOutputFrameCount < localOutputFrameCount) {
return parentOutputFrameCount; return parentOutputFrameCount;
} else { } else {
...@@ -1328,126 +937,161 @@ MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, m ...@@ -1328,126 +937,161 @@ MA_API ma_uint64 ma_effect_get_expected_output_frame_count(ma_effect* pEffect, m
ma_result ma_effect_append(ma_effect* pEffect, ma_effect* pParent) ma_result ma_effect_append(ma_effect* pEffect, ma_effect* pParent)
{ {
ma_effect_base* pBaseEffect = (ma_effect_base*)pEffect;
ma_effect_base* pBaseParent = (ma_effect_base*)pParent;
if (pEffect == NULL || pParent == NULL || pEffect == pParent) { if (pEffect == NULL || pParent == NULL || pEffect == pParent) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
/* The effect must be detached before reinserting into the list. */ /* The effect must be detached before reinserting into the list. */
if (pEffect->pPrev != NULL || pEffect->pNext != NULL) { if (pBaseEffect->pPrev != NULL || pBaseEffect->pNext != NULL) {
return MA_INVALID_OPERATION; return MA_INVALID_OPERATION;
} }
MA_ASSERT(pEffect->pPrev == NULL); MA_ASSERT(pBaseEffect->pPrev == NULL);
MA_ASSERT(pEffect->pNext == NULL); MA_ASSERT(pBaseEffect->pNext == NULL);
/* Update the effect first. */ /* Update the effect first. */
pEffect->pPrev = pParent; pBaseEffect->pPrev = pBaseParent;
pEffect->pNext = pParent->pNext; pBaseEffect->pNext = pBaseParent->pNext;
/* Now update the parent. Slot the effect between the parent and the parent's next item, if it has one. */ /* Now update the parent. Slot the effect between the parent and the parent's next item, if it has one. */
if (pParent->pNext != NULL) { if (pBaseParent->pNext != NULL) {
pParent->pNext->pPrev = pEffect; pBaseParent->pNext->pPrev = pEffect;
} }
pParent->pNext = pEffect; pBaseParent->pNext = pEffect;
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_result ma_effect_prepend(ma_effect* pEffect, ma_effect* pChild) ma_result ma_effect_prepend(ma_effect* pEffect, ma_effect* pChild)
{ {
ma_effect_base* pBaseEffect = (ma_effect_base*)pEffect;
ma_effect_base* pBaseChild = (ma_effect_base*)pChild;
if (pChild == NULL || pChild == NULL || pEffect == pChild) { if (pChild == NULL || pChild == NULL || pEffect == pChild) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
/* The effect must be detached before reinserting into the list. */ /* The effect must be detached before reinserting into the list. */
if (pEffect->pPrev != NULL || pEffect->pNext != NULL) { if (pBaseEffect->pPrev != NULL || pBaseEffect->pNext != NULL) {
return MA_INVALID_OPERATION; return MA_INVALID_OPERATION;
} }
MA_ASSERT(pEffect->pPrev == NULL); MA_ASSERT(pBaseEffect->pPrev == NULL);
MA_ASSERT(pEffect->pNext == NULL); MA_ASSERT(pBaseEffect->pNext == NULL);
/* Update the effect first. */ /* Update the effect first. */
pEffect->pNext = pChild; pBaseEffect->pNext = pBaseChild;
pEffect->pPrev = pChild->pPrev; pBaseEffect->pPrev = pBaseChild->pPrev;
/* Now update the child. Slot the effect between the child and the child's previous item, if it has one. */ /* Now update the child. Slot the effect between the child and the child's previous item, if it has one. */
if (pChild->pPrev != NULL) { if (pBaseChild->pPrev != NULL) {
pChild->pPrev->pNext = pEffect; pBaseChild->pPrev->pNext = pEffect;
} }
pChild->pPrev = pEffect; pBaseChild->pPrev = pEffect;
return MA_SUCCESS; return MA_SUCCESS;
} }
ma_result ma_effect_detach(ma_effect* pEffect) ma_result ma_effect_detach(ma_effect* pEffect)
{ {
if (pEffect == NULL) { ma_effect_base* pBaseEffect = (ma_effect_base*)pEffect;
if (pBaseEffect == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
if (pEffect->pPrev != NULL) { if (pBaseEffect->pPrev != NULL) {
pEffect->pPrev->pNext = pEffect->pNext; pBaseEffect->pPrev->pNext = pBaseEffect->pNext;
pEffect->pPrev = NULL; pBaseEffect->pPrev = NULL;
} }
if (pEffect->pNext != NULL) { if (pBaseEffect->pNext != NULL) {
pEffect->pNext->pPrev = pEffect->pPrev; pBaseEffect->pNext->pPrev = pBaseEffect->pPrev;
pEffect->pNext = NULL; pBaseEffect->pNext = NULL;
} }
return MA_SUCCESS; return MA_SUCCESS;
} }
MA_API ma_result ma_effect_get_output_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels) MA_API ma_result ma_effect_get_output_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
{ {
ma_effect_base* pBaseEffect = (ma_effect_base*)pEffect;
ma_result result;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
if (pFormat != NULL) { if (pFormat != NULL) {
*pFormat = ma_format_unknown; *pFormat = ma_format_unknown;
} }
if (pChannels != NULL) { if (pChannels != NULL) {
*pChannels = 0; *pChannels = 0;
} }
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pEffect == NULL) { if (pBaseEffect == NULL || pBaseEffect->onGetOutputDataFormat == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
if (pFormat != NULL) { result = pBaseEffect->onGetOutputDataFormat(pEffect, &format, &channels, &sampleRate);
*pFormat = pEffect->formatOut; if (result != MA_SUCCESS) {
return result;
} }
if (pFormat != NULL) {
*pFormat = format;
}
if (pChannels != NULL) { if (pChannels != NULL) {
*pChannels = pEffect->channelsOut; *pChannels = channels;
}
if (pSampleRate != NULL) {
*pSampleRate = sampleRate;
} }
return MA_SUCCESS; return MA_SUCCESS;
} }
MA_API ma_result ma_effect_get_input_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels) MA_API ma_result ma_effect_get_input_data_format(ma_effect* pEffect, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate)
{ {
ma_effect* pRootEffect; ma_effect_base* pRootEffect;
ma_result result;
ma_format format;
ma_uint32 channels;
ma_uint32 sampleRate;
if (pFormat != NULL) { if (pFormat != NULL) {
*pFormat = ma_format_unknown; *pFormat = ma_format_unknown;
} }
if (pChannels != NULL) { if (pChannels != NULL) {
*pChannels = 0; *pChannels = 0;
} }
if (pSampleRate != NULL) {
*pSampleRate = 0;
}
if (pEffect == NULL) { pRootEffect = ma_effect_get_root(pEffect);
if (pRootEffect == NULL || pRootEffect->onGetInputDataFormat == NULL) {
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
pRootEffect = ma_effect_get_root(pEffect); result = pRootEffect->onGetInputDataFormat(pRootEffect, &format, &channels, &sampleRate);
if (result != MA_SUCCESS) {
return result;
}
if (pFormat != NULL) { if (pFormat != NULL) {
*pFormat = pRootEffect->formatIn; *pFormat = format;
} }
if (pChannels != NULL) { if (pChannels != NULL) {
*pChannels = pRootEffect->channelsIn; *pChannels = channels;
}
if (pSampleRate != NULL) {
*pSampleRate = sampleRate;
} }
return MA_SUCCESS; return MA_SUCCESS;
...@@ -2090,8 +1734,8 @@ static ma_result ma_volume_and_clip_and_effect_pcm_frames(void* pDst, ma_format ...@@ -2090,8 +1734,8 @@ static ma_result ma_volume_and_clip_and_effect_pcm_frames(void* pDst, ma_format
} }
/* We need to know the effect's input and output formats so we can do pre- and post-effect data conversion if necessary. */ /* We need to know the effect's input and output formats so we can do pre- and post-effect data conversion if necessary. */
ma_effect_get_input_data_format( pEffect, &effectFormatIn, &effectChannelsIn ); ma_effect_get_input_data_format( pEffect, &effectFormatIn, &effectChannelsIn, NULL);
ma_effect_get_output_data_format(pEffect, &effectFormatOut, &effectChannelsOut); ma_effect_get_output_data_format(pEffect, &effectFormatOut, &effectChannelsOut, NULL);
effectBufferInCapInFrames = sizeof(effectBufferIn ) / ma_get_bytes_per_frame(effectFormatIn, effectChannelsIn ); effectBufferInCapInFrames = sizeof(effectBufferIn ) / ma_get_bytes_per_frame(effectFormatIn, effectChannelsIn );
effectBufferOutCapInFrames = sizeof(effectBufferOut) / ma_get_bytes_per_frame(effectFormatOut, effectChannelsOut); effectBufferOutCapInFrames = sizeof(effectBufferOut) / ma_get_bytes_per_frame(effectFormatOut, effectChannelsOut);
...@@ -2741,6 +2385,10 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source* ...@@ -2741,6 +2385,10 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source*
ma_uint64 totalFramesProcessed = 0; ma_uint64 totalFramesProcessed = 0;
void* pRunningAccumulationBuffer = pMixer->pAccumulationBuffer; void* pRunningAccumulationBuffer = pMixer->pAccumulationBuffer;
ma_bool32 preEffectConversionRequired = MA_FALSE; ma_bool32 preEffectConversionRequired = MA_FALSE;
ma_format effectFormatIn = ma_format_unknown;
ma_uint32 effectChannelsIn = 0;
ma_format effectFormatOut = ma_format_unknown;
ma_uint32 effectChannelsOut = 0;
MA_ASSERT(pMixer != NULL); MA_ASSERT(pMixer != NULL);
MA_ASSERT(pDataSource != NULL); MA_ASSERT(pDataSource != NULL);
...@@ -2750,7 +2398,18 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source* ...@@ -2750,7 +2398,18 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source*
} }
if (pEffect != NULL) { if (pEffect != NULL) {
preEffectConversionRequired = (formatIn != pEffect->formatIn || channelsIn != pEffect->channelsIn); /* We need to know the effect's input and output data format before we'll be able to apply it properly. */
result = ma_effect_get_input_data_format(pEffect, &effectFormatIn, &effectChannelsIn, NULL);
if (result != MA_SUCCESS) {
return result;
}
result = ma_effect_get_output_data_format(pEffect, &effectFormatOut, &effectChannelsOut, NULL);
if (result != MA_SUCCESS) {
return result;
}
preEffectConversionRequired = (formatIn != effectFormatIn || channelsIn != effectChannelsIn);
} }
while (totalFramesProcessed < frameCount) { while (totalFramesProcessed < frameCount) {
...@@ -2771,8 +2430,8 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source* ...@@ -2771,8 +2430,8 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source*
/* Slow path. Need to apply an effect. This requires the use of an intermediary buffer. */ /* Slow path. Need to apply an effect. This requires the use of an intermediary buffer. */
ma_uint8 effectInBuffer [MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 effectInBuffer [MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint8 effectOutBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 effectOutBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 effectInBufferCap = sizeof(effectInBuffer) / ma_get_bytes_per_frame(pEffect->formatIn, pEffect->channelsIn); ma_uint32 effectInBufferCap = sizeof(effectInBuffer) / ma_get_bytes_per_frame(effectFormatIn, effectChannelsIn);
ma_uint32 effectOutBufferCap = sizeof(effectOutBuffer) / ma_get_bytes_per_frame(pEffect->formatOut, pEffect->channelsOut); ma_uint32 effectOutBufferCap = sizeof(effectOutBuffer) / ma_get_bytes_per_frame(effectFormatOut, effectChannelsOut);
ma_uint64 framesMapped; ma_uint64 framesMapped;
if (framesToProcess > effectOutBufferCap) { if (framesToProcess > effectOutBufferCap) {
...@@ -2796,12 +2455,12 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source* ...@@ -2796,12 +2455,12 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source*
ma_effect_process_pcm_frames(pEffect, pMappedBuffer, &framesMapped, effectOutBuffer, &framesToProcess); ma_effect_process_pcm_frames(pEffect, pMappedBuffer, &framesMapped, effectOutBuffer, &framesToProcess);
} else { } else {
/* Slow path. Need to convert the data before applying the effect. */ /* Slow path. Need to convert the data before applying the effect. */
ma_convert_pcm_frames_format_and_channels(effectInBuffer, pEffect->formatIn, pEffect->channelsIn, pMappedBuffer, formatIn, channelsIn, framesMapped, ma_dither_mode_none); ma_convert_pcm_frames_format_and_channels(effectInBuffer, effectFormatIn, effectChannelsIn, pMappedBuffer, formatIn, channelsIn, framesMapped, ma_dither_mode_none);
ma_effect_process_pcm_frames(pEffect, effectInBuffer, &framesMapped, effectOutBuffer, &framesToProcess); ma_effect_process_pcm_frames(pEffect, effectInBuffer, &framesMapped, effectOutBuffer, &framesToProcess);
} }
/* The effect has been applied so now we can mix it. */ /* The effect has been applied so now we can mix it. */
ma_mix_pcm_frames_ex(pRunningAccumulationBuffer, pMixer->format, pMixer->channels, effectOutBuffer, pEffect->formatOut, pEffect->channelsOut, framesToProcess, volume); ma_mix_pcm_frames_ex(pRunningAccumulationBuffer, pMixer->format, pMixer->channels, effectOutBuffer, effectFormatOut, effectChannelsOut, framesToProcess, volume);
/* We're finished with the input data. */ /* We're finished with the input data. */
result = ma_data_source_unmap(pDataSource, framesMapped); /* Do this last because the result code is used below to determine whether or not we need to loop. */ result = ma_data_source_unmap(pDataSource, framesMapped); /* Do this last because the result code is used below to determine whether or not we need to loop. */
...@@ -2828,12 +2487,15 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source* ...@@ -2828,12 +2487,15 @@ static ma_result ma_mixer_mix_data_source_mmap(ma_mixer* pMixer, ma_data_source*
static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source* pDataSource, ma_uint64 frameCount, float volume, ma_effect* pEffect, ma_format formatIn, ma_uint32 channelsIn, ma_bool32 loop) static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source* pDataSource, ma_uint64 frameCount, float volume, ma_effect* pEffect, ma_format formatIn, ma_uint32 channelsIn, ma_bool32 loop)
{ {
ma_result result;
ma_uint8 preMixBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 preMixBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 preMixBufferCap; ma_uint32 preMixBufferCap;
ma_uint64 totalFramesProcessed = 0; ma_uint64 totalFramesProcessed = 0;
void* pRunningAccumulationBuffer = pMixer->pAccumulationBuffer; void* pRunningAccumulationBuffer = pMixer->pAccumulationBuffer;
ma_format preMixFormat; ma_format effectFormatIn = ma_format_unknown;
ma_uint32 preMixChannels; ma_uint32 effectChannelsIn = 0;
ma_format preMixFormat = ma_format_unknown;
ma_uint32 preMixChannels = 0;
ma_bool32 preEffectConversionRequired = MA_FALSE; ma_bool32 preEffectConversionRequired = MA_FALSE;
ma_bool32 atEnd = MA_FALSE; ma_bool32 atEnd = MA_FALSE;
...@@ -2848,9 +2510,18 @@ static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source* ...@@ -2848,9 +2510,18 @@ static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source*
preMixFormat = formatIn; preMixFormat = formatIn;
preMixChannels = channelsIn; preMixChannels = channelsIn;
} else { } else {
preMixFormat = pEffect->formatOut; /* We need to know the effect's input and output data format before we'll be able to apply it properly. */
preMixChannels = pEffect->channelsOut; result = ma_effect_get_input_data_format(pEffect, &effectFormatIn, &effectChannelsIn, NULL);
preEffectConversionRequired = (formatIn != pEffect->formatIn || channelsIn != pEffect->channelsIn); if (result != MA_SUCCESS) {
return result;
}
result = ma_effect_get_output_data_format(pEffect, &preMixFormat, &preMixChannels, NULL);
if (result != MA_SUCCESS) {
return result;
}
preEffectConversionRequired = (formatIn != effectFormatIn || channelsIn != effectChannelsIn);
} }
preMixBufferCap = sizeof(preMixBuffer) / ma_get_bytes_per_frame(preMixFormat, preMixChannels); preMixBufferCap = sizeof(preMixBuffer) / ma_get_bytes_per_frame(preMixFormat, preMixChannels);
...@@ -2877,7 +2548,7 @@ static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source* ...@@ -2877,7 +2548,7 @@ static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source*
ma_uint8 callbackBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 callbackBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint8 effectInBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE]; ma_uint8 effectInBuffer[MA_DATA_CONVERTER_STACK_BUFFER_SIZE];
ma_uint32 callbackBufferCap = sizeof(callbackBuffer) / ma_get_bytes_per_frame(formatIn, channelsIn); ma_uint32 callbackBufferCap = sizeof(callbackBuffer) / ma_get_bytes_per_frame(formatIn, channelsIn);
ma_uint32 effectInBufferCap = sizeof(effectInBuffer) / ma_get_bytes_per_frame(pEffect->formatIn, pEffect->channelsIn); ma_uint32 effectInBufferCap = sizeof(effectInBuffer) / ma_get_bytes_per_frame(effectFormatIn, effectChannelsIn);
ma_uint64 effectFrameCountOut; ma_uint64 effectFrameCountOut;
ma_uint64 effectFrameCountIn; ma_uint64 effectFrameCountIn;
ma_uint64 framesReadFromCallback; ma_uint64 framesReadFromCallback;
...@@ -2899,7 +2570,7 @@ static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source* ...@@ -2899,7 +2570,7 @@ static ma_result ma_mixer_mix_data_source_read(ma_mixer* pMixer, ma_data_source*
} else { } else {
/* Slow path. Conversion between the callback and the effect required. */ /* Slow path. Conversion between the callback and the effect required. */
framesReadFromCallback = ma_data_source_read_pcm_frames(pDataSource, callbackBuffer, framesToReadFromCallback, loop); framesReadFromCallback = ma_data_source_read_pcm_frames(pDataSource, callbackBuffer, framesToReadFromCallback, loop);
ma_convert_pcm_frames_format_and_channels(effectInBuffer, pEffect->formatIn, pEffect->channelsIn, callbackBuffer, formatIn, channelsIn, framesReadFromCallback, ma_dither_mode_none); ma_convert_pcm_frames_format_and_channels(effectInBuffer, effectFormatIn, effectChannelsIn, callbackBuffer, formatIn, channelsIn, framesReadFromCallback, ma_dither_mode_none);
} }
/* We have our input data for the effect so now we just process as much as we can based on our input and output frame counts. */ /* We have our input data for the effect so now we just process as much as we can based on our input and output frame counts. */
...@@ -3151,23 +2822,20 @@ MA_API ma_result ma_mixer_get_output_data_format(ma_mixer* pMixer, ma_format* pF ...@@ -3151,23 +2822,20 @@ MA_API ma_result ma_mixer_get_output_data_format(ma_mixer* pMixer, ma_format* pF
return MA_INVALID_ARGS; return MA_INVALID_ARGS;
} }
if (pFormat != NULL) { /* If we have an effect, the output data format will be the effect's output data format. */
if (pMixer->pEffect != NULL) { if (pMixer->pEffect != NULL) {
*pFormat = pMixer->pEffect->formatIn; return ma_effect_get_output_data_format(pMixer->pEffect, pFormat, pChannels, NULL);
} else { } else {
if (pFormat != NULL) {
*pFormat = pMixer->format; *pFormat = pMixer->format;
} }
}
if (pChannels != NULL) { if (pChannels != NULL) {
if (pMixer->pEffect != NULL) {
*pChannels = pMixer->pEffect->channelsIn;
} else {
*pChannels = pMixer->channels; *pChannels = pMixer->channels;
} }
}
return MA_SUCCESS; return MA_SUCCESS;
}
} }
MA_API ma_result ma_mixer_get_input_data_format(ma_mixer* pMixer, ma_format* pFormat, ma_uint32* pChannels) MA_API ma_result ma_mixer_get_input_data_format(ma_mixer* pMixer, ma_format* pFormat, ma_uint32* pChannels)
......
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