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