1 /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */
9 #include "vg/vg_stdint.h"
10 #include "vg/vg_platform.h"
14 #include "vg/vg_console.h"
15 #include "vg/vg_store.h"
16 #include "vg/vg_profiler.h"
23 #pragma GCC push_options
24 #pragma GCC optimize ("O3")
25 #pragma GCC diagnostic push
26 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
30 #define STB_VORBIS_MAX_CHANNELS 2
31 #include "submodules/stb/stb_vorbis.c"
38 #pragma GCC pop_options
39 #pragma GCC diagnostic pop
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
50 #define FADEOUT_LENGTH 1100
51 #define FADEOUT_DIVISOR (1.0f/(float)FADEOUT_LENGTH)
53 #define AUDIO_DECODE_SIZE (1024*256) /* 256 kb decoding buffers */
55 enum audio_source_mode
58 k_audio_source_compressed
,
61 typedef struct audio_clip audio_clip
;
65 enum audio_source_mode source_mode
;
71 typedef struct audio_mix_info audio_mix_info
;
81 typedef struct audio_player audio_player
;
84 aatree_ptr active_entity
; /* non-nil if currently playing */
92 typedef struct audio_entity audio_entity
;
101 u32 fadeout
, fadeout_current
;
108 * play again: if already playing, leave in queue while it fadeouts
109 * oneshot: create a ghost entity
113 static struct vg_audio_system
116 ma_device miniaudio_device
;
117 ma_device_config miniaudio_dconfig
;
119 SDL_AudioDeviceID sdl_output_device
;
128 SDL_mutex
*mux_checker
,
131 /* Audio engine, thread 1 */
132 struct active_audio_player
138 aatree_pool_node pool_node
;
141 stb_vorbis
*vorbis_handle
;
142 stb_vorbis_alloc vorbis_alloc
;
144 active_players
[ SFX_MAX_SYSTEMS
];
146 aatree active_pool_info
; /* note: just using the pool */
147 aatree_ptr active_pool_head
;
149 /* System queue, and access from thread 0 */
150 audio_entity entity_queue
[SFX_MAX_SYSTEMS
];
152 int debug_ui
, debug_ui_3d
;
159 volume_target_internal
,
162 vg_audio
= { .volume_console
= 1.0f
};
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
,
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.
176 VG_STATIC
int audio_lock_checker_load(void)
179 SDL_LockMutex( vg_audio
.mux_checker
);
180 value
= vg_audio
.sync_locked
;
181 SDL_UnlockMutex( vg_audio
.mux_checker
);
185 VG_STATIC
void audio_lock_checker_store( int value
)
187 SDL_LockMutex( vg_audio
.mux_checker
);
188 vg_audio
.sync_locked
= value
;
189 SDL_UnlockMutex( vg_audio
.mux_checker
);
192 VG_STATIC
void audio_require_lock(void)
194 if( audio_lock_checker_load() )
197 vg_error( "Modifying sound effects systems requires locking\n" );
201 VG_STATIC
void audio_lock(void)
203 SDL_LockMutex( vg_audio
.mux_sync
);
204 audio_lock_checker_store(1);
207 VG_STATIC
void audio_unlock(void)
209 audio_lock_checker_store(0);
210 SDL_UnlockMutex( vg_audio
.mux_sync
);
213 VG_STATIC
void audio_mixer_callback( void *user
, u8
*stream
, int frame_count
);
214 VG_STATIC
void vg_audio_init(void)
216 vg_audio
.mux_checker
= SDL_CreateMutex();
217 vg_audio
.mux_sync
= SDL_CreateMutex();
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 },
228 vg_convar_push( (struct vg_convar
){
230 .data
= &vg_audio
.volume_console
,
231 .data_type
= k_convar_dtype_f32
,
232 .opt_f32
= { .min
=0.0f
, .max
=2.0f
, .clamp
=1 },
236 /* allocate memory */
239 vg_audio
.audio_pool
=
240 vg_create_linear_allocator( vg_mem
.rtmemory
, 1024*1024*32,
244 u32 decode_size
= AUDIO_DECODE_SIZE
* SFX_MAX_SYSTEMS
;
245 vg_audio
.decode_buffer
= vg_linear_alloc( vg_mem
.rtmemory
, decode_size
);
248 vg_audio
.active_pool_info
.base
= vg_audio
.active_players
;
249 vg_audio
.active_pool_info
.offset
= offsetof(struct active_audio_player
,
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
);
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
;
266 vg_audio
.sdl_output_device
=
267 SDL_OpenAudioDevice( NULL
, 0, &spec_desired
, &spec_got
,
268 SDL_AUDIO_ALLOW_SAMPLES_CHANGE
);
270 if( vg_audio
.sdl_output_device
)
272 SDL_PauseAudioDevice( vg_audio
.sdl_output_device
, 0 );
277 "SDL_OpenAudioDevice failed. Your default audio device must support:\n"
278 " Frequency: 44100 hz\n"
279 " Buffer size: 512\n"
281 " Format: s16 or f32\n" );
284 vg_success( "Ready\n" );
287 VG_STATIC
void vg_audio_free(void)
289 SDL_CloseAudioDevice( vg_audio
.sdl_output_device
);
296 static aatree_ptr
audio_alloc_entity_internal(void)
298 aatree_ptr playerid
= aatree_pool_alloc( &vg_audio
.active_pool_info
,
299 &vg_audio
.active_pool_head
);
301 if( playerid
== AATREE_PTR_NIL
)
302 return AATREE_PTR_NIL
;
304 struct active_audio_player
*aap
= &vg_audio
.active_players
[ playerid
];
310 VG_STATIC
void audio_entity_free_internal( aatree_ptr id
)
312 struct active_audio_player
*aap
= &vg_audio
.active_players
[ id
];
315 /* Notify player that we've finished */
316 if( aap
->ent
.player
)
317 aap
->ent
.player
->active_entity
= AATREE_PTR_NIL
;
320 aatree_pool_free( &vg_audio
.active_pool_info
, id
,
321 &vg_audio
.active_pool_head
);
324 VG_STATIC
void *audio_entity_vorbis_ptr( aatree_ptr entid
)
326 u8
*buf
= (u8
*)vg_audio
.decode_buffer
,
327 *loc
= &buf
[AUDIO_DECODE_SIZE
*entid
];
332 VG_STATIC
void audio_entity_start( audio_entity
*src
)
334 aatree_ptr entid
= audio_alloc_entity_internal();
335 if( entid
== AATREE_PTR_NIL
)
338 audio_entity
*ent
= &vg_audio
.active_players
[ entid
].ent
;
340 ent
->info
= src
->info
;
341 ent
->name
= src
->info
.source
->path
;
343 ent
->player
= src
->player
;
346 ent
->fadeout_current
= 0;
348 /* Notify main player we are dequeud and playing */
351 src
->player
->enqued
= 0;
352 src
->player
->active_entity
= entid
;
355 if( src
->info
.source
->source_mode
== k_audio_source_compressed
)
357 /* Setup vorbis decoder */
358 struct active_audio_player
*aap
= &vg_audio
.active_players
[ entid
];
360 stb_vorbis_alloc alloc
= {
361 .alloc_buffer
= (char *)audio_entity_vorbis_ptr( entid
),
362 .alloc_buffer_length_in_bytes
= AUDIO_DECODE_SIZE
366 stb_vorbis
*decoder
= stb_vorbis_open_memory(
367 src
->info
.source
->data
,
368 src
->info
.source
->size
, &err
, &alloc
);
372 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
373 src
->info
.source
->path
, err
);
375 audio_entity_free_internal( entid
);
380 ent
->length
= stb_vorbis_stream_length_in_samples( decoder
);
383 aap
->vorbis_handle
= decoder
;
387 ent
->length
= src
->info
.source
->size
;
392 * Read everything from the queue
394 VG_STATIC
void audio_system_enque(void)
396 /* Process incoming sound queue */
399 vg_audio
.volume_target_internal
= vg_audio
.volume_target
;
402 for( int i
=0; i
<vg_audio
.queue_len
; i
++ )
404 audio_entity
*src
= &vg_audio
.entity_queue
[ i
];
409 if( src
->player
->active_entity
== AATREE_PTR_NIL
)
411 audio_entity_start( src
);
415 /* Otherwise try start fadeout but dont remove from queue */
417 aatree_ptr entid
= src
->player
->active_entity
;
418 audio_entity
*ent
= &vg_audio
.active_players
[ entid
].ent
;
421 ent
->fadeout
= FADEOUT_LENGTH
;
422 ent
->fadeout_current
= FADEOUT_LENGTH
;
425 vg_audio
.entity_queue
[ wr
++ ] = *src
;
430 audio_entity_start( src
);
434 vg_audio
.queue_len
= wr
;
436 /* Localize others memory */
437 for( int i
=0; i
<SFX_MAX_SYSTEMS
; i
++ )
439 struct active_audio_player
*aap
= &vg_audio
.active_players
[i
];
443 if( aap
->ent
.player
)
445 /* Only copy information in whilst not requeing */
446 if( aap
->ent
.player
->enqued
== 0 )
448 aap
->ent
.info
= aap
->ent
.player
->info
;
450 if( (aap
->ent
.info
.flags
& AUDIO_FLAG_KILL
) && !aap
->ent
.fadeout
)
452 aap
->ent
.fadeout
= FADEOUT_LENGTH
;
453 aap
->ent
.fadeout_current
= FADEOUT_LENGTH
;
463 * Redistribute sound systems
465 VG_STATIC
void audio_system_cleanup(void)
469 for( int i
=0; i
<SFX_MAX_SYSTEMS
; i
++ )
471 struct active_audio_player
*aap
= &vg_audio
.active_players
[i
];
474 audio_entity
*src
= &aap
->ent
;
475 if( src
->cur
< src
->length
|| (src
->info
.flags
& AUDIO_FLAG_LOOP
))
481 audio_entity_free_internal( i
);
490 * Get effective volume and pan from this entity
492 VG_STATIC
void audio_entity_spacialize( audio_entity
*ent
,
493 float *vol
, float *pan
)
495 if( ent
->info
.vol
< 0.01f
)
497 *vol
= ent
->info
.vol
;
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]) )
509 vg_error( "NaN listener/world position (%s)\n", ent
->name
);
516 v3_sub( ent
->info
.world_position
, vg_audio
.listener_pos
, delta
);
518 float dist2
= v3_length2( delta
);
520 if( dist2
< 0.0001f
)
527 float dist
= sqrtf( dist2
),
528 attn
= (dist
/ ent
->info
.vol
) +1.0f
;
530 v3_muls( delta
, 1.0f
/dist
, delta
);
531 *pan
= v3_dot( vg_audio
.listener_ears
, delta
);
532 *vol
= 1.0f
/(attn
*attn
);
536 VG_STATIC
void audio_decode_uncompressed_mono( i16
*src
, u32 count
, float *dst
)
538 for( u32 i
=0; i
<count
; i
++ )
540 dst
[ i
*2 + 0 ] = ((float)src
[i
]) * (1.0f
/32767.0f
);
541 dst
[ i
*2 + 1 ] = ((float)src
[i
]) * (1.0f
/32767.0f
);
546 * adapted from stb_vorbis.h, since the original does not handle mono->stereo
549 stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis
*f
, float *buffer
,
553 c
= VG_MIN( 1, f
->channels
- 1 );
557 int k
= f
->channel_buffer_end
- f
->channel_buffer_start
;
562 for( int j
=0; j
< k
; ++j
)
564 *buffer
++ = f
->channel_buffers
[ 0 ][f
->channel_buffer_start
+j
];
565 *buffer
++ = f
->channel_buffers
[ c
][f
->channel_buffer_start
+j
];
569 f
->channel_buffer_start
+= k
;
574 if( !stb_vorbis_get_frame_float( f
, NULL
, NULL
))
582 * ........ more wrecked code sorry!
585 stb_vorbis_get_samples_i16_downmixed( stb_vorbis
*f
, i16
*buffer
, int len
)
588 c
= VG_MIN( 1, f
->channels
- 1 );
592 int k
= f
->channel_buffer_end
- f
->channel_buffer_start
;
597 for( int j
=0; j
< k
; ++j
)
599 float sl
= f
->channel_buffers
[ 0 ][f
->channel_buffer_start
+j
],
600 sr
= f
->channel_buffers
[ c
][f
->channel_buffer_start
+j
];
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;
607 f
->channel_buffer_start
+= k
;
612 if( !stb_vorbis_get_frame_float( f
, NULL
, NULL
))
619 VG_STATIC
void audio_entity_get_samples( aatree_ptr id
, u32 count
, float *buf
)
621 vg_profile_begin( &_vg_prof_audio_decode
);
623 struct active_audio_player
*aap
= &vg_audio
.active_players
[id
];
624 audio_entity
*ent
= &aap
->ent
;
626 u32 remaining
= count
;
627 u32 cursor
= ent
->cur
;
632 u32 samples_this_run
= VG_MIN( remaining
, ent
->length
- cursor
);
633 remaining
-= samples_this_run
;
635 float *dst
= &buf
[ buffer_pos
* 2 ];
637 int source_mode
= ent
->info
.source
->source_mode
;
639 if( source_mode
== k_audio_source_mono
)
641 i16
*src_buffer
= ent
->info
.source
->data
,
642 *src
= &src_buffer
[cursor
];
644 audio_decode_uncompressed_mono( src
, samples_this_run
, dst
);
646 else if( source_mode
== k_audio_source_compressed
)
648 int read_samples
= stb_vorbis_get_samples_float_interleaved_stereo(
653 if( read_samples
!= samples_this_run
)
655 vg_warn( "Invalid samples read (%s)\n", ent
->info
.source
->path
);
659 cursor
+= samples_this_run
;
660 buffer_pos
+= samples_this_run
;
662 if( (ent
->info
.flags
& AUDIO_FLAG_LOOP
) && remaining
)
664 if( source_mode
== k_audio_source_compressed
)
666 stb_vorbis_seek_start( aap
->vorbis_handle
);
678 buf
[ buffer_pos
*2 + 0 ] = 0.0f
;
679 buf
[ buffer_pos
*2 + 1 ] = 0.0f
;
686 vg_profile_end( &_vg_prof_audio_decode
);
689 VG_STATIC
void audio_entity_mix( aatree_ptr id
, float *buffer
,
692 audio_entity
*ent
= &vg_audio
.active_players
[id
].ent
;
694 u32 cursor
= ent
->cur
, buffer_pos
= 0;
695 float *pcf
= alloca( frame_count
* 2 * sizeof(float) );
697 u32 frames_write
= frame_count
;
698 float fadeout_divisor
= 1.0f
/ (float)ent
->fadeout
;
700 float vol
= ent
->info
.vol
,
703 audio_entity_get_samples( id
, frame_count
, pcf
);
705 vg_profile_begin( &_vg_prof_audio_mix
);
707 if( ent
->info
.flags
& AUDIO_FLAG_SPACIAL_3D
)
708 audio_entity_spacialize( ent
, &vol
, &pan
);
710 for( u32 j
=0; j
<frame_count
; j
++ )
712 float frame_vol
= vol
* vg_audio
.volume
;
715 /* Force this system to be removed now */
716 if( ent
->fadeout_current
== 0 )
718 ent
->info
.flags
= 0x00;
719 ent
->cur
= ent
->length
;
723 frame_vol
*= (float)ent
->fadeout_current
* fadeout_divisor
;
724 ent
->fadeout_current
--;
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
;
736 vg_profile_end( &_vg_prof_audio_mix
);
739 VG_STATIC
void audio_mixer_callback( void *user
, u8
*stream
, int byte_count
)
741 audio_system_enque();
743 int frame_count
= byte_count
/(2*sizeof(float));
746 float *pOut32F
= (float *)stream
;
747 for( int i
=0; i
<frame_count
*2; i
++ )
750 float start_vol
= vg_audio
.volume
;
753 for( int i
=0; i
<SFX_MAX_SYSTEMS
; i
++ )
755 struct active_audio_player
*aap
= &vg_audio
.active_players
[i
];
756 vg_audio
.volume
= start_vol
;
759 audio_entity_mix( i
, pOut32F
, frame_count
);
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
;
766 if( vol_chg
> fabsf( vol_diff
) )
767 vg_audio
.volume
= vg_audio
.volume_target_internal
;
769 vg_audio
.volume
+= vg_signf( vol_diff
) * vol_chg
;
772 audio_system_cleanup();
776 vg_profile_increment( &_vg_prof_audio_decode
);
777 vg_profile_increment( &_vg_prof_audio_mix
);
779 vg_prof_audio_mix
= _vg_prof_audio_mix
;
780 vg_prof_audio_decode
= _vg_prof_audio_decode
;
782 vg_audio
.samples_last
= frame_count
;
786 VG_STATIC
void audio_clip_load( audio_clip
*clip
, void *lin_alloc
)
788 if( lin_alloc
== NULL
)
789 lin_alloc
= vg_audio
.audio_pool
;
791 if( clip
->source_mode
== k_audio_source_mono
)
793 vg_linear_clear( vg_mem
.scratch
);
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
801 void *filedata
= vg_file_read( vg_mem
.scratch
, clip
->path
, &fsize
);
804 stb_vorbis
*decoder
= stb_vorbis_open_memory(
805 filedata
, fsize
, &err
, &alloc
);
809 vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n",
811 vg_fatal_exit_loop( "Vorbis decode error" );
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
);
819 clip
->data
= vg_linear_alloc( lin_alloc
, vg_align8(data_size
) );
820 clip
->size
= length_samples
;
823 int read_samples
= stb_vorbis_get_samples_i16_downmixed(
824 decoder
, clip
->data
, length_samples
);
826 if( read_samples
!= length_samples
)
827 vg_fatal_exit_loop( "Decode error" );
829 float mb
= (float)(data_size
) / (1024.0f
*1024.0f
);
830 vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip
->path
, mb
,
834 /* load in directly */
835 else if( clip
->source_mode
== k_audio_source_compressed
)
838 clip
->data
= vg_file_read( lin_alloc
, clip
->path
, &clip
->size
);
842 vg_fatal_exit_loop( "Audio failed to load" );
844 float mb
= (float)(clip
->size
) / (1024.0f
*1024.0f
);
845 vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip
->path
, mb
);
849 VG_STATIC
void audio_clip_loadn( audio_clip
*arr
, int count
, void *lin_alloc
)
851 for( int i
=0; i
<count
; i
++ )
852 audio_clip_load( &arr
[i
], lin_alloc
);
855 /* Mark change to be uploaded through queue system */
856 VG_STATIC
void audio_player_commit( audio_player
*sys
)
858 audio_require_lock();
860 if( vg_audio
.queue_len
>= vg_list_size( vg_audio
.entity_queue
) )
862 vg_warn( "Audio commit queue full\n" );
868 vg_warn( "[2] Audio commit spamming; already enqued (%s)\n", sys
->name
);
873 audio_entity
*ent
= &vg_audio
.entity_queue
[ vg_audio
.queue_len
++ ];
874 ent
->info
= sys
->info
;
878 VG_STATIC
void audio_require_init( audio_player
*player
)
884 vg_fatal_exit_loop( "Must init audio player before playing! \n" );
887 VG_STATIC
void audio_require_clip_loaded( audio_clip
*clip
)
889 if( clip
->data
&& clip
->size
)
893 vg_fatal_exit_loop( "Must load audio clip before playing! \n" );
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
)
900 audio_require_lock();
901 audio_require_init( player
);
902 audio_require_clip_loaded( clip
);
904 if( player
->info
.flags
& AUDIO_FLAG_KILL
)
906 vg_error( "Can't start audio clip on player that is/has disconnected" );
912 vg_warn( "[1] Audio commit spamming; already enqued (%s)\n",
917 player
->info
.source
= clip
;
918 audio_player_commit( player
);
921 VG_STATIC
void audio_play_oneshot( audio_clip
*clip
, float volume
)
923 audio_require_lock();
924 audio_require_clip_loaded( clip
);
926 if( vg_audio
.queue_len
>= vg_list_size( vg_audio
.entity_queue
) )
928 vg_warn( "Audio commit queue full\n" );
932 audio_entity
*ent
= &vg_audio
.entity_queue
[ vg_audio
.queue_len
++ ];
934 ent
->info
.flags
= AUDIO_FLAG_ONESHOT
;
935 ent
->info
.pan
= 0.0f
;
936 ent
->info
.source
= clip
;
937 ent
->info
.vol
= volume
;
941 VG_STATIC
void audio_player_init( audio_player
*player
)
943 player
->active_entity
= AATREE_PTR_NIL
;
952 * Safety enforced Get/set attributes
955 VG_STATIC
int audio_player_is_playing( audio_player
*sys
)
957 audio_require_lock();
959 if( sys
->active_entity
!= AATREE_PTR_NIL
)
965 VG_STATIC
void audio_player_set_position( audio_player
*sys
, v3f pos
)
967 audio_require_lock();
968 v3_copy( pos
, sys
->info
.world_position
);
971 VG_STATIC
void audio_player_set_vol( audio_player
*sys
, float vol
)
973 audio_require_lock();
975 if( !vg_validf(vol
) )
977 vg_warn( "NaN volume (%s)\n", sys
->name
);
981 if( (vol
< 0.0f
) || (vol
> 100.0f
) )
983 vg_warn( "Invalid volume (%s: %f)\n", sys
->name
, vol
);
990 VG_STATIC
float audio_player_get_vol( audio_player
*sys
)
992 audio_require_lock();
993 return sys
->info
.vol
;
996 VG_STATIC
void audio_player_set_pan( audio_player
*sys
, float pan
)
998 audio_require_lock();
1002 VG_STATIC
float audio_player_get_pan( audio_player
*sys
)
1004 audio_require_lock();
1005 return sys
->info
.pan
;
1008 VG_STATIC
void audio_player_set_flags( audio_player
*sys
, u32 flags
)
1010 audio_require_lock();
1011 sys
->info
.flags
= flags
;
1014 VG_STATIC u32
audio_player_get_flags( audio_player
*sys
)
1016 audio_require_lock();
1017 return sys
->info
.flags
;
1020 VG_STATIC
void audio_set_master_vol( float vol
)
1022 audio_require_lock();
1023 vg_audio
.volume_target
= vol
;
1026 VG_STATIC
void audio_push_console_vol(void)
1029 audio_set_master_vol( vg_audio
.volume_console
);
1037 VG_STATIC
void audio_debug_ui( m4x4f mtx_pv
)
1039 if( !vg_audio
.debug_ui
)
1046 u32 cursor
, flags
, length
;
1050 infos
[ SFX_MAX_SYSTEMS
];
1051 int num_systems
= 0;
1055 for( int i
=0; i
<SFX_MAX_SYSTEMS
; i
++ )
1057 struct active_audio_player
*aap
= &vg_audio
.active_players
[i
];
1062 audio_entity
*ent
= &aap
->ent
;
1063 struct sound_info
*snd
= &infos
[ num_systems
++ ];
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
);
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,
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;
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
;
1094 snprintf( perf
, 127, "Mem: %.1f/%.1fmb (%.1f%%)\n", usage
, total
, percent
);
1096 ui_text( vg_uictx
.cursor
, perf
, 1, 0 );
1097 vg_uictx
.cursor
[1] += 20;
1099 ui_rect overlap_buffer
[ SFX_MAX_SYSTEMS
];
1100 u32 overlap_length
= 0;
1102 /* Draw audio stack */
1103 for( int i
=0; i
<num_systems
; i
++ )
1105 struct sound_info
*inf
= &infos
[i
];
1107 vg_uictx
.cursor
[2] = 200;
1108 vg_uictx
.cursor
[3] = 18;
1110 u32 alpha
= 0xa0000000;
1114 ui_fill_rect( vg_uictx
.cursor
, 0x00333333|alpha
);
1116 ui_px baseline
= vg_uictx
.cursor
[0],
1118 c
= baseline
+ ((float)inf
->cursor
/ (float)inf
->length
) * w
;
1121 vg_uictx
.cursor
[2] = 2;
1122 vg_uictx
.cursor
[0] = c
;
1123 ui_fill_rect( vg_uictx
.cursor
, 0xffffffff );
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 );
1130 if( inf
->flags
& AUDIO_FLAG_SPACIAL_3D
)
1133 v3_copy( inf
->pos
, wpos
);
1135 m4x4_mulv( mtx_pv
, wpos
, wpos
);
1137 if( wpos
[3] <= 0.0f
)
1138 goto projected_behind
;
1140 v2_muls( wpos
, (1.0f
/wpos
[3]) * 0.5f
, wpos
);
1141 v2_add( wpos
, (v2f
){ 0.5f
, 0.5f
}, wpos
);
1144 wr
[0] = wpos
[0] * vg
.window_x
;
1145 wr
[1] = (1.0f
-wpos
[1]) * vg
.window_y
;
1149 for( int j
=0; j
<12; j
++ )
1152 for( int k
=0; k
<overlap_length
; k
++ )
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])) )
1169 ui_text( wr
, perf
, 1, 0 );
1171 ui_rect_copy( wr
, overlap_buffer
[ overlap_length
++ ] );
1178 vg_uictx
.cursor
[1] += 1;
1182 #endif /* VG_AUDIO_H */