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