mirror of
https://github.com/mackron/miniaudio.git
synced 2026-04-23 16:54:03 +02:00
Add support for custom weights to the channel router.
This commit is contained in:
@@ -577,6 +577,7 @@ typedef enum
|
|||||||
{
|
{
|
||||||
mal_channel_mix_mode_rectangular = 0, // Simple averaging based on the plane(s) the channel is sitting on.
|
mal_channel_mix_mode_rectangular = 0, // Simple averaging based on the plane(s) the channel is sitting on.
|
||||||
mal_channel_mix_mode_simple, // Drop excess channels; zeroed out extra channels.
|
mal_channel_mix_mode_simple, // Drop excess channels; zeroed out extra channels.
|
||||||
|
mal_channel_mix_mode_custom_weights, // Use custom weights specified in mal_channel_router_config.
|
||||||
mal_channel_mix_mode_planar_blend = mal_channel_mix_mode_rectangular,
|
mal_channel_mix_mode_planar_blend = mal_channel_mix_mode_rectangular,
|
||||||
mal_channel_mix_mode_default = mal_channel_mix_mode_planar_blend
|
mal_channel_mix_mode_default = mal_channel_mix_mode_planar_blend
|
||||||
} mal_channel_mix_mode;
|
} mal_channel_mix_mode;
|
||||||
@@ -645,6 +646,7 @@ typedef struct
|
|||||||
mal_channel channelMapIn[MAL_MAX_CHANNELS];
|
mal_channel channelMapIn[MAL_MAX_CHANNELS];
|
||||||
mal_channel channelMapOut[MAL_MAX_CHANNELS];
|
mal_channel channelMapOut[MAL_MAX_CHANNELS];
|
||||||
mal_channel_mix_mode mixingMode;
|
mal_channel_mix_mode mixingMode;
|
||||||
|
float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS]; // [in][out]. Only used when mixingMode is set to mal_channel_mix_mode_custom_weights.
|
||||||
mal_bool32 noSSE2 : 1;
|
mal_bool32 noSSE2 : 1;
|
||||||
mal_bool32 noAVX2 : 1;
|
mal_bool32 noAVX2 : 1;
|
||||||
mal_bool32 noAVX512 : 1;
|
mal_bool32 noAVX512 : 1;
|
||||||
@@ -663,7 +665,6 @@ struct mal_channel_router
|
|||||||
mal_bool32 useAVX512 : 1;
|
mal_bool32 useAVX512 : 1;
|
||||||
mal_bool32 useNEON : 1;
|
mal_bool32 useNEON : 1;
|
||||||
mal_uint8 shuffleTable[MAL_MAX_CHANNELS];
|
mal_uint8 shuffleTable[MAL_MAX_CHANNELS];
|
||||||
float weights[MAL_MAX_CHANNELS][MAL_MAX_CHANNELS];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -918,8 +919,8 @@ mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_f
|
|||||||
// 2) Planar Blending
|
// 2) Planar Blending
|
||||||
// Channels are blended based on a set of planes that each speaker emits audio from.
|
// Channels are blended based on a set of planes that each speaker emits audio from.
|
||||||
//
|
//
|
||||||
// Planar Blending
|
// Rectangular / Planar Blending
|
||||||
// ---------------
|
// -----------------------------
|
||||||
// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
|
// In this mode, channel positions are associated with a set of planes where the channel conceptually emits audio from. An example is the front/left speaker.
|
||||||
// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
|
// This speaker is positioned to the front of the listener, so you can think of it as emitting audio from the front plane. It is also positioned to the left
|
||||||
// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
|
// of the listener so you can think of it as also emitting audio from the left plane. Now consider the (unrealistic) situation where the input channel map
|
||||||
@@ -949,7 +950,8 @@ mal_format_converter_config mal_format_converter_config_init_deinterleaved(mal_f
|
|||||||
// Note that input and output data is always deinterleaved 32-bit floating point.
|
// Note that input and output data is always deinterleaved 32-bit floating point.
|
||||||
//
|
//
|
||||||
// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
|
// Initialize the channel router with mal_channel_router_init(). You will need to pass in a config object which specifies the input and output configuration,
|
||||||
// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing.
|
// mixing mode and a callback for sending data to the router. This callback will be called when input data needs to be sent to the router for processing. Note
|
||||||
|
// that the mixing mode is only used when a 1:1 mapping is unavailable. This includes the custom weights mode.
|
||||||
//
|
//
|
||||||
// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
|
// Read data from the channel router with mal_channel_router_read_deinterleaved(). Output data is always 32-bit floating point.
|
||||||
//
|
//
|
||||||
@@ -24906,7 +24908,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
|
|||||||
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
||||||
|
|
||||||
if (channelPosIn == channelPosOut) {
|
if (channelPosIn == channelPosOut) {
|
||||||
pRouter->weights[iChannelIn][iChannelOut] = 1;
|
pRouter->config.weights[iChannelIn][iChannelOut] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24921,7 +24923,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
|
|||||||
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
||||||
|
|
||||||
if (channelPosOut != MAL_CHANNEL_NONE && channelPosOut != MAL_CHANNEL_MONO && channelPosOut != MAL_CHANNEL_LFE) {
|
if (channelPosOut != MAL_CHANNEL_NONE && channelPosOut != MAL_CHANNEL_MONO && channelPosOut != MAL_CHANNEL_LFE) {
|
||||||
pRouter->weights[iChannelIn][iChannelOut] = 1;
|
pRouter->config.weights[iChannelIn][iChannelOut] = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24949,7 +24951,7 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
|
|||||||
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
|
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
|
||||||
|
|
||||||
if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) {
|
if (channelPosIn != MAL_CHANNEL_NONE && channelPosIn != MAL_CHANNEL_MONO && channelPosIn != MAL_CHANNEL_LFE) {
|
||||||
pRouter->weights[iChannelIn][iChannelOut] += monoWeight;
|
pRouter->config.weights[iChannelIn][iChannelOut] += monoWeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -24959,56 +24961,67 @@ mal_result mal_channel_router_init(const mal_channel_router_config* pConfig, mal
|
|||||||
|
|
||||||
|
|
||||||
// Input and output channels that are not present on the other side need to be blended in based on spatial locality.
|
// Input and output channels that are not present on the other side need to be blended in based on spatial locality.
|
||||||
if (pRouter->config.mixingMode != mal_channel_mix_mode_simple) {
|
switch (pRouter->config.mixingMode)
|
||||||
// Unmapped input channels.
|
{
|
||||||
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
|
case mal_channel_mix_mode_rectangular:
|
||||||
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
|
{
|
||||||
|
// Unmapped input channels.
|
||||||
|
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
|
||||||
|
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
|
||||||
|
|
||||||
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
|
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
|
||||||
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
|
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) {
|
||||||
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
|
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
|
||||||
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
||||||
|
|
||||||
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
|
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
|
||||||
float weight = 0;
|
float weight = 0;
|
||||||
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
|
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
|
||||||
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
|
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only apply the weight if we haven't already got some contribution from the respective channels.
|
// Only apply the weight if we haven't already got some contribution from the respective channels.
|
||||||
if (pRouter->weights[iChannelIn][iChannelOut] == 0) {
|
if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
|
||||||
pRouter->weights[iChannelIn][iChannelOut] = weight;
|
pRouter->config.weights[iChannelIn][iChannelOut] = weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Unmapped output channels.
|
// Unmapped output channels.
|
||||||
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
|
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
|
||||||
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
|
||||||
|
|
||||||
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
|
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
|
||||||
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
|
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
|
||||||
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
|
for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
|
||||||
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
|
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
|
||||||
|
|
||||||
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
|
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) {
|
||||||
float weight = 0;
|
float weight = 0;
|
||||||
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
|
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
|
||||||
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
|
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only apply the weight if we haven't already got some contribution from the respective channels.
|
// Only apply the weight if we haven't already got some contribution from the respective channels.
|
||||||
if (pRouter->weights[iChannelIn][iChannelOut] == 0) {
|
if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
|
||||||
pRouter->weights[iChannelIn][iChannelOut] = weight;
|
pRouter->config.weights[iChannelIn][iChannelOut] = weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} break;
|
||||||
|
|
||||||
|
case mal_channel_mix_mode_custom_weights:
|
||||||
|
case mal_channel_mix_mode_simple:
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
/* Fallthrough. */
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MAL_SUCCESS;
|
return MAL_SUCCESS;
|
||||||
@@ -25060,7 +25073,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
|
|||||||
mal_uint64 iFrame = 0;
|
mal_uint64 iFrame = 0;
|
||||||
#if defined(MAL_SUPPORT_NEON)
|
#if defined(MAL_SUPPORT_NEON)
|
||||||
if (mal_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
if (mal_channel_router__can_use_neon(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
||||||
float32x4_t weight = vmovq_n_f32(pRouter->weights[iChannelIn][iChannelOut]);
|
float32x4_t weight = vmovq_n_f32(pRouter->config.weights[iChannelIn][iChannelOut]);
|
||||||
|
|
||||||
mal_uint64 frameCount4 = frameCount/4;
|
mal_uint64 frameCount4 = frameCount/4;
|
||||||
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
|
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
|
||||||
@@ -25075,7 +25088,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
|
|||||||
#endif
|
#endif
|
||||||
#if defined(MAL_SUPPORT_AVX512)
|
#if defined(MAL_SUPPORT_AVX512)
|
||||||
if (mal_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
if (mal_channel_router__can_use_avx512(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
||||||
__m512 weight = _mm512_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
|
__m512 weight = _mm512_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
|
||||||
|
|
||||||
mal_uint64 frameCount16 = frameCount/16;
|
mal_uint64 frameCount16 = frameCount/16;
|
||||||
for (mal_uint64 iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) {
|
for (mal_uint64 iFrame16 = 0; iFrame16 < frameCount16; iFrame16 += 1) {
|
||||||
@@ -25090,7 +25103,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
|
|||||||
#endif
|
#endif
|
||||||
#if defined(MAL_SUPPORT_AVX2)
|
#if defined(MAL_SUPPORT_AVX2)
|
||||||
if (mal_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
if (mal_channel_router__can_use_avx2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
||||||
__m256 weight = _mm256_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
|
__m256 weight = _mm256_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
|
||||||
|
|
||||||
mal_uint64 frameCount8 = frameCount/8;
|
mal_uint64 frameCount8 = frameCount/8;
|
||||||
for (mal_uint64 iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) {
|
for (mal_uint64 iFrame8 = 0; iFrame8 < frameCount8; iFrame8 += 1) {
|
||||||
@@ -25105,7 +25118,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
|
|||||||
#endif
|
#endif
|
||||||
#if defined(MAL_SUPPORT_SSE2)
|
#if defined(MAL_SUPPORT_SSE2)
|
||||||
if (mal_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
if (mal_channel_router__can_use_sse2(pRouter, ppSamplesOut[iChannelOut], ppSamplesIn[iChannelIn])) {
|
||||||
__m128 weight = _mm_set1_ps(pRouter->weights[iChannelIn][iChannelOut]);
|
__m128 weight = _mm_set1_ps(pRouter->config.weights[iChannelIn][iChannelOut]);
|
||||||
|
|
||||||
mal_uint64 frameCount4 = frameCount/4;
|
mal_uint64 frameCount4 = frameCount/4;
|
||||||
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
|
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
|
||||||
@@ -25118,10 +25131,10 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
|
|||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{ // Reference.
|
{ // Reference.
|
||||||
float weight0 = pRouter->weights[iChannelIn][iChannelOut];
|
float weight0 = pRouter->config.weights[iChannelIn][iChannelOut];
|
||||||
float weight1 = pRouter->weights[iChannelIn][iChannelOut];
|
float weight1 = pRouter->config.weights[iChannelIn][iChannelOut];
|
||||||
float weight2 = pRouter->weights[iChannelIn][iChannelOut];
|
float weight2 = pRouter->config.weights[iChannelIn][iChannelOut];
|
||||||
float weight3 = pRouter->weights[iChannelIn][iChannelOut];
|
float weight3 = pRouter->config.weights[iChannelIn][iChannelOut];
|
||||||
|
|
||||||
mal_uint64 frameCount4 = frameCount/4;
|
mal_uint64 frameCount4 = frameCount/4;
|
||||||
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
|
for (mal_uint64 iFrame4 = 0; iFrame4 < frameCount4; iFrame4 += 1) {
|
||||||
@@ -25135,7 +25148,7 @@ void mal_channel_router__do_routing(mal_channel_router* pRouter, mal_uint64 fram
|
|||||||
|
|
||||||
// Leftover.
|
// Leftover.
|
||||||
for (; iFrame < frameCount; ++iFrame) {
|
for (; iFrame < frameCount; ++iFrame) {
|
||||||
ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->weights[iChannelIn][iChannelOut];
|
ppSamplesOut[iChannelOut][iFrame] += ppSamplesIn[iChannelIn][iFrame] * pRouter->config.weights[iChannelIn][iChannelOut];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28493,6 +28506,7 @@ mal_uint64 mal_sine_wave_read_ex(mal_sine_wave* pSineWave, mal_uint64 frameCount
|
|||||||
//
|
//
|
||||||
// v0.8.14 - 2018-12-16
|
// v0.8.14 - 2018-12-16
|
||||||
// - Core Audio: Fix a bug where the device state is not set correctly after stopping.
|
// - Core Audio: Fix a bug where the device state is not set correctly after stopping.
|
||||||
|
// - Add support for custom weights to the channel router.
|
||||||
// - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
|
// - Update decoders to use updated APIs in dr_flac, dr_mp3 and dr_wav.
|
||||||
//
|
//
|
||||||
// v0.8.13 - 2018-12-04
|
// v0.8.13 - 2018-12-04
|
||||||
|
|||||||
Reference in New Issue
Block a user