VAE - Virtual Audio Engine 1
Small Data Driven Audio Engine
vae_bank_loader.hpp
Go to the documentation of this file.
1#ifndef _VAE_BANK_LOADER
2#define _VAE_BANK_LOADER
3
4#include "../vae_types.hpp"
5#include "../vae_config.hpp"
6#include "../vae_util.hpp"
7#include "../pod/vae_source.hpp"
8#include "../pod/vae_event.hpp"
9#include "../pod/vae_bank.hpp"
10
12#include "../../wrapped/vae_fs.hpp"
13
14#ifndef _JSON_H
15 #include "../../../external/headeronly/json.h"
16 #include "../../../external/headeronly/json.c"
17#endif
18
19
20namespace vae { namespace core {
21 class BankLoader {
22 static void* allocate(size_t size, int zero, void* context) {
24 void* ptr = reinterpret_cast<void*>(allocator.allocate(size));
25 if (zero) {
26 memset(ptr, 0, size);
27 }
28 return ptr;
29 }
30
31 static void deallocate(void* ptr, void* context) {
33 allocator.deallocate(reinterpret_cast<char*>(ptr), 0);
34 }
35
37 public:
38 /**
39 * @brief Load a bank.json and all wav files referenced in it.
40 * @param path The path to the bank folder. Folder must contain a bank.json. Wav files are relative to this path
41 * @param bank The bank object to populae
42 * @return Result
43 */
44 Result load(const char* path, Size length, const char* rootPath, Bank& bank) {
46
47 /**
48 * Open file and decode json
49 */
50 PathString folder; // Folder to the bank
51 const char* encoded = path; // The plain json text
52
53 String jsonText;
54
55 if (length == 0) { // length 0 indicates the file is on disk
56 VAE_DEBUG("Started loading bank %s", path)
57 VAE_PROFILER_SCOPE_NAMED("BankFile IO")
58 folder = rootPath;
59 folder.append(path);
60 folder.append("/");
61 PathString json = folder;
62 json.append("bank.json");
63 fs::File file(json.c_str());
64 jsonText.resize(file.size());
65 if (!file.readAll(jsonText.data())) {
66 VAE_DEBUG("Failed to read file")
68 }
69 length = jsonText.size();
70 encoded = jsonText.c_str();
71 }
72
73
74
75 json_settings settings = { };
76 settings.mem_alloc = allocate;
77 settings.mem_free = deallocate;
78
79 json_value* json;
80 {
81 VAE_PROFILER_SCOPE_NAMED("BankFile Parsing")
82 json = json_parse_ex(&settings, encoded, length, 0);
83 }
84 if (json == nullptr) { return Result::BankFormatError; }
85 json_value& data = (*json);
86
87 bank.name = (const char*) data["name"];
88 bank.id = (json_int_t) data["id"];
89 bank.path = path;
90
91 /**
92 * Deserialize Source and preload signal data
93 */
94 auto sources = data["sources"];
95 if (sources.type == json_array) {
96 VAE_PROFILER_SCOPE_NAMED("Deserialize sources")
97 bank.sources.resize(sources.u.array.length);
98 for (auto& iraw : sources.u.array) {
99 auto& i = *iraw;
100 SourceHandle id = (json_int_t) i["id"];
101
102 if (sources.u.array.length <= id) { return Result::BankFormatIndexError; }
103
104 String asd = (const char*) i["name"];
105
106 Source& s = bank.sources[id];
107 if (s.id != InvalidSourceHandle) {
108 VAE_ERROR("Duplicate Source id %i in bank %i", s.id, bank.id)
110 }
111 s.id = id;
112 s.name = (const char*) i["name"];
113 s.path = (const char*) i["path"];
114
115 if (i["gain"].type == json_double) s.gain = (double) i["gain"];
116 if (i["resample"].type == json_boolean) s.resample = i["resample"];
117 if (i["stream"].type == json_boolean) s.stream = i["stream"];
118
119 IdString format = { i["format"] };
120 if (format == "wav") s.format = Source::Format::wav;
121 if (format == "ogg") s.format = Source::Format::ogg;
122 if (format == "generator") s.format = Source::Format::generator;
123
124 auto result = mSourceLoader.load(s, folder.c_str());
125 if (result != Result::Success) {
126 VAE_ERROR("Failed to load source %s", s.path.c_str())
127 }
128 }
129 }
130
131 /**
132 * Deserialize Mixers. Don't initialize anything yet
133 */
134 auto mixers = data["mixers"];
135 if (mixers.type == json_array) {
136 VAE_PROFILER_SCOPE_NAMED("Deserialize mixers")
137 bank.mixers.resize(mixers.u.array.length);
138 for (auto& iraw : mixers.u.array) {
139 auto& i = *iraw;
140
141 MixerHandle id = (json_int_t) i["id"];
142
143 if (mixers.u.array.length <= id) {
144 VAE_ERROR("Mixer %i:%i id out of bounds.", id, bank.id);
146 }
147
148 auto& m = bank.mixers[id];
149
150 if (m.id != InvalidMixerHandle) {
151 VAE_ERROR("Duplicate Mixer id %i in bank %i", m.id, bank.id)
153 }
154
155 m.name = (const char*) i["name"];
156 m.id = id;
157
158 if (i["parent"].type == json_integer) m.parent = (json_int_t) i["parent"];
159 if (i["gain"].type == json_double) m.gain = (double) i["gain"];
160
161 if (m.id != Mixer::MasterMixerHandle && m.id <= m.parent) {
162 // Mixer can only write to mixers with lower ids than themselves
163 // this avoids recursion and makes mixing easier
164 VAE_ERROR("Mixer %i:%i tried to mix to %i. ", id, bank.id, m.parent);
166 }
167
168 auto effects = i["effects"];
169 if (effects.type != json_array || effects.u.array.length == 0) { continue; }
170
171
172 for (auto& iraw : effects.u.array) {
173 auto& i = *iraw;
174
175 json_int_t index = i["index"];
176 auto& e = m.effects[index];
177 e.bypassed = i["bypassed"];
178 e.name = i["name"];
179
180 int paramIndex = 0;
181 for (auto& j : i["parameters"].u.array) {
182
183 e.parameters[paramIndex].name = (const char*) (*j)[0];
184 e.parameters[paramIndex].value = (double) (*j)[1];
185 paramIndex++;
186 }
187 }
188 }
189 }
190
191 auto events = data["events"];
192 {
193 VAE_PROFILER_SCOPE_NAMED("Deserialize events")
194 bank.events.resize(events.u.array.length);
195 for (auto& iraw : events.u.array) {
196 auto& i = *iraw;
197 EventHandle id = (json_int_t) i["id"];
198
199 if (events.u.array.length <= id) {
200 VAE_ERROR("Event %i:%i id out of bounds.", id, bank.id)
202 }
203
204 Event& e = bank.events[id];
205
206 if (e.id != InvalidEventHandle) {
207 VAE_ERROR("Duplicate Event id %i in bank %i", e.id, bank.id)
209 }
210
211 e.id = id;
212 e.name = (const char*) i["name"];
213
214 IdString action = { i["action"] };
215 if (action == "start_rand") e.action = Event::Action::random;
216 if (action == "emit") e.action = Event::Action::emit;
217 if (action == "stop") e.action = Event::Action::stop;
218 if (action == "start") e.action = Event::Action::start;
219
220 if (i["force_mixer"].type == json_boolean) e.force_mixer = i["force_mixer"];
221 if (i["spatial"].type == json_boolean) e.spatial = i["spatial"];
222 if (i["critical"].type == json_boolean) e.critical = i["critical"];
223 if (i["loop"].type == json_boolean) e.loop = i["loop"];
224 if (i["hrtf"].type == json_boolean) e.HRTF = i["hrtf"];
225 if (i["attenuate"].type == json_boolean) e.attenuate = i["attenuate"];
226 if (i["mixer"].type == json_integer) e.mixer = (json_int_t) i["mixer"];
227 if (i["gain"].type == json_double) e.gain = (double) i["gain"];
228 if (i["source"].type == json_integer) e.source = (json_int_t) i["source"];
229 if (i["on_end"].type == json_integer) e.on_end = (json_int_t) i["on_end"];
230
231 if (i["chained_events"].type == json_array) {
232 auto onStart = i["chained_events"].u.array;
233 if (StaticConfig::MaxChainedEvents < onStart.length) {
234 VAE_ERROR("Event %i:%i has too many chained chained_events events.", id, bank.id)
236 }
237 for (size_t j = 0; j < onStart.length; j++) {
238 e.chained_events[j] = (json_int_t) (*onStart.values[j]);
239 }
240 }
241
242 }
243 }
244
245 json_value_free_ex(&settings, json);
246 return Result::Success;
247 }
248 };
249} } // namespace vae::vore
250
251#endif // _VAE_BANK_LOADER
const char * c_str() const
Definition: TString.hpp:83
Size size() const
Definition: TString.hpp:86
void resize(Size size)
Definition: TString.hpp:131
void append(const String &str)
Definition: TString.hpp:102
char * data()
Definition: TString.hpp:89
Result load(const char *path, Size length, const char *rootPath, Bank &bank)
Load a bank.json and all wav files referenced in it.
static void * allocate(size_t size, int zero, void *context)
static void deallocate(void *ptr, void *context)
bool readAll(char *dest)
Definition: vae_fs.hpp:72
constexpr Size MaxChainedEvents
How many chained events can fit in chain_events on the core::Event structure.
Definition: vae.hpp:302
Contains Typedefinitions and basic structures use by the public API and internally.
Definition: vae.hpp:31
unsigned int Size
How the elements are addressed in the heapbuffer.
Definition: vae.hpp:33
SmallHandle MixerHandle
Definition: vae.hpp:44
constexpr SourceHandle InvalidSourceHandle
Definition: vae.hpp:56
constexpr EventHandle InvalidEventHandle
Definition: vae.hpp:55
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
@ FileOpenError
File system could not load file.
@ BankFormatError
Generic bank loading error.
@ TooManyRecords
Can't fit all data in fixed size array.
@ BankFormatDuplicateIndex
A index is used muktiple times.
@ BankFormatBadMixHirarchy
A mixer can only write to mixers with lower ids than themselves (no recursion)
@ BankFormatIndexError
A index is out of bounds.
GenericHandle SourceHandle
Definition: vae.hpp:42
constexpr MixerHandle InvalidMixerHandle
Definition: vae.hpp:58
Bank object containing Sources, Mixers and Events Can be loaded and unloaded at runtime.
Definition: vae_bank.hpp:14
NameString name
Name of the bank used for debugging, needs to be last so it can be zero sized.
Definition: vae_bank.hpp:20
HeapBuffer< Mixer > mixers
Audio Mixers which can have effects ! is presorted !
Definition: vae_bank.hpp:16
PathString path
Path to the bank definition file.
Definition: vae_bank.hpp:19
BankHandle id
Definition: vae_bank.hpp:18
HeapBuffer< Event > events
Events defined.
Definition: vae_bank.hpp:17
HeapBuffer< Source > sources
Audio sources defined.
Definition: vae_bank.hpp:15
An Event is used to control most of the eingines behavior.
Definition: vae_event.hpp:14
EventHandle id
Own id.
Definition: vae_event.hpp:30
bool loop
gapless looping
Definition: vae_event.hpp:22
EventHandle on_end
Event fired once the source is finished, not called when there's no source.
Definition: vae_event.hpp:33
SourceHandle source
Handle to a source.
Definition: vae_event.hpp:29
Sample gain
Volume applied to triggered voice.
Definition: vae_event.hpp:31
@ 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.
bool attenuate
whether distance is taken into consideration
Definition: vae_event.hpp:25
NameString name
Name for debugging.
Definition: vae_event.hpp:34
MixerHandle mixer
Mixer the source gets written to.
Definition: vae_event.hpp:28
bool force_mixer
Prevents overriding the mixer from chained events or fireEvent.
Definition: vae_event.hpp:21
enum vae::core::Event::Action action
bool HRTF
Listener and event has to have hrtf set.
Definition: vae_event.hpp:23
EventHandle chained_events[StaticConfig::MaxChainedEvents]
Events called when the source starts playing.
Definition: vae_event.hpp:32
bool critical
wheather the voice can be killer
Definition: vae_event.hpp:26
bool spatial
no spatial rendering at all
Definition: vae_event.hpp:24
static constexpr MixerHandle MasterMixerHandle
This is the master mixer for a bank.
Definition: vae_mixer.hpp:13
bool stream
If false entire sample will be loaded in ram, there's no streaming for now.
Definition: vae_source.hpp:11
@ ogg
Uses stb_vorbis to decode oggs.
@ wav
Uses dr_wav to decode wavs.
@ generator
Not implemented.
PathString path
Filesystem path.
Definition: vae_source.hpp:22
bool resample
Whether the sound will be resampled when loading it.
Definition: vae_source.hpp:12
enum vae::core::Source::Format format
NameString name
Name for debugging.
Definition: vae_source.hpp:23
Sample gain
Gain applied to every voice creatd frin this source.
Definition: vae_source.hpp:20
SourceHandle id
Definition: vae_source.hpp:10
Result load(Source &s, const char *path)
Loads a wav file for the resource.
Allocator used for all heapbuffers in VAE.
T * allocate(size_t n) noexcept
void deallocate(T *ptr, std::size_t n) noexcept
#define VAE_ERROR(msg,...)
Definition: vae_logger.hpp:80
#define VAE_DEBUG(msg,...)
Definition: vae_logger.hpp:83
#define VAE_PROFILER_SCOPE_NAMED(name)
Profiles a scope and names it.
#define VAE_PROFILER_SCOPE()
Profiles a scope.