From 9ba489360c20e5e0129e0ab278191ec7c8fda099 Mon Sep 17 00:00:00 2001 From: Chris Robinson Date: Fri, 4 Feb 2022 18:05:25 -0800 Subject: Add options to encode 3- and 4-channel UHJ to uhjencoder The generated files won't play correctly if the player doesn't know they're 3- and 4-channel UHJ (the third and fourth channels must be ignored, or decoded, for playback). This is largely just for completion's sake, just in case someone has a use for it. --- utils/uhjencoder.cpp | 90 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 19 deletions(-) (limited to 'utils/uhjencoder.cpp') diff --git a/utils/uhjencoder.cpp b/utils/uhjencoder.cpp index 2c99d2e1..b380ed86 100644 --- a/utils/uhjencoder.cpp +++ b/utils/uhjencoder.cpp @@ -67,14 +67,17 @@ struct UhjEncoder { /* Delays and processing storage for the unfiltered signal. */ alignas(16) std::array mS{}; alignas(16) std::array mD{}; + alignas(16) std::array mT{}; + alignas(16) std::array mQ{}; /* History for the FIR filter. */ - alignas(16) std::array mWXHistory{}; + alignas(16) std::array mWXHistory1{}; + alignas(16) std::array mWXHistory2{}; alignas(16) std::array mTemp{}; - void encode(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const FloatBufferLine *InSamples, const size_t SamplesToDo); + void encode(const al::span OutSamples, + const al::span InSamples, const size_t SamplesToDo); DEF_NEWDEL(UhjEncoder) }; @@ -95,15 +98,13 @@ const PhaseShifterT PShift{}; * where j is a wide-band +90 degree phase shift. T is excluded from 2-channel * output, and Q is excluded from 2- and 3-channel output. */ -void UhjEncoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut, - const FloatBufferLine *InSamples, const size_t SamplesToDo) +void UhjEncoder::encode(const al::span OutSamples, + const al::span InSamples, const size_t SamplesToDo) { - float *RESTRICT left{al::assume_aligned<16>(LeftOut.data())}; - float *RESTRICT right{al::assume_aligned<16>(RightOut.data())}; - const float *RESTRICT winput{al::assume_aligned<16>(InSamples[0].data())}; const float *RESTRICT xinput{al::assume_aligned<16>(InSamples[1].data())}; const float *RESTRICT yinput{al::assume_aligned<16>(InSamples[2].data())}; + const float *RESTRICT zinput{al::assume_aligned<16>(InSamples[3].data())}; /* Combine the previously delayed S/D signal with the input. */ @@ -119,23 +120,58 @@ void UhjEncoder::encode(const FloatBufferSpan LeftOut, const FloatBufferSpan Rig [](const float y) noexcept -> float { return 0.6554516f*y; }); /* D += j(-0.3420201*W + 0.5098604*X) */ - auto tmpiter = std::copy(mWXHistory.cbegin(), mWXHistory.cend(), mTemp.begin()); + auto tmpiter = std::copy(mWXHistory1.cbegin(), mWXHistory1.cend(), mTemp.begin()); std::transform(winput, winput+SamplesToDo, xinput, tmpiter, [](const float w, const float x) noexcept -> float { return -0.3420201f*w + 0.5098604f*x; }); - std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory.size(), mWXHistory.begin()); + std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory1.size(), mWXHistory1.begin()); PShift.processAccum({mD.data(), SamplesToDo}, mTemp.data()); /* Left = (S + D)/2.0 */ + float *RESTRICT left{al::assume_aligned<16>(OutSamples[0].data())}; for(size_t i{0};i < SamplesToDo;i++) left[i] = (mS[i] + mD[i]) * 0.5f; /* Right = (S - D)/2.0 */ + float *RESTRICT right{al::assume_aligned<16>(OutSamples[1].data())}; for(size_t i{0};i < SamplesToDo;i++) right[i] = (mS[i] - mD[i]) * 0.5f; + if(OutSamples.size() > 2) + { + /* T = -0.7071068*Y */ + sideiter = mT.begin() + sFilterDelay; + std::transform(yinput, yinput+SamplesToDo, sideiter, + [](const float y) noexcept -> float { return -0.7071068f*y; }); + + /* T += j(-0.1432*W + 0.6512*X) */ + tmpiter = std::copy(mWXHistory2.cbegin(), mWXHistory2.cend(), mTemp.begin()); + std::transform(winput, winput+SamplesToDo, xinput, tmpiter, + [](const float w, const float x) noexcept -> float + { return -0.1432f*w + 0.6512f*x; }); + std::copy_n(mTemp.cbegin()+SamplesToDo, mWXHistory2.size(), mWXHistory2.begin()); + PShift.processAccum({mT.data(), SamplesToDo}, mTemp.data()); + + float *RESTRICT t{al::assume_aligned<16>(OutSamples[2].data())}; + for(size_t i{0};i < SamplesToDo;i++) + t[i] = mT[i]; + } + if(OutSamples.size() > 3) + { + /* Q = 0.9772*Z */ + sideiter = mQ.begin() + sFilterDelay; + std::transform(zinput, zinput+SamplesToDo, sideiter, + [](const float z) noexcept -> float { return 0.9772f*z; }); + + float *RESTRICT q{al::assume_aligned<16>(OutSamples[3].data())}; + for(size_t i{0};i < SamplesToDo;i++) + q[i] = mQ[i]; + } + /* Copy the future samples to the front for next time. */ std::copy(mS.cbegin()+SamplesToDo, mS.cbegin()+SamplesToDo+sFilterDelay, mS.begin()); std::copy(mD.cbegin()+SamplesToDo, mD.cbegin()+SamplesToDo+sFilterDelay, mD.begin()); + std::copy(mT.cbegin()+SamplesToDo, mT.cbegin()+SamplesToDo+sFilterDelay, mT.begin()); + std::copy(mQ.cbegin()+SamplesToDo, mQ.cbegin()+SamplesToDo+sFilterDelay, mQ.begin()); } @@ -214,9 +250,25 @@ int main(int argc, char **argv) return 1; } + uint uhjchans{2}; size_t num_files{0}, num_encoded{0}; for(int fidx{1};fidx < argc;++fidx) { + if(strcmp(argv[fidx], "-bhj") == 0) + { + uhjchans = 2; + continue; + } + if(strcmp(argv[fidx], "-thj") == 0) + { + uhjchans = 3; + continue; + } + if(strcmp(argv[fidx], "-phj") == 0) + { + uhjchans = 4; + continue; + } ++num_files; std::string outname{argv[fidx]}; @@ -335,7 +387,7 @@ int main(int argc, char **argv) SF_INFO outinfo{}; outinfo.frames = ininfo.frames; outinfo.samplerate = ininfo.samplerate; - outinfo.channels = 2; + outinfo.channels = static_cast(uhjchans); outinfo.format = SF_FORMAT_PCM_24 | SF_FORMAT_FLAC; SndFilePtr outfile{sf_open(outname.c_str(), SFM_WRITE, &outinfo)}; if(!outfile) @@ -345,11 +397,11 @@ int main(int argc, char **argv) } auto encoder = std::make_unique(); - auto splbuf = al::vector(static_cast(ininfo.channels+9)); + auto splbuf = al::vector(static_cast(9+ininfo.channels)+uhjchans); auto ambmem = al::span{&splbuf[0], 4}; - auto encmem = al::span{&splbuf[4], 2}; - auto srcmem = al::span{splbuf[6].data(), BufferLineSize}; - auto outmem = al::span{splbuf[7].data(), BufferLineSize*2}; + auto encmem = al::span{&splbuf[4], 4}; + auto srcmem = al::span{splbuf[8].data(), BufferLineSize}; + auto outmem = al::span{splbuf[9].data(), BufferLineSize*uhjchans}; /* A number of initial samples need to be skipped to cut the lead-in * from the all-pass filter delay. The same number of samples need to @@ -361,7 +413,7 @@ int main(int argc, char **argv) sf_count_t LeadOut{UhjEncoder::sFilterDelay}; while(LeadIn > 0 || LeadOut > 0) { - auto inmem = splbuf[9].data(); + auto inmem = outmem.data() + outmem.size(); auto sgot = sf_readf_float(infile.get(), inmem, BufferLineSize); sgot = std::max(sgot, 0); @@ -416,7 +468,7 @@ int main(int argc, char **argv) } } - encoder->encode(encmem[0], encmem[1], ambmem.data(), got); + encoder->encode(encmem.subspan(0, uhjchans), ambmem, got); if(LeadIn >= got) { LeadIn -= got; @@ -424,13 +476,13 @@ int main(int argc, char **argv) } got -= LeadIn; - for(size_t c{0};c < 2;++c) + for(size_t c{0};c < uhjchans;++c) { constexpr float max_val{8388607.0f / 8388608.0f}; auto clamp = [](float v, float mn, float mx) noexcept { return std::min(std::max(v, mn), mx); }; for(size_t i{0};i < got;++i) - outmem[i*2 + c] = clamp(encmem[c][LeadIn+i], -1.0f, max_val); + outmem[i*uhjchans + c] = clamp(encmem[c][LeadIn+i], -1.0f, max_val); } LeadIn = 0; -- cgit v1.2.3