diff options
author | Chris Robinson <[email protected]> | 2021-12-11 17:50:24 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2021-12-11 17:50:24 -0800 |
commit | 01dd34f305b9ad2c8e6bf0642cd976f9788fdf3a (patch) | |
tree | 75f50acecb284a587e13265efb14ed573af90245 /core | |
parent | a75d35bbb06f74e58cb2232d4ca2ce47950f08cd (diff) |
Add an internal Super Stereo format
It's not available as an AL buffer format (yet) since I'm not sure how to
expose it. Internally it seems fine as a separate channel configuration, but
because OpenAL combines the channel configuration and sample type, a flag may
work better there.
Diffstat (limited to 'core')
-rw-r--r-- | core/buffer_storage.cpp | 1 | ||||
-rw-r--r-- | core/buffer_storage.h | 28 | ||||
-rw-r--r-- | core/uhjfilter.h | 3 | ||||
-rw-r--r-- | core/voice.cpp | 28 | ||||
-rw-r--r-- | core/voice.h | 1 |
5 files changed, 49 insertions, 12 deletions
diff --git a/core/buffer_storage.cpp b/core/buffer_storage.cpp index 5179db13..1c80e7ef 100644 --- a/core/buffer_storage.cpp +++ b/core/buffer_storage.cpp @@ -36,6 +36,7 @@ uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept case FmtUHJ2: return 2; case FmtUHJ3: return 3; case FmtUHJ4: return 4; + case FmtSuperStereo: return 2; } return 0; } diff --git a/core/buffer_storage.h b/core/buffer_storage.h index 64943453..091882f9 100644 --- a/core/buffer_storage.h +++ b/core/buffer_storage.h @@ -4,6 +4,8 @@ #include <atomic> #include "albyte.h" +#include "alnumeric.h" +#include "ambidefs.h" using uint = unsigned int; @@ -30,6 +32,7 @@ enum FmtChannels : unsigned char { FmtUHJ2, /* 2-channel UHJ, aka "BHJ", stereo-compatible */ FmtUHJ3, /* 3-channel UHJ, aka "THJ" */ FmtUHJ4, /* 4-channel UHJ, aka "PHJ" */ + FmtSuperStereo, /* Stereo processed with Super Stereo. */ }; enum class AmbiLayout : unsigned char { @@ -48,6 +51,21 @@ uint ChannelsFromFmt(FmtChannels chans, uint ambiorder) noexcept; inline uint FrameSizeFromFmt(FmtChannels chans, FmtType type, uint ambiorder) noexcept { return ChannelsFromFmt(chans, ambiorder) * BytesFromFmt(type); } +constexpr bool IsBFormat(FmtChannels chans) noexcept +{ return chans == FmtBFormat2D || chans == FmtBFormat3D; } + +/* Super Stereo is considered part of the UHJ family here, since it goes + * through similar processing as UHJ, both result in a B-Format signal, and + * needs the same consideration as BHJ (three channel result with only two + * channel input). + */ +constexpr bool IsUHJ(FmtChannels chans) noexcept +{ return chans == FmtUHJ2 || chans == FmtUHJ3 || chans == FmtUHJ4 || chans == FmtSuperStereo; } + +/** Ambisonic formats are either B-Format or UHJ formats. */ +constexpr bool IsAmbisonic(FmtChannels chans) noexcept +{ return IsBFormat(chans) || IsUHJ(chans); } + using CallbackType = int(*)(void*, void*, int); @@ -69,8 +87,14 @@ struct BufferStorage { { return ChannelsFromFmt(mChannels, mAmbiOrder); } inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } - inline bool isBFormat() const noexcept - { return mChannels == FmtBFormat2D || mChannels == FmtBFormat3D; } + inline uint mixerChannelsFromFmt() const noexcept + { + if(mChannels == FmtUHJ2 || mChannels == FmtSuperStereo) return 3; + return ChannelsFromFmt(mChannels, minu(mAmbiOrder, MaxAmbiOrder)); + } + + inline bool isBFormat() const noexcept { return IsBFormat(mChannels); } + inline bool isUhj() const noexcept { return IsUHJ(mChannels); } }; #endif /* CORE_BUFFER_STORAGE_H */ diff --git a/core/uhjfilter.h b/core/uhjfilter.h index 574cb800..9e692599 100644 --- a/core/uhjfilter.h +++ b/core/uhjfilter.h @@ -78,6 +78,9 @@ struct UhjDecoder : public UhjFilterBase { void decodeStereo(const al::span<BufferLine> samples, const size_t offset, const size_t samplesToDo, const size_t forwardSamples); + using DecoderFunc = void (UhjDecoder::*)(const al::span<BufferLine> samples, + const size_t offset, const size_t samplesToDo, const size_t forwardSamples); + DEF_NEWDEL(UhjDecoder) }; diff --git a/core/voice.cpp b/core/voice.cpp index a8c5b281..923d7275 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -207,7 +207,7 @@ void LoadSamples(const al::span<DeviceBase::MixerBufferLine> dstSamples, const s #define HANDLE_FMT(T) case T: \ { \ constexpr size_t sampleSize{sizeof(al::FmtTypeTraits<T>::Type)}; \ - if(srcchans == FmtUHJ2) \ + if(srcchans == FmtUHJ2 || srcchans == FmtSuperStereo) \ { \ src += srcOffset*2u*sampleSize; \ al::LoadSampleArray<T>(dstSamples[0].data() + dstOffset, src, \ @@ -510,8 +510,7 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo Device->mSampleData.data() + Device->mSampleData.size() - mChans.size(), mChans.size()}; const uint PostPadding{MaxResamplerEdge + - ((mFmtChannels==FmtUHJ2 || mFmtChannels==FmtUHJ3 || mFmtChannels==FmtUHJ4) - ? uint{UhjDecoder::sFilterDelay} : 0u)}; + (mDecoder ? uint{UhjDecoder::sFilterDelay} : 0u)}; uint buffers_done{0u}; uint OutPos{0u}; do { @@ -628,7 +627,8 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo { const size_t srcOffset{(increment*DstBufferSize + DataPosFrac)>>MixerFracBits}; SrcBufferSize = SrcBufferSize - PostPadding + MaxResamplerEdge; - mDecoder->decode(MixingSamples, MaxResamplerEdge, SrcBufferSize, srcOffset); + ((*mDecoder).*mDecoderFunc)(MixingSamples, MaxResamplerEdge, SrcBufferSize, + srcOffset); } } @@ -814,10 +814,17 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo void Voice::prepare(DeviceBase *device) { - if((mFmtChannels == FmtUHJ2 || mFmtChannels == FmtUHJ3 || mFmtChannels==FmtUHJ4) && !mDecoder) + if(IsUHJ(mFmtChannels)) + { mDecoder = std::make_unique<UhjDecoder>(); - else if(mFmtChannels != FmtUHJ2 && mFmtChannels != FmtUHJ3 && mFmtChannels != FmtUHJ4) + mDecoderFunc = (mFmtChannels == FmtSuperStereo) ? &UhjDecoder::decodeStereo + : &UhjDecoder::decode; + } + else + { mDecoder = nullptr; + mDecoderFunc = nullptr; + } /* Clear the stepping value explicitly so the mixer knows not to mix this * until the update gets applied. @@ -833,7 +840,8 @@ void Voice::prepare(DeviceBase *device) if(mAmbiOrder && device->mAmbiOrder > mAmbiOrder) { const uint8_t *OrderFromChan{(mFmtChannels == FmtBFormat2D - || mFmtChannels == FmtUHJ2 || mFmtChannels == FmtUHJ3) ? + || mFmtChannels == FmtUHJ2 || mFmtChannels == FmtUHJ3 + || mFmtChannels == FmtSuperStereo) ? AmbiIndex::OrderFrom2DChannel().data() : AmbiIndex::OrderFromChannel().data()}; const auto scales = AmbiScale::GetHFOrderScales(mAmbiOrder, device->mAmbiOrder); @@ -850,9 +858,9 @@ void Voice::prepare(DeviceBase *device) * use different shelf filters after mixing it and with any old speaker * setup the user has. To make this work, we apply the expected shelf * filters for decoding UHJ2 to quad (only needs LF scaling), and act - * as if those 4 channels are encoded back onto first-order B-Format, - * which then upsamples to higher order as normal (only needs HF - * scaling). + * as if those 4 quad channels are encoded right back onto first-order + * B-Format, which then upsamples to higher order as normal (only needs + * HF scaling). * * This isn't perfect, but without an entirely separate and limited * UHJ2 path, it's better than nothing. diff --git a/core/voice.h b/core/voice.h index 94385884..ba5e82cc 100644 --- a/core/voice.h +++ b/core/voice.h @@ -214,6 +214,7 @@ struct Voice { uint mAmbiOrder; std::unique_ptr<UhjDecoder> mDecoder; + UhjDecoder::DecoderFunc mDecoderFunc{}; /** Current target parameters used for mixing. */ uint mStep{0}; |