From 298318bd8a44c8bbc8875aabb50b936dc14e8626 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Thu, 25 Aug 2022 19:56:00 -0700 Subject: [PATCH] Apply the upsampler before rotation Although this will necessitate applying a full device-order rotation for all ambisonic sources, it should be slightly better since it's effectively rotated after being mixed to higher order. This will also allow using more optimized upsamplers for 2D ambisonic sources vs 3D (while still allowing the sources to be rotated in 3D). --- alc/alu.cpp | 107 +++++++++++----------------------------------------- 1 file changed, 23 insertions(+), 84 deletions(-) diff --git a/alc/alu.cpp b/alc/alu.cpp index dcd3c7d2..f55c196d 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -341,95 +341,34 @@ inline uint dither_rng(uint *seed) noexcept } -/* Ambisonic upsampler functions. These are effectively matrix multiply - * operations. They take the 'coeffs' as the input matrix, and combine it with - * an "upsampler" matrix, resulting in a coefficient matrix that behaves as if - * the initial coefficients were applied to the B-Format input, which was then - * decoded to a speaker array at its input order, and finally encoded back into - * the higher order mix. +/* Ambisonic upsampler function. It's effectively a matrix multiply. It takes + * an 'upsampler' and 'rotator' as the input matrices, resulting in a matrix + * that behaves as if the B-Format input was first decoded to a speaker array + * at its input order, encoded back into the higher order mix, then finally + * rotated. */ -void UpsampleFirstOrder(const al::span,MaxAmbiChannels> coeffs) +template +void UpsampleBFormatTransform(size_t coeffs_order, + const std::array,N> &matrix1, + const al::span,MaxAmbiChannels> coeffs) { - auto transpose = [coeffs]() noexcept + auto copy_coeffs = [coeffs]() noexcept { - std::array,4> res{}; - for(size_t i{0};i < 4;++i) - { - for(size_t j{0};j < 4;++j) - res[i][j] = coeffs[j][i]; - } + std::array,MaxAmbiChannels> res{}; + for(size_t i{0};i < MaxAmbiChannels;++i) + res[i] = coeffs[i]; return res; }; - const auto input = transpose(); + const auto matrix2 = copy_coeffs(); - static_assert(input[0].size() == AmbiScale::FirstOrderUp.size(), - "Mismatched first-order input and upsampler size"); - - for(size_t i{0};i < input.size();++i) + const size_t num_chans{AmbiChannelsFromOrder(coeffs_order)}; + for(size_t i{0};i < matrix1.size();++i) { - for(size_t j{0};j < AmbiScale::FirstOrderUp[0].size();++j) + for(size_t j{0};j < num_chans;++j) { double sum{0.0}; - for(size_t k{0};k < input[0].size();++k) - sum += double{input[i][k]} * AmbiScale::FirstOrderUp[k][j]; - coeffs[j][i] = static_cast(sum); - } - } -} - -void UpsampleSecondOrder(const al::span,MaxAmbiChannels> coeffs) -{ - auto transpose = [coeffs]() noexcept - { - std::array,9> res{}; - for(size_t i{0};i < 9;++i) - { - for(size_t j{0};j < 9;++j) - res[i][j] = coeffs[j][i]; - } - return res; - }; - const auto input = transpose(); - - static_assert(input[0].size() == AmbiScale::SecondOrderUp.size(), - "Mismatched second-order input and upsampler size"); - - for(size_t i{0};i < input.size();++i) - { - for(size_t j{0};j < AmbiScale::SecondOrderUp[0].size();++j) - { - double sum{0.0}; - for(size_t k{0};k < input[0].size();++k) - sum += double{input[i][k]} * AmbiScale::SecondOrderUp[k][j]; - coeffs[j][i] = static_cast(sum); - } - } -} - -void UpsampleThirdOrder(const al::span,MaxAmbiChannels> coeffs) -{ - auto transpose = [coeffs]() noexcept - { - std::array,16> res{}; - for(size_t i{0};i < 16;++i) - { - for(size_t j{0};j < 16;++j) - res[i][j] = coeffs[j][i]; - } - return res; - }; - const auto input = transpose(); - - static_assert(input[0].size() == AmbiScale::ThirdOrderUp.size(), - "Mismatched third-order input and upsampler size"); - - for(size_t i{0};i < input.size();++i) - { - for(size_t j{0};j < AmbiScale::ThirdOrderUp[0].size();++j) - { - double sum{0.0}; - for(size_t k{0};k < input[0].size();++k) - sum += double{input[i][k]} * AmbiScale::ThirdOrderUp[k][j]; + for(size_t k{0};k < num_chans;++k) + sum += double{matrix1[i][k]} * matrix2[j][k]; coeffs[j][i] = static_cast(sum); } } @@ -966,16 +905,16 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con shrot[1][1] = U[0]; shrot[1][2] = -V[0]; shrot[1][3] = -N[0]; shrot[2][1] = -U[1]; shrot[2][2] = V[1]; shrot[2][3] = N[1]; shrot[3][1] = U[2]; shrot[3][2] = -V[2]; shrot[3][3] = -N[2]; - AmbiRotator(shrot, static_cast(minu(voice->mAmbiOrder, Device->mAmbiOrder))); + AmbiRotator(shrot, static_cast(Device->mAmbiOrder)); /* If the device is higher order than the voice, "upsample" the matrix */ if(Device->mAmbiOrder > voice->mAmbiOrder) { if(voice->mAmbiOrder == 1) - UpsampleFirstOrder(shrot); + UpsampleBFormatTransform(Device->mAmbiOrder, AmbiScale::FirstOrderUp, shrot); else if(voice->mAmbiOrder == 2) - UpsampleSecondOrder(shrot); + UpsampleBFormatTransform(Device->mAmbiOrder, AmbiScale::SecondOrderUp, shrot); else if(voice->mAmbiOrder == 3) - UpsampleThirdOrder(shrot); + UpsampleBFormatTransform(Device->mAmbiOrder, AmbiScale::ThirdOrderUp, shrot); } /* Convert the rotation matrix for input ordering and scaling, and