1#ifndef _VAE_SPATIAL_PROCESSOR
2#define _VAE_SPATIAL_PROCESSOR
4#include "../vae_types.hpp"
5#include "../vae_util.hpp"
6#include "../pod/vae_bank.hpp"
7#include "../voices/vae_voice.hpp"
8#include "../voices/vae_voice_filter.hpp"
9#include "../voices/vae_voice_pan.hpp"
10#include "../voices/vae_voice_hrtf.hpp"
11#include "../vae_voice_manager.hpp"
12#include "../vae_spatial_manager.hpp"
13#include "../algo/vae_spcap.hpp"
14#include "../algo/vae_hrtf_util.hpp"
16#include "../fs/vae_hrtf_loader.hpp"
17#include "../../../external/glm/glm/gtc/matrix_transform.hpp"
19namespace vae {
namespace core {
50 Size actuallyRendered = 0;
53 if (v.
bank != bank.
id) { return true; }
57 VAE_DEBUG(
"Spatial voice is missing emitter")
62 auto& signal = source.signal;
64 const auto signalLength = signal.size();
66 if (signalLength == 0) {
return false; }
70 if (signal.sampleRate != sampleRate) {
80 Real distanceAttenuated;
81 Vec3 relativeDirection;
84 VAE_PROFILER_SCOPE_NAMED(
"Attenuation calculation")
87 glm::mat4x4 lookAt = glm::lookAt(l.position, l.position + l.front, l.up);
89 relativeDirection = (lookAt * glm::vec4(emitter.position, 1.f));
92 const Real distance = std::max(glm::length(relativeDirection), 0.1f);
93 relativeDirection /= distance;
96 distanceAttenuated = distance;
97 distanceAttenuated = std::max(distanceAttenuated, Real(1));
98 distanceAttenuated = Real(1) / distanceAttenuated;
100 distanceAttenuated = 1.0;
102 distanceAttenuated *= gain;
110 target.setValidSize(frames);
119 bool finished =
false;
122 VAE_PROFILER_SCOPE_NAMED(
"Voice Filter")
123 auto& fd = manager.getVoiceFilter(vi);
127 fd.highpassScratch[0] = 0;
128 fd.lowpassScratch[0] = signal[0][v.time];
136 remaining = std::min(
138 SampleIndex(std::floor((signalLength - v.time) / speed - fd.timeFract))
140 finished = remaining != frames;
147 position = v.
time + (s * speed) + fd.timeFract;
148 const Real lastPosition = std::floor(position);
149 const Size lastIndex = (
Size) lastPosition;
150 const Size nextIndex = (
Size) lastPosition + 1;
152 Real mix = position - lastPosition;
156 const Sample last = signal[0][lastIndex % signalLength];
157 const Sample next = signal[0][nextIndex % signalLength];
159 const Sample in = (last +
mix * (next - last)) * gain;
163 const Sample lpd = in + fd.lowpass * (fd.lowpassScratch[0] - in);
164 fd.lowpassScratch[0] = lpd;
166 const Sample hps = fd.highpassScratch[0];
167 const Sample hpd = hps + fd.highpass * (in - hps);
168 fd.highpassScratch[0] = hpd;
174 fd.timeFract = position - v.
time;
185 v.time = (v.time + frames);
195 in = signal[0] + v.time;
196 finished = remaining != frames;
206 Size closestIndex = HRTFUtil::closest(
mHRTF, relativeDirection);
208 if (closestIndex == ~
Size(0)) {
return true; }
213 hrtfVoice.convolutionIndex = 0;
214 hrtfVoice.convolutionBuffer.set();
219 hrtfVoice, remaining, target, in, distanceAttenuated
225 auto& lastPan = manager.getVoicePan(vi);
227 auto& currentVolumes = currentPan.volumes;
228 auto& lastVolumes = lastPan.volumes;
234 const auto pan = [&](const auto& panner) {
238 relativeDirection, currentVolumes,
239 distanceAttenuated, emitter.spread
244 for (
Size c = 0; c < channels; c++) {
245 lastVolumes[c] = currentVolumes[c];
251 const Sample sample = in[s];
254 for (
Size c = 0; c < channels; c++) {
255 target[c][s] += sample * (lastVolumes[c] + t * (currentVolumes[c] - lastVolumes[c]));
261 switch (l.configuration) {
270 lastPan = std::move(currentPan);
274 emitter.autoplaying =
false;
279 return actuallyRendered;
283 Result result = mHRTFLoader.
load(path, length, rootPath, sampleRate, mHRTF);
285 for (
auto& i : mVoiceHRTFs) {
286 i.convolutionBuffer.resize(mHRTF.
irLength);
287 i.convolutionBuffer.set();
bool resize(const Size length, uchar channels)
! Will not keep the contents! Resizes the buffer to the desired length and channel count.
Basically a bad std::vector without exceptions which can also work with foreign memory.
Result load(const char *path, Size length, const char *rootPath, const Size sampleRate, HRTF &hrtf)
Listeners & getListeners()
bool hasEmitter(EmitterHandle e)
Emitter & getEmitter(EmitterHandle e)
HRTFLoader mHRTFLoader
Struct to decode the hrtf.
ScratchBuffer mScratchBuffer
Temporary filtered/looped signal TODO this will not work with parallel bank processing.
HeapBuffer< VoiceHRTF > mVoiceHRTFs
Working data for convolution.
Size mix(VoiceManger &manager, Bank &bank, SpatialManager &spatial, SampleIndex frames, Size sampleRate)
Process a single bank.
HRTF mHRTF
Currently loaded HRTF, there can only be one.
Result loadHRTF(const char *path, Size length, const char *rootPath, Size sampleRate)
Result init(Size hrtfVoices)
There is only one voice pool and VAE and it's managed here.
void forEachVoice(const Func &&func)
Callback provided to iterate voices, needs to return a bool to indicate when a voice needs to be stop...
T min(const T &v1, const T &v2)
constexpr Sample MinVolume
Minimum volume before sounds will skip rendering.
constexpr Size MaxBlock
Maximum block size.
constexpr unsigned char MaxChannels
Maximum channel count used to pre allocate buffers.
AudioBuffer::Size SampleIndex
constexpr int _VAE_SPATIAL_PROCESSOR_SIZE
Contains Typedefinitions and basic structures use by the public API and internally.
unsigned int Size
How the elements are addressed in the heapbuffer.
float Sample
Default sample types used where ever possible, changing this means the engine needs to be recompiled,...
Result
Return Types for most engine functions.
Bank object containing Sources, Mixers and Events Can be loaded and unloaded at runtime.
HeapBuffer< Mixer > mixers
Audio Mixers which can have effects ! is presorted !
HeapBuffer< Source > sources
Audio sources defined.
HeapBuffer< Position > positions
static void apply(HRTF::Position &hrtf, VoiceHRTF &hrtfVoice, SampleIndex frames, ScratchBuffer &target, const Sample *in, Sample distanceAttenuated)
Applies simple time domain convolution.
static const SPCAPConfig< 2 > StereroSPCAP
static const SPCAPConfig< 1 > MonoSPCAP
TODO there's probably a smart way to make this all constexpr.
static const SPCAPConfig< 4 > QuadSPCAP
static const SPCAPConfig< 5 > SuroundSPCAP
static const SPCAPConfig< 2 > HeadphoneSPCAP
SourceHandle source
If invalid, means voice is not playing.
bool spatialized
If the voice has spatialization data.
ListenerHandle listener
If it's spatialized it's rendered for this listener.
Sample gain
Volume of the voice.
bool audible
Whether the voice was heard by any listener.
BankHandle bank
Which bank it belongs to.
bool filtered
This will enable high/lowpass filters and variable speed playback. Gets turned on when signal does no...
MixerHandle mixer
Where the voice should mix to.
EmitterHandle emitter
Emitter used to control voice properties.
bool loop
Voice will loop until killed.
SampleIndex time
Current time in samples.
#define VAE_PROFILER_SCOPE_NAMED(name)
Profiles a scope and names it.
#define VAE_ASSERT(condition)