aboutsummaryrefslogtreecommitdiffstats
path: root/utils/uhjencoder.cpp
diff options
context:
space:
mode:
authorChris Robinson <[email protected]>2022-02-04 18:05:25 -0800
committerChris Robinson <[email protected]>2022-02-04 18:05:25 -0800
commit9ba489360c20e5e0129e0ab278191ec7c8fda099 (patch)
tree5c4940291236bd298b44dde54f21cc717709bdb0 /utils/uhjencoder.cpp
parent97dbd9a19149b4d71bf375d3354be6eccaa92f7b (diff)
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.
Diffstat (limited to 'utils/uhjencoder.cpp')
-rw-r--r--utils/uhjencoder.cpp90
1 files changed, 71 insertions, 19 deletions
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<float,BufferLineSize+sFilterDelay> mS{};
alignas(16) std::array<float,BufferLineSize+sFilterDelay> mD{};
+ alignas(16) std::array<float,BufferLineSize+sFilterDelay> mT{};
+ alignas(16) std::array<float,BufferLineSize+sFilterDelay> mQ{};
/* History for the FIR filter. */
- alignas(16) std::array<float,sFilterDelay*2 - 1> mWXHistory{};
+ alignas(16) std::array<float,sFilterDelay*2 - 1> mWXHistory1{};
+ alignas(16) std::array<float,sFilterDelay*2 - 1> mWXHistory2{};
alignas(16) std::array<float,BufferLineSize + sFilterDelay*2> mTemp{};
- void encode(const FloatBufferSpan LeftOut, const FloatBufferSpan RightOut,
- const FloatBufferLine *InSamples, const size_t SamplesToDo);
+ void encode(const al::span<FloatBufferLine> OutSamples,
+ const al::span<FloatBufferLine,4> InSamples, const size_t SamplesToDo);
DEF_NEWDEL(UhjEncoder)
};
@@ -95,15 +98,13 @@ const PhaseShifterT<UhjEncoder::sFilterDelay*2> 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<FloatBufferLine> OutSamples,
+ const al::span<FloatBufferLine,4> 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<int>(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<UhjEncoder>();
- auto splbuf = al::vector<FloatBufferLine, 16>(static_cast<uint>(ininfo.channels+9));
+ auto splbuf = al::vector<FloatBufferLine, 16>(static_cast<uint>(9+ininfo.channels)+uhjchans);
auto ambmem = al::span<FloatBufferLine,4>{&splbuf[0], 4};
- auto encmem = al::span<FloatBufferLine,2>{&splbuf[4], 2};
- auto srcmem = al::span<float,BufferLineSize>{splbuf[6].data(), BufferLineSize};
- auto outmem = al::span<float,BufferLineSize*2>{splbuf[7].data(), BufferLineSize*2};
+ auto encmem = al::span<FloatBufferLine,4>{&splbuf[4], 4};
+ auto srcmem = al::span<float,BufferLineSize>{splbuf[8].data(), BufferLineSize};
+ auto outmem = al::span<float>{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<sf_count_t>(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;