Refactor, GLFW->SDL
[vg.git] / vg_audio.h
1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
2
3 #ifndef VG_AUDIO_H
4 #define VG_AUDIO_H
5
6 #include "vg/vg.h"
7 #include "vg/vg_stdint.h"
8 #include "vg/vg_platform.h"
9 #include "vg/vg_io.h"
10 #include "vg/vg_m.h"
11 #include "vg/vg_ui.h"
12 #include "vg/vg_console.h"
13 #include "vg/vg_store.h"
14
15 #include <sys/time.h>
16
17 #ifdef __GNUC__
18 #ifndef __clang__
19 #pragma GCC push_options
20 #pragma GCC optimize ("O3")
21 #pragma GCC diagnostic push
22 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
23 #endif
24 #endif
25
26 #define STB_VORBIS_MAX_CHANNELS 2
27 #include "submodules/stb/stb_vorbis.c"
28 #undef L
29 #undef R
30 #undef C
31
32 #ifdef __GNUC__
33 #ifndef __clang__
34 #pragma GCC pop_options
35 #pragma GCC diagnostic pop
36 #endif
37 #endif
38
39 #define SFX_MAX_SYSTEMS 32
40 #define AUDIO_FLAG_LOOP 0x1
41 #define AUDIO_FLAG_ONESHOT 0x2
42 #define AUDIO_FLAG_SPACIAL_3D 0x4
43 #define AUDIO_FLAG_AUTO_START 0x8
44 #define AUDIO_FLAG_KILL 0x10
45
46 #define FADEOUT_LENGTH 1100
47 #define FADEOUT_DIVISOR (1.0f/(float)FADEOUT_LENGTH)
48
49 #define AUDIO_DECODE_SIZE (1024*256) /* 256 kb decoding buffers */
50
51 enum audio_source_mode
52 {
53 k_audio_source_mono,
54 k_audio_source_compressed,
55 };
56
57 typedef struct audio_clip audio_clip;
58 struct audio_clip
59 {
60 const char *path;
61 enum audio_source_mode source_mode;
62
63 u32 size;
64 void *data;
65 };
66
67 typedef struct audio_mix_info audio_mix_info;
68 struct audio_mix_info
69 {
70 audio_clip *source;
71 v3f world_position;
72
73 float vol, pan;
74 u32 flags;
75 };
76
77 typedef struct audio_player audio_player;
78 struct audio_player
79 {
80 aatree_ptr active_entity; /* non-nil if currently playing */
81 audio_mix_info info;
82 int enqued, init;
83
84 /* Diagnostic */
85 const char *name;
86 };
87
88 typedef struct audio_entity audio_entity;
89 struct audio_entity
90 {
91 audio_player *player;
92 audio_mix_info info;
93
94 u32 length, cur;
95
96 /* Effects */
97 u32 fadeout, fadeout_current;
98 const char *name;
99 };
100
101 /*
102 * TODO list sunday
103 *
104 * play again: if already playing, leave in queue while it fadeouts
105 * oneshot: create a ghost entity
106 *
107 */
108
109 static struct vg_audio_system
110 {
111 #if 0
112 ma_device miniaudio_device;
113 ma_device_config miniaudio_dconfig;
114 #endif
115 SDL_AudioDeviceID sdl_output_device;
116
117 void *audio_pool,
118 *decode_buffer;
119 u32 samples_last;
120
121 /* synchro */
122 int sync_locked;
123
124 SDL_mutex *mux_checker,
125 *mux_sync;
126
127 /* Audio engine, thread 1 */
128 struct active_audio_player
129 {
130 int active;
131 union
132 {
133 audio_entity ent;
134 aatree_pool_node pool_node;
135 };
136
137 stb_vorbis *vorbis_handle;
138 stb_vorbis_alloc vorbis_alloc;
139 }
140 active_players[ SFX_MAX_SYSTEMS ];
141
142 aatree active_pool_info; /* note: just using the pool */
143 aatree_ptr active_pool_head;
144
145 /* System queue, and access from thread 0 */
146 audio_entity entity_queue[SFX_MAX_SYSTEMS];
147 int queue_len;
148 int debug_ui, debug_ui_3d;
149
150 v3f listener_pos,
151 listener_ears;
152 }
153 vg_audio;
154
155 static struct vg_profile
156 _vg_prof_audio_decode = {.mode = k_profile_mode_accum,
157 .name = "[T2] audio_decode()"},
158 _vg_prof_audio_mix = {.mode = k_profile_mode_accum,
159 .name = "[T2] audio_mix()"},
160 vg_prof_audio_decode,
161 vg_prof_audio_mix;
162
163 /*
164 * These functions are called from the main thread and used to prevent bad
165 * access. TODO: They should be no-ops in release builds.
166 */
167 VG_STATIC int audio_lock_checker_load(void)
168 {
169 int value;
170 SDL_LockMutex( vg_audio.mux_checker );
171 value = vg_audio.sync_locked;
172 SDL_UnlockMutex( vg_audio.mux_checker );
173 return value;
174 }
175
176 VG_STATIC void audio_lock_checker_store( int value )
177 {
178 SDL_LockMutex( vg_audio.mux_checker );
179 vg_audio.sync_locked = value;
180 SDL_UnlockMutex( vg_audio.mux_checker );
181 }
182
183 VG_STATIC void audio_require_lock(void)
184 {
185 if( audio_lock_checker_load() )
186 return;
187
188 vg_error( "Modifying sound effects systems requires locking\n" );
189 abort();
190 }
191
192 VG_STATIC void audio_lock(void)
193 {
194 SDL_LockMutex( vg_audio.mux_sync );
195 audio_lock_checker_store(1);
196 }
197
198 VG_STATIC void audio_unlock(void)
199 {
200 audio_lock_checker_store(0);
201 SDL_UnlockMutex( vg_audio.mux_sync );
202 }
203
204 VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int frame_count );
205 VG_STATIC void vg_audio_init(void)
206 {
207 vg_audio.mux_checker = SDL_CreateMutex();
208 vg_audio.mux_sync = SDL_CreateMutex();
209
210 /* TODO: Move here? */
211 vg_convar_push( (struct vg_convar){
212 .name = "debug_audio",
213 .data = &vg_audio.debug_ui,
214 .data_type = k_convar_dtype_i32,
215 .opt_i32 = { .min=0, .max=1, .clamp=1 },
216 .persistent = 1
217 });
218
219 /* allocate memory */
220
221 /* 32mb fixed */
222 vg_audio.audio_pool =
223 vg_create_linear_allocator( vg_mem.rtmemory, 1024*1024*32,
224 VG_MEMORY_SYSTEM );
225
226 /* fixed */
227 u32 decode_size = AUDIO_DECODE_SIZE * SFX_MAX_SYSTEMS;
228 vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size );
229
230 /* setup pool */
231 vg_audio.active_pool_info.base = vg_audio.active_players;
232 vg_audio.active_pool_info.offset = offsetof(struct active_audio_player,
233 pool_node );
234 vg_audio.active_pool_info.stride = sizeof(struct active_audio_player);
235 vg_audio.active_pool_info.p_cmp = NULL;
236 aatree_init_pool( &vg_audio.active_pool_info, SFX_MAX_SYSTEMS );
237
238 SDL_AudioSpec spec_desired, spec_got;
239 spec_desired.callback = audio_mixer_callback;
240 spec_desired.channels = 2;
241 spec_desired.format = AUDIO_F32;
242 spec_desired.freq = 44100;
243 spec_desired.padding = 0;
244 spec_desired.samples = 512;
245 spec_desired.silence = 0;
246 spec_desired.size = 0;
247 spec_desired.userdata = NULL;
248
249 vg_audio.sdl_output_device =
250 SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,
251 SDL_AUDIO_ALLOW_SAMPLES_CHANGE );
252
253 if( vg_audio.sdl_output_device )
254 {
255 SDL_PauseAudioDevice( vg_audio.sdl_output_device, 0 );
256 }
257 else
258 {
259 vg_fatal_exit_loop(
260 "SDL_OpenAudioDevice failed. Your default audio device must support:\n"
261 " Frequency: 44100 hz\n"
262 " Buffer size: 512\n"
263 " Channels: 2\n"
264 " Format: s16 or f32\n" );
265 }
266
267 vg_success( "Ready\n" );
268 }
269
270 VG_STATIC void vg_audio_free(void * nothing)
271 {
272 SDL_CloseAudioDevice( vg_audio.sdl_output_device );
273 }
274
275 /*
276 * thread 1
277 */
278
279 static aatree_ptr audio_alloc_entity_internal(void)
280 {
281 aatree_ptr playerid = aatree_pool_alloc( &vg_audio.active_pool_info,
282 &vg_audio.active_pool_head );
283
284 if( playerid == AATREE_PTR_NIL )
285 return AATREE_PTR_NIL;
286
287 struct active_audio_player *aap = &vg_audio.active_players[ playerid ];
288 aap->active = 1;
289
290 return playerid;
291 }
292
293 VG_STATIC void audio_entity_free_internal( aatree_ptr id )
294 {
295 struct active_audio_player *aap = &vg_audio.active_players[ id ];
296 aap->active = 0;
297
298 /* Notify player that we've finished */
299 if( aap->ent.player )
300 aap->ent.player->active_entity = AATREE_PTR_NIL;
301
302 /* delete */
303 aatree_pool_free( &vg_audio.active_pool_info, id,
304 &vg_audio.active_pool_head );
305 }
306
307 VG_STATIC void *audio_entity_vorbis_ptr( aatree_ptr entid )
308 {
309 u8 *buf = (u8*)vg_audio.decode_buffer,
310 *loc = &buf[AUDIO_DECODE_SIZE*entid];
311
312 return (void *)loc;
313 }
314
315 VG_STATIC void audio_entity_start( audio_entity *src )
316 {
317 aatree_ptr entid = audio_alloc_entity_internal();
318 if( entid == AATREE_PTR_NIL )
319 return;
320
321 audio_entity *ent = &vg_audio.active_players[ entid ].ent;
322
323 ent->info = src->info;
324 ent->name = src->info.source->path;
325 ent->cur = 0;
326 ent->player = src->player;
327
328 ent->fadeout = 0;
329 ent->fadeout_current = 0;
330
331 /* Notify main player we are dequeud and playing */
332 if( src->player )
333 {
334 src->player->enqued = 0;
335 src->player->active_entity = entid;
336 }
337
338 if( src->info.source->source_mode == k_audio_source_compressed )
339 {
340 /* Setup vorbis decoder */
341 struct active_audio_player *aap = &vg_audio.active_players[ entid ];
342
343 stb_vorbis_alloc alloc = {
344 .alloc_buffer = (char *)audio_entity_vorbis_ptr( entid ),
345 .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
346 };
347
348 int err;
349 stb_vorbis *decoder = stb_vorbis_open_memory(
350 src->info.source->data,
351 src->info.source->size, &err, &alloc );
352
353 if( !decoder )
354 {
355 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
356 src->info.source->path, err );
357
358 audio_entity_free_internal( entid );
359 return;
360 }
361 else
362 {
363 ent->length = stb_vorbis_stream_length_in_samples( decoder );
364 }
365
366 aap->vorbis_handle = decoder;
367 }
368 else
369 {
370 ent->length = src->info.source->size;
371 }
372 }
373
374 /*
375 * Read everything from the queue
376 */
377 VG_STATIC void audio_system_enque(void)
378 {
379 /* Process incoming sound queue */
380 audio_lock();
381
382 int wr = 0;
383 for( int i=0; i<vg_audio.queue_len; i++ )
384 {
385 audio_entity *src = &vg_audio.entity_queue[ i ];
386
387 if( src->player )
388 {
389 /* Start new */
390 if( src->player->active_entity == AATREE_PTR_NIL )
391 {
392 audio_entity_start( src );
393 }
394 else
395 {
396 /* Otherwise try start fadeout but dont remove from queue */
397
398 aatree_ptr entid = src->player->active_entity;
399 audio_entity *ent = &vg_audio.active_players[ entid ].ent;
400 if( !ent->fadeout )
401 {
402 ent->fadeout = FADEOUT_LENGTH;
403 ent->fadeout_current = FADEOUT_LENGTH;
404 }
405
406 vg_audio.entity_queue[ wr ++ ] = *src;
407 }
408 }
409 else
410 {
411 audio_entity_start( src );
412 }
413 }
414
415 vg_audio.queue_len = wr;
416
417 /* Localize others memory */
418 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
419 {
420 struct active_audio_player *aap = &vg_audio.active_players[i];
421 if( !aap->active )
422 continue;
423
424 if( aap->ent.player )
425 {
426 /* Only copy information in whilst not requeing */
427 if( aap->ent.player->enqued == 0 )
428 {
429 aap->ent.info = aap->ent.player->info;
430
431 if( (aap->ent.info.flags & AUDIO_FLAG_KILL) && !aap->ent.fadeout )
432 {
433 aap->ent.fadeout = FADEOUT_LENGTH;
434 aap->ent.fadeout_current = FADEOUT_LENGTH;
435 }
436 }
437 }
438 }
439
440 audio_unlock();
441 }
442
443 /*
444 * Redistribute sound systems
445 */
446 VG_STATIC void audio_system_cleanup(void)
447 {
448 audio_lock();
449
450 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
451 {
452 struct active_audio_player *aap = &vg_audio.active_players[i];
453 if( aap->active )
454 {
455 audio_entity *src = &aap->ent;
456 if( src->cur < src->length || (src->info.flags & AUDIO_FLAG_LOOP ))
457 {
458 /* Good to keep */
459 }
460 else
461 {
462 audio_entity_free_internal( i );
463 }
464 }
465 }
466
467 audio_unlock();
468 }
469
470 /*
471 * Get effective volume and pan from this entity
472 */
473 VG_STATIC void audio_entity_spacialize( audio_entity *ent, float *vol, float *pan )
474 {
475 if( ent->info.vol < 0.01f )
476 {
477 *vol = ent->info.vol;
478 *pan = 0.0f;
479 return;
480 }
481
482 v3f delta;
483 v3_sub( ent->info.world_position, vg_audio.listener_pos, delta );
484
485 float dist2 = v3_length2( delta );
486
487 if( dist2 < 0.0001f )
488 {
489 *pan = 0.0f;
490 *vol = 1.0f;
491 }
492 else
493 {
494 float dist = sqrtf( dist2 ),
495 attn = (dist / ent->info.vol) +1.0f;
496
497 v3_muls( delta, 1.0f/dist, delta );
498 *pan = v3_dot( vg_audio.listener_ears, delta );
499 *vol = 1.0f/(attn*attn);
500 }
501 }
502
503 VG_STATIC void audio_decode_uncompressed_mono( i16 *src, u32 count, float *dst )
504 {
505 for( u32 i=0; i<count; i++ )
506 {
507 dst[ i*2 + 0 ] = ((float)src[i]) * (1.0f/32767.0f);
508 dst[ i*2 + 1 ] = ((float)src[i]) * (1.0f/32767.0f);
509 }
510 }
511
512 /*
513 * adapted from stb_vorbis.h, since the original does not handle mono->stereo
514 */
515 VG_STATIC int
516 stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer,
517 int len )
518 {
519 int n = 0,
520 c = VG_MIN( 1, f->channels - 1 );
521
522 while( n < len )
523 {
524 int k = f->channel_buffer_end - f->channel_buffer_start;
525
526 if( n+k >= len )
527 k = len - n;
528
529 for( int j=0; j < k; ++j )
530 {
531 *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j];
532 *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j];
533 }
534
535 n += k;
536 f->channel_buffer_start += k;
537
538 if( n == len )
539 break;
540
541 if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
542 break;
543 }
544
545 return n;
546 }
547
548 /*
549 * ........ more wrecked code sorry!
550 */
551 VG_STATIC int
552 stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len )
553 {
554 int n = 0,
555 c = VG_MIN( 1, f->channels - 1 );
556
557 while( n < len )
558 {
559 int k = f->channel_buffer_end - f->channel_buffer_start;
560
561 if( n+k >= len )
562 k = len - n;
563
564 for( int j=0; j < k; ++j )
565 {
566 float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j],
567 sr = f->channel_buffers[ c ][f->channel_buffer_start+j];
568
569 *buffer++ = vg_clampf( 0.5f*(sl+sr), -1.0f, 1.0f ) * 32767.0f;
570 //*buffer++ = vg_clampf( sr, -1.0f, 1.0f ) * 32767.0f;
571 }
572
573 n += k;
574 f->channel_buffer_start += k;
575
576 if( n == len )
577 break;
578
579 if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
580 break;
581 }
582
583 return n;
584 }
585
586 VG_STATIC void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
587 {
588 vg_profile_begin( &_vg_prof_audio_decode );
589
590 struct active_audio_player *aap = &vg_audio.active_players[id];
591 audio_entity *ent = &aap->ent;
592
593 u32 remaining = count;
594 u32 cursor = ent->cur;
595 u32 buffer_pos = 0;
596
597 while( remaining )
598 {
599 u32 samples_this_run = VG_MIN( remaining, ent->length - cursor );
600 remaining -= samples_this_run;
601
602 float *dst = &buf[ buffer_pos * 2 ];
603
604 int source_mode = ent->info.source->source_mode;
605
606 if( source_mode == k_audio_source_mono )
607 {
608 i16 *src_buffer = ent->info.source->data,
609 *src = &src_buffer[cursor];
610
611 audio_decode_uncompressed_mono( src, samples_this_run, dst );
612 }
613 else if( source_mode == k_audio_source_compressed )
614 {
615 int read_samples = stb_vorbis_get_samples_float_interleaved_stereo(
616 aap->vorbis_handle,
617 dst,
618 samples_this_run );
619
620 if( read_samples != samples_this_run )
621 {
622 vg_warn( "Invalid samples read (%s)\n", ent->info.source->path );
623 }
624 }
625
626 cursor += samples_this_run;
627 buffer_pos += samples_this_run;
628
629 if( (ent->info.flags & AUDIO_FLAG_LOOP) && remaining )
630 {
631 if( source_mode == k_audio_source_compressed )
632 {
633 stb_vorbis_seek_start( aap->vorbis_handle );
634 }
635
636 cursor = 0;
637 continue;
638 }
639 else
640 break;
641 }
642
643 while( remaining )
644 {
645 buf[ buffer_pos*2 + 0 ] = 0.0f;
646 buf[ buffer_pos*2 + 1 ] = 0.0f;
647 buffer_pos ++;
648
649 remaining --;
650 }
651
652 ent->cur = cursor;
653 vg_profile_end( &_vg_prof_audio_decode );
654 }
655
656 VG_STATIC void audio_entity_mix( aatree_ptr id, float *buffer,
657 u32 frame_count )
658 {
659 audio_entity *ent = &vg_audio.active_players[id].ent;
660
661 u32 cursor = ent->cur, buffer_pos = 0;
662 float *pcf = alloca( frame_count * 2 * sizeof(float) );
663
664 u32 frames_write = frame_count;
665 float fadeout_divisor = 1.0f / (float)ent->fadeout;
666
667 float vol = ent->info.vol,
668 pan = ent->info.pan;
669
670 audio_entity_get_samples( id, frame_count, pcf );
671
672 vg_profile_begin( &_vg_prof_audio_mix );
673
674 if( ent->info.flags & AUDIO_FLAG_SPACIAL_3D )
675 audio_entity_spacialize( ent, &vol, &pan );
676
677 for( u32 j=0; j<frame_count; j++ )
678 {
679 float frame_vol = vol;
680
681 if( ent->fadeout )
682 {
683 /* Force this system to be removed now */
684 if( ent->fadeout_current == 0 )
685 {
686 ent->info.flags = 0x00;
687 ent->cur = ent->length;
688 break;
689 }
690
691 frame_vol *= (float)ent->fadeout_current * fadeout_divisor;
692 ent->fadeout_current --;
693 }
694
695 float sl = 1.0f-pan,
696 sr = 1.0f+pan;
697
698 buffer[ buffer_pos*2+0 ] += pcf[ buffer_pos*2+0 ] * frame_vol * sl;
699 buffer[ buffer_pos*2+1 ] += pcf[ buffer_pos*2+1 ] * frame_vol * sr;
700
701 buffer_pos ++;
702 }
703
704 vg_profile_end( &_vg_prof_audio_mix );
705 }
706
707 VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count )
708 {
709 audio_system_enque();
710
711 int frame_count = byte_count/(2*sizeof(float));
712
713 /* Clear buffer */
714 float *pOut32F = (float *)stream;
715 for( int i=0; i<frame_count*2; i ++ )
716 pOut32F[i] = 0.0f;
717
718 /* Mix all sounds */
719 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
720 {
721 struct active_audio_player *aap = &vg_audio.active_players[i];
722
723 if( aap->active )
724 {
725 audio_entity_mix( i, pOut32F, frame_count );
726 }
727 }
728
729 /* redistribute */
730 audio_system_cleanup();
731
732 audio_lock();
733 vg_profile_increment( &_vg_prof_audio_decode );
734 vg_profile_increment( &_vg_prof_audio_mix );
735
736 vg_prof_audio_mix = _vg_prof_audio_mix;
737 vg_prof_audio_decode = _vg_prof_audio_decode;
738
739 vg_audio.samples_last = frame_count;
740 audio_unlock();
741 }
742
743 VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc )
744 {
745 if( lin_alloc == NULL )
746 lin_alloc = vg_audio.audio_pool;
747
748 if( clip->source_mode == k_audio_source_mono )
749 {
750 vg_linear_clear( vg_mem.scratch );
751 u32 fsize;
752
753 stb_vorbis_alloc alloc = {
754 .alloc_buffer = vg_linear_alloc( vg_mem.scratch, AUDIO_DECODE_SIZE ),
755 .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
756 };
757
758 void *filedata = vg_file_read( vg_mem.scratch, clip->path, &fsize );
759
760 int err;
761 stb_vorbis *decoder = stb_vorbis_open_memory(
762 filedata, fsize, &err, &alloc );
763
764 if( !decoder )
765 {
766 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
767 clip->path, err );
768 vg_fatal_exit_loop( "Vorbis decode error" );
769 }
770
771 /* only mono is supported in uncompressed */
772 u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ),
773 data_size = length_samples * sizeof(i16);
774
775 audio_lock();
776 clip->data = vg_linear_alloc( lin_alloc, data_size );
777 clip->size = length_samples;
778 audio_unlock();
779
780 int read_samples = stb_vorbis_get_samples_i16_downmixed(
781 decoder, clip->data, length_samples );
782
783 if( read_samples != length_samples )
784 vg_fatal_exit_loop( "Decode error" );
785
786 float mb = (float)(data_size) / (1024.0f*1024.0f);
787 vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb,
788 length_samples );
789 }
790
791 /* load in directly */
792 else if( clip->source_mode == k_audio_source_compressed )
793 {
794 audio_lock();
795 clip->data = vg_file_read( lin_alloc, clip->path, &clip->size );
796 audio_unlock();
797
798 if( !clip->data )
799 vg_fatal_exit_loop( "Audio failed to load" );
800
801 float mb = (float)(clip->size) / (1024.0f*1024.0f);
802 vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
803 }
804 }
805
806 VG_STATIC void audio_clip_loadn( audio_clip *arr, int count, void *lin_alloc )
807 {
808 for( int i=0; i<count; i++ )
809 audio_clip_load( &arr[i], lin_alloc );
810 }
811
812 /* Mark change to be uploaded through queue system */
813 VG_STATIC void audio_player_commit( audio_player *sys )
814 {
815 audio_require_lock();
816
817 if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) )
818 {
819 vg_warn( "Audio commit queue full\n" );
820 return;
821 }
822
823 if( sys->enqued )
824 {
825 vg_warn( "[2] Audio commit spamming; already enqued (%s)\n", sys->name );
826 return;
827 }
828
829 sys->enqued = 1;
830 audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ];
831 ent->info = sys->info;
832 ent->player = sys;
833 }
834
835 VG_STATIC void audio_require_init( audio_player *player )
836 {
837 if( player->init )
838 return;
839
840 vg_fatal_exit_loop( "Must init audio player before playing! \n" );
841 }
842
843 VG_STATIC void audio_require_clip_loaded( audio_clip *clip )
844 {
845 if( clip->data && clip->size )
846 return;
847
848 vg_fatal_exit_loop( "Must load audio clip before playing! \n" );
849 }
850
851 /* Play a clip using player. If its already playing something, it will
852 * fadeout quickly and start the next sound */
853 VG_STATIC void audio_player_playclip( audio_player *player, audio_clip *clip )
854 {
855 audio_require_lock();
856 audio_require_init( player );
857 audio_require_clip_loaded( clip );
858
859 if( player->info.flags & AUDIO_FLAG_KILL )
860 {
861 vg_error( "Can't start audio clip on player that is/has disconnected" );
862 return;
863 }
864
865 if( player->enqued )
866 {
867 vg_warn( "[1] Audio commit spamming; already enqued (%s)\n",
868 player->name );
869 return;
870 }
871
872 player->info.source = clip;
873 audio_player_commit( player );
874 }
875
876 VG_STATIC void audio_play_oneshot( audio_clip *clip, float volume )
877 {
878 audio_require_lock();
879 audio_require_clip_loaded( clip );
880
881 if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) )
882 {
883 vg_warn( "Audio commit queue full\n" );
884 return;
885 }
886
887 audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ];
888
889 ent->info.flags = AUDIO_FLAG_ONESHOT;
890 ent->info.pan = 0.0f;
891 ent->info.source = clip;
892 ent->info.vol = volume;
893 ent->player = NULL;
894 }
895
896 VG_STATIC void audio_player_init( audio_player *player )
897 {
898 player->active_entity = AATREE_PTR_NIL;
899 player->init = 1;
900 }
901
902 /*
903 * Effects
904 */
905
906 /*
907 * Safety enforced Get/set attributes
908 */
909
910 VG_STATIC int audio_player_is_playing( audio_player *sys )
911 {
912 audio_require_lock();
913
914 if( sys->active_entity != AATREE_PTR_NIL )
915 return 1;
916 else
917 return 0;
918 }
919
920 VG_STATIC void audio_player_set_position( audio_player *sys, v3f pos )
921 {
922 audio_require_lock();
923 v3_copy( pos, sys->info.world_position );
924 }
925
926 VG_STATIC void audio_player_set_vol( audio_player *sys, float vol )
927 {
928 audio_require_lock();
929 sys->info.vol = vol;
930 }
931
932 VG_STATIC float audio_player_get_vol( audio_player *sys )
933 {
934 audio_require_lock();
935 return sys->info.vol;
936 }
937
938 VG_STATIC void audio_player_set_pan( audio_player *sys, float pan )
939 {
940 audio_require_lock();
941 sys->info.pan = pan;
942 }
943
944 VG_STATIC float audio_player_get_pan( audio_player *sys )
945 {
946 audio_require_lock();
947 return sys->info.pan;
948 }
949
950 VG_STATIC void audio_player_set_flags( audio_player *sys, u32 flags )
951 {
952 audio_require_lock();
953 sys->info.flags = flags;
954 }
955
956 VG_STATIC u32 audio_player_get_flags( audio_player *sys )
957 {
958 audio_require_lock();
959 return sys->info.flags;
960 }
961
962
963 /*
964 * Debugging
965 */
966
967 VG_STATIC void audio_debug_ui( m4x4f mtx_pv )
968 {
969 if( !vg_audio.debug_ui )
970 return;
971
972 /* Get data */
973 struct sound_info
974 {
975 const char *name;
976 u32 cursor, flags, length;
977 v3f pos;
978 float vol;
979 }
980 infos[ SFX_MAX_SYSTEMS ];
981 int num_systems = 0;
982
983 audio_lock();
984
985 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
986 {
987 struct active_audio_player *aap = &vg_audio.active_players[i];
988
989 if( !aap->active )
990 continue;
991
992 audio_entity *ent = &aap->ent;
993 struct sound_info *snd = &infos[ num_systems ++ ];
994
995 snd->name = ent->name;
996 snd->cursor = ent->cur;
997 snd->flags = ent->info.flags;
998 snd->length = ent->length;
999 snd->vol = ent->info.vol*100.0f;
1000 v3_copy( ent->info.world_position, snd->pos );
1001 }
1002
1003 float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
1004 vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
1005 &vg_prof_audio_mix }, 2,
1006 budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
1007 250, 0 }, 3 );
1008
1009 audio_unlock();
1010
1011 char perf[128];
1012
1013 /* Draw UI */
1014 vg_uictx.cursor[0] = 258;
1015 vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12;
1016 vg_uictx.cursor[2] = 150;
1017 vg_uictx.cursor[3] = 12;
1018
1019 float mb1 = 1024.0f*1024.0f,
1020 usage = vg_linear_get_cur( vg_audio.audio_pool ) / mb1,
1021 total = vg_linear_get_capacity( vg_audio.audio_pool ) / mb1,
1022 percent = (usage/total) * 100.0f;
1023
1024 snprintf( perf, 127, "Mem: %.1f/%.1fmb (%.1f%%)\n", usage, total, percent );
1025
1026 ui_text( vg_uictx.cursor, perf, 1, 0 );
1027 vg_uictx.cursor[1] += 20;
1028
1029 ui_rect overlap_buffer[ SFX_MAX_SYSTEMS ];
1030 u32 overlap_length = 0;
1031
1032 /* Draw audio stack */
1033 for( int i=0; i<num_systems; i ++ )
1034 {
1035 struct sound_info *inf = &infos[i];
1036
1037 vg_uictx.cursor[2] = 200;
1038 vg_uictx.cursor[3] = 18;
1039
1040 u32 alpha = 0xa0000000;
1041
1042 ui_new_node();
1043 {
1044 ui_fill_rect( vg_uictx.cursor, 0x00333333|alpha );
1045
1046 ui_px baseline = vg_uictx.cursor[0],
1047 w = 200,
1048 c = baseline + ((float)inf->cursor / (float)inf->length) * w;
1049
1050 /* cursor */
1051 vg_uictx.cursor[2] = 2;
1052 vg_uictx.cursor[0] = c;
1053 ui_fill_rect( vg_uictx.cursor, 0xffffffff );
1054
1055 vg_uictx.cursor[0] = baseline + 2;
1056 vg_uictx.cursor[1] += 2;
1057 snprintf( perf, 127, "%s %.1f%%", infos[i].name, infos[i].vol );
1058 ui_text( vg_uictx.cursor, perf, 1, 0 );
1059
1060 if( inf->flags & AUDIO_FLAG_SPACIAL_3D )
1061 {
1062 v4f wpos;
1063 v3_copy( inf->pos, wpos );
1064 wpos[3] = 1.0f;
1065 m4x4_mulv( mtx_pv, wpos, wpos );
1066
1067 if( wpos[3] < 0.0f )
1068 goto projected_behind;
1069
1070 v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos );
1071 v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
1072
1073 ui_rect wr;
1074 wr[0] = wpos[0] * vg.window_x;
1075 wr[1] = (1.0f-wpos[1]) * vg.window_y;
1076 wr[2] = 100;
1077 wr[3] = 17;
1078
1079 for( int j=0; j<12; j++ )
1080 {
1081 int collide = 0;
1082 for( int k=0; k<overlap_length; k++ )
1083 {
1084 ui_px *wk = overlap_buffer[k];
1085 if( ((wr[0] <= wk[0]+wk[2]) && (wr[0]+wr[2] >= wk[0])) &&
1086 ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) )
1087 {
1088 collide = 1;
1089 break;
1090 }
1091 }
1092
1093 if( !collide )
1094 break;
1095 else
1096 wr[1] += 18;
1097 }
1098
1099 ui_text( wr, perf, 1, 0 );
1100
1101 ui_rect_copy( wr, overlap_buffer[ overlap_length ++ ] );
1102 }
1103 }
1104
1105 projected_behind:
1106
1107 ui_end_down();
1108 vg_uictx.cursor[1] += 1;
1109 }
1110 }
1111
1112 #endif /* VG_AUDIO_H */