switching output audio devices
[vg.git] / vg_audio.h
1 /* Copyright (C) 2021-2023 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_console.h"
14 #include "vg/vg_store.h"
15 #include "vg/vg_profiler.h"
16 #include "vg/vg_audio_synth_bird.h"
17
18 #ifdef __GNUC__
19 #ifndef __clang__
20 #pragma GCC push_options
21 #pragma GCC optimize ("O3")
22 #pragma GCC diagnostic push
23 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
24 #endif
25 #endif
26
27 #define STB_VORBIS_MAX_CHANNELS 2
28 #include "submodules/stb/stb_vorbis.c"
29 #undef L
30 #undef R
31 #undef C
32
33 #ifdef __GNUC__
34 #ifndef __clang__
35 #pragma GCC pop_options
36 #pragma GCC diagnostic pop
37 #endif
38 #endif
39
40 #define AUDIO_FRAME_SIZE 512
41 #define AUDIO_MIX_FRAME_SIZE 256
42
43 #define AUDIO_CHANNELS 32
44 #define AUDIO_LFOS 8
45 #define AUDIO_FILTERS 16
46 #define AUDIO_FLAG_LOOP 0x1
47 #define AUDIO_FLAG_NO_DOPPLER 0x2
48 #define AUDIO_FLAG_SPACIAL_3D 0x4
49 #define AUDIO_FLAG_AUTO_START 0x8
50 #define AUDIO_FLAG_FORMAT 0x1E00
51
52 enum audio_format
53 {
54 k_audio_format_mono = 0x000u,
55 k_audio_format_stereo = 0x200u,
56 k_audio_format_vorbis = 0x400u,
57 k_audio_format_none0 = 0x600u,
58 k_audio_format_none1 = 0x800u,
59 k_audio_format_none2 = 0xA00u,
60 k_audio_format_none3 = 0xC00u,
61 k_audio_format_none4 = 0xE00u,
62
63 k_audio_format_bird = 0x1000u,
64 k_audio_format_gen = 0x1200u,
65 k_audio_format_none6 = 0x1400u,
66 k_audio_format_none7 = 0x1600u,
67 k_audio_format_none8 = 0x1800u,
68 k_audio_format_none9 = 0x1A00u,
69 k_audio_format_none10 = 0x1C00u,
70 k_audio_format_none11 = 0x1E00u,
71 };
72
73 #define AUDIO_DECODE_SIZE (1024*256) /* 256 kb decoding buffers */
74 #define AUDIO_MUTE_VOLUME 0.0f
75 #define AUDIO_BASE_VOLUME 1.0f
76
77 typedef struct audio_clip audio_clip;
78 typedef struct audio_channel audio_channel;
79 typedef struct audio_lfo audio_lfo;
80
81 struct audio_clip{
82 union { /* TODO oof.. */
83 u64 _p64_;
84 const char *path;
85 void *func;
86 };
87
88 u32 flags;
89 u32 size;
90
91 union{
92 u64 _p64;
93 void *data;
94 };
95 };
96
97 struct vg_audio_system{
98 SDL_AudioDeviceID sdl_output_device;
99 char *force_device_name; /* NULL: using default */
100
101 void *audio_pool,
102 *decode_buffer;
103 u32 samples_last;
104
105 /* synchro */
106 int sync_locked;
107
108 SDL_SpinLock sl_checker,
109 sl_sync;
110
111 struct audio_lfo{
112 u32 time, time_startframe;
113 float sqrt_polynomial_coefficient;
114
115 struct{
116 enum lfo_wave_type{
117 k_lfo_triangle,
118 k_lfo_square,
119 k_lfo_saw,
120 k_lfo_polynomial_bipolar
121 }
122 wave_type;
123
124 u32 period;
125 float polynomial_coefficient;
126 }
127 _, editable_state;
128 u32 editble_state_write_mask;
129 }
130 oscillators[ AUDIO_LFOS ];
131
132 struct audio_channel{
133 int allocated;
134 u16 group;
135 u8 world_id;
136
137 char name[32]; /* only editable while allocated == 0 */
138 audio_clip *source; /* ... */
139 u32 flags; /* ... */
140 u32 colour; /* ... */
141
142 /* internal non-readable state
143 * -----------------------------*/
144 u32 cursor, source_length;
145
146 float volume_movement_start,
147 pan_movement_start;
148
149 u32 volume_movement,
150 pan_movement;
151
152 union{
153 struct synth_bird *bird_handle;
154 stb_vorbis *vorbis_handle;
155 };
156
157 stb_vorbis_alloc vorbis_alloc;
158
159 enum channel_activity{
160 k_channel_activity_reset, /* will advance if allocated==1, to wake */
161 k_channel_activity_wake, /* will advance to either of next two */
162 k_channel_activity_alive,
163 k_channel_activity_end,
164 k_channel_activity_error
165 }
166 activity,
167 readable_activity;
168
169 /*
170 * editable structure, can be modified inside _lock and _unlock
171 * the edit mask tells which to copy into internal _, or to discard
172 * ----------------------------------------------------------------------
173 */
174 struct channel_state{
175 int relinquished;
176
177 float volume, /* current volume */
178 volume_target, /* target volume */
179 pan,
180 pan_target,
181 sampling_rate;
182
183 u32 volume_rate,
184 pan_rate;
185
186 v4f spacial_falloff; /* xyz, range */
187
188 audio_lfo *lfo;
189 float lfo_amount;
190 }
191 _, editable_state;
192 u32 editble_state_write_mask;
193 }
194 channels[ AUDIO_CHANNELS ];
195
196 int debug_ui, debug_ui_3d, debug_dsp;
197
198 v3f internal_listener_pos,
199 internal_listener_ears,
200 internal_listener_velocity,
201
202 external_listener_pos,
203 external_listener_ears,
204 external_lister_velocity;
205
206 float internal_global_volume,
207 external_global_volume;
208 }
209 static vg_audio = { .external_global_volume = 1.0f };
210
211 #include "vg/vg_audio_dsp.h"
212
213 static struct vg_profile
214 _vg_prof_audio_decode = {.mode = k_profile_mode_accum,
215 .name = "[T2] audio_decode()"},
216 _vg_prof_audio_mix = {.mode = k_profile_mode_accum,
217 .name = "[T2] audio_mix()"},
218 _vg_prof_dsp = {.mode = k_profile_mode_accum,
219 .name = "[T2] dsp_process()"},
220 vg_prof_audio_decode,
221 vg_prof_audio_mix,
222 vg_prof_audio_dsp;
223
224 /*
225 * These functions are called from the main thread and used to prevent bad
226 * access. TODO: They should be no-ops in release builds.
227 */
228 static int audio_lock_checker_load(void)
229 {
230 int value;
231 SDL_AtomicLock( &vg_audio.sl_checker );
232 value = vg_audio.sync_locked;
233 SDL_AtomicUnlock( &vg_audio.sl_checker );
234 return value;
235 }
236
237 static void audio_lock_checker_store( int value )
238 {
239 SDL_AtomicLock( &vg_audio.sl_checker );
240 vg_audio.sync_locked = value;
241 SDL_AtomicUnlock( &vg_audio.sl_checker );
242 }
243
244 static void audio_require_lock(void)
245 {
246 if( audio_lock_checker_load() )
247 return;
248
249 vg_error( "Modifying sound effects systems requires locking\n" );
250 abort();
251 }
252
253 static void audio_lock(void)
254 {
255 SDL_AtomicLock( &vg_audio.sl_sync );
256 audio_lock_checker_store(1);
257 }
258
259 static void audio_unlock(void)
260 {
261 audio_lock_checker_store(0);
262 SDL_AtomicUnlock( &vg_audio.sl_sync );
263 }
264 static void audio_mixer_callback( void *user, u8 *stream, int frame_count );
265
266 static void vg_audio_device_init(void){
267 SDL_AudioSpec spec_desired, spec_got;
268 spec_desired.callback = audio_mixer_callback;
269 spec_desired.channels = 2;
270 spec_desired.format = AUDIO_F32;
271 spec_desired.freq = 44100;
272 spec_desired.padding = 0;
273 spec_desired.samples = AUDIO_FRAME_SIZE;
274 spec_desired.silence = 0;
275 spec_desired.size = 0;
276 spec_desired.userdata = NULL;
277
278 vg_audio.sdl_output_device =
279 SDL_OpenAudioDevice( vg_audio.force_device_name, 0,
280 &spec_desired, &spec_got,0 );
281
282 vg_info( "Start audio device (%u, F32, %u) @%s\n",
283 spec_desired.freq,
284 AUDIO_FRAME_SIZE,
285 vg_audio.force_device_name );
286
287 if( vg_audio.sdl_output_device ){
288 SDL_PauseAudioDevice( vg_audio.sdl_output_device, 0 );
289 vg_success( "Unpaused device %d.\n", vg_audio.sdl_output_device );
290 }
291 else{
292 vg_error(
293 "SDL_OpenAudioDevice failed. Your default audio device must support:\n"
294 " Frequency: 44100 hz\n"
295 " Buffer size: 512\n"
296 " Channels: 2\n"
297 " Format: s16 or f32\n" );
298 }
299 }
300
301
302 static void vg_audio_init(void){
303 vg_console_reg_var( "debug_audio", &vg_audio.debug_ui,
304 k_var_dtype_i32, VG_VAR_CHEAT );
305 vg_console_reg_var( "debug_dsp", &vg_audio.debug_dsp,
306 k_var_dtype_i32, VG_VAR_CHEAT );
307 vg_console_reg_var( "volume", &vg_audio.external_global_volume,
308 k_var_dtype_f32, VG_VAR_PERSISTENT );
309
310 /* allocate memory */
311 /* 32mb fixed */
312 vg_audio.audio_pool =
313 vg_create_linear_allocator( vg_mem.rtmemory, 1024*1024*32,
314 VG_MEMORY_SYSTEM );
315
316 /* fixed */
317 u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS;
318 vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size );
319
320 vg_dsp_init();
321 vg_audio_device_init();
322 }
323
324 static void vg_audio_free(void)
325 {
326 vg_dsp_free();
327 SDL_CloseAudioDevice( vg_audio.sdl_output_device );
328 }
329
330 /*
331 * thread 1
332 */
333
334 #define AUDIO_EDIT_VOLUME_SLOPE 0x1
335 #define AUDIO_EDIT_VOLUME 0x2
336 #define AUDIO_EDIT_LFO_PERIOD 0x4
337 #define AUDIO_EDIT_LFO_WAVE 0x8
338 #define AUDIO_EDIT_LFO_ATTACHMENT 0x10
339 #define AUDIO_EDIT_SPACIAL 0x20
340 #define AUDIO_EDIT_OWNERSHIP 0x40
341 #define AUDIO_EDIT_SAMPLING_RATE 0x80
342
343 static void audio_channel_init( audio_channel *ch, audio_clip *clip,
344 u32 flags ){
345 audio_require_lock();
346 ch->group = 0;
347 ch->world_id = 0;
348 ch->source = clip;
349 ch->flags = flags;
350 ch->colour = 0x00333333;
351
352 if( (ch->source->flags & AUDIO_FLAG_FORMAT) == k_audio_format_bird )
353 strcpy( ch->name, "[array]" );
354 else if( (ch->source->flags & AUDIO_FLAG_FORMAT) == k_audio_format_gen )
355 strcpy( ch->name, "[program]" );
356 else
357 vg_strncpy( clip->path, ch->name, 32, k_strncpy_always_add_null );
358
359 ch->allocated = 1;
360
361 ch->editable_state.relinquished = 0;
362 ch->editable_state.volume = 1.0f;
363 ch->editable_state.volume_target = 1.0f;
364 ch->editable_state.pan = 0.0f;
365 ch->editable_state.pan_target = 0.0f;
366 ch->editable_state.volume_rate = 0;
367 ch->editable_state.pan_rate = 0;
368 v4_copy((v4f){0.0f,0.0f,0.0f,1.0f},ch->editable_state.spacial_falloff);
369 ch->editable_state.lfo = NULL;
370 ch->editable_state.lfo_amount = 0.0f;
371 ch->editable_state.sampling_rate = 1.0f;
372 ch->editble_state_write_mask = 0x00;
373 }
374
375 static void audio_channel_group( audio_channel *ch, u16 group )
376 {
377 audio_require_lock();
378 ch->group = group;
379 ch->colour = (((u32)group * 29986577) & 0x00ffffff) | 0xff000000;
380 }
381
382 static void audio_channel_world( audio_channel *ch, u8 world_id )
383 {
384 audio_require_lock();
385 ch->world_id = world_id;
386 }
387
388 static audio_channel *audio_get_first_idle_channel(void)
389 {
390 audio_require_lock();
391 for( int i=0; i<AUDIO_CHANNELS; i++ ){
392 audio_channel *ch = &vg_audio.channels[i];
393
394 if( !ch->allocated ){
395 return ch;
396 }
397 }
398
399 return NULL;
400 }
401
402 static audio_channel *audio_get_group_idle_channel( u16 group, u32 max_count )
403 {
404 audio_require_lock();
405 u32 count = 0;
406 audio_channel *dest = NULL;
407
408 for( int i=0; i<AUDIO_CHANNELS; i++ ){
409 audio_channel *ch = &vg_audio.channels[i];
410
411 if( ch->allocated ){
412 if( ch->group == group ){
413 count ++;
414 }
415 }
416 else{
417 if( !dest )
418 dest = ch;
419 }
420 }
421
422 if( dest && (count < max_count) ){
423 return dest;
424 }
425
426 return NULL;
427 }
428
429 static audio_channel *audio_get_group_first_active_channel( u16 group )
430 {
431 audio_require_lock();
432 for( int i=0; i<AUDIO_CHANNELS; i++ ){
433 audio_channel *ch = &vg_audio.channels[i];
434 if( ch->allocated && (ch->group == group) )
435 return ch;
436 }
437 return NULL;
438 }
439
440 static int audio_channel_finished( audio_channel *ch )
441 {
442 audio_require_lock();
443 if( ch->readable_activity == k_channel_activity_end )
444 return 1;
445 else
446 return 0;
447 }
448
449 static audio_channel *audio_relinquish_channel( audio_channel *ch )
450 {
451 audio_require_lock();
452 ch->editable_state.relinquished = 1;
453 ch->editble_state_write_mask |= AUDIO_EDIT_OWNERSHIP;
454 return NULL;
455 }
456
457 static void audio_channel_slope_volume( audio_channel *ch, float length,
458 float new_volume )
459 {
460 audio_require_lock();
461 ch->editable_state.volume_target = new_volume;
462 ch->editable_state.volume_rate = length * 44100.0f;
463 ch->editble_state_write_mask |= AUDIO_EDIT_VOLUME_SLOPE;
464 }
465
466 static void audio_channel_set_sampling_rate( audio_channel *ch, float rate )
467 {
468 audio_require_lock();
469 ch->editable_state.sampling_rate = rate;
470 ch->editble_state_write_mask |= AUDIO_EDIT_SAMPLING_RATE;
471 }
472
473 static void audio_channel_edit_volume( audio_channel *ch,
474 float new_volume, int instant )
475 {
476 audio_require_lock();
477 if( instant ){
478 ch->editable_state.volume = new_volume;
479 ch->editble_state_write_mask |= AUDIO_EDIT_VOLUME;
480 }
481 else{
482 audio_channel_slope_volume( ch, 0.05f, new_volume );
483 }
484 }
485
486 static audio_channel *audio_channel_fadeout( audio_channel *ch, float length )
487 {
488 audio_require_lock();
489 audio_channel_slope_volume( ch, length, 0.0f );
490 return audio_relinquish_channel( ch );
491 }
492
493 static void audio_channel_fadein( audio_channel *ch, float length )
494 {
495 audio_require_lock();
496 audio_channel_edit_volume( ch, 0.0f, 1 );
497 audio_channel_slope_volume( ch, length, 1.0f );
498 }
499
500 static audio_channel *audio_channel_crossfade( audio_channel *ch,
501 audio_clip *new_clip,
502 float length, u32 flags )
503 {
504 audio_require_lock();
505 u32 cursor = 0;
506
507 if( ch )
508 ch = audio_channel_fadeout( ch, length );
509
510 audio_channel *replacement = audio_get_first_idle_channel();
511
512 if( replacement ){
513 audio_channel_init( replacement, new_clip, flags );
514 audio_channel_fadein( replacement, length );
515 }
516
517 return replacement;
518 }
519
520 static void audio_channel_sidechain_lfo( audio_channel *ch, int lfo_id,
521 float amount )
522 {
523 audio_require_lock();
524 ch->editable_state.lfo = &vg_audio.oscillators[ lfo_id ];
525 ch->editable_state.lfo_amount = amount;
526 ch->editble_state_write_mask |= AUDIO_EDIT_LFO_ATTACHMENT;
527 }
528
529 static void audio_channel_set_spacial( audio_channel *ch, v3f co, float range )
530 {
531 audio_require_lock();
532 if( ch->flags & AUDIO_FLAG_SPACIAL_3D ){
533 v3_copy( co, ch->editable_state.spacial_falloff );
534
535 if( range == 0.0f )
536 ch->editable_state.spacial_falloff[3] = 1.0f;
537 else
538 ch->editable_state.spacial_falloff[3] = 1.0f/range;
539
540 ch->editble_state_write_mask |= AUDIO_EDIT_SPACIAL;
541 }
542 else{
543 vg_warn( "Tried to set spacialization paramaters for 2D channel (%s)\n",
544 ch->name );
545 }
546 }
547
548 static int audio_oneshot_3d( audio_clip *clip, v3f position,
549 float range, float volume )
550 {
551 audio_require_lock();
552 audio_channel *ch = audio_get_first_idle_channel();
553
554 if( ch ){
555 audio_channel_init( ch, clip, AUDIO_FLAG_SPACIAL_3D );
556 audio_channel_set_spacial( ch, position, range );
557 audio_channel_edit_volume( ch, volume, 1 );
558 ch = audio_relinquish_channel( ch );
559
560 return 1;
561 }
562 else
563 return 0;
564 }
565
566 static int audio_oneshot( audio_clip *clip, float volume, float pan )
567 {
568 audio_require_lock();
569 audio_channel *ch = audio_get_first_idle_channel();
570
571 if( ch ){
572 audio_channel_init( ch, clip, 0x00 );
573 audio_channel_edit_volume( ch, volume, 1 );
574 ch = audio_relinquish_channel( ch );
575
576 return 1;
577 }
578 else
579 return 0;
580 }
581
582 static void audio_set_lfo_wave( int id, enum lfo_wave_type type,
583 float coefficient )
584 {
585 audio_require_lock();
586 audio_lfo *lfo = &vg_audio.oscillators[ id ];
587 lfo->editable_state.polynomial_coefficient = coefficient;
588 lfo->editable_state.wave_type = type;
589
590 lfo->editble_state_write_mask |= AUDIO_EDIT_LFO_WAVE;
591 }
592
593 static void audio_set_lfo_frequency( int id, float freq )
594 {
595 audio_require_lock();
596 audio_lfo *lfo = &vg_audio.oscillators[ id ];
597 lfo->editable_state.period = 44100.0f / freq;
598 lfo->editble_state_write_mask |= AUDIO_EDIT_LFO_PERIOD;
599 }
600
601
602 /*
603 * Committers
604 * -----------------------------------------------------------------------------
605 */
606 static int audio_channel_load_source( audio_channel *ch )
607 {
608 u32 format = ch->source->flags & AUDIO_FLAG_FORMAT;
609
610 if( format == k_audio_format_vorbis ){
611 /* Setup vorbis decoder */
612 u32 index = ch - vg_audio.channels;
613
614 u8 *buf = (u8*)vg_audio.decode_buffer,
615 *loc = &buf[AUDIO_DECODE_SIZE*index];
616
617 stb_vorbis_alloc alloc = {
618 .alloc_buffer = (char *)loc,
619 .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
620 };
621
622 int err;
623 stb_vorbis *decoder = stb_vorbis_open_memory(
624 ch->source->data,
625 ch->source->size, &err, &alloc );
626
627 if( !decoder ){
628 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
629 ch->source->path, err );
630 return 0;
631 }
632 else{
633 ch->source_length = stb_vorbis_stream_length_in_samples( decoder );
634 ch->vorbis_handle = decoder;
635 }
636 }
637 else if( format == k_audio_format_bird ){
638 u32 index = ch - vg_audio.channels;
639
640 u8 *buf = (u8*)vg_audio.decode_buffer;
641 struct synth_bird *loc = (void *)&buf[AUDIO_DECODE_SIZE*index];
642
643 memcpy( loc, ch->source->data, ch->source->size );
644 synth_bird_reset( loc );
645
646 ch->bird_handle = loc;
647 ch->source_length = synth_bird_get_length_in_samples( loc );
648 }
649 else if( format == k_audio_format_stereo ){
650 ch->source_length = ch->source->size / 2;
651 }
652 else if( format == k_audio_format_gen ){
653 ch->source_length = 0xffffffff;
654 }
655 else{
656 ch->source_length = ch->source->size;
657 }
658
659 return 1;
660 }
661
662 static void audio_decode_uncompressed_mono( i16 *src, u32 count, float *dst )
663 {
664 for( u32 i=0; i<count; i++ ){
665 dst[ i*2 + 0 ] = ((float)src[i]) * (1.0f/32767.0f);
666 dst[ i*2 + 1 ] = ((float)src[i]) * (1.0f/32767.0f);
667 }
668 }
669
670 /*
671 * adapted from stb_vorbis.h, since the original does not handle mono->stereo
672 */
673 static int
674 stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer,
675 int len )
676 {
677 int n = 0,
678 c = VG_MIN( 1, f->channels - 1 );
679
680 while( n < len ) {
681 int k = f->channel_buffer_end - f->channel_buffer_start;
682
683 if( n+k >= len )
684 k = len - n;
685
686 for( int j=0; j < k; ++j ) {
687 *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j];
688 *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j];
689 }
690
691 n += k;
692 f->channel_buffer_start += k;
693
694 if( n == len )
695 break;
696
697 if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
698 break;
699 }
700
701 return n;
702 }
703
704 /*
705 * ........ more wrecked code sorry!
706 */
707 static int
708 stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len )
709 {
710 int n = 0,
711 c = VG_MIN( 1, f->channels - 1 );
712
713 while( n < len ) {
714 int k = f->channel_buffer_end - f->channel_buffer_start;
715
716 if( n+k >= len )
717 k = len - n;
718
719 for( int j=0; j < k; ++j ) {
720 float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j],
721 sr = f->channel_buffers[ c ][f->channel_buffer_start+j];
722
723 *buffer++ = vg_clampf( 0.5f*(sl+sr), -1.0f, 1.0f ) * 32767.0f;
724 //*buffer++ = vg_clampf( sr, -1.0f, 1.0f ) * 32767.0f;
725 }
726
727 n += k;
728 f->channel_buffer_start += k;
729
730 if( n == len )
731 break;
732
733 if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
734 break;
735 }
736
737 return n;
738 }
739
740 static inline float audio_lfo_pull_sample( audio_lfo *lfo )
741 {
742 lfo->time ++;
743
744 if( lfo->time >= lfo->_.period )
745 lfo->time = 0;
746
747 float t = lfo->time;
748 t /= (float)lfo->_.period;
749
750 if( lfo->_.wave_type == k_lfo_polynomial_bipolar ){
751 /*
752 * #
753 * # #
754 * # #
755 * # #
756 * ### # ###
757 * ## #
758 * # #
759 * # #
760 * ##
761 */
762
763 t *= 2.0f;
764 t -= 1.0f;
765
766 return (( 2.0f * lfo->sqrt_polynomial_coefficient * t ) /
767 /* --------------------------------------- */
768 ( 1.0f + lfo->_.polynomial_coefficient * t*t )
769
770 ) * (1.0f-fabsf(t));
771 }
772 else{
773 return 0.0f;
774 }
775 }
776
777 static void audio_channel_get_samples( audio_channel *ch,
778 u32 count, float *buf )
779 {
780 vg_profile_begin( &_vg_prof_audio_decode );
781
782 u32 remaining = count;
783 u32 buffer_pos = 0;
784
785 u32 format = ch->source->flags & AUDIO_FLAG_FORMAT;
786
787 while( remaining ){
788 u32 samples_this_run = VG_MIN(remaining, ch->source_length - ch->cursor);
789 remaining -= samples_this_run;
790
791 float *dst = &buf[ buffer_pos * 2 ];
792
793 if( format == k_audio_format_stereo ){
794 for( int i=0;i<samples_this_run; i++ ){
795 dst[i*2+0] = 0.0f;
796 dst[i*2+1] = 0.0f;
797 }
798 }
799 else if( format == k_audio_format_vorbis ){
800 int read_samples = stb_vorbis_get_samples_float_interleaved_stereo(
801 ch->vorbis_handle,
802 dst,
803 samples_this_run );
804
805 if( read_samples != samples_this_run ){
806 vg_warn( "Invalid samples read (%s)\n", ch->source->path );
807
808 for( int i=0; i<samples_this_run; i++ ){
809 dst[i*2+0] = 0.0f;
810 dst[i*2+1] = 0.0f;
811 }
812 }
813 }
814 else if( format == k_audio_format_bird ){
815 synth_bird_generate_samples( ch->bird_handle, dst, samples_this_run );
816 }
817 else if( format == k_audio_format_gen ){
818 void (*fn)( void *data, f32 *buf, u32 count ) = ch->source->func;
819 fn( ch->source->data, dst, samples_this_run );
820 }
821 else{
822 i16 *src_buffer = ch->source->data,
823 *src = &src_buffer[ch->cursor];
824
825 audio_decode_uncompressed_mono( src, samples_this_run, dst );
826 }
827
828 ch->cursor += samples_this_run;
829 buffer_pos += samples_this_run;
830
831 if( (ch->flags & AUDIO_FLAG_LOOP) && remaining ){
832 if( format == k_audio_format_vorbis )
833 stb_vorbis_seek_start( ch->vorbis_handle );
834 else if( format == k_audio_format_bird )
835 synth_bird_reset( ch->bird_handle );
836
837 ch->cursor = 0;
838 continue;
839 }
840 else
841 break;
842 }
843
844 while( remaining ){
845 buf[ buffer_pos*2 + 0 ] = 0.0f;
846 buf[ buffer_pos*2 + 1 ] = 0.0f;
847 buffer_pos ++;
848
849 remaining --;
850 }
851
852 vg_profile_end( &_vg_prof_audio_decode );
853 }
854
855 static void audio_channel_mix( audio_channel *ch, float *buffer )
856 {
857 float framevol_l = vg_audio.internal_global_volume,
858 framevol_r = vg_audio.internal_global_volume;
859
860 float frame_samplerate = ch->_.sampling_rate;
861
862 if( ch->flags & AUDIO_FLAG_SPACIAL_3D ){
863 v3f delta;
864 v3_sub( ch->_.spacial_falloff, vg_audio.internal_listener_pos, delta );
865
866 float dist = v3_length( delta ),
867 vol = vg_maxf( 0.0f, 1.0f - ch->_.spacial_falloff[3]*dist );
868
869 if( dist <= 0.01f ){
870
871 }
872 else{
873 v3_muls( delta, 1.0f/dist, delta );
874 float pan = v3_dot( vg_audio.internal_listener_ears, delta );
875 vol = powf( vol, 5.0f );
876
877 framevol_l *= (vol * 0.5f) * (1.0f - pan);
878 framevol_r *= (vol * 0.5f) * (1.0f + pan);
879
880 if( !(ch->source->flags & AUDIO_FLAG_NO_DOPPLER) ){
881 const float vs = 323.0f;
882
883 float dv = v3_dot(delta,vg_audio.internal_listener_velocity);
884 float doppler = (vs+dv)/vs;
885 doppler = vg_clampf( doppler, 0.6f, 1.4f );
886
887 if( fabsf(doppler-1.0f) > 0.01f )
888 frame_samplerate *= doppler;
889 }
890 }
891
892 if( !vg_validf( framevol_l ) ||
893 !vg_validf( framevol_r ) ||
894 !vg_validf( frame_samplerate ) ){
895 vg_fatal_error( "Invalid sampling conditions.\n"
896 "This crash is to protect your ears.\n"
897 " channel: %p (%s)\n"
898 " sample_rate: %f\n"
899 " volume: L%f R%f\n"
900 " listener: %.2f %.2f %.2f [%.2f %.2f %.2f]\n",
901 ch, ch->name, frame_samplerate,
902 framevol_l, framevol_r,
903 vg_audio.internal_listener_pos[0],
904 vg_audio.internal_listener_pos[1],
905 vg_audio.internal_listener_pos[2],
906 vg_audio.internal_listener_ears[0],
907 vg_audio.internal_listener_ears[1],
908 vg_audio.internal_listener_ears[2]
909 );
910 }
911 }
912
913 u32 buffer_length = AUDIO_MIX_FRAME_SIZE;
914 if( frame_samplerate != 1.0f ){
915 float l = ceilf( (float)(AUDIO_MIX_FRAME_SIZE) * frame_samplerate );
916 buffer_length = l+1;
917 }
918
919 float pcf[ AUDIO_MIX_FRAME_SIZE * 2 * 2 ];
920
921 audio_channel_get_samples( ch, buffer_length, pcf );
922
923 vg_profile_begin( &_vg_prof_audio_mix );
924
925 float volume_movement = ch->volume_movement;
926 float const fvolume_rate = vg_maxf( 1.0f, ch->_.volume_rate );
927 const float inv_volume_rate = 1.0f/fvolume_rate;
928
929 float volume = ch->_.volume;
930 const float volume_start = ch->volume_movement_start;
931 const float volume_target = ch->_.volume_target;
932
933 for( u32 j=0; j<AUDIO_MIX_FRAME_SIZE; j++ ){
934 volume_movement += 1.0f;
935 float movement_t = volume_movement * inv_volume_rate;
936 movement_t = vg_minf( movement_t, 1.0f );
937 volume = vg_lerpf( volume_start, volume_target, movement_t );
938
939 float vol_norm = volume * volume;
940
941 if( ch->_.lfo )
942 vol_norm *= 1.0f + audio_lfo_pull_sample(ch->_.lfo) * ch->_.lfo_amount;
943
944 float vol_l = vol_norm * framevol_l,
945 vol_r = vol_norm * framevol_r,
946 sample_l,
947 sample_r;
948
949 if( frame_samplerate != 1.0f ){
950 /* absolutely garbage resampling, but it will do
951 */
952
953 float sample_index = frame_samplerate * (float)j;
954 float t = vg_fractf( sample_index );
955
956 u32 i0 = floorf( sample_index ),
957 i1 = i0+1;
958
959 sample_l = pcf[ i0*2+0 ]*(1.0f-t) + pcf[ i1*2+0 ]*t;
960 sample_r = pcf[ i0*2+1 ]*(1.0f-t) + pcf[ i1*2+1 ]*t;
961 }
962 else{
963 sample_l = pcf[ j*2+0 ];
964 sample_r = pcf[ j*2+1 ];
965 }
966
967 buffer[ j*2+0 ] += sample_l * vol_l;
968 buffer[ j*2+1 ] += sample_r * vol_r;
969 }
970
971 ch->volume_movement += AUDIO_MIX_FRAME_SIZE;
972 ch->volume_movement = VG_MIN( ch->volume_movement, ch->_.volume_rate );
973 ch->_.volume = volume;
974
975 vg_profile_end( &_vg_prof_audio_mix );
976 }
977
978 static void audio_mixer_callback( void *user, u8 *stream, int byte_count )
979 {
980 /*
981 * Copy data and move edit flags to commit flags
982 * ------------------------------------------------------------- */
983 audio_lock();
984
985 v3_copy( vg_audio.external_listener_pos, vg_audio.internal_listener_pos );
986 v3_copy( vg_audio.external_listener_ears, vg_audio.internal_listener_ears );
987 v3_copy( vg_audio.external_lister_velocity,
988 vg_audio.internal_listener_velocity );
989 vg_audio.internal_global_volume = vg_audio.external_global_volume;
990
991 for( int i=0; i<AUDIO_CHANNELS; i++ ){
992 audio_channel *ch = &vg_audio.channels[i];
993
994 if( !ch->allocated )
995 continue;
996
997 if( ch->activity == k_channel_activity_alive ){
998 if( (ch->cursor >= ch->source_length) &&
999 !(ch->flags & AUDIO_FLAG_LOOP) )
1000 {
1001 ch->activity = k_channel_activity_end;
1002 }
1003 }
1004
1005 /* process relinquishments */
1006 if( (ch->activity != k_channel_activity_reset) && ch->_.relinquished ){
1007 if( (ch->activity == k_channel_activity_end)
1008 || (ch->_.volume == 0.0f)
1009 || (ch->activity == k_channel_activity_error) )
1010 {
1011 ch->_.relinquished = 0;
1012 ch->allocated = 0;
1013 ch->activity = k_channel_activity_reset;
1014 continue;
1015 }
1016 }
1017
1018 /* process new channels */
1019 if( ch->activity == k_channel_activity_reset ){
1020 ch->_ = ch->editable_state;
1021 ch->cursor = 0;
1022 ch->source_length = 0;
1023 ch->activity = k_channel_activity_wake;
1024 }
1025
1026 if( ch->editble_state_write_mask & AUDIO_EDIT_OWNERSHIP )
1027 ch->_.relinquished = ch->editable_state.relinquished;
1028 else
1029 ch->editable_state.relinquished = ch->_.relinquished;
1030
1031
1032 if( ch->editble_state_write_mask & AUDIO_EDIT_VOLUME ){
1033 ch->_.volume = ch->editable_state.volume;
1034 ch->_.volume_target = ch->editable_state.volume;
1035 }
1036 else{
1037 ch->editable_state.volume = ch->_.volume;
1038 }
1039
1040
1041 if( ch->editble_state_write_mask & AUDIO_EDIT_VOLUME_SLOPE ){
1042 ch->volume_movement_start = ch->_.volume;
1043 ch->volume_movement = 0;
1044
1045 ch->_.volume_target = ch->editable_state.volume_target;
1046 ch->_.volume_rate = ch->editable_state.volume_rate;
1047 }
1048 else{
1049 ch->editable_state.volume_target = ch->_.volume_target;
1050 ch->editable_state.volume_rate = ch->_.volume_rate;
1051 }
1052
1053
1054 if( ch->editble_state_write_mask & AUDIO_EDIT_SAMPLING_RATE )
1055 ch->_.sampling_rate = ch->editable_state.sampling_rate;
1056 else
1057 ch->editable_state.sampling_rate = ch->_.sampling_rate;
1058
1059
1060 if( ch->editble_state_write_mask & AUDIO_EDIT_LFO_ATTACHMENT ){
1061 ch->_.lfo = ch->editable_state.lfo;
1062 ch->_.lfo_amount = ch->editable_state.lfo_amount;
1063 }
1064 else{
1065 ch->editable_state.lfo = ch->_.lfo;
1066 ch->editable_state.lfo_amount = ch->_.lfo_amount;
1067 }
1068
1069
1070 if( ch->editble_state_write_mask & AUDIO_EDIT_SPACIAL )
1071 v4_copy( ch->editable_state.spacial_falloff,ch->_.spacial_falloff );
1072 else
1073 v4_copy( ch->_.spacial_falloff,ch->editable_state.spacial_falloff );
1074
1075
1076 /* currently readonly, i guess */
1077 ch->editable_state.pan_target = ch->_.pan_target;
1078 ch->editable_state.pan = ch->_.pan;
1079 ch->editble_state_write_mask = 0x00;
1080 }
1081
1082 for( int i=0; i<AUDIO_LFOS; i++ ){
1083 audio_lfo *lfo = &vg_audio.oscillators[ i ];
1084
1085 if( lfo->editble_state_write_mask & AUDIO_EDIT_LFO_WAVE ){
1086 lfo->_.wave_type = lfo->editable_state.wave_type;
1087
1088 if( lfo->_.wave_type == k_lfo_polynomial_bipolar ){
1089 lfo->_.polynomial_coefficient =
1090 lfo->editable_state.polynomial_coefficient;
1091 lfo->sqrt_polynomial_coefficient =
1092 sqrtf(lfo->_.polynomial_coefficient);
1093 }
1094 }
1095
1096 if( lfo->editble_state_write_mask & AUDIO_EDIT_LFO_PERIOD ){
1097 if( lfo->_.period ){
1098 float t = lfo->time;
1099 t/= (float)lfo->_.period;
1100
1101 lfo->_.period = lfo->editable_state.period;
1102 lfo->time = lfo->_.period * t;
1103 }
1104 else{
1105 lfo->time = 0;
1106 lfo->_.period = lfo->editable_state.period;
1107 }
1108 }
1109
1110 lfo->editble_state_write_mask = 0x00;
1111 }
1112
1113 dsp_update_tunings();
1114 audio_unlock();
1115
1116 /*
1117 * Process spawns
1118 * ------------------------------------------------------------- */
1119 for( int i=0; i<AUDIO_CHANNELS; i++ ){
1120 audio_channel *ch = &vg_audio.channels[i];
1121
1122 if( ch->activity == k_channel_activity_wake ){
1123 if( audio_channel_load_source( ch ) )
1124 ch->activity = k_channel_activity_alive;
1125 else
1126 ch->activity = k_channel_activity_error;
1127 }
1128 }
1129
1130 /*
1131 * Mix everything
1132 * -------------------------------------------------------- */
1133 int frame_count = byte_count/(2*sizeof(float));
1134
1135 /* Clear buffer */
1136 float *pOut32F = (float *)stream;
1137 for( int i=0; i<frame_count*2; i ++ )
1138 pOut32F[i] = 0.0f;
1139
1140 for( int i=0; i<AUDIO_LFOS; i++ ){
1141 audio_lfo *lfo = &vg_audio.oscillators[i];
1142 lfo->time_startframe = lfo->time;
1143 }
1144
1145 for( int i=0; i<AUDIO_CHANNELS; i ++ ){
1146 audio_channel *ch = &vg_audio.channels[i];
1147
1148 if( ch->activity == k_channel_activity_alive ){
1149 if( ch->_.lfo )
1150 ch->_.lfo->time = ch->_.lfo->time_startframe;
1151
1152 u32 remaining = frame_count,
1153 subpos = 0;
1154
1155 while( remaining ){
1156 audio_channel_mix( ch, pOut32F+subpos );
1157 remaining -= AUDIO_MIX_FRAME_SIZE;
1158 subpos += AUDIO_MIX_FRAME_SIZE*2;
1159 }
1160 }
1161 }
1162
1163 vg_profile_begin( &_vg_prof_dsp );
1164
1165 for( int i=0; i<frame_count; i++ )
1166 vg_dsp_process( pOut32F + i*2, pOut32F + i*2 );
1167
1168 vg_profile_end( &_vg_prof_dsp );
1169
1170 audio_lock();
1171
1172 for( int i=0; i<AUDIO_CHANNELS; i ++ ){
1173 audio_channel *ch = &vg_audio.channels[i];
1174 ch->readable_activity = ch->activity;
1175 }
1176
1177 /* Profiling information
1178 * ----------------------------------------------- */
1179 vg_profile_increment( &_vg_prof_audio_decode );
1180 vg_profile_increment( &_vg_prof_audio_mix );
1181 vg_profile_increment( &_vg_prof_dsp );
1182
1183 vg_prof_audio_mix = _vg_prof_audio_mix;
1184 vg_prof_audio_decode = _vg_prof_audio_decode;
1185 vg_prof_audio_dsp = _vg_prof_dsp;
1186
1187 vg_audio.samples_last = frame_count;
1188
1189 if( vg_audio.debug_dsp ){
1190 vg_dsp_update_texture();
1191 }
1192
1193 audio_unlock();
1194 }
1195
1196 static void audio_clip_load( audio_clip *clip, void *lin_alloc )
1197 {
1198 if( lin_alloc == NULL )
1199 lin_alloc = vg_audio.audio_pool;
1200
1201 #ifdef VG_AUDIO_FORCE_COMPRESSED
1202
1203 if( (clip->flags & AUDIO_FLAG_FORMAT) != k_audio_format_bird ){
1204 clip->flags &= ~AUDIO_FLAG_FORMAT;
1205 clip->flags |= k_audio_format_vorbis;
1206 }
1207
1208 #endif
1209
1210 /* load in directly */
1211 u32 format = clip->flags & AUDIO_FLAG_FORMAT;
1212
1213 /* TODO: This contains audio_lock() and unlock, but i don't know why
1214 * can probably remove them. Low priority to check this */
1215
1216 /* TODO: packed files for vorbis etc, should take from data if its not not
1217 * NULL when we get the clip
1218 */
1219
1220 if( format == k_audio_format_vorbis ){
1221 if( !clip->path ){
1222 vg_fatal_error( "No path specified, embeded vorbis unsupported" );
1223 }
1224
1225 audio_lock();
1226 clip->data = vg_file_read( lin_alloc, clip->path, &clip->size );
1227 audio_unlock();
1228
1229 if( !clip->data )
1230 vg_fatal_error( "Audio failed to load" );
1231
1232 float mb = (float)(clip->size) / (1024.0f*1024.0f);
1233 vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
1234 }
1235 else if( format == k_audio_format_stereo ){
1236 vg_fatal_error( "Unsupported format (Stereo uncompressed)" );
1237 }
1238 else if( format == k_audio_format_bird ){
1239 if( !clip->data ){
1240 vg_fatal_error( "No data, external birdsynth unsupported" );
1241 }
1242
1243 u32 total_size = clip->size + sizeof(struct synth_bird);
1244 total_size -= sizeof(struct synth_bird_settings);
1245 total_size = vg_align8( total_size );
1246
1247 if( total_size > AUDIO_DECODE_SIZE )
1248 vg_fatal_error( "Bird coding too long\n" );
1249
1250 struct synth_bird *bird = vg_linear_alloc( lin_alloc, total_size );
1251 memcpy( &bird->settings, clip->data, clip->size );
1252
1253 clip->data = bird;
1254 clip->size = total_size;
1255
1256 vg_info( "Loaded bird synthesis pattern (%u bytes)\n", total_size );
1257 }
1258 else{
1259 if( !clip->path ){
1260 vg_fatal_error( "No path specified, embeded mono unsupported" );
1261 }
1262
1263 vg_linear_clear( vg_mem.scratch );
1264 u32 fsize;
1265
1266 stb_vorbis_alloc alloc = {
1267 .alloc_buffer = vg_linear_alloc( vg_mem.scratch, AUDIO_DECODE_SIZE ),
1268 .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
1269 };
1270
1271 void *filedata = vg_file_read( vg_mem.scratch, clip->path, &fsize );
1272
1273 int err;
1274 stb_vorbis *decoder = stb_vorbis_open_memory(
1275 filedata, fsize, &err, &alloc );
1276
1277 if( !decoder ){
1278 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
1279 clip->path, err );
1280 vg_fatal_error( "Vorbis decode error" );
1281 }
1282
1283 /* only mono is supported in uncompressed */
1284 u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ),
1285 data_size = length_samples * sizeof(i16);
1286
1287 audio_lock();
1288 clip->data = vg_linear_alloc( lin_alloc, vg_align8(data_size) );
1289 clip->size = length_samples;
1290 audio_unlock();
1291
1292 int read_samples = stb_vorbis_get_samples_i16_downmixed(
1293 decoder, clip->data, length_samples );
1294
1295 if( read_samples != length_samples )
1296 vg_fatal_error( "Decode error" );
1297
1298 #if 0
1299 float mb = (float)(data_size) / (1024.0f*1024.0f);
1300 vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb,
1301 length_samples );
1302 #endif
1303 }
1304 }
1305
1306 static void audio_clip_loadn( audio_clip *arr, int count, void *lin_alloc )
1307 {
1308 for( int i=0; i<count; i++ )
1309 audio_clip_load( &arr[i], lin_alloc );
1310 }
1311
1312 static void audio_require_clip_loaded( audio_clip *clip )
1313 {
1314 if( clip->data && clip->size )
1315 return;
1316
1317 audio_unlock();
1318 vg_fatal_error( "Must load audio clip before playing! \n" );
1319 }
1320
1321 /*
1322 * Debugging
1323 */
1324
1325 static void audio_debug_ui(
1326
1327 #ifdef VG_3D
1328 m4x4f
1329 #else
1330 m3x3f
1331 #endif
1332 mtx_pv ){
1333
1334 if( !vg_audio.debug_ui )
1335 return;
1336
1337 audio_lock();
1338
1339 glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture );
1340 glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, 256, 256,
1341 GL_RGBA, GL_UNSIGNED_BYTE,
1342 vg_dsp.view_texture_buffer );
1343
1344 /*
1345 * Profiler
1346 * -----------------------------------------------------------------------
1347 */
1348
1349 float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
1350 vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
1351 &vg_prof_audio_mix,
1352 &vg_prof_audio_dsp}, 3,
1353 budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
1354 512, 0 }, 3, 0 );
1355
1356
1357 char perf[128];
1358
1359 /* Draw UI */
1360 ui_rect window = {
1361 0,
1362 0,
1363 800,
1364 AUDIO_CHANNELS * 18
1365 };
1366
1367 if( vg_audio.debug_dsp ){
1368 ui_rect view_thing = { 4, vg.window_y-512-4, 512, 512 };
1369 ui_image( view_thing, vg_dsp.view_texture );
1370 }
1371
1372 ui_rect overlap_buffer[ AUDIO_CHANNELS ];
1373 u32 overlap_length = 0;
1374
1375 /* Draw audio stack */
1376 for( int i=0; i<AUDIO_CHANNELS; i ++ ){
1377 audio_channel *ch = &vg_audio.channels[i];
1378
1379 ui_rect row;
1380 ui_split( window, k_ui_axis_h, 18, 1, row, window );
1381
1382 if( !ch->allocated ){
1383 ui_fill( row, 0x50333333 );
1384 continue;
1385 }
1386
1387 const char *formats[] =
1388 {
1389 " mono ",
1390 " stereo ",
1391 " vorbis ",
1392 " none0 ",
1393 " none1 ",
1394 " none2 ",
1395 " none3 ",
1396 " none4 ",
1397 "synth:bird",
1398 " none5 ",
1399 " none6 ",
1400 " none7 ",
1401 " none8 ",
1402 " none9 ",
1403 " none10 ",
1404 " none11 ",
1405 };
1406
1407 const char *activties[] =
1408 {
1409 "reset",
1410 "wake ",
1411 "alive",
1412 "end ",
1413 "error"
1414 };
1415
1416 u32 format_index = (ch->source->flags & AUDIO_FLAG_FORMAT)>>9;
1417
1418 snprintf( perf, 127, "%02d[%#04x.%#06x]%c%c%cD %s [%s] %4.2fv'%s'",
1419 i,
1420 ch->world_id, ch->group,
1421 (ch->editable_state.relinquished)? 'r': '_',
1422 0? 'r': '_',
1423 0? '3': '2',
1424 formats[format_index],
1425 activties[ch->readable_activity],
1426 ch->editable_state.volume,
1427 ch->name );
1428
1429 ui_fill( row, 0xa0000000 | ch->colour );
1430 ui_text( row, perf, 1, k_ui_align_middle_left, 0 );
1431
1432 #ifdef VG_3D
1433 if( AUDIO_FLAG_SPACIAL_3D ){
1434 v4f wpos;
1435 v3_copy( ch->editable_state.spacial_falloff, wpos );
1436
1437 wpos[3] = 1.0f;
1438 m4x4_mulv( mtx_pv, wpos, wpos );
1439
1440 if( wpos[3] > 0.0f ){
1441 v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos );
1442 v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
1443
1444 ui_rect wr;
1445 wr[0] = vg_clampf(wpos[0] * vg.window_x, -32000.0f,32000.0f);
1446 wr[1] = vg_clampf((1.0f-wpos[1]) * vg.window_y,-32000.0f,32000.0f);
1447 wr[2] = 1000;
1448 wr[3] = 17;
1449
1450 for( int j=0; j<12; j++ ){
1451 int collide = 0;
1452 for( int k=0; k<overlap_length; k++ ){
1453 ui_px *wk = overlap_buffer[k];
1454 if( ((wr[0] <= wk[0]+wk[2]) && (wr[0]+wr[2] >= wk[0])) &&
1455 ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) )
1456 {
1457 collide = 1;
1458 break;
1459 }
1460 }
1461
1462 if( !collide )
1463 break;
1464 else
1465 wr[1] += 18;
1466 }
1467
1468 ui_text( wr, perf, 1, k_ui_align_middle_left, 0 );
1469 rect_copy( wr, overlap_buffer[ overlap_length ++ ] );
1470 }
1471 }
1472 #endif
1473 }
1474
1475 audio_unlock();
1476 }
1477
1478 #endif /* VG_AUDIO_H */