VAE - Virtual Audio Engine 1
Small Data Driven Audio Engine
vae::core::Processor Class Reference

Non spatial voice processor. More...

#include <vae_processor.hpp>

Collaboration diagram for vae::core::Processor:

Public Member Functions

Result init ()
 
Size mix (VoiceManger &manager, Bank &bank, SampleIndex frames, Size sampleRate)
 Process a single bank. More...
 

Private Attributes

ScratchBuffer mScratchBuffer
 Temporary filtered/looped signal TODO this will not work with parallel bank processing. More...
 

Detailed Description

Non spatial voice processor.

Definition at line 18 of file vae_processor.hpp.

Member Function Documentation

◆ init()

Result vae::core::Processor::init ( )
inline

Definition at line 24 of file vae_processor.hpp.

24 {
26 return Result::Success;
27 }
bool resize(const Size length, uchar channels)
! Will not keep the contents! Resizes the buffer to the desired length and channel count.
ScratchBuffer mScratchBuffer
Temporary filtered/looped signal TODO this will not work with parallel bank processing.
constexpr Size MaxBlock
Maximum block size.
Definition: vae.hpp:276
constexpr unsigned char MaxChannels
Maximum channel count used to pre allocate buffers.
Definition: vae.hpp:268
Here is the call graph for this function:

◆ mix()

Size vae::core::Processor::mix ( VoiceManger manager,
Bank bank,
SampleIndex  frames,
Size  sampleRate 
)
inline

Process a single bank.

Parameters
manager
banks
frames
sampleRate
Returns
Voices renderd

Definition at line 38 of file vae_processor.hpp.

41 {
42 Size actuallyRendered = 0;
43 VAE_PROFILER_SCOPE_NAMED("Default Processor")
44 manager.forEachVoice([&](Voice& v, Size index) {
45 if (v.bank != bank.id) { return true; }
46 if (v.spatialized) { return true; }
47 VAE_PROFILER_SCOPE_NAMED("Default Voice")
48
49 auto& source = bank.sources[v.source];
50 auto& signal = source.signal;
51
52 const SampleIndex signalLength = signal.size();
53
54 if (signalLength == 0) { return false; }
55 if (signal.sampleRate != sampleRate) {
56 // VAE_DEBUG("Spatial Voice samplerate mismatch. Enabled filter.")
57 v.filtered = true; // implicitly filter to resample
58 }
59
60 v.time = v.time % signalLength; // Keep signal in bounds before starting
61
62 const auto signalChannels = signal.channels();
63 auto& mixer = bank.mixers[v.mixer];
64 auto& target = mixer.buffer;
65 const auto targetChannels = target.channels();
66 const auto gain = v.gain * source.gain;
67
68 // TODO skip inaudible sounds
69 actuallyRendered++;
70 v.audible = true;
71 auto& pan = manager.getVoicePan(index);
72 target.setValidSize(frames); // mark mixer as active
73
74 if (!v.filtered) {
75 VAE_PROFILER_SCOPE_NAMED("Render Voice Basic")
76 // Basic rendering to all output channels w/o any effects
77 v.started = true;
78 const SampleIndex needed = v.loop ? frames : std::min(frames, SampleIndex(signalLength - v.time));
79
80 if (v.loop) {
81 for (int c = 0; c < targetChannels; c++) {
82 const int channel = c % signalChannels;
83 for (SampleIndex s = 0; s < needed; s++) {
84 target[c][s] +=
85 signal[channel][((v.time + s) % signalLength)] * gain * pan.volumes[c];
86 }
87 }
88 } else {
89 // Having these seperate results in like a 3x speedup
90 // TODO check this in a more diffictult to branchpredict scenario
91 for (int c = 0; c < targetChannels; c++) {
92 for (SampleIndex s = 0; s < needed; s++) {
93 const int channel = c % signalChannels;
94 target[c][s] +=
95 signal[channel][v.time + s] * gain * pan.volumes[c];
96 }
97 }
98 }
99
100 v.time = v.time + frames; // progress voice
101 return needed == frames; // Finished if there are no samples left in source
102 }
103
104 // Filtered voice processing
105 {
106 VAE_PROFILER_SCOPE_NAMED("Render filtered Voice")
107 auto& fd = manager.getVoiceFilter(index);
108
109 if (!v.started) {
110 // Initialize filter variables when first playing the voice
111 for (int c = 0; c < StaticConfig::MaxChannels; c++) {
112 fd.highpassScratch[c] = 0;
113 fd.lowpassScratch[c] = signal[c % signalChannels][v.time];
114 }
115 v.started = true;
116 }
117
118 // fractional time, we need the value after the loop, so it's defined outside
119 Real position;
120
121 // Playback speed taking samplerate into account
122 const Real speed = fd.speed * (Sample(signal.sampleRate) / Sample(sampleRate));
123 const SampleIndex needed = v.loop ? frames : std::min(
124 frames, SampleIndex(std::floor((signalLength - v.time) / speed - fd.timeFract))
125 );
126
127 for (int c = 0; c < target.channels(); c++) {
128 for (SampleIndex s = 0; s < needed; s++) {
129 const int channel = c % signal.channels();
130 // Linear interpolation between two samples
131 position = v.time + (s * speed) + fd.timeFract;
132 const Real lastPosition = std::floor(position);
133 const Size lastIndex = (Size) lastPosition;
134 const Size nextIndex = (Size) lastPosition + 1;
135
136 Real mix = position - lastPosition;
137 // mix = 0.5 * (1.0 - cos((mix) * 3.1416)); // cosine interpolation, introduces new harmonics somehow
138 const Sample last = signal[channel][lastIndex % signalLength] * gain;
139 const Sample next = signal[channel][nextIndex % signalLength] * gain;
140 // linear resampling, sounds alright enough
141 const Sample in = last + mix * (next - last);
142
143 // * super simple lowpass and highpass filter
144 // just lerps with a previous value
145 const Sample lf = fd.lowpass;
146 const Sample lpd = in + lf * (fd.lowpassScratch[c] - in);
147 fd.lowpassScratch[c] = lpd;
148
149 const Sample hf = fd.highpass;
150 const Sample hps = fd.highpassScratch[c];
151 const Sample hpd = hps + hf * (in - hps);
152 fd.highpassScratch[c] = hpd;
153
154 target[c][s] += (lpd - hpd) * pan.volumes[c];
155 }
156 }
157 position += speed; // step to next sample
158 v.time = (SampleIndex) std::floor(position); // split the signal in normal sample position
159 fd.timeFract = position - v.time; // and fractional time for the next block
160 return needed == frames; // we might have reached the end; // is only true when exceeding signalLength and not looping
161 }
162 });
163 return actuallyRendered;
164 }
Size mix(VoiceManger &manager, Bank &bank, SampleIndex frames, Size sampleRate)
Process a single bank.
T min(const T &v1, const T &v2)
Definition: TMath.hpp:16
AudioBuffer::Size SampleIndex
Definition: vae_types.hpp:87
float Real
Definition: vae_types.hpp:48
unsigned int Size
How the elements are addressed in the heapbuffer.
Definition: vae.hpp:33
float Sample
Default sample types used where ever possible, changing this means the engine needs to be recompiled,...
Definition: vae.hpp:32
#define VAE_PROFILER_SCOPE_NAMED(name)
Profiles a scope and names it.
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mScratchBuffer

ScratchBuffer vae::core::Processor::mScratchBuffer
private

Temporary filtered/looped signal TODO this will not work with parallel bank processing.

Definition at line 22 of file vae_processor.hpp.


The documentation for this class was generated from the following file: