Commit cbcf6f6c authored by David Reid's avatar David Reid

Early work on new channel converter.

parent 5aff4af6
...@@ -30773,7 +30773,7 @@ float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = { ...@@ -30773,7 +30773,7 @@ float g_maChannelPlaneRatios[MA_CHANNEL_POSITION_COUNT][6] = {
{ 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */ { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f}, /* MA_CHANNEL_AUX_31 */
}; };
float ma_calculate_channel_position_planar_weight(ma_channel channelPositionA, ma_channel channelPositionB) float ma_calculate_channel_position_rectangular_weight(ma_channel channelPositionA, ma_channel channelPositionB)
{ {
/* /*
Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to Imagine the following simplified example: You have a single input speaker which is the front/left speaker which you want to convert to
...@@ -30821,7 +30821,7 @@ float ma_channel_router__calculate_input_channel_planar_weight(const ma_channel_ ...@@ -30821,7 +30821,7 @@ float ma_channel_router__calculate_input_channel_planar_weight(const ma_channel_
ma_assert(pRouter != NULL); ma_assert(pRouter != NULL);
(void)pRouter; (void)pRouter;
return ma_calculate_channel_position_planar_weight(channelPositionIn, channelPositionOut); return ma_calculate_channel_position_rectangular_weight(channelPositionIn, channelPositionOut);
} }
ma_bool32 ma_channel_router__is_spatial_channel_position(const ma_channel_router* pRouter, ma_channel channelPosition) ma_bool32 ma_channel_router__is_spatial_channel_position(const ma_channel_router* pRouter, ma_channel channelPosition)
...@@ -5,6 +5,44 @@ ...@@ -5,6 +5,44 @@
#include "ma_resampler.h" #include "ma_resampler.h"
typedef struct
{
ma_format format;
ma_uint32 channelsIn;
ma_uint32 channelsOut;
ma_channel channelMapIn[MA_MAX_CHANNELS];
ma_channel channelMapOut[MA_MAX_CHANNELS];
ma_channel_mix_mode mixingMode;
float weights[MA_MAX_CHANNELS][MA_MAX_CHANNELS]; /* [in][out]. Only used when mixingMode is set to ma_channel_mix_mode_custom_weights. */
} ma_channel_converter_config;
ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode);
typedef struct
{
ma_format format;
ma_uint32 channelsIn;
ma_uint32 channelsOut;
ma_channel channelMapIn[MA_MAX_CHANNELS];
ma_channel channelMapOut[MA_MAX_CHANNELS];
ma_channel_mix_mode mixingMode;
union
{
float f32[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
ma_int32 s16[MA_MAX_CHANNELS][MA_MAX_CHANNELS];
} weights;
ma_bool32 isPassthrough : 1;
ma_bool32 isSimpleShuffle : 1;
ma_bool32 isSimpleMonoExpansion : 1;
ma_bool32 isStereoToMono : 1;
ma_uint8 shuffleTable[MA_MAX_CHANNELS];
} ma_channel_converter;
ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter);
void ma_channel_converter_uninit(ma_channel_converter* pConverter);
ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount);
typedef struct typedef struct
{ {
ma_format formatIn; ma_format formatIn;
...@@ -61,6 +99,541 @@ ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConverter); ...@@ -61,6 +99,541 @@ ma_uint64 ma_data_converter_get_output_latency(ma_data_converter* pConverter);
#define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096 #define MA_DATA_CONVERTER_STACK_BUFFER_SIZE 4096
#endif #endif
#ifndef MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT
#define MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT 12
#endif
ma_channel_converter_config ma_channel_converter_config_init(ma_format format, ma_uint32 channelsIn, const ma_channel channelMapIn[MA_MAX_CHANNELS], ma_uint32 channelsOut, const ma_channel channelMapOut[MA_MAX_CHANNELS], ma_channel_mix_mode mixingMode)
{
ma_channel_converter_config config;
MA_ZERO_OBJECT(&config);
config.format = format;
config.channelsIn = channelsIn;
config.channelsOut = channelsOut;
ma_channel_map_copy(config.channelMapIn, channelMapIn, channelsIn);
ma_channel_map_copy(config.channelMapOut, channelMapOut, channelsOut);
config.mixingMode = mixingMode;
return config;
}
static ma_int32 ma_channel_converter_float_to_fp(float x)
{
return (ma_int32)(x * (1<<MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT));
}
static ma_bool32 ma_is_spatial_channel_position(ma_channel channelPosition)
{
int i;
if (channelPosition == MA_CHANNEL_NONE || channelPosition == MA_CHANNEL_MONO || channelPosition == MA_CHANNEL_LFE) {
return MA_FALSE;
}
for (i = 0; i < 6; ++i) { /* Each side of a cube. */
if (g_maChannelPlaneRatios[channelPosition][i] != 0) {
return MA_TRUE;
}
}
return MA_FALSE;
}
ma_result ma_channel_converter_init(const ma_channel_converter_config* pConfig, ma_channel_converter* pConverter)
{
ma_uint32 iChannelIn;
ma_uint32 iChannelOut;
if (pConverter == NULL) {
return MA_INVALID_ARGS;
}
MA_ZERO_OBJECT(pConverter);
if (pConfig == NULL) {
return MA_INVALID_ARGS;
}
if (!ma_channel_map_valid(pConfig->channelsIn, pConfig->channelMapIn)) {
return MA_INVALID_ARGS; /* Invalid input channel map. */
}
if (!ma_channel_map_valid(pConfig->channelsOut, pConfig->channelMapOut)) {
return MA_INVALID_ARGS; /* Invalid output channel map. */
}
if (pConfig->format != ma_format_s16 && pConfig->format != ma_format_f32) {
return MA_INVALID_ARGS; /* Invalid format. */
}
pConverter->format = pConfig->format;
pConverter->channelsIn = pConfig->channelsIn;
pConverter->channelsOut = pConfig->channelsOut;
ma_channel_map_copy(pConverter->channelMapIn, pConfig->channelMapIn, pConfig->channelsIn);
ma_channel_map_copy(pConverter->channelMapOut, pConfig->channelMapOut, pConfig->channelsOut);
pConverter->mixingMode = pConfig->mixingMode;
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; iChannelIn += 1) {
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
if (pConverter->format == ma_format_s16) {
pConverter->weights.f32[iChannelIn][iChannelOut] = pConfig->weights[iChannelIn][iChannelOut];
} else {
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(pConfig->weights[iChannelIn][iChannelOut]);
}
}
}
/* If the input and output channels and channel maps are the same we should use a passthrough. */
if (pConverter->channelsIn == pConverter->channelsOut) {
if (ma_channel_map_equal(pConverter->channelsIn, pConverter->channelMapIn, pConverter->channelMapOut)) {
pConverter->isPassthrough = MA_TRUE;
}
if (ma_channel_map_blank(pConverter->channelsIn, pConverter->channelMapIn) || ma_channel_map_blank(pConverter->channelsOut, pConverter->channelMapOut)) {
pConverter->isPassthrough = MA_TRUE;
}
}
/*
We can use a simple case for expanding the mono channel. This will used when expanding a mono input into any output so long
as no LFE is present in the output.
*/
if (!pConverter->isPassthrough) {
if (pConverter->channelsIn == 1 && pConverter->channelMapIn[0] == MA_CHANNEL_MONO) {
/* Optimal case if no LFE is in the output channel map. */
pConverter->isSimpleMonoExpansion = MA_TRUE;
if (ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, MA_CHANNEL_LFE)) {
pConverter->isSimpleMonoExpansion = MA_FALSE;
}
}
}
/* Another optimized case is stereo to mono. */
if (!pConverter->isPassthrough) {
if (pConverter->channelsOut == 1 && pConverter->channelMapOut[0] == MA_CHANNEL_MONO && pConverter->channelsIn == 2) {
/* Optimal case if no LFE is in the input channel map. */
pConverter->isStereoToMono = MA_TRUE;
if (ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, MA_CHANNEL_LFE)) {
pConverter->isStereoToMono = MA_FALSE;
}
}
}
/*
Here is where we do a bit of pre-processing to know how each channel should be combined to make up the output. Rules:
1) If it's a passthrough, do nothing - it's just a simple memcpy().
2) If the channel counts are the same and every channel position in the input map is present in the output map, use a
simple shuffle. An example might be different 5.1 channel layouts.
3) Otherwise channels are blended based on spatial locality.
*/
if (!pConverter->isPassthrough) {
if (pConverter->channelsIn == pConverter->channelsOut) {
ma_bool32 areAllChannelPositionsPresent = MA_TRUE;
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_bool32 isInputChannelPositionInOutput = MA_FALSE;
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
isInputChannelPositionInOutput = MA_TRUE;
break;
}
}
if (!isInputChannelPositionInOutput) {
areAllChannelPositionsPresent = MA_FALSE;
break;
}
}
if (areAllChannelPositionsPresent) {
pConverter->isSimpleShuffle = MA_TRUE;
/*
All the router will be doing is rearranging channels which means all we need to do is use a shuffling table which is just
a mapping between the index of the input channel to the index of the output channel.
*/
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
if (pConverter->channelMapIn[iChannelIn] == pConverter->channelMapOut[iChannelOut]) {
pConverter->shuffleTable[iChannelIn] = (ma_uint8)iChannelOut;
break;
}
}
}
}
}
}
/*
Here is where weights are calculated. Note that we calculate the weights at all times, even when using a passthrough and simple
shuffling. We use different algorithms for calculating weights depending on our mixing mode.
In simple mode we don't do any blending (except for converting between mono, which is done in a later step). Instead we just
map 1:1 matching channels. In this mode, if no channels in the input channel map correspond to anything in the output channel
map, nothing will be heard!
*/
/* In all cases we need to make sure all channels that are present in both channel maps have a 1:1 mapping. */
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
if (channelPosIn == channelPosOut) {
if (pConverter->format == ma_format_s16) {
pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
} else {
pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
}
}
}
}
/*
The mono channel is accumulated on all other channels, except LFE. Make sure in this loop we exclude output mono channels since
they were handled in the pass above.
*/
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
if (channelPosIn == MA_CHANNEL_MONO) {
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
if (channelPosOut != MA_CHANNEL_NONE && channelPosOut != MA_CHANNEL_MONO && channelPosOut != MA_CHANNEL_LFE) {
if (pConverter->format == ma_format_s16) {
pConverter->weights.s16[iChannelIn][iChannelOut] = (1 << MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT);
} else {
pConverter->weights.f32[iChannelIn][iChannelOut] = 1;
}
}
}
}
}
/* The output mono channel is the average of all non-none, non-mono and non-lfe input channels. */
{
ma_uint32 len = 0;
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
len += 1;
}
}
if (len > 0) {
float monoWeight = 1.0f / len;
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
if (channelPosOut == MA_CHANNEL_MONO) {
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
if (channelPosIn != MA_CHANNEL_NONE && channelPosIn != MA_CHANNEL_MONO && channelPosIn != MA_CHANNEL_LFE) {
if (pConverter->format == ma_format_s16) {
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(monoWeight);
} else {
pConverter->weights.f32[iChannelIn][iChannelOut] = monoWeight;
}
}
}
}
}
}
}
/* Input and output channels that are not present on the other side need to be blended in based on spatial locality. */
switch (pConverter->mixingMode)
{
case ma_channel_mix_mode_rectangular:
{
/* Unmapped input channels. */
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
if (ma_is_spatial_channel_position(channelPosIn)) {
if (!ma_channel_map_contains_channel_position(pConverter->channelsOut, pConverter->channelMapOut, channelPosIn)) {
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
if (ma_is_spatial_channel_position(channelPosOut)) {
float weight = 0;
if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
}
/* Only apply the weight if we haven't already got some contribution from the respective channels. */
if (pConverter->format == ma_format_s16) {
if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(weight);
}
} else {
if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
}
}
}
}
}
}
}
/* Unmapped output channels. */
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
ma_channel channelPosOut = pConverter->channelMapOut[iChannelOut];
if (ma_is_spatial_channel_position(channelPosOut)) {
if (!ma_channel_map_contains_channel_position(pConverter->channelsIn, pConverter->channelMapIn, channelPosOut)) {
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
ma_channel channelPosIn = pConverter->channelMapIn[iChannelIn];
if (ma_is_spatial_channel_position(channelPosIn)) {
float weight = 0;
if (pConverter->mixingMode == ma_channel_mix_mode_rectangular) {
weight = ma_calculate_channel_position_rectangular_weight(channelPosIn, channelPosOut);
}
/* Only apply the weight if we haven't already got some contribution from the respective channels. */
if (pConverter->format == ma_format_s16) {
if (pConverter->weights.s16[iChannelIn][iChannelOut] == 0) {
pConverter->weights.s16[iChannelIn][iChannelOut] = ma_channel_converter_float_to_fp(weight);
}
} else {
if (pConverter->weights.f32[iChannelIn][iChannelOut] == 0) {
pConverter->weights.f32[iChannelIn][iChannelOut] = weight;
}
}
}
}
}
}
}
} break;
case ma_channel_mix_mode_custom_weights:
case ma_channel_mix_mode_simple:
default:
{
/* Fallthrough. */
} break;
}
return MA_SUCCESS;
}
void ma_channel_converter_uninit(ma_channel_converter* pConverter)
{
if (pConverter == NULL) {
return;
}
}
static ma_result ma_channel_converter_process_pcm_frames__passthrough(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
MA_ASSERT(pConverter != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFramesIn != NULL);
ma_copy_memory_64(pFramesOut, pFramesIn, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
return MA_SUCCESS;
}
static ma_result ma_channel_converter_process_pcm_frames__simple_shuffle(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
ma_uint32 iFrame;
ma_uint32 iChannelIn;
MA_ASSERT(pConverter != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFramesIn != NULL);
MA_ASSERT(pConverter->channelsIn == pConverter->channelsOut);
if (pConverter->format == ma_format_s16) {
/* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
pFramesOutS16[pConverter->shuffleTable[iChannelIn]] = pFramesInS16[iChannelIn];
}
}
} else {
/* */ float* pFramesOutF32 = ( float*)pFramesOut;
const float* pFramesInF32 = (const float*)pFramesIn;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
pFramesOutF32[pConverter->shuffleTable[iChannelIn]] = pFramesInF32[iChannelIn];
}
}
}
return MA_SUCCESS;
}
static ma_result ma_channel_converter_process_pcm_frames__simple_mono_expansion(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
ma_uint64 iFrame;
MA_ASSERT(pConverter != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFramesIn != NULL);
if (pConverter->format == ma_format_s16) {
/* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
if (pConverter->channelsOut == 2) {
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
pFramesOutS16[iFrame*2 + 0] = pFramesInS16[iFrame];
pFramesOutS16[iFrame*2 + 1] = pFramesInS16[iFrame];
}
} else {
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
ma_uint32 iChannel;
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
pFramesOutS16[iFrame*pConverter->channelsOut + iChannel] = pFramesInS16[iFrame];
}
}
}
} else {
/* */ float* pFramesOutF32 = ( float*)pFramesOut;
const float* pFramesInF32 = (const float*)pFramesIn;
if (pConverter->channelsOut == 2) {
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
pFramesOutF32[iFrame*2 + 0] = pFramesInF32[iFrame];
pFramesOutF32[iFrame*2 + 1] = pFramesInF32[iFrame];
}
} else {
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
ma_uint32 iChannel;
for (iChannel = 0; iChannel < pConverter->channelsOut; iChannel += 1) {
pFramesOutF32[iFrame*pConverter->channelsOut + iChannel] = pFramesInF32[iFrame];
}
}
}
}
return MA_SUCCESS;
}
static ma_result ma_channel_converter_process_pcm_frames__stereo_to_mono(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
ma_uint64 iFrame;
MA_ASSERT(pConverter != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFramesIn != NULL);
MA_ASSERT(pConverter->channelsIn == 2);
MA_ASSERT(pConverter->channelsOut == 1);
if (pConverter->format == ma_format_s16) {
/* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
pFramesOutS16[iFrame] = (ma_int16)(((ma_int32)pFramesInS16[iFrame*2+0] + (ma_int32)pFramesInS16[iFrame*2+1]) / 2);
}
} else {
/* */ float* pFramesOutF32 = ( float*)pFramesOut;
const float* pFramesInF32 = (const float*)pFramesIn;
for (iFrame = 0; iFrame < frameCount; ++iFrame) {
pFramesOutF32[iFrame] = (pFramesInF32[iFrame*2+0] + pFramesInF32[iFrame*2+0]) * 0.5f;
}
}
return MA_SUCCESS;
}
static ma_result ma_channel_converter_process_pcm_frames__weights(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
ma_uint32 iFrame;
ma_uint32 iChannelIn;
ma_uint32 iChannelOut;
MA_ASSERT(pConverter != NULL);
MA_ASSERT(pFramesOut != NULL);
MA_ASSERT(pFramesIn != NULL);
/* This is the more complicated case. Each of the output channels is accumulated with 0 or more input channels. */
/* Clear. */
ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
/* Accumulate. */
if (pConverter->format == ma_format_s16) {
/* */ ma_int16* pFramesOutS16 = ( ma_int16*)pFramesOut;
const ma_int16* pFramesInS16 = (const ma_int16*)pFramesIn;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
ma_int32 s = pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut];
s += (pFramesInS16[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.s16[iChannelIn][iChannelOut]) >> MA_CHANNEL_CONVERTER_FIXED_POINT_SHIFT;
pFramesOutS16[iFrame*pConverter->channelsOut + iChannelOut] = (ma_int16)ma_clamp(s, -32768, 32767);
}
}
}
} else {
/* */ float* pFramesOutF32 = ( float*)pFramesOut;
const float* pFramesInF32 = (const float*)pFramesIn;
for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
for (iChannelIn = 0; iChannelIn < pConverter->channelsIn; ++iChannelIn) {
for (iChannelOut = 0; iChannelOut < pConverter->channelsOut; ++iChannelOut) {
pFramesOutF32[iFrame*pConverter->channelsOut + iChannelOut] += pFramesInF32[iFrame*pConverter->channelsIn + iChannelIn] * pConverter->weights.f32[iChannelIn][iChannelOut];
}
}
}
}
return MA_SUCCESS;
}
ma_result ma_channel_converter_process_pcm_frames(ma_channel_converter* pConverter, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount)
{
if (pConverter == NULL) {
return MA_INVALID_ARGS;
}
if (pFramesOut == NULL) {
return MA_INVALID_ARGS;
}
if (pFramesIn == NULL) {
ma_zero_memory_64(pFramesOut, frameCount * ma_get_bytes_per_frame(pConverter->format, pConverter->channelsOut));
return MA_SUCCESS;
}
if (pConverter->isPassthrough) {
return ma_channel_converter_process_pcm_frames__passthrough(pConverter, pFramesOut, pFramesIn, frameCount);
} else if (pConverter->isSimpleShuffle) {
return ma_channel_converter_process_pcm_frames__simple_shuffle(pConverter, pFramesOut, pFramesIn, frameCount);
} else if (pConverter->isSimpleMonoExpansion) {
return ma_channel_converter_process_pcm_frames__simple_mono_expansion(pConverter, pFramesOut, pFramesIn, frameCount);
} else if (pConverter->isStereoToMono) {
return ma_channel_converter_process_pcm_frames__stereo_to_mono(pConverter, pFramesOut, pFramesIn, frameCount);
} else {
return ma_channel_converter_process_pcm_frames__weights(pConverter, pFramesOut, pFramesIn, frameCount);
}
return MA_SUCCESS;
}
ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut) ma_data_converter_config ma_data_converter_config_init(ma_format formatIn, ma_format formatOut, ma_uint32 channelsIn, ma_uint32 channelsOut, ma_uint32 sampleRateIn, ma_uint32 sampleRateOut)
{ {
ma_data_converter_config config; ma_data_converter_config config;
...@@ -156,8 +729,8 @@ ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_dat ...@@ -156,8 +729,8 @@ ma_result ma_data_converter_init(const ma_data_converter_config* pConfig, ma_dat
/* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */ /* We can enable passthrough optimizations if applicable. Note that we'll only be able to do this if the sample rate is static. */
if (pConverter->hasPreFormatConversion == MA_FALSE && if (pConverter->hasPreFormatConversion == MA_FALSE &&
pConverter->hasPostFormatConversion == MA_FALSE && pConverter->hasPostFormatConversion == MA_FALSE &&
pConverter->hasChannelRouter == MA_FALSE && pConverter->hasChannelRouter == MA_FALSE &&
pConverter->hasResampler == MA_FALSE) { pConverter->hasResampler == MA_FALSE) {
pConverter->isPassthrough = MA_TRUE; pConverter->isPassthrough = MA_TRUE;
} }
...@@ -254,7 +827,7 @@ static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_conve ...@@ -254,7 +827,7 @@ static ma_result ma_data_converter_process_pcm_frames__format_only(ma_data_conve
static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conversion(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
{ {
ma_result result; ma_result result = MA_SUCCESS;
ma_uint64 frameCountIn; ma_uint64 frameCountIn;
ma_uint64 frameCountOut; ma_uint64 frameCountOut;
ma_uint64 framesProcessedIn; ma_uint64 framesProcessedIn;
...@@ -326,7 +899,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv ...@@ -326,7 +899,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv
} }
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; break;
} }
} else { } else {
/* No pre-format required. Just read straight from the input buffer. */ /* No pre-format required. Just read straight from the input buffer. */
...@@ -340,7 +913,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv ...@@ -340,7 +913,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv
result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration); result = ma_resampler_process_pcm_frames(&pConverter->resampler, pFramesInThisIteration, &frameCountInThisIteration, pTempBufferOut, &frameCountOutThisIteration);
if (result != MA_SUCCESS) { if (result != MA_SUCCESS) {
return result; break;
} }
} }
...@@ -362,7 +935,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv ...@@ -362,7 +935,7 @@ static ma_result ma_data_converter_process_pcm_frames__resample_with_format_conv
*pFrameCountOut = framesProcessedOut; *pFrameCountOut = framesProcessedOut;
} }
return MA_SUCCESS; return result;
} }
static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut) static ma_result ma_data_converter_process_pcm_frames__resample_only(ma_data_converter* pConverter, const void* pFramesIn, ma_uint64* pFrameCountIn, void* pFramesOut, ma_uint64* pFrameCountOut)
......
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