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