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