]> git.bts.cx Git - benzene.git/blob - src_platform/sdl/bz/audio/playback.c
Sprites
[benzene.git] / src_platform / sdl / bz / audio / playback.c
1 #include <bz/audio/playback_internal.h>
2
3 #include <bz/audio/ogg.h>
4 #include <bz/audio/speech.h>
5 #include <bz/audio/wav.h>
6 #include <bz/math/math.h>
7 #include <bz/math/random.h>
8 #include <bz/memory/allocator.h>
9 #include <bz/resources/resource.h>
10 #include <bz/types/identifier_internal.h>
11 #include <parson.h>
12 #include <SDL.h>
13
14 // FIXME
15 #define CHANNELS 2
16 #define SAMPLE_RATE 48000
17 #define SAMPLE_COUNT 512
18
19 //#define STREAM_DATA_SIZE 16*4096 // FIXME
20
21 enum BZAudioVariableType {
22 BZAudioVariableTypeConstant,
23 BZAudioVariableTypeParameter,
24 };
25 typedef enum BZAudioVariableType BZAudioVariableType;
26
27 struct BZAudioVariable {
28 BZAudioVariableType type;
29 union {
30 float constantValue;
31 BZIdentifierHash parameterIdentifier;
32 };
33 };
34 typedef struct BZAudioVariable BZAudioVariable;
35
36 struct BZAudioPlaybackEngineBus {
37 BZIdentifierHash identifier;
38 BZAudioVariable volume;
39 uint8_t *dataBuffer;
40 BZIdentifierHash targetBusIdentifier;
41 };
42 typedef struct BZAudioPlaybackEngineBus BZAudioPlaybackEngineBus;
43
44 struct BZAudioPlaybackEngineParameter {
45 BZIdentifierHash identifier;
46 float value;
47 };
48 typedef struct BZAudioPlaybackEngineParameter BZAudioPlaybackEngineParameter;
49
50 enum BZAudioPlaybackEngineSourceState {
51 BZAudioPlaybackEngineSourceStateFree,
52 BZAudioPlaybackEngineSourceStateEnqueued,
53 BZAudioPlaybackEngineSourceStatePlaying,
54 };
55 typedef enum BZAudioPlaybackEngineSourceState BZAudioPlaybackEngineSourceState;
56
57 typedef void (*BZAudioPlaybackEngineSourceScheduleCallback)(BZAudioPlaybackEngineID engine, void *sourceData, va_list args);
58 typedef size_t (*BZAudioPlaybackEngineSourceDataCallback)(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData);
59
60 struct BZAudioPlaybackEngineSource {
61 BZIdentifierHash identifier;
62 BZAudioVariable volume;
63 BZIdentifierHash targetBusIdentifier;
64
65 BZAudioPlaybackEngineSourceState state;
66
67 BZAudioPlaybackEngineSourceScheduleCallback scheduleCallback;
68 BZAudioPlaybackEngineSourceDataCallback dataCallback;
69
70 void *sourceData;
71 };
72 typedef struct BZAudioPlaybackEngineSource BZAudioPlaybackEngineSource;
73
74 //enum BZAudioSoundbankEventType {
75 // BZAudioSoundbankEventTypeStop,
76 // BZAudioSoundbankEventTypeSound,
77 // BZAudioSoundbankEventTypeStream,
78 // BZAudioSoundbankEventTypeVoice,
79 //};
80 //typedef enum BZAudioSoundbankEventType BZAudioSoundbankEventType;
81
82 struct BZAudioPlaybackEngineEvent {
83 BZIdentifierHash identifier;
84 size_t sourceCount;
85 BZAudioPlaybackEngineSource **sources;
86 };
87 typedef struct BZAudioPlaybackEngineEvent BZAudioPlaybackEngineEvent;
88
89 #define kParameterCount 32
90
91 struct BZAudioPlaybackEngine {
92 SDL_AudioDeviceID audioDevice;
93 SDL_AudioFormat audioDeviceFormat;
94
95 uint8_t audioDeviceChannels;
96 uint32_t audioDeviceRate;
97 uint8_t silenceValue;
98
99 size_t dataBufferSize;
100 uint8_t *tmpDataBuffer;
101
102 size_t busCount;
103 BZAudioPlaybackEngineBus *buses;
104
105 SDL_mutex *schedulingMutex;
106
107 BZAudioPlaybackEngineParameter parameters[kParameterCount];
108
109 size_t sourceCount;
110 BZAudioPlaybackEngineSource *sources;
111
112 size_t eventCount;
113 BZAudioPlaybackEngineEvent *events;
114 };
115
116 static void audioVariableFromJSON(BZAudioVariable *variableOut, void *obj, const char *key, float defaultValue) {
117 JSON_Object *jsonObject = (JSON_Object *)obj;
118
119 if (json_object_has_value_of_type(jsonObject, key, JSONNumber)) {
120 variableOut->type = BZAudioVariableTypeConstant;
121 variableOut->constantValue = json_object_get_number(jsonObject, key);
122 } else if (json_object_has_value_of_type(jsonObject, key, JSONString)) {
123 variableOut->type = BZAudioVariableTypeParameter;
124 const char *parameterIdentifier = json_object_get_string(jsonObject, key);
125 variableOut->parameterIdentifier = bzIdentifierHashFromIdentifier(parameterIdentifier);
126 } else {
127 variableOut->type = BZAudioVariableTypeConstant;
128 variableOut->constantValue = defaultValue;
129 }
130 }
131
132 static float audioPlaybackResolveParameter(BZAudioPlaybackEngineID engine, const BZAudioVariable *variable) {
133 switch (variable->type) {
134 case BZAudioVariableTypeConstant:
135 return variable->constantValue;
136
137 case BZAudioVariableTypeParameter: {
138 for (size_t i = 0; i < kParameterCount; ++i) {
139 if (engine->parameters[i].identifier == variable->parameterIdentifier) {
140 return engine->parameters[i].value;
141 }
142 }
143 return 0.0f;
144 }
145 }
146 }
147
148 static void audioDataCallback(void *userdata, uint8_t *stream, int len) {
149 BZAudioPlaybackEngineID engine = (BZAudioPlaybackEngine *)userdata;
150 bzAssert(len <= engine->dataBufferSize);
151
152 SDL_LockMutex(engine->schedulingMutex);
153
154 SDL_memset(engine->buses[0].dataBuffer, engine->silenceValue, engine->dataBufferSize);
155
156 for (size_t i = engine->busCount - 1; i < engine->busCount; --i) {
157 BZAudioPlaybackEngineBus *bus = &engine->buses[i];
158
159 SDL_memset(bus->dataBuffer, engine->silenceValue, engine->dataBufferSize);
160
161 for (size_t j = 0; j < engine->sourceCount; ++j) {
162 BZAudioPlaybackEngineSource *source = &engine->sources[j];
163 if (source->state != BZAudioPlaybackEngineSourceStateFree && source->targetBusIdentifier == bus->identifier) {
164 if (source->state == BZAudioPlaybackEngineSourceStateEnqueued) {
165 source->state = BZAudioPlaybackEngineSourceStatePlaying;
166 }
167
168 size_t dataOut = source->dataCallback(engine->tmpDataBuffer, engine->dataBufferSize, engine, source->sourceData);
169 float volume = audioPlaybackResolveParameter(engine, &source->volume);
170 SDL_MixAudioFormat(bus->dataBuffer, engine->tmpDataBuffer, engine->audioDeviceFormat, dataOut, bzLerp(0, 128, volume)); // FIXME, log2?
171
172 if (dataOut < engine->dataBufferSize) {
173 source->state = BZAudioPlaybackEngineSourceStateFree;
174 }
175 }
176 }
177
178 for (size_t j = engine->busCount - 1; j > i; --j) {
179 BZAudioPlaybackEngineBus *mixBus = &engine->buses[j];
180 if (mixBus->targetBusIdentifier == bus->identifier) {
181 float busVolume = audioPlaybackResolveParameter(engine, &mixBus->volume); // FIXME, log2?
182 SDL_MixAudioFormat(bus->dataBuffer, mixBus->dataBuffer, engine->audioDeviceFormat, len, bzLerp(0, 128, busVolume));
183 }
184 }
185 }
186
187 SDL_memset(stream, engine->silenceValue, engine->dataBufferSize);
188 SDL_MixAudioFormat(stream, engine->buses[0].dataBuffer, engine->audioDeviceFormat, len, bzLerp(0, 128, 1.0f)); // FIXME, volume...
189
190 SDL_UnlockMutex(engine->schedulingMutex);
191 }
192
193 BZAudioPlaybackEngineID bzAudioPlaybackInit(BZMemoryArenaID arena, const char *identifierFmt, ...) {
194 bzMakeIdentifier(identifier, identifierFmt);
195
196 BZAudioPlaybackEngineID engine = bzMemoryAlloc(arena, sizeof(BZAudioPlaybackEngine));
197
198 SDL_AudioSpec desiredSpec;
199 SDL_AudioSpec obtainedSpec;
200
201 SDL_memset(&desiredSpec, 0, sizeof(SDL_AudioSpec));
202 desiredSpec.freq = SAMPLE_RATE;
203 desiredSpec.format = AUDIO_F32;
204 desiredSpec.channels = CHANNELS;
205 desiredSpec.samples = SAMPLE_COUNT;
206 desiredSpec.callback = audioDataCallback;
207 desiredSpec.userdata = engine;
208
209 engine->audioDevice = SDL_OpenAudioDevice(NULL, SDL_FALSE, &desiredSpec, &obtainedSpec, SDL_AUDIO_ALLOW_ANY_CHANGE);
210 if (engine->audioDevice == 0) {
211 bzError("Failed to initialize audio device");
212 }
213
214 engine->audioDeviceFormat = obtainedSpec.format;
215 engine->audioDeviceChannels = obtainedSpec.channels;
216 engine->audioDeviceRate = obtainedSpec.freq;
217 engine->silenceValue = obtainedSpec.silence;
218
219 size_t byteSize = SDL_AUDIO_BITSIZE(obtainedSpec.format) >> 2;
220 engine->dataBufferSize = obtainedSpec.samples * byteSize * sizeof(uint8_t);
221
222 engine->tmpDataBuffer = bzMemoryAlloc(arena, engine->dataBufferSize);
223
224 BZResourceID handle = bzResourcesOpenResource("audio", "assets/audio/%s.config.json", identifier);
225 size_t length = bzResourcesFileLength(handle);
226
227 char *data = bzMemoryAllocTmp(arena, length);
228 bzResourcesReadBytes(handle, data, length);
229 bzResourcesCloseResource(handle);
230
231 //json_set_allocation_functions
232 JSON_Value *configJson = json_parse_string(data);
233 JSON_Object *configJsonObject = json_object(configJson);
234
235 JSON_Array *busesArray = json_object_get_array(configJsonObject, "buses");
236 engine->busCount = json_array_get_count(busesArray) + 1;// + 1; // 0 is implicit and main bus
237 engine->buses = bzMemoryAlloc(arena, engine->busCount * sizeof(BZAudioPlaybackEngineBus));
238 for (size_t i = 0; i < engine->busCount; ++i) {
239 BZAudioPlaybackEngineBus *bus = &engine->buses[i];
240
241 size_t queueSize = 1;
242
243 if (i > 0) {
244 JSON_Object *busObject = json_array_get_object(busesArray, i - 1);
245 const char *identifier = json_object_get_string(busObject, "identifier");
246 bus->identifier = bzIdentifierHashFromIdentifier(identifier);
247 audioVariableFromJSON(&bus->volume, busObject, "volume", 1.0f);
248
249 size_t configQueueSize = json_object_get_number(busObject, "queue_size");
250 if (configQueueSize > 0) {
251 queueSize = configQueueSize;
252 }
253 }
254
255 bus->dataBuffer = bzMemoryAlloc(arena, engine->dataBufferSize);
256 }
257
258 engine->schedulingMutex = SDL_CreateMutex();
259
260 json_value_free(configJson);
261 bzMemoryArenaResetTmp(arena);
262
263 // Run
264 SDL_PauseAudioDevice(engine->audioDevice, 0);
265
266 return engine;
267 }
268
269 void bzAudioPlaybackTeardown(BZAudioPlaybackEngineID engine) {
270 SDL_CloseAudioDevice(engine->audioDevice);
271 }
272
273 void bzAudioPlaybackSetParameter(BZAudioPlaybackEngineID engine, BZIdentifierHash parameterIdentifier, float value) {
274 for (size_t i = 0; i < kParameterCount; ++i) {
275 BZAudioPlaybackEngineParameter *parameter = &engine->parameters[i];
276 if (parameter->identifier == parameterIdentifier || parameter->identifier == 0) {
277 parameter->identifier = parameterIdentifier;
278 parameter->value = value;
279 return;
280 }
281 }
282 }
283
284 void bzAudioPlaybackPostEvent(BZAudioPlaybackEngineID engine, BZIdentifierHash eventIdentifier, ...) {
285 SDL_LockMutex(engine->schedulingMutex);
286
287 va_list args;
288
289 for (size_t i = 0; i < engine->eventCount; ++i) {
290 BZAudioPlaybackEngineEvent *event = &engine->events[i];
291 if (event->identifier == eventIdentifier) {
292 size_t idx = bzRandomInteger(event->sourceCount);
293 BZAudioPlaybackEngineSource *source = event->sources[idx];
294 source->state = BZAudioPlaybackEngineSourceStateEnqueued;
295
296 va_start(args, eventIdentifier);
297 source->scheduleCallback(engine, source->sourceData, args);
298 va_end(args);
299 }
300 }
301
302 SDL_UnlockMutex(engine->schedulingMutex);
303 }
304
305 size_t bzAudioCalculateMemorySizeForConversion(BZAudioPlaybackEngineID engine, uint32_t rate, uint8_t channels, uint16_t bps, bool isFloat, bool isSigned, size_t dataSize) {
306 SDL_AudioFormat format = 0;
307
308 switch (bps) {
309 case 8:
310 format = isSigned ? AUDIO_S8 : AUDIO_U8;
311 break;
312
313 case 16:
314 format = isSigned ? AUDIO_S16 : AUDIO_U16;
315 break;
316
317 case 32:
318 format = isFloat ? AUDIO_F32 : AUDIO_S32;
319 break;
320
321 default:
322 bzError("Unhandled sound format");
323 break;
324 }
325
326 SDL_AudioCVT cvt;
327 SDL_BuildAudioCVT(&cvt, format, channels, rate, engine->audioDeviceFormat, engine->audioDeviceChannels, engine->audioDeviceRate);
328
329 cvt.len = dataSize;
330
331 return cvt.len * cvt.len_mult;
332 }
333
334 size_t bzAudioPlaybackConvertSample(BZAudioPlaybackEngineID engine, uint32_t rate, uint8_t channels, uint16_t bps, bool isFloat, bool isSigned, void *dst, size_t dataSize, void *src) {
335 SDL_AudioFormat format = 0;
336
337 switch (bps) {
338 case 8:
339 format = isSigned ? AUDIO_S8 : AUDIO_U8;
340 break;
341
342 case 16:
343 format = isSigned ? AUDIO_S16 : AUDIO_U16;
344 break;
345
346 case 32:
347 format = isFloat ? AUDIO_F32 : AUDIO_S32;
348 break;
349
350 default:
351 bzError("Unhandled sound format");
352 break;
353 }
354
355 SDL_AudioCVT cvt;
356 SDL_BuildAudioCVT(&cvt, format, channels, rate, engine->audioDeviceFormat, engine->audioDeviceChannels, engine->audioDeviceRate);
357
358 cvt.len = dataSize;
359 cvt.buf = dst;//bzMemoryAlloc(arena, cvt.len * cvt.len_mult);
360
361 if (src != cvt.buf) {
362 SDL_memcpy(cvt.buf, src, dataSize);
363 }
364
365 int r = SDL_ConvertAudio(&cvt);
366 bzAssert(r == 0);
367
368 return cvt.len_cvt;
369 }
370
371 ///////////
372
373 struct BZAudioSoundSource {
374 size_t readPosition;
375 size_t soundSize;
376 uint8_t *soundData;
377 };
378 typedef struct BZAudioSoundSource BZAudioSoundSource;
379
380 static void soundScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) {
381 BZAudioSoundSource *source = sourceData;
382 source->readPosition = 0;
383 }
384
385 static size_t soundDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
386 BZAudioSoundSource *source = sourceData;
387
388 size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition);
389 SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize);
390 source->readPosition += copySize;
391
392 return copySize;
393 }
394
395 static size_t audioPlaybackCountSounds(JSON_Array *soundsArray) {
396 size_t baseSoundCount = json_array_get_count(soundsArray);
397
398 size_t soundCount = 0;
399 for (size_t i = 0; i < baseSoundCount; ++i) {
400 ++soundCount; // FIXME, count the number of concurrent sounds
401 }
402
403 return soundCount;
404 }
405
406 // NB: Sounds.
407 // You can have multiple sounds playing at each time.
408 // We create a n sources for each sound, where n is the number of times that sound can be played
409 // However, we only load the data once, and all sounds reference the same data.
410 static void audioPlaybackLoadSounds(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *soundsArray) {
411 size_t soundCount = audioPlaybackCountSounds(soundsArray);
412 for (size_t i = 0; i < soundCount; ++i) {
413 BZAudioPlaybackEngineSource *source = &sources[i];
414
415 JSON_Object *soundObject = json_array_get_object(soundsArray, i);
416
417 const char *identifier = json_object_get_string(soundObject, "identifier");
418 source->identifier = bzIdentifierHashFromIdentifier(identifier);
419
420 audioVariableFromJSON(&source->volume, soundObject, "volume", 1.0f);
421
422 const char *targetBusIdentifier = json_object_get_string(soundObject, "bus");
423 if (targetBusIdentifier != NULL) {
424 source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier);
425 }
426
427 source->state = BZAudioPlaybackEngineSourceStateFree;
428
429 source->scheduleCallback = soundScheduleCallback;
430 source->dataCallback = soundDataCallback;
431 BZAudioSoundSource *soundSource = bzMemoryAlloc(arena, sizeof(BZAudioSoundSource));
432 source->sourceData = soundSource;
433
434 const char *filename = json_object_get_string(soundObject, "file");
435
436 size_t size;
437 uint32_t rate;
438 uint16_t channels;
439 uint16_t bps;
440 void *wavData = bzAudioLoadWAV(&size, arena, &rate, &channels, &bps, filename); // FIXME, temporary memory...
441
442 size_t bufferSize = bzAudioCalculateMemorySizeForConversion(engine, rate, channels, bps, false, bps > 8, size);
443 soundSource->soundData = bzMemoryAlloc(arena, bufferSize);
444 soundSource->soundSize = bzAudioPlaybackConvertSample(engine, rate, channels, bps, false, bps > 8, soundSource->soundData, size, wavData);
445
446 // FIXME, Wavedata shouldn't be used.
447 }
448 }
449
450 struct BZAudioStreamSource {
451 BZAudioOGGStream *oggStream;
452
453 size_t readPosition;
454
455 size_t channels;
456 size_t rate;
457 size_t oggFrameSize;
458
459 size_t soundSize;
460 uint8_t *soundData;
461 };
462 typedef struct BZAudioStreamSource BZAudioStreamSource;
463
464 static void streamScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) {
465 BZAudioStreamSource *source = sourceData;
466 source->readPosition = 0;
467
468 size_t readPosition;
469 size_t soundSize;
470 uint8_t *soundData;
471 }
472
473 static size_t streamDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
474 BZAudioStreamSource *source = sourceData;
475
476 size_t copiedSize = 0;
477 for (;;) {
478 if (source->readPosition == source->soundSize) {
479 size_t decodedDataSize = 0;
480
481 while (decodedDataSize == 0) {
482 decodedDataSize = bzAudioQueryOGGData(source->oggStream, source->oggFrameSize, (float *)source->soundData);
483 if (decodedDataSize == 0) {
484 bzAudioResetOGGData(source->oggStream);
485 }
486 }
487 source->soundSize = bzAudioPlaybackConvertSample(engine, source->rate, source->channels, 32, true, true, source->soundData, decodedDataSize, source->soundData);
488 source->readPosition = 0;
489 }
490
491 size_t copySize = bzMin(dataOutSize - copiedSize, source->soundSize - source->readPosition);
492 SDL_memcpy(&dataOut[copiedSize], &source->soundData[source->readPosition], copySize);
493 copiedSize += copySize;
494 source->readPosition += copySize;
495
496 if (copiedSize == dataOutSize) {
497 break;
498 }
499 }
500
501 return copiedSize;
502 }
503
504 static size_t audioPlaybackCountStreams(JSON_Array *streamsArray) {
505 return json_array_get_count(streamsArray);
506 }
507
508 // NB: Streams.
509 // You can only have one instance of each stream playing at any time.
510 // We create one source for each stream and then allocate data required to convert it.
511 static void audioPlaybackLoadStreams(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *streamsArray) {
512 size_t streamCount = audioPlaybackCountStreams(streamsArray);
513 for (size_t i = 0; i < streamCount; ++i) {
514 BZAudioPlaybackEngineSource *source = &sources[i];
515
516 JSON_Object *streamObject = json_array_get_object(streamsArray, i);
517
518 const char *identifier = json_object_get_string(streamObject, "identifier");
519 source->identifier = bzIdentifierHashFromIdentifier(identifier);
520
521 audioVariableFromJSON(&source->volume, streamObject, "volume", 1.0f);
522
523 const char *targetBusIdentifier = json_object_get_string(streamObject, "bus");
524 if (targetBusIdentifier != NULL) {
525 source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier);
526 }
527
528 source->state = BZAudioPlaybackEngineSourceStateFree;
529
530 source->scheduleCallback = streamScheduleCallback;
531 source->dataCallback = streamDataCallback;
532 BZAudioStreamSource *streamSource = bzMemoryAlloc(arena, sizeof(BZAudioStreamSource));
533 source->sourceData = streamSource;
534
535 const char *filename = json_object_get_string(streamObject, "file");
536
537 BZAudioOGGStreamDetails oggStreamDetails;
538 streamSource->oggStream = bzAudioOpenOGGStream(&oggStreamDetails, arena, filename);
539
540 streamSource->channels = oggStreamDetails.channels;
541 streamSource->rate = oggStreamDetails.rate;
542 streamSource->oggFrameSize = oggStreamDetails.channels * oggStreamDetails.maxSamples * sizeof(float);
543 size_t soundMaxSize = bzAudioCalculateMemorySizeForConversion(engine, oggStreamDetails.rate, oggStreamDetails.channels, 32, true, true, streamSource->oggFrameSize);
544
545 size_t decodeMemorySize = bzMax(streamSource->oggFrameSize, soundMaxSize);
546 streamSource->soundData = bzMemoryAlloc(arena, decodeMemorySize);
547 }
548 }
549
550 struct BZAudioVoiceSource {
551 BZAudioSoundSource soundSource;
552 BZAudioVoice *voice;
553 size_t voiceMaxSize;
554 size_t channels;
555 size_t rate;
556 };
557 typedef struct BZAudioVoiceSource BZAudioVoiceSource;
558
559 static void voiceScheduleCallback(BZAudioPlaybackEngineID engine, void *sourceData, va_list args) {
560 BZAudioVoiceSource *source = sourceData;
561
562 const char *speechText = va_arg(args, const char *);
563
564 size_t dataSize = bzAudioGenerateSpeech(source->voice, (short *)source->soundSource.soundData, source->voiceMaxSize, speechText);
565
566 source->soundSource.soundSize = bzAudioPlaybackConvertSample(engine, source->rate, source->channels, 16, false, true, source->soundSource.soundData, dataSize, source->soundSource.soundData);
567 source->soundSource.readPosition = 0;
568 }
569
570 /*static size_t voiceDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
571 BZAudioVoiceSource *source = sourceData;
572
573 size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition);
574 SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize);
575 source->readPosition += copySize;
576
577 return copySize;
578 }*/
579
580 static size_t audioPlaybackCountVoices(JSON_Array *voicesArray) {
581 return json_array_get_count(voicesArray);
582 }
583
584 // NB: Voices.
585 // You can only have one instance of each voice playing at any time.
586 // We create one source for each voice and then allocate data required to synthesize.
587 // Events can be enqueued(??)
588 static void audioPlaybackLoadVoices(BZAudioPlaybackEngineSource *sources, BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *voicesArray) {
589 size_t voiceCount = audioPlaybackCountVoices(voicesArray);
590 for (size_t i = 0; i < voiceCount; ++i) {
591 BZAudioPlaybackEngineSource *source = &sources[i];
592
593 JSON_Object *voiceObject = json_array_get_object(voicesArray, i);
594
595 const char *identifier = json_object_get_string(voiceObject, "identifier");
596 source->identifier = bzIdentifierHashFromIdentifier(identifier);
597
598 audioVariableFromJSON(&source->volume, voiceObject, "volume", 1.0f);
599
600 const char *targetBusIdentifier = json_object_get_string(voiceObject, "bus");
601 if (targetBusIdentifier != NULL) {
602 source->targetBusIdentifier = bzIdentifierHashFromIdentifier(targetBusIdentifier);
603 }
604
605 source->state = BZAudioPlaybackEngineSourceStateFree;
606
607 source->scheduleCallback = voiceScheduleCallback;
608 source->dataCallback = soundDataCallback;
609 BZAudioVoiceSource *voiceSource = bzMemoryAlloc(arena, sizeof(BZAudioVoiceSource));
610 source->sourceData = voiceSource;
611
612 BZAudioVoiceDetails voiceDetails;
613 voiceSource->voice = bzAudioLoadSpeechVoice(&voiceDetails, arena, "%s", identifier);
614
615 voiceSource->channels = voiceDetails.channels;
616 voiceSource->rate = voiceDetails.rate;
617
618 voiceSource->voiceMaxSize = voiceDetails.channels * voiceDetails.maxSamples * sizeof(short);
619 size_t soundMaxSize = bzAudioCalculateMemorySizeForConversion(engine, voiceDetails.rate, voiceDetails.channels, 16, false, true, voiceSource->voiceMaxSize);
620
621 size_t decodeMemorySize = bzMax(voiceSource->voiceMaxSize, soundMaxSize);
622 voiceSource->soundSource.soundData = bzMemoryAlloc(arena, decodeMemorySize);
623 }
624 }
625
626 static void audioPlaybackLoadEvents(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, JSON_Array *eventsArray) {
627 engine->eventCount = json_array_get_count(eventsArray);
628 engine->events = bzMemoryAlloc(arena, engine->eventCount * sizeof(BZAudioPlaybackEngineEvent));
629 for (size_t i = 0; i < engine->eventCount; ++i) {
630 BZAudioPlaybackEngineEvent *event = &engine->events[i];
631
632 JSON_Object *eventObject = json_array_get_object(eventsArray, i);
633
634 const char *identifier = json_object_get_string(eventObject, "identifier");
635 event->identifier = bzIdentifierHashFromIdentifier(identifier);
636
637 const char *sourceIdentifier = NULL;
638 JSON_Array *sourcesArray = NULL;
639
640 if (json_object_has_value_of_type(eventObject, "source", JSONArray)) {
641 sourcesArray = json_object_get_array(eventObject, "source");
642 event->sourceCount = json_array_get_count(sourcesArray);
643 } else if (json_object_has_value_of_type(eventObject, "source", JSONString)) {
644 event->sourceCount = 1;
645 sourceIdentifier = json_object_get_string(eventObject, "source");
646 } else {
647 // FIXME, stop event
648 }
649
650 event->sources = bzMemoryAlloc(arena, event->sourceCount * sizeof(BZAudioPlaybackEngineSource *));
651 for (size_t j = 0; j < event->sourceCount; ++j) {
652 if (sourcesArray != NULL) {
653 sourceIdentifier = json_array_get_string(sourcesArray, j);
654 }
655
656 BZIdentifierHash sourceIdentifierHash = bzIdentifierHashFromIdentifier(sourceIdentifier);
657 for (size_t k = 0; k < engine->sourceCount; ++k) {
658 BZAudioPlaybackEngineSource *source = &engine->sources[k];
659 if (source->identifier == sourceIdentifierHash) {
660 event->sources[j] = source;
661 break;
662 }
663 }
664
665 bzAssertMessage(event->sources[j] != NULL, "Could not find source for event '%s': %s", identifier, sourceIdentifier);
666 }
667 }
668 }
669
670 void bzAudioPlaybackUseSoundbank(BZAudioPlaybackEngineID engine, BZMemoryArenaID arena, const char *identifierFmt, ...) {
671 //FIXME, reset
672
673 bzMakeIdentifier(identifier, identifierFmt);
674
675 BZResourceID handle = bzResourcesOpenResource("soundbanks", "assets/soundbanks/%s.soundbank.json", identifier);
676 size_t length = bzResourcesFileLength(handle);
677
678 char *data = bzMemoryAllocTmp(arena, length);
679 bzResourcesReadBytes(handle, data, length);
680 bzResourcesCloseResource(handle);
681
682 //json_set_allocation_functions
683 JSON_Value *soundbankJson = json_parse_string(data);
684 JSON_Object *soundbankJsonObject = json_object(soundbankJson);
685
686 JSON_Array *soundsArray = json_object_get_array(soundbankJsonObject, "sounds");
687 JSON_Array *streamsArray = json_object_get_array(soundbankJsonObject, "streams");
688 JSON_Array *voicesArray = json_object_get_array(soundbankJsonObject, "voices");
689
690 size_t soundsCount = audioPlaybackCountSounds(soundsArray);
691 size_t streamsCount = audioPlaybackCountStreams(streamsArray);
692 engine->sourceCount = soundsCount + streamsCount + audioPlaybackCountVoices(voicesArray);
693 engine->sources = bzMemoryAlloc(arena, engine->sourceCount * sizeof(BZAudioPlaybackEngineSource));
694
695 audioPlaybackLoadSounds(&engine->sources[0], engine, arena, soundsArray);
696 audioPlaybackLoadStreams(&engine->sources[soundsCount], engine, arena, streamsArray);
697 audioPlaybackLoadVoices(&engine->sources[soundsCount + streamsCount], engine, arena, voicesArray);
698
699 JSON_Array *eventsArray = json_object_get_array(soundbankJsonObject, "events");
700 audioPlaybackLoadEvents(engine, arena, eventsArray);
701
702 json_value_free(soundbankJson);
703 bzMemoryArenaResetTmp(arena);
704 }