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