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