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