VAE - Virtual Audio Engine 1
Small Data Driven Audio Engine
vae_engine.hpp
Go to the documentation of this file.
1#ifndef _VAE_ENGINE
2#define _VAE_ENGINE
3
4#include "./vae_types.hpp"
5#include "./vae_util.hpp"
6#include "./vae_config.hpp"
7
8
9
11#include "./pod/vae_emitter.hpp"
18
19#include "../../external/tklb/src/util/TMath.hpp"
20#include "./vae_logger.hpp"
22
23
24#ifndef VAE_NO_AUDIO_DEVICE
26#endif // !VAE_NO_AUDIO_DEVICE
27
28#ifndef VAE_NO_AUDIO_THREAD
29 #include <mutex>
30 #include <thread> // mAudioThread
31 #include <condition_variable> // mAudioConsumed
32#endif // !VAE_NO_AUDIO_THREAD
33
34/**
35 * @brief Marks a function for export to the generated vae::EnginePimpl class
36 * [generated from generate_pimpl.py](../../scripts/README.md)
37 * @see vae::EnginePimpl
38 */
39#define _VAE_PUBLIC_API
40
41
42namespace vae { namespace core {
43 /**
44 * @brief Central class handling all outside communication.
45 * @details Can be sealed off by using EnginePimpl instead
46 */
47
48 class Engine {
49 EngineConfig mConfig; ///< Config object provided at initlalization
50
51 VoiceManger mVoiceManager; ///< Holds and handle voices
52 SpatialManager mSpatialManager; ///< Holds and manages spatial emitters
53 BankManager mBankManager; ///< Holds and manages banks
54
55 Processor mProcessor; ///< Default Voice processor
56 MixerProcessor mMixerProcessor; ///< Mixer channel processor
57 SpatialProcessor mSpatialProcessor; ///< Spatial voice processor
58
59 ScratchBuffer mScratchBuffer; ///< used to combine the signal from all banks and push it to the device
60 SampleIndex mTime = 0; ///< Global engine time in samples
61 Time mTimeFract = 0; ///< Global engine time in seconds
62 Sample mLimiterLastPeak = 1.0; ///< Master limiter last peak
63 Sample mMasterVolume = 1.0; ///< Master Colume applied after limiting
64
65 #ifndef VAE_NO_AUDIO_DEVICE
66 Device* mDevice = nullptr; ///< Output device
67 #endif //
68
69 #ifndef VAE_NO_AUDIO_THREAD
70 using Thread = std::thread;
71 using ConditionVariable = std::condition_variable;
72 Thread* mAudioThread; ///< Thread processing voices and mixers
73 ConditionVariable mAudioConsumed; ///< Notifies the audio thread when more audio is needed
74 std::mutex mMutex; ///< Mutex needed to use mAudioConsumed, doesn't actually do anything else
75 bool mAudioThreadRunning = false;
76 #endif // !VAE_NO_AUDIO_THREAD
77
78 #ifndef VAE_NO_AUDIO_DEVICE
79 /**
80 * @brief Main processing function.
81 * @details Called either from onBufferSwap or threadedProcess
82 * This does't need to be locked since it only renders the banks.
83 * The data accessed from this class will only be used from this thread.
84 * The bank however needs to be locks which happens in the bankmanager
85 */
86 void process() {
88 VAE_PROFILER_SCOPE_NAMED("Engine Process")
89 auto& d = *mDevice;
90 auto sampleRate = mConfig.internalSampleRate;
91
92 const Time step = 1.0 / Time(sampleRate);
93
94 // process until device can't take any more audio
95 while (true) {
96 // ! this is an underestimate when resampling so we don't have any leftovers
97 auto remaining = d.canPush();
98
99 static_assert(32 <= StaticConfig::MaxBlock, "MaxBlock needs to be larger");
100
101 if (remaining < 32) {
102 break; // ! Don't even bother with small blocks, we'll get em next time
103 }
104
105 // clamp to max processable size, the preallocated scratch buffers can't take any larger blocks
106 remaining = std::min(remaining, StaticConfig::MaxBlock);
107
108 mScratchBuffer.setValidSize(remaining);
109
110 Size renderedNormal = 0;
111 Size renderedSpatial = 0;
112 // TODO PERF VAE banks could be processed in parallel
113 // however each bank needs to get a temporary own copy of the voice
114 // or else this will be false sharing city
115 mBankManager.forEach([&](Bank& i) {
116 renderedNormal += mProcessor.mix(mVoiceManager, i, remaining, sampleRate);
117 renderedSpatial += mSpatialProcessor.mix(
118 mVoiceManager, i, mSpatialManager, remaining, sampleRate
119 );
120 mMixerProcessor.mix(mVoiceManager, i, remaining);
121 auto& bankMaster = i.mixers[Mixer::MasterMixerHandle].buffer;
122 mScratchBuffer.add(bankMaster);
123 bankMaster.set(0);
124 });
125
126 VAE_PROFILER_PLOT("Rendered Normal Voices", int64_t(renderedNormal));
127 VAE_PROFILER_PLOT("Rendered Spatial Voices", int64_t(renderedSpatial));
128 VAE_PROFILER_PLOT("Rendered Total Voices", int64_t(renderedSpatial + renderedNormal));
129
130 {
131 VAE_PROFILER_SCOPE_NAMED("Peak limiting")
132 // Shitty peak limiter
133 mLimiterLastPeak *= Sample(0.99); // return to normal slowly
135 Sample currentPeak = 0;
136 for (Uchar c = 0; c < mScratchBuffer.channels(); c++) {
137 for (Size i = 0; i < remaining; i++) {
138 currentPeak = std::max(currentPeak, mScratchBuffer[c][i]);
139 }
140 }
141 currentPeak *= mMasterVolume; // pretend we already applied the master volume
143 mLimiterLastPeak += Sample(0.05); // add a little extra so we really stay away from clipping
144 }
145 const Sample gain = mMasterVolume / mLimiterLastPeak; // this can be higher than 1 one but the result can't
146 VAE_PROFILER_PLOT("Limited Master Volume", int64_t(gain * 1000));
147 mScratchBuffer.multiply(gain); // apply the master volume and limiter
148 d.push(mScratchBuffer);
150 mTime += remaining;
151 mTimeFract += step * remaining;
152 }
156 }
157
158 /**
159 * @brief Called from audio device when it needs more audio.
160 * This will do synchronous processing.
161 * @param device
162 */
163 void onBufferSwap(Device* device) {
164 (void) device;
165 process();
166 }
167
168 #endif // !VAE_NO_AUDIO_DEVICE
169
170 #ifndef VAE_NO_AUDIO_THREAD
171 /**
172 * @brief Called from own audio thread, not the device
173 *
174 */
176 VEA_PROFILER_THREAD_NAME("Audio thread")
177 while(mAudioThreadRunning) {
178 process(); // Process one block in advance so there's no underrun
179 std::unique_lock<std::mutex> l(mMutex);
180 mAudioConsumed.wait(l); // Wait until we got work
181 }
182 }
183
184 /**
185 * @brief Called from audio device when using seperate audio thread.
186 * This will only notify the adio thread to do work.
187 * @param device
188 */
190 (void) device;
191 mAudioConsumed.notify_one();
192 }
193 #endif // !VAE_NO_AUDIO_THREAD
194
195
196 public:
197
198 Engine() = default;
199
200 /**
201 * Don't allow any kind of move of copy of the object
202 */
203 Engine(const Engine&) = delete;
204 Engine(const Engine*) = delete;
205 Engine(Engine&&) = delete;
206 Engine& operator= (const Engine&) = delete;
208
210 VAE_PROFILER_SCOPE_NAMED("Destroy Engine")
211 stop();
213 VAE_INFO("Engine destructed")
214 }
215
216 /** @name Engine Controls
217 * Main engine functionality
218 */
219 ///@{
220
221 /**
222 * @brief Initializes the engine and does most of the upfront allocations. Run this before start !
223 * @details Everything will be allocated according to the provided config.
224 * Loading a Bank will still cause an allocation.
225 * If there are already banks loaded, they will be reloaded to have the correct samplerate.
226 * @see start
227 * @param config Optional config to setup the internals.
228 * @return Result
229 */
231 VAE_PROFILER_SCOPE_NAMED("Engine init")
232 VAE_DEBUG("Initializing engine...")
233 mConfig = config;
234 mScratchBuffer.resize(StaticConfig::MaxBlock, StaticConfig::MaxChannels);
236 mScratchBuffer.sampleRate = mConfig.internalSampleRate;
238 mSpatialManager.init(mConfig.preAllocatedEmitters);
241 mBankManager.init(mConfig.rootPath, mConfig.internalSampleRate);
242 VAE_DEBUG("Engine initialized")
243 return Result::Success;
244 }
245
246 /**
247 * @brief Tries to open default device and start audio thread. Call this after start.
248 * @see init
249 * @return Result
250 */
252 #ifndef VAE_NO_AUDIO_DEVICE
253 VEA_PROFILER_THREAD_NAME("Application Thread")
255 {
256 VAE_PROFILER_SCOPE_NAMED("Device Instance")
258 mDevice = backend.createDevice(mConfig);
259 }
262 VAE_DEBUG("Mixing in buffer switch")
263 } else {
264 #ifndef VAE_NO_AUDIO_THREAD
266 mAudioThreadRunning = true;
267 VAE_PROFILER_SCOPE_NAMED("Start audio thread")
269 VAE_DEBUG("Mixing in seperate thread")
270 #else
271 VAE_ERROR("Can't mix in audio thread since it's disabled via VAE_NO_AUDIO_THREAD")
272 #endif // !VAE_NO_AUDIO_THREAD
273 }
274 {
275 VAE_PROFILER_SCOPE_NAMED("Device Instance")
277 }
278 #endif // !VAE_NO_AUDIO_DEVICE
280 }
281
282 /**
283 * @brief Stops processing and waits for audio thead to clean up
284 * @return Result
285 */
287 #ifndef VAE_NO_AUDIO_DEVICE
289 #ifndef VAE_NO_AUDIO_THREAD
291 mAudioThreadRunning = false;
292 mAudioConsumed.notify_one(); // make sure the audio thread knows it's time to go
293 if(mAudioThread->joinable()) {
294 mAudioThread->join();
295 delete mAudioThread;
296 VAE_DEBUG("Audio thread stopped")
297 } else {
298 VAE_ERROR("Can't join audio thread")
299 }
300 }
301 #endif // !VAE_NO_AUDIO_THREAD
302 if (mDevice != nullptr) {
303 delete mDevice;
304 mDevice = nullptr;
305 }
306 #endif // !VAE_NO_AUDIO_DEVICE
307 return Result::Success;
308 }
309
310
311 /**
312 * @brief Update function needs to be called regularly to handle outbound events and other housekeeping.
313 * @details If this isn't called regularly events might be lost and chained events not fired.
314 * When EngineConfig::updateInAudioThread is true, this doesn't need to be called manually.
315 * @see EngineConfig::updateInAudioThread
316 */
319 VAE_PROFILER_SCOPE_NAMED("Engine Update")
320 // Update emitters and start voices nearby
324 [&](EventHandle event, BankHandle bank, EmitterHandle emitter) {
325 fireEvent(bank, event, emitter);
326 }
327 );
328
329 // Handle finished voices and their events
331 if (v.emitter != InvalidEmitterHandle) {
333 // Make sure the event can be triggered again by that emitter
334 }
335 auto onEnd = mBankManager.get(v.bank).events[v.event].on_end;
336 if (onEnd != InvalidEventHandle) {
337 fireEvent(v.bank, onEnd, v.emitter, 1.0, v.mixer);
338 }
339 return true;
340 });
343 }
344
345#define VAE_NO_AUDIO_DEVICE
346 #ifdef VAE_NO_AUDIO_DEVICE
347 template <typename T>
348 void process(const SampleIndex frames, T* output, int channels) {
349 SampleIndex time = 0;
350 while (time < frames) {
351 // clamp to max processable size, the preallocated scratch buffers can't take any larger blocks
352 SampleIndex remaining = std::min(remaining, StaticConfig::MaxBlock);
353
354 mScratchBuffer.setValidSize(remaining);
355
356 // however each bank needs to get a temporary own copy of the voice
357 // or else this will be false sharing city
358 mBankManager.forEach([&](Bank& i) {
362 );
363 mMixerProcessor.mix(mVoiceManager, i, remaining);
364 auto& bankMaster = i.mixers[Mixer::MasterMixerHandle].buffer;
365 mScratchBuffer.add(bankMaster);
366 bankMaster.set(0);
367 });
368
370 mScratchBuffer.putInterleaved(output + time * channels, remaining);
372 mTime += remaining;
373 time += remaining;
374 }
375 }
376 #endif // VAE_NO_AUDIO_DEVICE
377
378 /**
379 * @brief Main mechanism to start and stop sounds
380 * @see Event
381 * @param bankHandle bank id where the event is provided
382 * @param eventHandle id of the event
383 * @param emitterHandle handle of the emitter, needed for spatial audio or controlling the voice
384 * @param gain optional volume factor
385 * @param mixerHandle optional id of mixer channel sound will be routed to, this will override the one set in the event
386 * @param listenerHandle For which listener this event will be adible for, default to all
387 * @return Result
388 */
390 BankHandle bankHandle, EventHandle eventHandle,
391 EmitterHandle emitterHandle,
392 Sample gain = 1.0,
393 MixerHandle mixerHandle = InvalidMixerHandle,
394 ListenerHandle listenerHandle = AllListeners
395 ) {
397 if (emitterHandle != InvalidEmitterHandle && !mSpatialManager.hasEmitter(emitterHandle)) {
398 VAE_ERROR("No emitter %u registered", emitterHandle)
400 }
401
402 if (!mBankManager.has(bankHandle)) {
403 VAE_ERROR("Fired event %i on unloaded bank %i", eventHandle, bankHandle)
404 return Result::InvalidBank;
405 }
406
407 auto& bank = mBankManager.get(bankHandle);
408
409 if (bank.events.size() <= eventHandle) {
410 VAE_WARN("Fired missing event %i on bank %i", eventHandle, bankHandle)
412 }
413
414 auto& event = bank.events[eventHandle];
415
416 Result result;
417
418 if (event.action == Event::Action::start) {
419 if (event.source != InvalidSourceHandle) {
420 VAE_DEBUG_EVENT("Event %i:%i starts source %i", eventHandle, bankHandle, event.source)
421 // Has source attached
422 if (event.spatial) {
423 // Spatialized sounds will play for every listener
424 result = mSpatialManager.forListeners(listenerHandle, [&](Listener& l) {
425 return mVoiceManager.play(
426 event, bankHandle, gain, emitterHandle, l.id, mixerHandle
427 );
428 });
429 } else {
430 // non spatialized sounds just once
431 result = mVoiceManager.play(
432 event, bankHandle, gain, emitterHandle, listenerHandle, mixerHandle
433 );
434 }
435 }
436
437 // Fire all other chained events
438 for (auto& i : event.chained_events) {
439 if (i == InvalidEventHandle) { continue; }
440 VAE_DEBUG_EVENT("Event %i:%i starts chained event %i", eventHandle, bankHandle, i)
441 result = fireEvent(
442 bankHandle, i, emitterHandle, gain,
443 mixerHandle, listenerHandle
444 );
445 }
446
447 if (result != Result::Success) {
448 VAE_DEBUG_EVENT("Event %i:%i failed to start voices", eventHandle, bankHandle)
449 return result; // ! someting went wrong
450 }
451 }
452
453 else if (event.action == Event::Action::random) {
454 for (int index = rand() % StaticConfig::MaxChainedEvents; 0 <= index; index--) {
455 auto& i = event.chained_events[index];
456 if (i == InvalidEventHandle) { continue; }
457 VAE_DEBUG_EVENT("Event %i:%i starts random event %i", eventHandle, bankHandle, i)
458 result = fireEvent(
459 bankHandle, i, emitterHandle, gain,
460 mixerHandle, listenerHandle
461 );
462 break;
463 }
464 }
465
466 else if (event.action == Event::Action::stop) {
467 // TODO test stopping
468 if (event.source != InvalidSourceHandle) {
469 VAE_DEBUG_EVENT("Event %i:%i stops source %i", eventHandle, bankHandle, event.source)
470 mVoiceManager.stop(event.source, &Voice::source, emitterHandle);
471 }
472 for (auto& i : event.chained_events) {
473 if (i == InvalidEventHandle) { continue; }
474 // kill every voice started from these events
475 VAE_DEBUG_EVENT("Event %i:%i stops voices from event %i", eventHandle, bankHandle, i)
476 mVoiceManager.stop(i, &Voice::event, emitterHandle);
477 }
478 if (event.mixer != Mixer::MasterMixerHandle) {
479 // kill every voice in a mixer channel
480 VAE_DEBUG_EVENT("Event %i:%i stops voices in mixer %i", eventHandle, bankHandle, event.mixer)
481 mVoiceManager.stop(event.mixer, &Voice::mixer, emitterHandle);
482 }
483 }
484
485 else if (event.action == Event::Action::emit) {
486 VAE_DEBUG_EVENT("Event %i:%i emits event", eventHandle, bankHandle)
487 if (mConfig.eventCallback != nullptr) {
489 constexpr int as = sizeof(data);
491 data.bank = bankHandle;
492 data.event = eventHandle;
493 data.emitter = emitterHandle;
494 mConfig.eventCallback(&data);
495 }
496 }
497
498 return Result::Success;
499 }
500
501 /**
502 * @brief Works like fireEvent but with a global Event identifier
503 * @see fireEvent
504 * @param globalHandle The GlobalEventHandle combines both bank and event id
505 * @param emitterHandle optional handle of the emitter, needed for spatial audio
506 * @param gain optional volume factor
507 * @param mixerHandle id of mixer channel sound will be routed to, this will override the one set in the event
508 * @return Result
509 */
511 GlobalEventHandle globalHandle,
512 EmitterHandle emitterHandle,
513 Sample gain = 1.0,
514 MixerHandle mixerHandle = InvalidMixerHandle,
515 ListenerHandle listenerHandle = AllListeners
516 ) {
517 BankHandle bankHandle;
518 EventHandle eventHandle;
519 splitGlobalEventHandle(globalHandle, bankHandle, eventHandle);
520 return fireEvent(
521 bankHandle, eventHandle, emitterHandle,
522 gain, mixerHandle, listenerHandle
523 );
524 }
525
526 /**
527 * @brief Get the number of currently playing Voices
528 */
531 }
532
534 return mTime;
535 }
536
537 /**
538 * @brief Set the global output volume before the limiter.
539 * @details The engine can't clip, but if the output is too load
540 * the signal will be squashed in the limiter.
541 * @param volume 1.0 is the default, not interpolated for now
542 */
544 mMasterVolume = volume;
545 }
546
547 /**
548 * @brief Check if the compiled version matches
549 */
550 bool _VAE_PUBLIC_API checkVersion(int major, int minor, int patch) {
551 return
552 VAE_VERSION_MAJOR == major &&
553 VAE_VERSION_MINOR == minor &&
554 VAE_VERSION_PATCH == patch;
555 }
556
557 ///@}
558
559#pragma region emitter
560
561 /** @name Emitter manipulation
562 * Contains everything related to emitter creation and basic manipulation of voices started from them
563 */
564 ///@{
565
566 /**
567 * @brief Creates an emitter and returns the handle
568 * @return EmitterHandle Random handle
569 */
572 }
573
574 /**
575 * @brief Emitter which triggers an event once a listener is close enough
576 *
577 * @param bank
578 * @param event
579 * @param maxDist
580 * @param locDir
581 * @param spread
582 * @return EmitterHandle Handle like a normal emitter
583 */
585 BankHandle bank, EventHandle event, float maxDist,
586 const LocationDirection& locDir, float spread
587 ) {
588 return mSpatialManager.createAutoEmitter(bank, event, maxDist, locDir, spread);
589 }
590
591 /**
592 * @brief Adds an emitter with a custom handle, can be an internal ID for example
593 * @details migt be desireable to make EmitterHandle the same size as a pointer
594 * so this can simply be the pointer of the entity that is associated with it.
595 * @param h
596 * @return Result
597 */
599 return mSpatialManager.addEmitter(h);
600 }
601
602 /**
603 * @brief Unregister a emiter an kill all its voices
604 * @param h
605 * @return Result
606 */
610 }
611
612 /**
613 * @brief Set the Emitter position, orientation and spread
614 * @param emitter The emitter
615 * @param locDir The desired location
616 * @param spread The width of the panning (if it's spatial and not HRTF)
617 * @return Result
618 */
620 EmitterHandle emitter, const LocationDirection& locDir, float spread
621 ) {
622 return mSpatialManager.setEmitter(emitter, locDir, spread);
623 }
624
625 /**
626 * @brief Stop all voices from emitter.
627 * @param emitter
628 * @return Result
629 */
631 return mVoiceManager.stop(emitter, &Voice::emitter);
632 }
633
634 /**
635 * @brief Sets the volume of all active voices with this emitter
636 * @param emitter
637 * @param gain
638 */
641 }
642
643 /**
644 * @brief Set the current time of all voices with the emitter.
645 * @param emitter
646 * @param time Time in samples
647 */
650 }
651
652 /**
653 * @brief Set the playback speed
654 * @param emitter
655 * @param speed 1.0 is the default speed, pitch will be affected as well.
656 */
657 void _VAE_PUBLIC_API setSpeed(EmitterHandle emitter, float speed) {
659 }
660
661 /**
662 * @brief Simple lowpass filter for the voices
663 * @param emitter
664 * @param cutoff 0-1. 0 doesn't filter, 1 filter the wholespektrum
665 */
666 void _VAE_PUBLIC_API setLowpass(EmitterHandle emitter, float cutoff) {
668 }
669
670 /**
671 * @brief Simple highpass filter for the voices
672 * @param emitter
673 * @param cutoff 0-1. 0 doesn't filter, 1 filter the wholespektrum
674 */
675 void _VAE_PUBLIC_API setHighpass(EmitterHandle emitter, float cutoff) {
677 }
678
679 ///@}
680#pragma endregion emitter
681
682 /**
683 * @brief Set the Mixer Volume
684 *
685 * @param bank
686 * @param mixer
687 * @param volume
688 * @return Result
689 */
691 mBankManager.get(bank).mixers[mixer].gain = volume;
692 return Result::Success;
693 }
694
695 /**
696 * @brief Bypass a effect in a mixer
697 *
698 * @param bank
699 * @param mixer
700 * @param index See setMixerEffectParameter
701 * @param mute
702 * @return Result
703 */
704 Result muteMixerEffect(BankHandle bank, MixerHandle mixer, Size index, bool mute) {
705 mBankManager.get(bank).mixers[mixer].effects[index].bypassed = mute;
706 return Result::Success;
707 }
708
709 /**
710 * @brief Set the Mixer Effect Parameter
711 *
712 * @param bank
713 * @param mixer
714 * @param index which effects slot out of StaticConfig::MaxMixerEffects
715 * @param param which param by index out of StaticConfig::MaxEffectsParameter
716 * @param value value of the parameter from 0 to 1
717 * @return Result
718 */
720 VAE_PROFILER_SCOPE_NAMED("Set Mixer Effect");
721 // TODO this is garbage but needs a event queue anyways
722 auto& b = mBankManager.get(bank);
723 auto& m = b.mixers[mixer];
724 auto& e = m.effects[index];
725 auto& p = e.parameters[param];
726 p.value = value;
727 return Result::Success;
728 }
729#pragma region mixer
730
731
732
733#pragma endregion emitter
734
735#pragma region listener
736
737 /**
738 * @brief Create a Listener object
739 * @details TODO make listener 0 implicit
740 * @return ListenerHandle
741 */
744 }
745
746 /**
747 * @brief Unregister listener
748 * @param listener
749 */
751 return mSpatialManager.removeListener(listener);
752 }
753
754 /**
755 * @brief Set the position of a listener
756 * @param listener
757 * @return Result
758 */
760 return mSpatialManager.setListener(listener, locOr);
761 }
762
763 Result _VAE_PUBLIC_API loadHRTF(const char* path, Size size = 0) {
765 path, size, mConfig.rootPath,
767 );
768 }
769
770#pragma endregion listsner
771
772
773#pragma region bank_handling
774
775 /** @name Ressource Management
776 * Everything related to Bank and other ressource loading/unloading
777 */
778 ///@{
779 /**
780 * @brief Load bank from filesystem.
781 * @details This operation might take a little time but won't lock the audio thread
782 * until the bank is inserted. This should be safe to do at any time.
783 * @param path
784 * @return Result
785 */
786 Result _VAE_PUBLIC_API loadBank(const char* path, Size size = 0) {
788 }
789
790 /**
791 * @brief Load bank from memory.
792 * @see loadBank
793 * @param bank Moved and now owned by the engine
794 * @return Result
795 */
798 }
799
800 /**
801 * @brief Add or replace a source in a bank.
802 * @param bankHandle
803 * @param source Moved and now owned by bank
804 * @return Result
805 */
806 Result addSource(BankHandle bankHandle, Source& source) {
807 return mBankManager.addSource(bankHandle, source, mConfig.internalSampleRate);
808 }
809
810 /**
811 * @brief Add or replace event in a bank
812 * Locks audio thread
813 * @param bankHandle
814 * @param event Moved and now owned by bank
815 * @return Result
816 */
817 Result addEvent(BankHandle bankHandle, Event& event) {
818 return mBankManager.addEvent(bankHandle, event);
819 }
820
821 Result addMixer(BankHandle bankHandle, Mixer& mixer) {
822 return mBankManager.addMixer(bankHandle, mixer);
823 }
824
826 return mBankManager.addBank(bank);
827 }
828
829 /**
830 * @brief Unload bank from handle.
831 * Locks audio thread and stops all voices from that bank.
832 * @param bankHandle
833 * @return Result
834 */
837 VAE_INFO("Start Unload bank %i", bankHandle)
838 mVoiceManager.stop(bankHandle, &Voice::bank);
839 return mBankManager.unloadFromId(bankHandle);
840 }
841
842 /**
843 * @brief Unload every bank and data associated with it
844 */
847 VAE_INFO("Start unloading all banks")
849 }
850
851 ///@}
852#pragma endregion bank_handling
853
854
855 }; // Engine class
856 constexpr int _VAE_ENGINE_SIZE = sizeof(Engine);
857} } // namespace vae::core
858
859#endif // _VAE_ENGINE
#define TKLB_DELEGATE(func, thisPrtRef)
Definition: TDelegate.hpp:59
void multiply(const AudioBufferTpl< T2, STORAGE2 > &buffer, Size length=0, Size offsetSrc=0, Size offsetDst=0)
Multiply two buffers.
void add(const AudioBufferTpl< T2, STORAGE2 > &buffer, Size length=0, Size offsetSrc=0, Size offsetDst=0)
Add the provided buffer.
uchar channels() const
Returns the amount of channels.
void setValidSize(const Size v)
Set the amount of valid samples currently in the buffer This is mostly a convenience flag since the a...
void set(const T2 *samples, Size length, const uchar channel=0, const Size offsetDst=0)
Set a single channel from an array.
Size putInterleaved(T2 *buffer, Size length=0, const Size offset=0) const
Puts the interleaved contents in the target buffer.
static BackendDummy & instance()
Backend interface used to query devices before creating a actual device object.
Definition: vae_backend.hpp:14
virtual Device * createDevice(const EngineConfig &)=0
Creates a device instance for this backend.
Holds all the banks.
Result load(const char *path, Size size, const char *rootPath, int sampleRate)
Result unloadFromId(BankHandle bankHandle)
Result addEvent(BankHandle bankHandle, Event &event)
Result addMixer(BankHandle bankHandle, Mixer &mixer)
Bank & get(BankHandle bank)
void forEach(const Func &&func)
Iterate all loaded banks.
Result addSource(BankHandle bankHandle, Source &source, int sampleRate)
Result addBank(Bank &bank)
bool has(BankHandle bank)
Interface for audio devices.
Definition: vae_device.hpp:18
virtual bool openDevice(DeviceInfo &device)=0
Opens a specific audio device.
void setCallback(Callback callback)
Definition: vae_device.hpp:196
Central class handling all outside communication.
Definition: vae_engine.hpp:48
Result addMixer(BankHandle bankHandle, Mixer &mixer)
Definition: vae_engine.hpp:821
SpatialProcessor mSpatialProcessor
Spatial voice processor.
Definition: vae_engine.hpp:57
Engine & operator=(const Engine &)=delete
Result _VAE_PUBLIC_API fireGlobalEvent(GlobalEventHandle globalHandle, EmitterHandle emitterHandle, Sample gain=1.0, MixerHandle mixerHandle=InvalidMixerHandle, ListenerHandle listenerHandle=AllListeners)
Works like fireEvent but with a global Event identifier.
Definition: vae_engine.hpp:510
void process(const SampleIndex frames, T *output, int channels)
Definition: vae_engine.hpp:348
Result _VAE_PUBLIC_API addEmitter(EmitterHandle h)
Adds an emitter with a custom handle, can be an internal ID for example.
Definition: vae_engine.hpp:598
Processor mProcessor
Default Voice processor.
Definition: vae_engine.hpp:55
Result setMixerEffectParameter(BankHandle bank, MixerHandle mixer, Size index, Size param, Sample value)
Set the Mixer Effect Parameter.
Definition: vae_engine.hpp:719
ConditionVariable mAudioConsumed
Notifies the audio thread when more audio is needed.
Definition: vae_engine.hpp:73
Result muteMixerEffect(BankHandle bank, MixerHandle mixer, Size index, bool mute)
Bypass a effect in a mixer.
Definition: vae_engine.hpp:704
Result addEvent(BankHandle bankHandle, Event &event)
Add or replace event in a bank Locks audio thread.
Definition: vae_engine.hpp:817
void _VAE_PUBLIC_API setSpeed(EmitterHandle emitter, float speed)
Set the playback speed.
Definition: vae_engine.hpp:657
EmitterHandle _VAE_PUBLIC_API createEmitter()
Creates an emitter and returns the handle.
Definition: vae_engine.hpp:570
Sample mMasterVolume
Master Colume applied after limiting.
Definition: vae_engine.hpp:63
Result _VAE_PUBLIC_API fireEvent(BankHandle bankHandle, EventHandle eventHandle, EmitterHandle emitterHandle, Sample gain=1.0, MixerHandle mixerHandle=InvalidMixerHandle, ListenerHandle listenerHandle=AllListeners)
Main mechanism to start and stop sounds.
Definition: vae_engine.hpp:389
ScratchBuffer mScratchBuffer
used to combine the signal from all banks and push it to the device
Definition: vae_engine.hpp:59
Thread * mAudioThread
Thread processing voices and mixers.
Definition: vae_engine.hpp:72
std::condition_variable ConditionVariable
Definition: vae_engine.hpp:71
void _VAE_PUBLIC_API setLowpass(EmitterHandle emitter, float cutoff)
Simple lowpass filter for the voices.
Definition: vae_engine.hpp:666
Result setMixerVolume(BankHandle bank, MixerHandle mixer, Sample volume)
Set the Mixer Volume.
Definition: vae_engine.hpp:690
Result loadBank(Bank &bank)
Load bank from memory.
Definition: vae_engine.hpp:796
Result _VAE_PUBLIC_API init(const EngineConfig &config={})
Initializes the engine and does most of the upfront allocations.
Definition: vae_engine.hpp:230
Engine(const Engine *)=delete
SampleIndex mTime
Global engine time in samples.
Definition: vae_engine.hpp:60
void _VAE_PUBLIC_API setMasterVolume(Sample volume)
Set the global output volume before the limiter.
Definition: vae_engine.hpp:543
Engine(Engine &&)=delete
bool _VAE_PUBLIC_API checkVersion(int major, int minor, int patch)
Check if the compiled version matches.
Definition: vae_engine.hpp:550
Size _VAE_PUBLIC_API getActiveVoiceCount() const
Get the number of currently playing Voices.
Definition: vae_engine.hpp:529
Result _VAE_PUBLIC_API removeListener(ListenerHandle listener)
Unregister listener.
Definition: vae_engine.hpp:750
Size _VAE_PUBLIC_API getStreamTime() const
Definition: vae_engine.hpp:533
Engine(const Engine &)=delete
Don't allow any kind of move of copy of the object.
std::mutex mMutex
Mutex needed to use mAudioConsumed, doesn't actually do anything else.
Definition: vae_engine.hpp:74
Result _VAE_PUBLIC_API stop()
Stops processing and waits for audio thead to clean up.
Definition: vae_engine.hpp:286
MixerProcessor mMixerProcessor
Mixer channel processor.
Definition: vae_engine.hpp:56
Result _VAE_PUBLIC_API loadBank(const char *path, Size size=0)
Load bank from filesystem.
Definition: vae_engine.hpp:786
Result _VAE_PUBLIC_API loadHRTF(const char *path, Size size=0)
Definition: vae_engine.hpp:763
void _VAE_PUBLIC_API setVolume(EmitterHandle emitter, Sample gain)
Sets the volume of all active voices with this emitter.
Definition: vae_engine.hpp:639
void onThreadedBufferSwap(Device *device)
Called from audio device when using seperate audio thread.
Definition: vae_engine.hpp:189
EmitterHandle _VAE_PUBLIC_API createAutoEmitter(BankHandle bank, EventHandle event, float maxDist, const LocationDirection &locDir, float spread)
Emitter which triggers an event once a listener is close enough.
Definition: vae_engine.hpp:584
Result _VAE_PUBLIC_API start()
Tries to open default device and start audio thread.
Definition: vae_engine.hpp:251
std::thread Thread
Definition: vae_engine.hpp:70
Result addBank(Bank &bank)
Definition: vae_engine.hpp:825
void onBufferSwap(Device *device)
Called from audio device when it needs more audio.
Definition: vae_engine.hpp:163
Result addSource(BankHandle bankHandle, Source &source)
Add or replace a source in a bank.
Definition: vae_engine.hpp:806
Result _VAE_PUBLIC_API removeEmitter(EmitterHandle h)
Unregister a emiter an kill all its voices.
Definition: vae_engine.hpp:607
Sample mLimiterLastPeak
Master limiter last peak.
Definition: vae_engine.hpp:62
void _VAE_PUBLIC_API unloadAllBanks()
Unload every bank and data associated with it.
Definition: vae_engine.hpp:845
Result _VAE_PUBLIC_API unloadBankFromId(BankHandle bankHandle)
Unload bank from handle.
Definition: vae_engine.hpp:835
Time mTimeFract
Global engine time in seconds.
Definition: vae_engine.hpp:61
void threadedProcess()
Called from own audio thread, not the device.
Definition: vae_engine.hpp:175
SpatialManager mSpatialManager
Holds and manages spatial emitters.
Definition: vae_engine.hpp:52
void _VAE_PUBLIC_API seek(EmitterHandle emitter, Size time)
Set the current time of all voices with the emitter.
Definition: vae_engine.hpp:648
void _VAE_PUBLIC_API update()
Update function needs to be called regularly to handle outbound events and other housekeeping.
Definition: vae_engine.hpp:317
VoiceManger mVoiceManager
Holds and handle voices.
Definition: vae_engine.hpp:51
ListenerHandle _VAE_PUBLIC_API createListener()
Create a Listener object.
Definition: vae_engine.hpp:742
Device * mDevice
Output device.
Definition: vae_engine.hpp:66
BankManager mBankManager
Holds and manages banks.
Definition: vae_engine.hpp:53
void _VAE_PUBLIC_API setHighpass(EmitterHandle emitter, float cutoff)
Simple highpass filter for the voices.
Definition: vae_engine.hpp:675
Result _VAE_PUBLIC_API setListener(ListenerHandle listener, const LocationOrientation &locOr)
Set the position of a listener.
Definition: vae_engine.hpp:759
EngineConfig mConfig
Config object provided at initlalization.
Definition: vae_engine.hpp:49
Result _VAE_PUBLIC_API setEmitter(EmitterHandle emitter, const LocationDirection &locDir, float spread)
Set the Emitter position, orientation and spread.
Definition: vae_engine.hpp:619
Result _VAE_PUBLIC_API stopEmitter(EmitterHandle emitter)
Stop all voices from emitter.
Definition: vae_engine.hpp:630
void process()
Main processing function.
Definition: vae_engine.hpp:86
void mix(VoiceManger &manager, Bank &bank, SampleIndex frames)
Process the mixers for a single bank.
Non spatial voice processor.
Size mix(VoiceManger &manager, Bank &bank, SampleIndex frames, Size sampleRate)
Process a single bank.
Result addEmitter(EmitterHandle e)
Result setListener(ListenerHandle listener, const LocationOrientation &locOr)
EmitterHandle createAutoEmitter(BankHandle bank, EventHandle event, float maxDist, const LocationDirection &locDir, Sample spread)
bool hasEmitter(EmitterHandle e)
Result removeListener(ListenerHandle listener)
Result setEmitter(EmitterHandle emitter, const LocationDirection &locDir, Sample spread)
Result removeEmitter(EmitterHandle e)
Result forListeners(ListenerHandle handle, const Func &&func)
void update(VoiceManger &manager, Callback callback)
Emitter & getEmitter(EmitterHandle e)
Size mix(VoiceManger &manager, Bank &bank, SpatialManager &spatial, SampleIndex frames, Size sampleRate)
Process a single bank.
Result loadHRTF(const char *path, Size length, const char *rootPath, Size sampleRate)
There is only one voice pool and VAE and it's managed here.
Result stop(Voice &v)
Stops a voice.
Result play(Event &event, const BankHandle bank, const Sample gain, const EmitterHandle emitter, const ListenerHandle listener, const MixerHandle mixer)
void setVoiceProperty(EmitterHandle emitter, T Voice::*member, const T &value)
void forEachFinishedVoice(const Func &&func)
const double step
Definition: main.cpp:20
static void set(void *dst, const unsigned char val, size_t size)
memset wrapper
Definition: TMemoryUtil.hpp:40
T min(const T &v1, const T &v2)
Definition: TMath.hpp:16
T max(const T &v1, const T &v2)
Definition: TMath.hpp:21
constexpr Size MaxBlock
Maximum block size.
Definition: vae.hpp:276
constexpr Size MaxChainedEvents
How many chained events can fit in chain_events on the core::Event structure.
Definition: vae.hpp:302
constexpr unsigned char MaxChannels
Maximum channel count used to pre allocate buffers.
Definition: vae.hpp:268
const char *const audioFrame
Definition: vae_profiler.hpp:5
AudioBuffer::Size SampleIndex
Definition: vae_types.hpp:87
unsigned char Uchar
Definition: vae_types.hpp:45
constexpr int _VAE_ENGINE_SIZE
Definition: vae_engine.hpp:856
constexpr void splitGlobalEventHandle(const GlobalEventHandle &handle, BankHandle &bank, EventHandle &event)
Definition: vae_util.hpp:18
Contains Typedefinitions and basic structures use by the public API and internally.
Definition: vae.hpp:31
constexpr ListenerHandle AllListeners
Will address all listeners.
Definition: vae.hpp:60
constexpr EmitterHandle InvalidEmitterHandle
Definition: vae.hpp:61
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
SmallHandle MixerHandle
Definition: vae.hpp:44
constexpr SourceHandle InvalidSourceHandle
Definition: vae.hpp:56
SmallHandle BankHandle
Definition: vae.hpp:40
double Time
Time sotred in seconds.
Definition: vae.hpp:34
SmallHandle ListenerHandle
Definition: vae.hpp:45
constexpr EventHandle InvalidEventHandle
Definition: vae.hpp:55
LargeHandle EmitterHandle
Definition: vae.hpp:43
GenericHandle EventHandle
The handle used to address events within a bank.
Definition: vae.hpp:41
Result
Return Types for most engine functions.
Definition: vae.hpp:73
@ InvalidBank
Valid bank handle needed.
@ InvalidEmitter
Emitter probably wasn't registered.
@ ValidHandleRequired
Handle provided wasn't valid but needs to be.
@ GenericFailure
:(
@ DeviceError
Can't open audio device.
constexpr MixerHandle InvalidMixerHandle
Definition: vae.hpp:58
LargeHandle GlobalEventHandle
Used to globally address events, holds space for BankHandle and EventHandle.
Definition: vae.hpp:47
Settings for the engine defined at EnginePimpl::init.
Definition: vae.hpp:157
void * eventCallbackContext
Custom data that can be accached to the EventCallback to maintain context.
Definition: vae.hpp:180
const char * rootPath
Path where the bank files are located, needs to end with a trailing /.
Definition: vae.hpp:161
bool updateInAudioThread
If this is true update() does not need to be called on the engine instance.
Definition: vae.hpp:248
EventCallback eventCallback
Each time a event of the type emit gets triggered Used to get information about ending sounds and sim...
Definition: vae.hpp:174
bool processInBufferSwitch
If enabled, all processing and mixing will happen in the audio callback.
Definition: vae.hpp:256
Size internalSampleRate
Samplerate requested from device.
Definition: vae.hpp:168
Struct containing relevant data passed to EventCallback provided in the EngineConfig.
Definition: vae.hpp:144
void * context
Can point to custom context data also provided when setting the callback, ! not context based on even...
Definition: vae.hpp:145
EmitterHandle emitter
Which emitter.
Definition: vae.hpp:148
EventHandle event
Which event.
Definition: vae.hpp:147
BankHandle bank
Which bank the event is from.
Definition: vae.hpp:146
Emitters have a position and direction vector.
Definition: vae.hpp:121
Listener uses additional up vector.
Definition: vae.hpp:133
Bank object containing Sources, Mixers and Events Can be loaded and unloaded at runtime.
Definition: vae_bank.hpp:14
HeapBuffer< Mixer > mixers
Audio Mixers which can have effects ! is presorted !
Definition: vae_bank.hpp:16
HeapBuffer< Event > events
Events defined.
Definition: vae_bank.hpp:17
bool autoplaying
whether the event was already triggered
Definition: vae_emitter.hpp:16
An Event is used to control most of the eingines behavior.
Definition: vae_event.hpp:14
@ random
triggers one random chained_events event
@ emit
Emits an event to the EventCallback defined in the engine config.
@ start
Starts a source if defined and every Event in chained_events.
@ stop
Stops a source if defined and stops every voice started from a event in chained_events.
ListenerHandle id
static constexpr MixerHandle MasterMixerHandle
This is the master mixer for a bank.
Definition: vae_mixer.hpp:13
Real speed
Playback speed, will alter pitch.
Real lowpass
Lowpasses the signal as the value approaches 1.
Real highpass
Highpasses the signal as the value approaches 1.
Barebones voice.
Definition: vae_voice.hpp:17
SourceHandle source
If invalid, means voice is not playing.
Definition: vae_voice.hpp:28
Sample gain
Volume of the voice.
Definition: vae_voice.hpp:33
EventHandle event
Which event triggered the voice to be played.
Definition: vae_voice.hpp:29
BankHandle bank
Which bank it belongs to.
Definition: vae_voice.hpp:27
MixerHandle mixer
Where the voice should mix to.
Definition: vae_voice.hpp:31
EmitterHandle emitter
Emitter used to control voice properties.
Definition: vae_voice.hpp:30
SampleIndex time
Current time in samples.
Definition: vae_voice.hpp:34
#define VAE_VERSION_MAJOR
Definition: vae.hpp:16
#define VAE_VERSION_MINOR
Definition: vae.hpp:17
#define VAE_VERSION_PATCH
Definition: vae.hpp:18
#define _VAE_PUBLIC_API
Marks a function for export to the generated vae::EnginePimpl class generated from generate_pimpl....
Definition: vae_engine.hpp:39
#define VAE_ERROR(msg,...)
Definition: vae_logger.hpp:80
#define VAE_WARN(msg,...)
Definition: vae_logger.hpp:85
#define VAE_INFO(msg,...)
Definition: vae_logger.hpp:84
#define VAE_DEBUG_EVENT(msg,...)
Definition: vae_logger.hpp:96
#define VAE_DEBUG(msg,...)
Definition: vae_logger.hpp:83
#define VAE_PROFILER_FRAME_MARK_START(name)
Starts a named frame, uses const defined above to maintain the same pointer.
#define VAE_PROFILER_SCOPE_NAMED(name)
Profiles a scope and names it.
#define VAE_PROFILER_FRAME_MARK_END(name)
Stops a named frame.
#define VAE_PROFILER_SCOPE()
Profiles a scope.
#define VAE_PROFILER_PLOT(name, value)
Records a value.
#define VEA_PROFILER_THREAD_NAME(name)
Sets name for current thread.
#define VAE_PROFILER_FRAME_MARK()
Marks a frame to calculate FPS, not really needed for audio.
Internal types used across VAE.
Holds all voices and starts/stops them.