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