all changes
[vg.git] / 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 VG_GAME
7
8 #include "vg/vg.h"
9 #include "vg/vg_stdint.h"
10 #include "vg/vg_platform.h"
11 #include "vg/vg_io.h"
12 #include "vg/vg_m.h"
13 #include "vg/vg_ui.h"
14 #include "vg/vg_console.h"
15 #include "vg/vg_store.h"
16 #include "vg/vg_profiler.h"
17
18 #include <sys/time.h>
19 #include <math.h>
20
21 #ifdef __GNUC__
22 #ifndef __clang__
23 #pragma GCC push_options
24 #pragma GCC optimize ("O3")
25 #pragma GCC diagnostic push
26 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
27 #endif
28 #endif
29
30 #define STB_VORBIS_MAX_CHANNELS 2
31 #include "submodules/stb/stb_vorbis.c"
32 #undef L
33 #undef R
34 #undef C
35
36 #ifdef __GNUC__
37 #ifndef __clang__
38 #pragma GCC pop_options
39 #pragma GCC diagnostic pop
40 #endif
41 #endif
42
43 #define SFX_MAX_SYSTEMS 32
44 #define AUDIO_FLAG_LOOP 0x1
45 #define AUDIO_FLAG_ONESHOT 0x2
46 #define AUDIO_FLAG_SPACIAL_3D 0x4
47 #define AUDIO_FLAG_AUTO_START 0x8
48 #define AUDIO_FLAG_KILL 0x10
49
50 #define FADEOUT_LENGTH 1100
51 #define FADEOUT_DIVISOR (1.0f/(float)FADEOUT_LENGTH)
52
53 #define AUDIO_DECODE_SIZE (1024*256) /* 256 kb decoding buffers */
54
55 enum audio_source_mode
56 {
57 k_audio_source_mono,
58 k_audio_source_compressed,
59 };
60
61 typedef struct audio_clip audio_clip;
62 struct audio_clip
63 {
64 const char *path;
65 enum audio_source_mode source_mode;
66
67 u32 size;
68 void *data;
69 };
70
71 typedef struct audio_mix_info audio_mix_info;
72 struct audio_mix_info
73 {
74 audio_clip *source;
75 v3f world_position;
76
77 float vol, pan;
78 u32 flags;
79 };
80
81 typedef struct audio_player audio_player;
82 struct audio_player
83 {
84 aatree_ptr active_entity; /* non-nil if currently playing */
85 audio_mix_info info;
86 int enqued, init;
87
88 /* Diagnostic */
89 const char *name;
90 };
91
92 typedef struct audio_entity audio_entity;
93 struct audio_entity
94 {
95 audio_player *player;
96 audio_mix_info info;
97
98 u32 length, cur;
99
100 /* Effects */
101 u32 fadeout, fadeout_current;
102 const char *name;
103 };
104
105 /*
106 * TODO list sunday
107 *
108 * play again: if already playing, leave in queue while it fadeouts
109 * oneshot: create a ghost entity
110 *
111 */
112
113 static struct vg_audio_system
114 {
115 #if 0
116 ma_device miniaudio_device;
117 ma_device_config miniaudio_dconfig;
118 #endif
119 SDL_AudioDeviceID sdl_output_device;
120
121 void *audio_pool,
122 *decode_buffer;
123 u32 samples_last;
124
125 /* synchro */
126 int sync_locked;
127
128 SDL_mutex *mux_checker,
129 *mux_sync;
130
131 /* Audio engine, thread 1 */
132 struct active_audio_player
133 {
134 int active;
135 union
136 {
137 audio_entity ent;
138 aatree_pool_node pool_node;
139 };
140
141 stb_vorbis *vorbis_handle;
142 stb_vorbis_alloc vorbis_alloc;
143 }
144 active_players[ SFX_MAX_SYSTEMS ];
145
146 aatree active_pool_info; /* note: just using the pool */
147 aatree_ptr active_pool_head;
148
149 /* System queue, and access from thread 0 */
150 audio_entity entity_queue[SFX_MAX_SYSTEMS];
151 int queue_len;
152 int debug_ui, debug_ui_3d;
153
154 v3f listener_pos,
155 listener_ears;
156
157 float volume,
158 volume_target,
159 volume_target_internal,
160 volume_console;
161 }
162 vg_audio = { .volume_console = 1.0f };
163
164 static struct vg_profile
165 _vg_prof_audio_decode = {.mode = k_profile_mode_accum,
166 .name = "[T2] audio_decode()"},
167 _vg_prof_audio_mix = {.mode = k_profile_mode_accum,
168 .name = "[T2] audio_mix()"},
169 vg_prof_audio_decode,
170 vg_prof_audio_mix;
171
172 /*
173 * These functions are called from the main thread and used to prevent bad
174 * access. TODO: They should be no-ops in release builds.
175 */
176 VG_STATIC int audio_lock_checker_load(void)
177 {
178 int value;
179 SDL_LockMutex( vg_audio.mux_checker );
180 value = vg_audio.sync_locked;
181 SDL_UnlockMutex( vg_audio.mux_checker );
182 return value;
183 }
184
185 VG_STATIC void audio_lock_checker_store( int value )
186 {
187 SDL_LockMutex( vg_audio.mux_checker );
188 vg_audio.sync_locked = value;
189 SDL_UnlockMutex( vg_audio.mux_checker );
190 }
191
192 VG_STATIC void audio_require_lock(void)
193 {
194 if( audio_lock_checker_load() )
195 return;
196
197 vg_error( "Modifying sound effects systems requires locking\n" );
198 abort();
199 }
200
201 VG_STATIC void audio_lock(void)
202 {
203 SDL_LockMutex( vg_audio.mux_sync );
204 audio_lock_checker_store(1);
205 }
206
207 VG_STATIC void audio_unlock(void)
208 {
209 audio_lock_checker_store(0);
210 SDL_UnlockMutex( vg_audio.mux_sync );
211 }
212
213 VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int frame_count );
214 VG_STATIC void vg_audio_init(void)
215 {
216 vg_audio.mux_checker = SDL_CreateMutex();
217 vg_audio.mux_sync = SDL_CreateMutex();
218
219 /* TODO: Move here? */
220 vg_convar_push( (struct vg_convar){
221 .name = "debug_audio",
222 .data = &vg_audio.debug_ui,
223 .data_type = k_convar_dtype_i32,
224 .opt_i32 = { .min=0, .max=1, .clamp=1 },
225 .persistent = 1
226 });
227
228 vg_convar_push( (struct vg_convar){
229 .name = "volume",
230 .data = &vg_audio.volume_console,
231 .data_type = k_convar_dtype_f32,
232 .opt_f32 = { .min=0.0f, .max=2.0f, .clamp=1 },
233 .persistent = 1
234 });
235
236 /* allocate memory */
237
238 /* 32mb fixed */
239 vg_audio.audio_pool =
240 vg_create_linear_allocator( vg_mem.rtmemory, 1024*1024*32,
241 VG_MEMORY_SYSTEM );
242
243 /* fixed */
244 u32 decode_size = AUDIO_DECODE_SIZE * SFX_MAX_SYSTEMS;
245 vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size );
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 SDL_AudioSpec spec_desired, spec_got;
256 spec_desired.callback = audio_mixer_callback;
257 spec_desired.channels = 2;
258 spec_desired.format = AUDIO_F32;
259 spec_desired.freq = 44100;
260 spec_desired.padding = 0;
261 spec_desired.samples = 512;
262 spec_desired.silence = 0;
263 spec_desired.size = 0;
264 spec_desired.userdata = NULL;
265
266 vg_audio.sdl_output_device =
267 SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,
268 SDL_AUDIO_ALLOW_SAMPLES_CHANGE );
269
270 if( vg_audio.sdl_output_device )
271 {
272 SDL_PauseAudioDevice( vg_audio.sdl_output_device, 0 );
273 }
274 else
275 {
276 vg_fatal_exit_loop(
277 "SDL_OpenAudioDevice failed. Your default audio device must support:\n"
278 " Frequency: 44100 hz\n"
279 " Buffer size: 512\n"
280 " Channels: 2\n"
281 " Format: s16 or f32\n" );
282 }
283
284 vg_success( "Ready\n" );
285 }
286
287 VG_STATIC void vg_audio_free(void)
288 {
289 SDL_CloseAudioDevice( vg_audio.sdl_output_device );
290 }
291
292 /*
293 * thread 1
294 */
295
296 static aatree_ptr audio_alloc_entity_internal(void)
297 {
298 aatree_ptr playerid = aatree_pool_alloc( &vg_audio.active_pool_info,
299 &vg_audio.active_pool_head );
300
301 if( playerid == AATREE_PTR_NIL )
302 return AATREE_PTR_NIL;
303
304 struct active_audio_player *aap = &vg_audio.active_players[ playerid ];
305 aap->active = 1;
306
307 return playerid;
308 }
309
310 VG_STATIC void audio_entity_free_internal( aatree_ptr id )
311 {
312 struct active_audio_player *aap = &vg_audio.active_players[ id ];
313 aap->active = 0;
314
315 /* Notify player that we've finished */
316 if( aap->ent.player )
317 aap->ent.player->active_entity = AATREE_PTR_NIL;
318
319 /* delete */
320 aatree_pool_free( &vg_audio.active_pool_info, id,
321 &vg_audio.active_pool_head );
322 }
323
324 VG_STATIC void *audio_entity_vorbis_ptr( aatree_ptr entid )
325 {
326 u8 *buf = (u8*)vg_audio.decode_buffer,
327 *loc = &buf[AUDIO_DECODE_SIZE*entid];
328
329 return (void *)loc;
330 }
331
332 VG_STATIC void audio_entity_start( audio_entity *src )
333 {
334 aatree_ptr entid = audio_alloc_entity_internal();
335 if( entid == AATREE_PTR_NIL )
336 return;
337
338 audio_entity *ent = &vg_audio.active_players[ entid ].ent;
339
340 ent->info = src->info;
341 ent->name = src->info.source->path;
342 ent->cur = 0;
343 ent->player = src->player;
344
345 ent->fadeout = 0;
346 ent->fadeout_current = 0;
347
348 /* Notify main player we are dequeud and playing */
349 if( src->player )
350 {
351 src->player->enqued = 0;
352 src->player->active_entity = entid;
353 }
354
355 if( src->info.source->source_mode == k_audio_source_compressed )
356 {
357 /* Setup vorbis decoder */
358 struct active_audio_player *aap = &vg_audio.active_players[ entid ];
359
360 stb_vorbis_alloc alloc = {
361 .alloc_buffer = (char *)audio_entity_vorbis_ptr( entid ),
362 .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
363 };
364
365 int err;
366 stb_vorbis *decoder = stb_vorbis_open_memory(
367 src->info.source->data,
368 src->info.source->size, &err, &alloc );
369
370 if( !decoder )
371 {
372 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
373 src->info.source->path, err );
374
375 audio_entity_free_internal( entid );
376 return;
377 }
378 else
379 {
380 ent->length = stb_vorbis_stream_length_in_samples( decoder );
381 }
382
383 aap->vorbis_handle = decoder;
384 }
385 else
386 {
387 ent->length = src->info.source->size;
388 }
389 }
390
391 /*
392 * Read everything from the queue
393 */
394 VG_STATIC void audio_system_enque(void)
395 {
396 /* Process incoming sound queue */
397 audio_lock();
398
399 vg_audio.volume_target_internal = vg_audio.volume_target;
400
401 int wr = 0;
402 for( int i=0; i<vg_audio.queue_len; i++ )
403 {
404 audio_entity *src = &vg_audio.entity_queue[ i ];
405
406 if( src->player )
407 {
408 /* Start new */
409 if( src->player->active_entity == AATREE_PTR_NIL )
410 {
411 audio_entity_start( src );
412 }
413 else
414 {
415 /* Otherwise try start fadeout but dont remove from queue */
416
417 aatree_ptr entid = src->player->active_entity;
418 audio_entity *ent = &vg_audio.active_players[ entid ].ent;
419 if( !ent->fadeout )
420 {
421 ent->fadeout = FADEOUT_LENGTH;
422 ent->fadeout_current = FADEOUT_LENGTH;
423 }
424
425 vg_audio.entity_queue[ wr ++ ] = *src;
426 }
427 }
428 else
429 {
430 audio_entity_start( src );
431 }
432 }
433
434 vg_audio.queue_len = wr;
435
436 /* Localize others memory */
437 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
438 {
439 struct active_audio_player *aap = &vg_audio.active_players[i];
440 if( !aap->active )
441 continue;
442
443 if( aap->ent.player )
444 {
445 /* Only copy information in whilst not requeing */
446 if( aap->ent.player->enqued == 0 )
447 {
448 aap->ent.info = aap->ent.player->info;
449
450 if( (aap->ent.info.flags & AUDIO_FLAG_KILL) && !aap->ent.fadeout )
451 {
452 aap->ent.fadeout = FADEOUT_LENGTH;
453 aap->ent.fadeout_current = FADEOUT_LENGTH;
454 }
455 }
456 }
457 }
458
459 audio_unlock();
460 }
461
462 /*
463 * Redistribute sound systems
464 */
465 VG_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 VG_STATIC void audio_entity_spacialize( audio_entity *ent,
493 float *vol, float *pan )
494 {
495 if( ent->info.vol < 0.01f )
496 {
497 *vol = ent->info.vol;
498 *pan = 0.0f;
499 return;
500 }
501
502 if( !vg_validf(vg_audio.listener_pos[0]) ||
503 !vg_validf(vg_audio.listener_pos[1]) ||
504 !vg_validf(vg_audio.listener_pos[2]) ||
505 !vg_validf(ent->info.world_position[0]) ||
506 !vg_validf(ent->info.world_position[1]) ||
507 !vg_validf(ent->info.world_position[2]) )
508 {
509 vg_error( "NaN listener/world position (%s)\n", ent->name );
510 *vol = 0.0f;
511 *pan = 0.0f;
512 return;
513 }
514
515 v3f delta;
516 v3_sub( ent->info.world_position, vg_audio.listener_pos, delta );
517
518 float dist2 = v3_length2( delta );
519
520 if( dist2 < 0.0001f )
521 {
522 *pan = 0.0f;
523 *vol = 1.0f;
524 }
525 else
526 {
527 float dist = sqrtf( dist2 ),
528 attn = (dist / ent->info.vol) +1.0f;
529
530 v3_muls( delta, 1.0f/dist, delta );
531 *pan = v3_dot( vg_audio.listener_ears, delta );
532 *vol = 1.0f/(attn*attn);
533 }
534 }
535
536 VG_STATIC void audio_decode_uncompressed_mono( i16 *src, u32 count, float *dst )
537 {
538 for( u32 i=0; i<count; i++ )
539 {
540 dst[ i*2 + 0 ] = ((float)src[i]) * (1.0f/32767.0f);
541 dst[ i*2 + 1 ] = ((float)src[i]) * (1.0f/32767.0f);
542 }
543 }
544
545 /*
546 * adapted from stb_vorbis.h, since the original does not handle mono->stereo
547 */
548 VG_STATIC int
549 stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer,
550 int len )
551 {
552 int n = 0,
553 c = VG_MIN( 1, f->channels - 1 );
554
555 while( n < len )
556 {
557 int k = f->channel_buffer_end - f->channel_buffer_start;
558
559 if( n+k >= len )
560 k = len - n;
561
562 for( int j=0; j < k; ++j )
563 {
564 *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j];
565 *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j];
566 }
567
568 n += k;
569 f->channel_buffer_start += k;
570
571 if( n == len )
572 break;
573
574 if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
575 break;
576 }
577
578 return n;
579 }
580
581 /*
582 * ........ more wrecked code sorry!
583 */
584 VG_STATIC int
585 stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len )
586 {
587 int n = 0,
588 c = VG_MIN( 1, f->channels - 1 );
589
590 while( n < len )
591 {
592 int k = f->channel_buffer_end - f->channel_buffer_start;
593
594 if( n+k >= len )
595 k = len - n;
596
597 for( int j=0; j < k; ++j )
598 {
599 float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j],
600 sr = f->channel_buffers[ c ][f->channel_buffer_start+j];
601
602 *buffer++ = vg_clampf( 0.5f*(sl+sr), -1.0f, 1.0f ) * 32767.0f;
603 //*buffer++ = vg_clampf( sr, -1.0f, 1.0f ) * 32767.0f;
604 }
605
606 n += k;
607 f->channel_buffer_start += k;
608
609 if( n == len )
610 break;
611
612 if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
613 break;
614 }
615
616 return n;
617 }
618
619 VG_STATIC void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
620 {
621 vg_profile_begin( &_vg_prof_audio_decode );
622
623 struct active_audio_player *aap = &vg_audio.active_players[id];
624 audio_entity *ent = &aap->ent;
625
626 u32 remaining = count;
627 u32 cursor = ent->cur;
628 u32 buffer_pos = 0;
629
630 while( remaining )
631 {
632 u32 samples_this_run = VG_MIN( remaining, ent->length - cursor );
633 remaining -= samples_this_run;
634
635 float *dst = &buf[ buffer_pos * 2 ];
636
637 int source_mode = ent->info.source->source_mode;
638
639 if( source_mode == k_audio_source_mono )
640 {
641 i16 *src_buffer = ent->info.source->data,
642 *src = &src_buffer[cursor];
643
644 audio_decode_uncompressed_mono( src, samples_this_run, dst );
645 }
646 else if( source_mode == k_audio_source_compressed )
647 {
648 int read_samples = stb_vorbis_get_samples_float_interleaved_stereo(
649 aap->vorbis_handle,
650 dst,
651 samples_this_run );
652
653 if( read_samples != samples_this_run )
654 {
655 vg_warn( "Invalid samples read (%s)\n", ent->info.source->path );
656 }
657 }
658
659 cursor += samples_this_run;
660 buffer_pos += samples_this_run;
661
662 if( (ent->info.flags & AUDIO_FLAG_LOOP) && remaining )
663 {
664 if( source_mode == k_audio_source_compressed )
665 {
666 stb_vorbis_seek_start( aap->vorbis_handle );
667 }
668
669 cursor = 0;
670 continue;
671 }
672 else
673 break;
674 }
675
676 while( remaining )
677 {
678 buf[ buffer_pos*2 + 0 ] = 0.0f;
679 buf[ buffer_pos*2 + 1 ] = 0.0f;
680 buffer_pos ++;
681
682 remaining --;
683 }
684
685 ent->cur = cursor;
686 vg_profile_end( &_vg_prof_audio_decode );
687 }
688
689 VG_STATIC void audio_entity_mix( aatree_ptr id, float *buffer,
690 u32 frame_count )
691 {
692 audio_entity *ent = &vg_audio.active_players[id].ent;
693
694 u32 cursor = ent->cur, buffer_pos = 0;
695 float *pcf = alloca( frame_count * 2 * sizeof(float) );
696
697 u32 frames_write = frame_count;
698 float fadeout_divisor = 1.0f / (float)ent->fadeout;
699
700 float vol = ent->info.vol,
701 pan = ent->info.pan;
702
703 audio_entity_get_samples( id, frame_count, pcf );
704
705 vg_profile_begin( &_vg_prof_audio_mix );
706
707 if( ent->info.flags & AUDIO_FLAG_SPACIAL_3D )
708 audio_entity_spacialize( ent, &vol, &pan );
709
710 for( u32 j=0; j<frame_count; j++ )
711 {
712 float frame_vol = vol * vg_audio.volume;
713 if( ent->fadeout )
714 {
715 /* Force this system to be removed now */
716 if( ent->fadeout_current == 0 )
717 {
718 ent->info.flags = 0x00;
719 ent->cur = ent->length;
720 break;
721 }
722
723 frame_vol *= (float)ent->fadeout_current * fadeout_divisor;
724 ent->fadeout_current --;
725 }
726
727 float sl = 1.0f-pan,
728 sr = 1.0f+pan;
729
730 buffer[ buffer_pos*2+0 ] += pcf[ buffer_pos*2+0 ] * frame_vol * sl;
731 buffer[ buffer_pos*2+1 ] += pcf[ buffer_pos*2+1 ] * frame_vol * sr;
732
733 buffer_pos ++;
734 }
735
736 vg_profile_end( &_vg_prof_audio_mix );
737 }
738
739 VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count )
740 {
741 audio_system_enque();
742
743 int frame_count = byte_count/(2*sizeof(float));
744
745 /* Clear buffer */
746 float *pOut32F = (float *)stream;
747 for( int i=0; i<frame_count*2; i ++ )
748 pOut32F[i] = 0.0f;
749
750 float start_vol = vg_audio.volume;
751
752 /* Mix all sounds */
753 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
754 {
755 struct active_audio_player *aap = &vg_audio.active_players[i];
756 vg_audio.volume = start_vol;
757
758 if( aap->active )
759 audio_entity_mix( i, pOut32F, frame_count );
760 }
761
762 float vol_diff = vg_audio.volume_target_internal - vg_audio.volume,
763 vol_rate = 1.0f / (44100.0f*0.25f),
764 vol_chg = frame_count * vol_rate;
765
766 if( vol_chg > fabsf( vol_diff ) )
767 vg_audio.volume = vg_audio.volume_target_internal;
768 else
769 vg_audio.volume += vg_signf( vol_diff ) * vol_chg;
770
771 /* redistribute */
772 audio_system_cleanup();
773
774 audio_lock();
775
776 vg_profile_increment( &_vg_prof_audio_decode );
777 vg_profile_increment( &_vg_prof_audio_mix );
778
779 vg_prof_audio_mix = _vg_prof_audio_mix;
780 vg_prof_audio_decode = _vg_prof_audio_decode;
781
782 vg_audio.samples_last = frame_count;
783 audio_unlock();
784 }
785
786 VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc )
787 {
788 if( lin_alloc == NULL )
789 lin_alloc = vg_audio.audio_pool;
790
791 if( clip->source_mode == k_audio_source_mono )
792 {
793 vg_linear_clear( vg_mem.scratch );
794 u32 fsize;
795
796 stb_vorbis_alloc alloc = {
797 .alloc_buffer = vg_linear_alloc( vg_mem.scratch, AUDIO_DECODE_SIZE ),
798 .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
799 };
800
801 void *filedata = vg_file_read( vg_mem.scratch, clip->path, &fsize );
802
803 int err;
804 stb_vorbis *decoder = stb_vorbis_open_memory(
805 filedata, fsize, &err, &alloc );
806
807 if( !decoder )
808 {
809 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
810 clip->path, err );
811 vg_fatal_exit_loop( "Vorbis decode error" );
812 }
813
814 /* only mono is supported in uncompressed */
815 u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ),
816 data_size = length_samples * sizeof(i16);
817
818 audio_lock();
819 clip->data = vg_linear_alloc( lin_alloc, data_size );
820 clip->size = length_samples;
821 audio_unlock();
822
823 int read_samples = stb_vorbis_get_samples_i16_downmixed(
824 decoder, clip->data, length_samples );
825
826 if( read_samples != length_samples )
827 vg_fatal_exit_loop( "Decode error" );
828
829 float mb = (float)(data_size) / (1024.0f*1024.0f);
830 vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb,
831 length_samples );
832 }
833
834 /* load in directly */
835 else if( clip->source_mode == k_audio_source_compressed )
836 {
837 audio_lock();
838 clip->data = vg_file_read( lin_alloc, clip->path, &clip->size );
839 audio_unlock();
840
841 if( !clip->data )
842 vg_fatal_exit_loop( "Audio failed to load" );
843
844 float mb = (float)(clip->size) / (1024.0f*1024.0f);
845 vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
846 }
847 }
848
849 VG_STATIC void audio_clip_loadn( audio_clip *arr, int count, void *lin_alloc )
850 {
851 for( int i=0; i<count; i++ )
852 audio_clip_load( &arr[i], lin_alloc );
853 }
854
855 /* Mark change to be uploaded through queue system */
856 VG_STATIC void audio_player_commit( audio_player *sys )
857 {
858 audio_require_lock();
859
860 if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) )
861 {
862 vg_warn( "Audio commit queue full\n" );
863 return;
864 }
865
866 if( sys->enqued )
867 {
868 vg_warn( "[2] Audio commit spamming; already enqued (%s)\n", sys->name );
869 return;
870 }
871
872 sys->enqued = 1;
873 audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ];
874 ent->info = sys->info;
875 ent->player = sys;
876 }
877
878 VG_STATIC void audio_require_init( audio_player *player )
879 {
880 if( player->init )
881 return;
882
883 audio_unlock();
884 vg_fatal_exit_loop( "Must init audio player before playing! \n" );
885 }
886
887 VG_STATIC void audio_require_clip_loaded( audio_clip *clip )
888 {
889 if( clip->data && clip->size )
890 return;
891
892 audio_unlock();
893 vg_fatal_exit_loop( "Must load audio clip before playing! \n" );
894 }
895
896 /* Play a clip using player. If its already playing something, it will
897 * fadeout quickly and start the next sound */
898 VG_STATIC void audio_player_playclip( audio_player *player, audio_clip *clip )
899 {
900 audio_require_lock();
901 audio_require_init( player );
902 audio_require_clip_loaded( clip );
903
904 if( player->info.flags & AUDIO_FLAG_KILL )
905 {
906 vg_error( "Can't start audio clip on player that is/has disconnected" );
907 return;
908 }
909
910 if( player->enqued )
911 {
912 vg_warn( "[1] Audio commit spamming; already enqued (%s)\n",
913 player->name );
914 return;
915 }
916
917 player->info.source = clip;
918 audio_player_commit( player );
919 }
920
921 VG_STATIC void audio_play_oneshot( audio_clip *clip, float volume )
922 {
923 audio_require_lock();
924 audio_require_clip_loaded( clip );
925
926 if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) )
927 {
928 vg_warn( "Audio commit queue full\n" );
929 return;
930 }
931
932 audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ];
933
934 ent->info.flags = AUDIO_FLAG_ONESHOT;
935 ent->info.pan = 0.0f;
936 ent->info.source = clip;
937 ent->info.vol = volume;
938 ent->player = NULL;
939 }
940
941 VG_STATIC void audio_player_init( audio_player *player )
942 {
943 player->active_entity = AATREE_PTR_NIL;
944 player->init = 1;
945 }
946
947 /*
948 * Effects
949 */
950
951 /*
952 * Safety enforced Get/set attributes
953 */
954
955 VG_STATIC int audio_player_is_playing( audio_player *sys )
956 {
957 audio_require_lock();
958
959 if( sys->active_entity != AATREE_PTR_NIL )
960 return 1;
961 else
962 return 0;
963 }
964
965 VG_STATIC void audio_player_set_position( audio_player *sys, v3f pos )
966 {
967 audio_require_lock();
968 v3_copy( pos, sys->info.world_position );
969 }
970
971 VG_STATIC void audio_player_set_vol( audio_player *sys, float vol )
972 {
973 audio_require_lock();
974
975 if( !vg_validf(vol) )
976 {
977 vg_warn( "NaN volume (%s)\n", sys->name );
978 vol = 0.0f;
979 }
980
981 if( (vol < 0.0f) || (vol > 100.0f) )
982 {
983 vg_warn( "Invalid volume (%s: %f)\n", sys->name, vol );
984 vol = 0.0f;
985 }
986
987 sys->info.vol = vol;
988 }
989
990 VG_STATIC float audio_player_get_vol( audio_player *sys )
991 {
992 audio_require_lock();
993 return sys->info.vol;
994 }
995
996 VG_STATIC void audio_player_set_pan( audio_player *sys, float pan )
997 {
998 audio_require_lock();
999 sys->info.pan = pan;
1000 }
1001
1002 VG_STATIC float audio_player_get_pan( audio_player *sys )
1003 {
1004 audio_require_lock();
1005 return sys->info.pan;
1006 }
1007
1008 VG_STATIC void audio_player_set_flags( audio_player *sys, u32 flags )
1009 {
1010 audio_require_lock();
1011 sys->info.flags = flags;
1012 }
1013
1014 VG_STATIC u32 audio_player_get_flags( audio_player *sys )
1015 {
1016 audio_require_lock();
1017 return sys->info.flags;
1018 }
1019
1020 VG_STATIC void audio_set_master_vol( float vol )
1021 {
1022 audio_require_lock();
1023 vg_audio.volume_target = vol;
1024 }
1025
1026 VG_STATIC void audio_push_console_vol(void)
1027 {
1028 audio_lock();
1029 audio_set_master_vol( vg_audio.volume_console );
1030 audio_unlock();
1031 }
1032
1033 /*
1034 * Debugging
1035 */
1036
1037 VG_STATIC void audio_debug_ui( m4x4f mtx_pv )
1038 {
1039 if( !vg_audio.debug_ui )
1040 return;
1041
1042 /* Get data */
1043 struct sound_info
1044 {
1045 const char *name;
1046 u32 cursor, flags, length;
1047 v3f pos;
1048 float vol;
1049 }
1050 infos[ SFX_MAX_SYSTEMS ];
1051 int num_systems = 0;
1052
1053 audio_lock();
1054
1055 for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
1056 {
1057 struct active_audio_player *aap = &vg_audio.active_players[i];
1058
1059 if( !aap->active )
1060 continue;
1061
1062 audio_entity *ent = &aap->ent;
1063 struct sound_info *snd = &infos[ num_systems ++ ];
1064
1065 snd->name = ent->name;
1066 snd->cursor = ent->cur;
1067 snd->flags = ent->info.flags;
1068 snd->length = ent->length;
1069 snd->vol = ent->info.vol*100.0f;
1070 v3_copy( ent->info.world_position, snd->pos );
1071 }
1072
1073 float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
1074 vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
1075 &vg_prof_audio_mix }, 2,
1076 budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
1077 250, 0 }, 3 );
1078
1079 audio_unlock();
1080
1081 char perf[128];
1082
1083 /* Draw UI */
1084 vg_uictx.cursor[0] = 258;
1085 vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12;
1086 vg_uictx.cursor[2] = 150;
1087 vg_uictx.cursor[3] = 12;
1088
1089 float mb1 = 1024.0f*1024.0f,
1090 usage = vg_linear_get_cur( vg_audio.audio_pool ) / mb1,
1091 total = vg_linear_get_capacity( vg_audio.audio_pool ) / mb1,
1092 percent = (usage/total) * 100.0f;
1093
1094 snprintf( perf, 127, "Mem: %.1f/%.1fmb (%.1f%%)\n", usage, total, percent );
1095
1096 ui_text( vg_uictx.cursor, perf, 1, 0 );
1097 vg_uictx.cursor[1] += 20;
1098
1099 ui_rect overlap_buffer[ SFX_MAX_SYSTEMS ];
1100 u32 overlap_length = 0;
1101
1102 /* Draw audio stack */
1103 for( int i=0; i<num_systems; i ++ )
1104 {
1105 struct sound_info *inf = &infos[i];
1106
1107 vg_uictx.cursor[2] = 200;
1108 vg_uictx.cursor[3] = 18;
1109
1110 u32 alpha = 0xa0000000;
1111
1112 ui_new_node();
1113 {
1114 ui_fill_rect( vg_uictx.cursor, 0x00333333|alpha );
1115
1116 ui_px baseline = vg_uictx.cursor[0],
1117 w = 200,
1118 c = baseline + ((float)inf->cursor / (float)inf->length) * w;
1119
1120 /* cursor */
1121 vg_uictx.cursor[2] = 2;
1122 vg_uictx.cursor[0] = c;
1123 ui_fill_rect( vg_uictx.cursor, 0xffffffff );
1124
1125 vg_uictx.cursor[0] = baseline + 2;
1126 vg_uictx.cursor[1] += 2;
1127 snprintf( perf, 127, "%s %.1f%%", infos[i].name, infos[i].vol );
1128 ui_text( vg_uictx.cursor, perf, 1, 0 );
1129
1130 if( inf->flags & AUDIO_FLAG_SPACIAL_3D )
1131 {
1132 v4f wpos;
1133 v3_copy( inf->pos, wpos );
1134 wpos[3] = 1.0f;
1135 m4x4_mulv( mtx_pv, wpos, wpos );
1136
1137 if( wpos[3] <= 0.0f )
1138 goto projected_behind;
1139
1140 v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos );
1141 v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
1142
1143 ui_rect wr;
1144 wr[0] = wpos[0] * vg.window_x;
1145 wr[1] = (1.0f-wpos[1]) * vg.window_y;
1146 wr[2] = 100;
1147 wr[3] = 17;
1148
1149 for( int j=0; j<12; j++ )
1150 {
1151 int collide = 0;
1152 for( int k=0; k<overlap_length; k++ )
1153 {
1154 ui_px *wk = overlap_buffer[k];
1155 if( ((wr[0] <= wk[0]+wk[2]) && (wr[0]+wr[2] >= wk[0])) &&
1156 ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) )
1157 {
1158 collide = 1;
1159 break;
1160 }
1161 }
1162
1163 if( !collide )
1164 break;
1165 else
1166 wr[1] += 18;
1167 }
1168
1169 ui_text( wr, perf, 1, 0 );
1170
1171 ui_rect_copy( wr, overlap_buffer[ overlap_length ++ ] );
1172 }
1173 }
1174
1175 projected_behind:
1176
1177 ui_end_down();
1178 vg_uictx.cursor[1] += 1;
1179 }
1180 }
1181
1182 #endif /* VG_AUDIO_H */