1 #include <bz/audio/playback_internal.h>
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>
16 #define SAMPLE_RATE 48000
17 #define SAMPLE_COUNT 512
19 //#define STREAM_DATA_SIZE 16*4096 // FIXME
21 enum BZAudioVariableType
{
22 BZAudioVariableTypeConstant
,
23 BZAudioVariableTypeParameter
,
25 typedef enum BZAudioVariableType BZAudioVariableType
;
27 struct BZAudioVariable
{
28 BZAudioVariableType type
;
31 BZIdentifierHash parameterIdentifier
;
34 typedef struct BZAudioVariable BZAudioVariable
;
36 struct BZAudioPlaybackEngineBus
{
37 BZIdentifierHash identifier
;
38 BZAudioVariable volume
;
40 BZIdentifierHash targetBusIdentifier
;
42 typedef struct BZAudioPlaybackEngineBus BZAudioPlaybackEngineBus
;
44 struct BZAudioPlaybackEngineParameter
{
45 BZIdentifierHash identifier
;
48 typedef struct BZAudioPlaybackEngineParameter BZAudioPlaybackEngineParameter
;
50 enum BZAudioPlaybackEngineSourceState
{
51 BZAudioPlaybackEngineSourceStateFree
,
52 BZAudioPlaybackEngineSourceStateEnqueued
,
53 BZAudioPlaybackEngineSourceStatePlaying
,
55 typedef enum BZAudioPlaybackEngineSourceState BZAudioPlaybackEngineSourceState
;
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
);
60 struct BZAudioPlaybackEngineSource
{
61 BZIdentifierHash identifier
;
62 BZAudioVariable volume
;
63 BZIdentifierHash targetBusIdentifier
;
65 BZAudioPlaybackEngineSourceState state
;
67 BZAudioPlaybackEngineSourceScheduleCallback scheduleCallback
;
68 BZAudioPlaybackEngineSourceDataCallback dataCallback
;
72 typedef struct BZAudioPlaybackEngineSource BZAudioPlaybackEngineSource
;
74 //enum BZAudioSoundbankEventType {
75 // BZAudioSoundbankEventTypeStop,
76 // BZAudioSoundbankEventTypeSound,
77 // BZAudioSoundbankEventTypeStream,
78 // BZAudioSoundbankEventTypeVoice,
80 //typedef enum BZAudioSoundbankEventType BZAudioSoundbankEventType;
82 struct BZAudioPlaybackEngineEvent
{
83 BZIdentifierHash identifier
;
85 BZAudioPlaybackEngineSource
**sources
;
87 typedef struct BZAudioPlaybackEngineEvent BZAudioPlaybackEngineEvent
;
89 #define kParameterCount 32
91 struct BZAudioPlaybackEngine
{
92 SDL_AudioDeviceID audioDevice
;
93 SDL_AudioFormat audioDeviceFormat
;
95 uint8_t audioDeviceChannels
;
96 uint32_t audioDeviceRate
;
99 size_t dataBufferSize
;
100 uint8_t *tmpDataBuffer
;
103 BZAudioPlaybackEngineBus
*buses
;
105 SDL_mutex
*schedulingMutex
;
107 BZAudioPlaybackEngineParameter parameters
[kParameterCount
];
110 BZAudioPlaybackEngineSource
*sources
;
113 BZAudioPlaybackEngineEvent
*events
;
116 static void audioVariableFromJSON(BZAudioVariable
*variableOut
, void *obj
, const char *key
, float defaultValue
) {
117 JSON_Object
*jsonObject
= (JSON_Object
*)obj
;
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
);
127 variableOut
->type
= BZAudioVariableTypeConstant
;
128 variableOut
->constantValue
= defaultValue
;
132 static float audioPlaybackResolveParameter(BZAudioPlaybackEngineID engine
, const BZAudioVariable
*variable
) {
133 switch (variable
->type
) {
134 case BZAudioVariableTypeConstant
:
135 return variable
->constantValue
;
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
;
148 static void audioDataCallback(void *userdata
, uint8_t *stream
, int len
) {
149 BZAudioPlaybackEngineID engine
= (BZAudioPlaybackEngine
*)userdata
;
150 bzAssert(len
<= engine
->dataBufferSize
);
152 SDL_LockMutex(engine
->schedulingMutex
);
154 SDL_memset(engine
->buses
[0].dataBuffer
, engine
->silenceValue
, engine
->dataBufferSize
);
156 for (size_t i
= engine
->busCount
- 1; i
< engine
->busCount
; --i
) {
157 BZAudioPlaybackEngineBus
*bus
= &engine
->buses
[i
];
159 SDL_memset(bus
->dataBuffer
, engine
->silenceValue
, engine
->dataBufferSize
);
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
;
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?
172 if (dataOut
< engine
->dataBufferSize
) {
173 source
->state
= BZAudioPlaybackEngineSourceStateFree
;
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
));
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...
190 SDL_UnlockMutex(engine
->schedulingMutex
);
193 BZAudioPlaybackEngineID
bzAudioPlaybackInit(BZMemoryArenaID arena
, const char *identifierFmt
, ...) {
194 bzMakeIdentifier(identifier
, identifierFmt
);
196 BZAudioPlaybackEngineID engine
= bzMemoryAlloc(arena
, sizeof(BZAudioPlaybackEngine
));
198 SDL_AudioSpec desiredSpec
;
199 SDL_AudioSpec obtainedSpec
;
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
;
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");
214 engine
->audioDeviceFormat
= obtainedSpec
.format
;
215 engine
->audioDeviceChannels
= obtainedSpec
.channels
;
216 engine
->audioDeviceRate
= obtainedSpec
.freq
;
217 engine
->silenceValue
= obtainedSpec
.silence
;
219 size_t byteSize
= SDL_AUDIO_BITSIZE(obtainedSpec
.format
) >> 2;
220 engine
->dataBufferSize
= obtainedSpec
.samples
* byteSize
* sizeof(uint8_t);
222 engine
->tmpDataBuffer
= bzMemoryAlloc(arena
, engine
->dataBufferSize
);
224 BZResourceID handle
= bzResourcesOpenResource("audio", "assets/audio/%s.config.json", identifier
);
225 size_t length
= bzResourcesFileLength(handle
);
227 char *data
= bzMemoryAllocTmp(arena
, length
);
228 bzResourcesReadBytes(handle
, data
, length
);
229 bzResourcesCloseResource(handle
);
231 //json_set_allocation_functions
232 JSON_Value
*configJson
= json_parse_string(data
);
233 JSON_Object
*configJsonObject
= json_object(configJson
);
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
];
241 size_t queueSize
= 1;
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
);
249 size_t configQueueSize
= json_object_get_number(busObject
, "queue_size");
250 if (configQueueSize
> 0) {
251 queueSize
= configQueueSize
;
255 bus
->dataBuffer
= bzMemoryAlloc(arena
, engine
->dataBufferSize
);
258 engine
->schedulingMutex
= SDL_CreateMutex();
260 json_value_free(configJson
);
261 bzMemoryArenaResetTmp(arena
);
264 SDL_PauseAudioDevice(engine
->audioDevice
, 0);
269 void bzAudioPlaybackTeardown(BZAudioPlaybackEngineID engine
) {
270 SDL_CloseAudioDevice(engine
->audioDevice
);
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
;
284 void bzAudioPlaybackPostEvent(BZAudioPlaybackEngineID engine
, BZIdentifierHash eventIdentifier
, ...) {
285 SDL_LockMutex(engine
->schedulingMutex
);
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
;
296 va_start(args
, eventIdentifier
);
297 source
->scheduleCallback(engine
, source
->sourceData
, args
);
302 SDL_UnlockMutex(engine
->schedulingMutex
);
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;
310 format
= isSigned
? AUDIO_S8
: AUDIO_U8
;
314 format
= isSigned
? AUDIO_S16
: AUDIO_U16
;
318 format
= isFloat
? AUDIO_F32
: AUDIO_S32
;
322 bzError("Unhandled sound format");
327 SDL_BuildAudioCVT(&cvt
, format
, channels
, rate
, engine
->audioDeviceFormat
, engine
->audioDeviceChannels
, engine
->audioDeviceRate
);
331 return cvt
.len
* cvt
.len_mult
;
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;
339 format
= isSigned
? AUDIO_S8
: AUDIO_U8
;
343 format
= isSigned
? AUDIO_S16
: AUDIO_U16
;
347 format
= isFloat
? AUDIO_F32
: AUDIO_S32
;
351 bzError("Unhandled sound format");
356 SDL_BuildAudioCVT(&cvt
, format
, channels
, rate
, engine
->audioDeviceFormat
, engine
->audioDeviceChannels
, engine
->audioDeviceRate
);
359 cvt
.buf
= dst
;//bzMemoryAlloc(arena, cvt.len * cvt.len_mult);
361 if (src
!= cvt
.buf
) {
362 SDL_memcpy(cvt
.buf
, src
, dataSize
);
365 int r
= SDL_ConvertAudio(&cvt
);
373 struct BZAudioSoundSource
{
378 typedef struct BZAudioSoundSource BZAudioSoundSource
;
380 static void soundScheduleCallback(BZAudioPlaybackEngineID engine
, void *sourceData
, va_list args
) {
381 BZAudioSoundSource
*source
= sourceData
;
382 source
->readPosition
= 0;
385 static size_t soundDataCallback(uint8_t *dataOut
, size_t dataOutSize
, BZAudioPlaybackEngineID engine
, void *sourceData
) {
386 BZAudioSoundSource
*source
= sourceData
;
388 size_t copySize
= bzMin(dataOutSize
, source
->soundSize
- source
->readPosition
);
389 SDL_memcpy(dataOut
, &source
->soundData
[source
->readPosition
], copySize
);
390 source
->readPosition
+= copySize
;
395 static size_t audioPlaybackCountSounds(JSON_Array
*soundsArray
) {
396 size_t baseSoundCount
= json_array_get_count(soundsArray
);
398 size_t soundCount
= 0;
399 for (size_t i
= 0; i
< baseSoundCount
; ++i
) {
400 ++soundCount
; // FIXME, count the number of concurrent 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
];
415 JSON_Object
*soundObject
= json_array_get_object(soundsArray
, i
);
417 const char *identifier
= json_object_get_string(soundObject
, "identifier");
418 source
->identifier
= bzIdentifierHashFromIdentifier(identifier
);
420 audioVariableFromJSON(&source
->volume
, soundObject
, "volume", 1.0f
);
422 const char *targetBusIdentifier
= json_object_get_string(soundObject
, "bus");
423 if (targetBusIdentifier
!= NULL
) {
424 source
->targetBusIdentifier
= bzIdentifierHashFromIdentifier(targetBusIdentifier
);
427 source
->state
= BZAudioPlaybackEngineSourceStateFree
;
429 source
->scheduleCallback
= soundScheduleCallback
;
430 source
->dataCallback
= soundDataCallback
;
431 BZAudioSoundSource
*soundSource
= bzMemoryAlloc(arena
, sizeof(BZAudioSoundSource
));
432 source
->sourceData
= soundSource
;
434 const char *filename
= json_object_get_string(soundObject
, "file");
440 void *wavData
= bzAudioLoadWAV(&size
, arena
, &rate
, &channels
, &bps
, filename
); // FIXME, temporary memory...
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
);
446 // FIXME, Wavedata shouldn't be used.
450 struct BZAudioStreamSource
{
451 BZAudioOGGStream
*oggStream
;
462 typedef struct BZAudioStreamSource BZAudioStreamSource
;
464 static void streamScheduleCallback(BZAudioPlaybackEngineID engine
, void *sourceData
, va_list args
) {
465 BZAudioStreamSource
*source
= sourceData
;
466 source
->readPosition
= 0;
473 static size_t streamDataCallback(uint8_t *dataOut
, size_t dataOutSize
, BZAudioPlaybackEngineID engine
, void *sourceData
) {
474 BZAudioStreamSource
*source
= sourceData
;
476 size_t copiedSize
= 0;
478 if (source
->readPosition
== source
->soundSize
) {
479 size_t decodedDataSize
= 0;
481 while (decodedDataSize
== 0) {
482 decodedDataSize
= bzAudioQueryOGGData(source
->oggStream
, source
->oggFrameSize
, (float *)source
->soundData
);
483 if (decodedDataSize
== 0) {
484 bzAudioResetOGGData(source
->oggStream
);
487 source
->soundSize
= bzAudioPlaybackConvertSample(engine
, source
->rate
, source
->channels
, 32, true, true, source
->soundData
, decodedDataSize
, source
->soundData
);
488 source
->readPosition
= 0;
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
;
496 if (copiedSize
== dataOutSize
) {
504 static size_t audioPlaybackCountStreams(JSON_Array
*streamsArray
) {
505 return json_array_get_count(streamsArray
);
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
];
516 JSON_Object
*streamObject
= json_array_get_object(streamsArray
, i
);
518 const char *identifier
= json_object_get_string(streamObject
, "identifier");
519 source
->identifier
= bzIdentifierHashFromIdentifier(identifier
);
521 audioVariableFromJSON(&source
->volume
, streamObject
, "volume", 1.0f
);
523 const char *targetBusIdentifier
= json_object_get_string(streamObject
, "bus");
524 if (targetBusIdentifier
!= NULL
) {
525 source
->targetBusIdentifier
= bzIdentifierHashFromIdentifier(targetBusIdentifier
);
528 source
->state
= BZAudioPlaybackEngineSourceStateFree
;
530 source
->scheduleCallback
= streamScheduleCallback
;
531 source
->dataCallback
= streamDataCallback
;
532 BZAudioStreamSource
*streamSource
= bzMemoryAlloc(arena
, sizeof(BZAudioStreamSource
));
533 source
->sourceData
= streamSource
;
535 const char *filename
= json_object_get_string(streamObject
, "file");
537 BZAudioOGGStreamDetails oggStreamDetails
;
538 streamSource
->oggStream
= bzAudioOpenOGGStream(&oggStreamDetails
, arena
, filename
);
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
);
545 size_t decodeMemorySize
= bzMax(streamSource
->oggFrameSize
, soundMaxSize
);
546 streamSource
->soundData
= bzMemoryAlloc(arena
, decodeMemorySize
);
550 struct BZAudioVoiceSource
{
551 BZAudioSoundSource soundSource
;
557 typedef struct BZAudioVoiceSource BZAudioVoiceSource
;
559 static void voiceScheduleCallback(BZAudioPlaybackEngineID engine
, void *sourceData
, va_list args
) {
560 BZAudioVoiceSource
*source
= sourceData
;
562 const char *speechText
= va_arg(args
, const char *);
564 size_t dataSize
= bzAudioGenerateSpeech(source
->voice
, (short *)source
->soundSource
.soundData
, source
->voiceMaxSize
, speechText
);
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;
570 /*static size_t voiceDataCallback(uint8_t *dataOut, size_t dataOutSize, BZAudioPlaybackEngineID engine, void *sourceData) {
571 BZAudioVoiceSource *source = sourceData;
573 size_t copySize = bzMin(dataOutSize, source->soundSize - source->readPosition);
574 SDL_memcpy(dataOut, &source->soundData[source->readPosition], copySize);
575 source->readPosition += copySize;
580 static size_t audioPlaybackCountVoices(JSON_Array
*voicesArray
) {
581 return json_array_get_count(voicesArray
);
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
];
593 JSON_Object
*voiceObject
= json_array_get_object(voicesArray
, i
);
595 const char *identifier
= json_object_get_string(voiceObject
, "identifier");
596 source
->identifier
= bzIdentifierHashFromIdentifier(identifier
);
598 audioVariableFromJSON(&source
->volume
, voiceObject
, "volume", 1.0f
);
600 const char *targetBusIdentifier
= json_object_get_string(voiceObject
, "bus");
601 if (targetBusIdentifier
!= NULL
) {
602 source
->targetBusIdentifier
= bzIdentifierHashFromIdentifier(targetBusIdentifier
);
605 source
->state
= BZAudioPlaybackEngineSourceStateFree
;
607 source
->scheduleCallback
= voiceScheduleCallback
;
608 source
->dataCallback
= soundDataCallback
;
609 BZAudioVoiceSource
*voiceSource
= bzMemoryAlloc(arena
, sizeof(BZAudioVoiceSource
));
610 source
->sourceData
= voiceSource
;
612 BZAudioVoiceDetails voiceDetails
;
613 voiceSource
->voice
= bzAudioLoadSpeechVoice(&voiceDetails
, arena
, "%s", identifier
);
615 voiceSource
->channels
= voiceDetails
.channels
;
616 voiceSource
->rate
= voiceDetails
.rate
;
618 voiceSource
->voiceMaxSize
= voiceDetails
.channels
* voiceDetails
.maxSamples
* sizeof(short);
619 size_t soundMaxSize
= bzAudioCalculateMemorySizeForConversion(engine
, voiceDetails
.rate
, voiceDetails
.channels
, 16, false, true, voiceSource
->voiceMaxSize
);
621 size_t decodeMemorySize
= bzMax(voiceSource
->voiceMaxSize
, soundMaxSize
);
622 voiceSource
->soundSource
.soundData
= bzMemoryAlloc(arena
, decodeMemorySize
);
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
];
632 JSON_Object
*eventObject
= json_array_get_object(eventsArray
, i
);
634 const char *identifier
= json_object_get_string(eventObject
, "identifier");
635 event
->identifier
= bzIdentifierHashFromIdentifier(identifier
);
637 const char *sourceIdentifier
= NULL
;
638 JSON_Array
*sourcesArray
= NULL
;
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");
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
);
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
;
665 bzAssertMessage(event
->sources
[j
] != NULL
, "Could not find source for event '%s': %s", identifier
, sourceIdentifier
);
670 void bzAudioPlaybackUseSoundbank(BZAudioPlaybackEngineID engine
, BZMemoryArenaID arena
, const char *identifierFmt
, ...) {
673 bzMakeIdentifier(identifier
, identifierFmt
);
675 BZResourceID handle
= bzResourcesOpenResource("soundbanks", "assets/soundbanks/%s.soundbank.json", identifier
);
676 size_t length
= bzResourcesFileLength(handle
);
678 char *data
= bzMemoryAllocTmp(arena
, length
);
679 bzResourcesReadBytes(handle
, data
, length
);
680 bzResourcesCloseResource(handle
);
682 //json_set_allocation_functions
683 JSON_Value
*soundbankJson
= json_parse_string(data
);
684 JSON_Object
*soundbankJsonObject
= json_object(soundbankJson
);
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");
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
));
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
);
699 JSON_Array
*eventsArray
= json_object_get_array(soundbankJsonObject
, "events");
700 audioPlaybackLoadEvents(engine
, arena
, eventsArray
);
702 json_value_free(soundbankJson
);
703 bzMemoryArenaResetTmp(arena
);