diff options
author | Chris Robinson <[email protected]> | 2021-12-15 02:01:22 -0800 |
---|---|---|
committer | Chris Robinson <[email protected]> | 2021-12-15 02:01:22 -0800 |
commit | 54c4bea48791f353baf66917f967ef3a39af9b0b (patch) | |
tree | 92ab9aad66fa8499cf338e22ce127c8db12d1cfa | |
parent | b489705b25bb3967e04f7099cb21797783ccf7c0 (diff) |
Add source properties for Super Stereo
When playing a stereo format, enabling Super Stereo causes the source to behave
as a B-Format source, with a variable width control.
-rw-r--r-- | al/source.cpp | 97 | ||||
-rw-r--r-- | al/source.h | 8 | ||||
-rw-r--r-- | alc/alc.cpp | 4 | ||||
-rw-r--r-- | alc/alu.cpp | 3 | ||||
-rw-r--r-- | alc/inprogext.h | 5 | ||||
-rw-r--r-- | core/buffer_storage.h | 7 | ||||
-rw-r--r-- | core/voice.cpp | 21 | ||||
-rw-r--r-- | core/voice.h | 1 |
8 files changed, 116 insertions, 30 deletions
diff --git a/al/source.cpp b/al/source.cpp index d8584956..a8d3e9c6 100644 --- a/al/source.cpp +++ b/al/source.cpp @@ -143,6 +143,7 @@ void UpdateSourceProps(const ALsource *source, Voice *voice, ALCcontext *context props->StereoPan = source->StereoPan; props->Radius = source->Radius; + props->EnhWidth = source->EnhWidth; props->Direct.Gain = source->Direct.Gain; props->Direct.GainHF = source->Direct.GainHF; @@ -492,38 +493,20 @@ void InitVoice(Voice *voice, ALsource *source, ALbufferQueueItem *BufferList, AL ALbuffer *buffer{BufferList->mBuffer}; voice->mFrequency = buffer->mSampleRate; - voice->mFmtChannels = buffer->mChannels; + voice->mFmtChannels = + (buffer->mChannels == FmtStereo && source->mStereoMode == SourceStereo::Enhanced) ? + FmtSuperStereo : buffer->mChannels; voice->mFmtType = buffer->mType; voice->mNumChannels = buffer->channelsFromFmt(); voice->mFrameSize = buffer->frameSizeFromFmt(); - voice->mAmbiLayout = buffer->isUhj() ? AmbiLayout::FuMa : buffer->mAmbiLayout; - voice->mAmbiScaling = buffer->isUhj() ? AmbiScaling::UHJ : buffer->mAmbiScaling; + voice->mAmbiLayout = IsUHJ(voice->mFmtChannels) ? AmbiLayout::FuMa : buffer->mAmbiLayout; + voice->mAmbiScaling = IsUHJ(voice->mFmtChannels) ? AmbiScaling::UHJ : buffer->mAmbiScaling; voice->mAmbiOrder = buffer->mAmbiOrder; if(buffer->mCallback) voice->mFlags |= VoiceIsCallback; else if(source->SourceType == AL_STATIC) voice->mFlags |= VoiceIsStatic; voice->mNumCallbackSamples = 0; - /* Even if storing really high order ambisonics, we only mix channels for - * orders up to MaxAmbiOrder. The rest are simply dropped. - */ - ALuint num_channels{buffer->mixerChannelsFromFmt()}; - if UNLIKELY(num_channels > device->mSampleData.size()) - { - ERR("Unexpected channel count: %u (limit: %zu, %d:%d)\n", num_channels, - device->mSampleData.size(), buffer->mChannels, buffer->mAmbiOrder); - num_channels = static_cast<ALuint>(device->mSampleData.size()); - } - if(voice->mChans.capacity() > 2 && num_channels < voice->mChans.capacity()) - { - decltype(voice->mChans){}.swap(voice->mChans); - decltype(voice->mPrevSamples){}.swap(voice->mPrevSamples); - } - voice->mChans.reserve(maxu(2, num_channels)); - voice->mChans.resize(num_channels); - voice->mPrevSamples.reserve(maxu(2, num_channels)); - voice->mPrevSamples.resize(num_channels); - voice->prepare(device); source->mPropsDirty.test_and_clear(std::memory_order_acq_rel); @@ -832,6 +815,26 @@ inline ALeffectslot *LookupEffectSlot(ALCcontext *context, ALuint id) noexcept } +al::optional<SourceStereo> StereoModeFromEnum(ALenum mode) +{ + switch(mode) + { + case AL_NORMAL_SOFT: return al::make_optional(SourceStereo::Normal); + case AL_SUPER_STEREO_SOFT: return al::make_optional(SourceStereo::Enhanced); + } + WARN("Unsupported stereo mode: 0x%04x\n", mode); + return al::nullopt; +} +ALenum EnumFromStereoMode(SourceStereo mode) +{ + switch(mode) + { + case SourceStereo::Normal: return AL_NORMAL_SOFT; + case SourceStereo::Enhanced: return AL_SUPER_STEREO_SOFT; + } + throw std::runtime_error{"Invalid SourceStereo: "+std::to_string(int(mode))}; +} + al::optional<SpatializeMode> SpatializeModeFromEnum(ALenum mode) { switch(mode) @@ -976,6 +979,10 @@ enum SourceProp : ALenum { /* ALC_SOFT_device_clock */ srcSampleOffsetClockSOFT = AL_SAMPLE_OFFSET_CLOCK_SOFT, srcSecOffsetClockSOFT = AL_SEC_OFFSET_CLOCK_SOFT, + + /* AL_SOFT_UHJ */ + srcStereoMode = AL_STEREO_MODE_SOFT, + srcSuperStereoWidth = AL_SUPER_STEREO_WIDTH_SOFT, }; @@ -1019,6 +1026,8 @@ ALuint FloatValsByProp(ALenum prop) case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: + case AL_STEREO_MODE_SOFT: + case AL_SUPER_STEREO_WIDTH_SOFT: return 1; case AL_STEREO_ANGLES: @@ -1084,6 +1093,8 @@ ALuint DoubleValsByProp(ALenum prop) case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: + case AL_STEREO_MODE_SOFT: + case AL_SUPER_STEREO_WIDTH_SOFT: return 1; case AL_SEC_OFFSET_LATENCY_SOFT: @@ -1277,6 +1288,13 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a Source->Radius = values[0]; return UpdateSourceProps(Source, Context); + case AL_SUPER_STEREO_WIDTH_SOFT: + CHECKSIZE(values, 1); + CHECKVAL(values[0] >= 0.0f && values[0] <= 1.0f); + + Source->EnhWidth = values[0]; + return UpdateSourceProps(Source, Context); + case AL_STEREO_ANGLES: CHECKSIZE(values, 2); CHECKVAL(std::isfinite(values[0]) && std::isfinite(values[1])); @@ -1340,6 +1358,7 @@ bool SetSourcefv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a case AL_SOURCE_SPATIALIZE_SOFT: case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: + case AL_STEREO_MODE_SOFT: CHECKSIZE(values, 1); ival = static_cast<int>(values[0]); return SetSourceiv(Source, Context, prop, {&ival, 1u}); @@ -1575,6 +1594,21 @@ bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a values[0]); return false; + case AL_STEREO_MODE_SOFT: + CHECKSIZE(values, 1); + { + const ALenum state{GetSourceState(Source, GetSourceVoice(Source, Context))}; + if(state == AL_PLAYING || state == AL_PAUSED) + SETERR_RETURN(Context, AL_INVALID_OPERATION, false, + "Setting buffer on playing or paused source %u", Source->id); + } + if(auto mode = StereoModeFromEnum(values[0])) + { + Source->mStereoMode = *mode; + return true; + } + return false; + case AL_AUXILIARY_SEND_FILTER: CHECKSIZE(values, 3); slotlock = std::unique_lock<std::mutex>{Context->mEffectSlotLock}; @@ -1651,6 +1685,7 @@ bool SetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a case AL_ROOM_ROLLOFF_FACTOR: case AL_SOURCE_RADIUS: case AL_SEC_LENGTH_SOFT: + case AL_SUPER_STEREO_WIDTH_SOFT: CHECKSIZE(values, 1); fvals[0] = static_cast<float>(values[0]); return SetSourcefv(Source, Context, prop, {fvals, 1u}); @@ -1721,6 +1756,7 @@ bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const case AL_DISTANCE_MODEL: case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: + case AL_STEREO_MODE_SOFT: CHECKSIZE(values, 1); CHECKVAL(values[0] <= INT_MAX && values[0] >= INT_MIN); @@ -1764,6 +1800,7 @@ bool SetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const case AL_ROOM_ROLLOFF_FACTOR: case AL_SOURCE_RADIUS: case AL_SEC_LENGTH_SOFT: + case AL_SUPER_STEREO_WIDTH_SOFT: CHECKSIZE(values, 1); fvals[0] = static_cast<float>(values[0]); return SetSourcefv(Source, Context, prop, {fvals, 1u}); @@ -1899,6 +1936,11 @@ bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a values[0] = Source->Radius; return true; + case AL_SUPER_STEREO_WIDTH_SOFT: + CHECKSIZE(values, 1); + values[0] = Source->EnhWidth; + return true; + case AL_BYTE_LENGTH_SOFT: case AL_SAMPLE_LENGTH_SOFT: case AL_SEC_LENGTH_SOFT: @@ -1986,6 +2028,7 @@ bool GetSourcedv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a case AL_DISTANCE_MODEL: case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: + case AL_STEREO_MODE_SOFT: CHECKSIZE(values, 1); if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false) values[0] = static_cast<double>(ivals[0]); @@ -2117,6 +2160,11 @@ bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a values[0] = EnumFromSpatializeMode(Source->mSpatialize); return true; + case AL_STEREO_MODE_SOFT: + CHECKSIZE(values, 1); + values[0] = EnumFromStereoMode(Source->mStereoMode); + return true; + /* 1x float/double */ case AL_CONE_INNER_ANGLE: case AL_CONE_OUTER_ANGLE: @@ -2136,6 +2184,7 @@ bool GetSourceiv(ALsource *Source, ALCcontext *Context, SourceProp prop, const a case AL_ROOM_ROLLOFF_FACTOR: case AL_CONE_OUTER_GAINHF: case AL_SOURCE_RADIUS: + case AL_SUPER_STEREO_WIDTH_SOFT: CHECKSIZE(values, 1); if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false) values[0] = static_cast<int>(dvals[0]); @@ -2252,6 +2301,7 @@ bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const case AL_ROOM_ROLLOFF_FACTOR: case AL_CONE_OUTER_GAINHF: case AL_SOURCE_RADIUS: + case AL_SUPER_STEREO_WIDTH_SOFT: CHECKSIZE(values, 1); if((err=GetSourcedv(Source, Context, prop, {dvals, 1u})) != false) values[0] = static_cast<int64_t>(dvals[0]); @@ -2298,6 +2348,7 @@ bool GetSourcei64v(ALsource *Source, ALCcontext *Context, SourceProp prop, const case AL_DISTANCE_MODEL: case AL_SOURCE_RESAMPLER_SOFT: case AL_SOURCE_SPATIALIZE_SOFT: + case AL_STEREO_MODE_SOFT: CHECKSIZE(values, 1); if((err=GetSourceiv(Source, Context, prop, {ivals, 1u})) != false) values[0] = ivals[0]; diff --git a/al/source.h b/al/source.h index 4d1f66dc..474d0a91 100644 --- a/al/source.h +++ b/al/source.h @@ -13,6 +13,7 @@ #include "alc/alu.h" #include "alc/context.h" +#include "alc/inprogext.h" #include "aldeque.h" #include "almalloc.h" #include "alnumeric.h" @@ -25,6 +26,11 @@ struct ALbuffer; struct ALeffectslot; +enum class SourceStereo : bool { + Normal = AL_NORMAL_SOFT, + Enhanced = AL_SUPER_STEREO_SOFT +}; + #define DEFAULT_SENDS 2 #define INVALID_VOICE_IDX static_cast<ALuint>(-1) @@ -59,6 +65,7 @@ struct ALsource { Resampler mResampler{ResamplerDefault}; DirectMode DirectChannels{DirectMode::Off}; SpatializeMode mSpatialize{SpatializeMode::Auto}; + SourceStereo mStereoMode{SourceStereo::Normal}; bool DryGainHFAuto{true}; bool WetGainAuto{true}; @@ -75,6 +82,7 @@ struct ALsource { std::array<float,2> StereoPan{{Deg2Rad( 30.0f), Deg2Rad(-30.0f)}}; float Radius{0.0f}; + float EnhWidth{0.593f}; /** Direct filter and auxiliary send info. */ struct { diff --git a/alc/alc.cpp b/alc/alc.cpp index 9f2abc13..54162d14 100644 --- a/alc/alc.cpp +++ b/alc/alc.cpp @@ -869,6 +869,10 @@ constexpr struct { DECL(AL_FORMAT_UHJ4CHN8_SOFT), DECL(AL_FORMAT_UHJ4CHN16_SOFT), DECL(AL_FORMAT_UHJ4CHN_FLOAT32_SOFT), + DECL(AL_STEREO_MODE_SOFT), + DECL(AL_NORMAL_SOFT), + DECL(AL_SUPER_STEREO_SOFT), + DECL(AL_SUPER_STEREO_WIDTH_SOFT), DECL(AL_STOP_SOURCES_ON_DISCONNECT_SOFT), }; diff --git a/alc/alu.cpp b/alc/alu.cpp index 93e18c94..54bb7001 100644 --- a/alc/alu.cpp +++ b/alc/alu.cpp @@ -777,6 +777,9 @@ void CalcPanningAndFilters(Voice *voice, const float xpos, const float ypos, con } voice->mFlags &= ~(VoiceHasHrtf | VoiceHasNfc); + if(auto *decoder{voice->mDecoder.get()}) + decoder->mWidthControl = props->EnhWidth; + if(IsAmbisonic(voice->mFmtChannels)) { /* Special handling for B-Format and UHJ sources. */ diff --git a/alc/inprogext.h b/alc/inprogext.h index 0875e5c9..99d997f5 100644 --- a/alc/inprogext.h +++ b/alc/inprogext.h @@ -87,6 +87,11 @@ ALCboolean ALC_APIENTRY alcReopenDeviceSOFT(ALCdevice *device, const ALCchar *de #define AL_FORMAT_UHJ4CHN8_SOFT 0x19A8 #define AL_FORMAT_UHJ4CHN16_SOFT 0x19A9 #define AL_FORMAT_UHJ4CHN_FLOAT32_SOFT 0x19AA + +#define AL_STEREO_MODE_SOFT 0x19B0 +#define AL_NORMAL_SOFT 0x0000 +#define AL_SUPER_STEREO_SOFT 0x0001 +#define AL_SUPER_STEREO_WIDTH_SOFT 0x19B1 #endif #ifndef AL_SOFT_hold_on_disconnect diff --git a/core/buffer_storage.h b/core/buffer_storage.h index cde7704e..ec934681 100644 --- a/core/buffer_storage.h +++ b/core/buffer_storage.h @@ -93,14 +93,7 @@ struct BufferStorage { { return ChannelsFromFmt(mChannels, mAmbiOrder); } inline uint frameSizeFromFmt() const noexcept { return channelsFromFmt() * bytesFromFmt(); } - 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/voice.cpp b/core/voice.cpp index 44ba897a..d93a4a18 100644 --- a/core/voice.cpp +++ b/core/voice.cpp @@ -814,6 +814,27 @@ void Voice::mix(const State vstate, ContextBase *Context, const uint SamplesToDo void Voice::prepare(DeviceBase *device) { + /* Even if storing really high order ambisonics, we only mix channels for + * orders up to MaxAmbiOrder. The rest are simply dropped. + */ + uint num_channels{(mFmtChannels == FmtUHJ2 || mFmtChannels == FmtSuperStereo) ? 3 : + ChannelsFromFmt(mFmtChannels, minu(mAmbiOrder, MaxAmbiOrder))}; + if(unlikely(num_channels > device->mSampleData.size())) + { + ERR("Unexpected channel count: %u (limit: %zu, %d:%d)\n", num_channels, + device->mSampleData.size(), mFmtChannels, mAmbiOrder); + num_channels = static_cast<uint>(device->mSampleData.size()); + } + if(mChans.capacity() > 2 && num_channels < mChans.capacity()) + { + decltype(mChans){}.swap(mChans); + decltype(mPrevSamples){}.swap(mPrevSamples); + } + mChans.reserve(maxu(2, num_channels)); + mChans.resize(num_channels); + mPrevSamples.reserve(maxu(2, num_channels)); + mPrevSamples.resize(num_channels); + if(IsUHJ(mFmtChannels)) { mDecoder = std::make_unique<UhjDecoder>(); diff --git a/core/voice.h b/core/voice.h index ba5e82cc..8866f8d4 100644 --- a/core/voice.h +++ b/core/voice.h @@ -138,6 +138,7 @@ struct VoiceProps { std::array<float,2> StereoPan; float Radius; + float EnhWidth; /** Direct filter and auxiliary send info. */ struct { |