Commit d8c9057b authored by David Reid's avatar David Reid

Add support for custom weights to the channel router.

parent ae709730
...@@ -577,6 +577,7 @@ typedef enum ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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.
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { for (mal_uint32 iChannelIn = 0; iChannelIn < pRouter->config.channelsIn; ++iChannelIn) {
if (!mal_channel_map_contains_channel_position(pRouter->config.channelsOut, pRouter->config.channelMapOut, channelPosIn)) { mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut]; 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_router__is_spatial_channel_position(pRouter, channelPosOut)) { for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
float weight = 0; mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut); if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
} float weight = 0;
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
// Only apply the weight if we haven't already got some contribution from the respective channels. weight = mal_channel_router__calculate_input_channel_planar_weight(pRouter, channelPosIn, channelPosOut);
if (pRouter->weights[iChannelIn][iChannelOut] == 0) { }
pRouter->weights[iChannelIn][iChannelOut] = weight;
// Only apply the weight if we haven't already got some contribution from the respective channels.
if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
pRouter->config.weights[iChannelIn][iChannelOut] = weight;
}
} }
} }
} }
} }
} }
}
// Unmapped output channels.
for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
if (mal_channel_router__is_spatial_channel_position(pRouter, 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) {
mal_channel channelPosIn = pRouter->config.channelMapIn[iChannelIn];
if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosIn)) { // Unmapped output channels.
float weight = 0; for (mal_uint32 iChannelOut = 0; iChannelOut < pRouter->config.channelsOut; ++iChannelOut) {
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) { mal_channel channelPosOut = pRouter->config.channelMapOut[iChannelOut];
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. if (mal_channel_router__is_spatial_channel_position(pRouter, channelPosOut)) {
if (pRouter->weights[iChannelIn][iChannelOut] == 0) { if (!mal_channel_map_contains_channel_position(pRouter->config.channelsIn, pRouter->config.channelMapIn, channelPosOut)) {
pRouter->weights[iChannelIn][iChannelOut] = weight; 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)) {
float weight = 0;
if (pRouter->config.mixingMode == mal_channel_mix_mode_planar_blend) {
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.
if (pRouter->config.weights[iChannelIn][iChannelOut] == 0) {
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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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
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