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.master
parent
97dbd9a191
commit
9ba489360c
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue