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