From: hgn Date: Wed, 8 Mar 2023 00:23:06 +0000 (+0000) Subject: audio upgrade 3 X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=commitdiff_plain;h=ff8e8a170cb29b450f7f26580683ec3ae81cf4bd audio upgrade 3 --- diff --git a/vg.h b/vg.h index b7475ad..eb858a3 100644 --- a/vg.h +++ b/vg.h @@ -176,9 +176,7 @@ struct vg engine_stage; /* graphics */ -#if 0 m4x4f pv; -#endif enum quality_profile { k_quality_profile_high = 0, @@ -550,9 +548,7 @@ VG_STATIC void _vg_gameloop_render(void) } /* FIXME */ -#if 0 audio_debug_ui( vg.pv ); -#endif vg_ui(); _vg_console_draw(); @@ -595,7 +591,6 @@ VG_STATIC void _vg_gameloop(void) _vg_gameloop_update(); _vg_gameloop_render(); - audio_push_console_vol(); SDL_GL_SwapWindow( vg.window ); _vg_run_synced(); diff --git a/vg_audio.h b/vg_audio.h index edbe8a2..20972c0 100644 --- a/vg_audio.h +++ b/vg_audio.h @@ -40,82 +40,35 @@ #endif #endif -#define SFX_MAX_SYSTEMS 32 +#define AUDIO_CHANNELS 32 +#define AUDIO_LFOS 8 #define AUDIO_FLAG_LOOP 0x1 -#define AUDIO_FLAG_ONESHOT 0x2 -#define AUDIO_FLAG_SPACIAL_3D 0x4 -#define AUDIO_FLAG_AUTO_START 0x8 -#define AUDIO_FLAG_KILL 0x10 +#define AUDIO_FLAG_SPACIAL_3D 0x2 -#define FADEOUT_LENGTH 1100 -#define FADEOUT_DIVISOR (1.0f/(float)FADEOUT_LENGTH) +/* Vorbis will ALWAYS use the maximum amount of channels it can */ +//#define AUDIO_FLAG_MONO 0x100 NOTE: This is the default, so its not used +#define AUDIO_FLAG_STEREO 0x200 +#define AUDIO_FLAG_VORBIS 0x400 #define AUDIO_DECODE_SIZE (1024*256) /* 256 kb decoding buffers */ - -enum audio_source_mode -{ - k_audio_source_mono, - k_audio_source_compressed, -}; +#define AUDIO_MUTE_VOLUME 0.0f +#define AUDIO_BASE_VOLUME 1.0f typedef struct audio_clip audio_clip; +typedef struct audio_channel audio_channel; +typedef struct audio_lfo audio_lfo; + struct audio_clip { const char *path; - enum audio_source_mode source_mode; + u32 flags; u32 size; void *data; }; -typedef struct audio_mix_info audio_mix_info; -struct audio_mix_info -{ - audio_clip *source; - v3f world_position; - - float vol, pan; - u32 flags; -}; - -typedef struct audio_player audio_player; -struct audio_player -{ - aatree_ptr active_entity; /* non-nil if currently playing */ - audio_mix_info info; - int enqued, init; - - /* Diagnostic */ - const char *name; -}; - -typedef struct audio_entity audio_entity; -struct audio_entity -{ - audio_player *player; - audio_mix_info info; - - u32 length, cur; - - /* Effects */ - u32 fadeout, fadeout_current; - const char *name; -}; - -/* - * TODO list sunday - * - * play again: if already playing, leave in queue while it fadeouts - * oneshot: create a ghost entity - * - */ - static struct vg_audio_system { -#if 0 - ma_device miniaudio_device; - ma_device_config miniaudio_dconfig; -#endif SDL_AudioDeviceID sdl_output_device; void *audio_pool, @@ -128,27 +81,87 @@ static struct vg_audio_system SDL_mutex *mux_checker, *mux_sync; - /* Audio engine, thread 1 */ - struct active_audio_player + struct audio_lfo { - int active; - union + u32 time, time_startframe; + float sqrt_polynomial_coefficient; + + struct { - audio_entity ent; - aatree_pool_node pool_node; - }; - + enum lfo_wave_type + { + k_lfo_triangle, + k_lfo_square, + k_lfo_saw, + k_lfo_polynomial_bipolar + } + wave_type; + + u32 period; + float polynomial_coefficient; + } + _, editable_state; + u32 editble_state_write_mask; + } + oscillators[ AUDIO_LFOS ]; + + struct audio_channel + { + int allocated; + char name[32]; /* only editable while allocated == 0 */ + audio_clip *source; /* ... */ + u32 flags; /* ... */ + + /* internal non-readable state + * -----------------------------*/ + u32 cursor, source_length; + + float volume_movement_start, + pan_movement_start; + + u32 volume_movement, + pan_movement; + stb_vorbis *vorbis_handle; stb_vorbis_alloc vorbis_alloc; - } - active_players[ SFX_MAX_SYSTEMS ]; - aatree active_pool_info; /* note: just using the pool */ - aatree_ptr active_pool_head; + enum channel_activity + { + k_channel_activity_reset, /* will advance if allocated==1, to wake */ + k_channel_activity_wake, /* will advance to either of next two */ + k_channel_activity_alive, + k_channel_activity_error + } + activity; + + /* + * editable structure, can be modified inside _lock and _unlock + * the edit mask tells which to copy into internal _, or to discard + * ---------------------------------------------------------------------- + */ + struct channel_state + { + int relinquished; + + float volume, /* current volume */ + volume_target, /* target volume */ + pan, + pan_target; + + u32 volume_rate, + pan_rate; + + v4f spacial_falloff; /* xyz, range */ + + audio_lfo *lfo; + float lfo_amount; + } + _, editable_state; + u32 editble_state_write_mask; + } + channels[ AUDIO_CHANNELS ]; /* System queue, and access from thread 0 */ - audio_entity entity_queue[SFX_MAX_SYSTEMS]; - int queue_len; int debug_ui, debug_ui_3d; v3f listener_pos, @@ -161,6 +174,7 @@ static struct vg_audio_system } vg_audio = { .volume_console = 1.0f }; + static struct vg_profile _vg_prof_audio_decode = {.mode = k_profile_mode_accum, .name = "[T2] audio_decode()"}, @@ -241,17 +255,9 @@ VG_STATIC void vg_audio_init(void) VG_MEMORY_SYSTEM ); /* fixed */ - u32 decode_size = AUDIO_DECODE_SIZE * SFX_MAX_SYSTEMS; + u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS; vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size ); - /* setup pool */ - vg_audio.active_pool_info.base = vg_audio.active_players; - vg_audio.active_pool_info.offset = offsetof(struct active_audio_player, - pool_node ); - vg_audio.active_pool_info.stride = sizeof(struct active_audio_player); - vg_audio.active_pool_info.p_cmp = NULL; - aatree_init_pool( &vg_audio.active_pool_info, SFX_MAX_SYSTEMS ); - SDL_AudioSpec spec_desired, spec_got; spec_desired.callback = audio_mixer_callback; spec_desired.channels = 2; @@ -293,244 +299,234 @@ VG_STATIC void vg_audio_free(void) * thread 1 */ -static aatree_ptr audio_alloc_entity_internal(void) +#define AUDIO_EDIT_VOLUME_SLOPE 0x1 +#define AUDIO_EDIT_VOLUME 0x2 +#define AUDIO_EDIT_LFO_PERIOD 0x4 +#define AUDIO_EDIT_LFO_WAVE 0x8 +#define AUDIO_EDIT_LFO_ATTACHMENT 0x10 +#define AUDIO_EDIT_SPACIAL 0x20 +#define AUDIO_EDIT_OWNERSHIP 0x40 + +static audio_channel *audio_request_channel( audio_clip *clip, u32 flags ) { - aatree_ptr playerid = aatree_pool_alloc( &vg_audio.active_pool_info, - &vg_audio.active_pool_head ); + for( int i=0; iallocated ) + { + ch->source = clip; + ch->flags = flags; + strcpy( ch->name, clip->path ); + + ch->allocated = 1; + + ch->editable_state.relinquished = 0; + ch->editable_state.volume = 1.0f; + ch->editable_state.volume_target = 1.0f; + ch->editable_state.pan = 0.0f; + ch->editable_state.pan_target = 0.0f; + ch->editable_state.volume_rate = 0; + ch->editable_state.pan_rate = 0; + v4_copy((v4f){0.0f,0.0f,0.0f,1.0f},ch->editable_state.spacial_falloff); + ch->editable_state.lfo = NULL; + ch->editable_state.lfo_amount = 0.0f; + ch->editble_state_write_mask = 0x00; + return ch; + } + } - struct active_audio_player *aap = &vg_audio.active_players[ playerid ]; - aap->active = 1; + return NULL; +} - return playerid; +static audio_channel *audio_relinquish_channel( audio_channel *ch ) +{ + ch->editable_state.relinquished = 1; + ch->editble_state_write_mask |= AUDIO_EDIT_OWNERSHIP; + return NULL; } -VG_STATIC void audio_entity_free_internal( aatree_ptr id ) +static audio_channel *audio_channel_slope_volume( audio_channel *ch, + float length, + float new_volume ) { - struct active_audio_player *aap = &vg_audio.active_players[ id ]; - aap->active = 0; + ch->editable_state.volume_target = new_volume; + ch->editable_state.volume_rate = length * 44100.0f; + ch->editble_state_write_mask |= AUDIO_EDIT_VOLUME_SLOPE; - /* Notify player that we've finished */ - if( aap->ent.player ) - aap->ent.player->active_entity = AATREE_PTR_NIL; + return ch; +} - /* delete */ - aatree_pool_free( &vg_audio.active_pool_info, id, - &vg_audio.active_pool_head ); +static audio_channel *audio_channel_edit_volume( audio_channel *ch, + float new_volume, int instant ) +{ + if( instant ) + { + ch->editable_state.volume = 0.0f; + ch->editble_state_write_mask |= AUDIO_EDIT_VOLUME; + return ch; + } + else + { + return audio_channel_slope_volume( ch, 0.05f, new_volume ); + } } -VG_STATIC void *audio_entity_vorbis_ptr( aatree_ptr entid ) +static audio_channel *audio_channel_fadeout( audio_channel *ch, float length ) { - u8 *buf = (u8*)vg_audio.decode_buffer, - *loc = &buf[AUDIO_DECODE_SIZE*entid]; + ch = audio_channel_slope_volume( ch, length, 0.0f ); + ch = audio_relinquish_channel( ch ); - return (void *)loc; + return ch; } -VG_STATIC void audio_entity_start( audio_entity *src ) +static audio_channel *audio_channel_fadein( audio_channel *ch, float length ) { - aatree_ptr entid = audio_alloc_entity_internal(); - if( entid == AATREE_PTR_NIL ) - return; + ch = audio_channel_edit_volume( ch, 0.0f, 1 ); + ch = audio_channel_slope_volume( ch, length, 1.0f ); + return ch; +} - audio_entity *ent = &vg_audio.active_players[ entid ].ent; +static audio_channel *audio_channel_crossfade( audio_channel *ch, + audio_clip *new_clip, + float length, u32 flags ) +{ + u32 cursor = 0; - ent->info = src->info; - ent->name = src->info.source->path; - ent->cur = 0; - ent->player = src->player; + if( ch ) + { + ch = audio_channel_fadeout( ch, length ); + } - ent->fadeout = 0; - ent->fadeout_current = 0; + audio_channel *replacement = audio_request_channel( new_clip, flags ); - /* Notify main player we are dequeud and playing */ - if( src->player ) + if( replacement ) { - src->player->enqued = 0; - src->player->active_entity = entid; + replacement = audio_channel_fadein( replacement, length ); } - if( src->info.source->source_mode == k_audio_source_compressed ) - { - /* Setup vorbis decoder */ - struct active_audio_player *aap = &vg_audio.active_players[ entid ]; - - stb_vorbis_alloc alloc = { - .alloc_buffer = (char *)audio_entity_vorbis_ptr( entid ), - .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE - }; + return replacement; +} - int err; - stb_vorbis *decoder = stb_vorbis_open_memory( - src->info.source->data, - src->info.source->size, &err, &alloc ); +static audio_channel *audio_channel_sidechain_lfo( audio_channel *ch, + int lfo_id, float amount ) +{ + ch->editable_state.lfo = &vg_audio.oscillators[ lfo_id ]; + ch->editable_state.lfo_amount = amount; + ch->editble_state_write_mask |= AUDIO_EDIT_LFO_ATTACHMENT; - if( !decoder ) - { - vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", - src->info.source->path, err ); + return ch; +} - audio_entity_free_internal( entid ); - return; - } - else - { - ent->length = stb_vorbis_stream_length_in_samples( decoder ); - } - - aap->vorbis_handle = decoder; +static audio_channel *audio_channel_set_spacial( audio_channel *ch, + v3f co, float range ) +{ + if( ch->flags & AUDIO_FLAG_SPACIAL_3D ) + { + v3_copy( co, ch->editable_state.spacial_falloff ); + ch->editable_state.spacial_falloff[3] = 1.0f/range; + ch->editble_state_write_mask |= AUDIO_EDIT_SPACIAL; } else { - ent->length = src->info.source->size; + vg_warn( "Tried to set spacialization paramaters for 2D channel (%s)\n", + ch->name ); } + + return ch; } -/* - * Read everything from the queue - */ -VG_STATIC void audio_system_enque(void) +static audio_channel *audio_oneshot_3d( audio_clip *clip, v3f position, + float range, float volume ) { - /* Process incoming sound queue */ - audio_lock(); + audio_channel *ch = audio_request_channel( clip, AUDIO_FLAG_SPACIAL_3D ); - vg_audio.volume_target_internal = vg_audio.volume_target; - - int wr = 0; - for( int i=0; iplayer ) - { - /* Start new */ - if( src->player->active_entity == AATREE_PTR_NIL ) - { - audio_entity_start( src ); - } - else - { - /* Otherwise try start fadeout but dont remove from queue */ + return ch; +} - aatree_ptr entid = src->player->active_entity; - audio_entity *ent = &vg_audio.active_players[ entid ].ent; - if( !ent->fadeout ) - { - ent->fadeout = FADEOUT_LENGTH; - ent->fadeout_current = FADEOUT_LENGTH; - } +static audio_channel *audio_oneshot( audio_clip *clip, float volume, float pan ) +{ + audio_channel *ch = audio_request_channel( clip, 0x00 ); - vg_audio.entity_queue[ wr ++ ] = *src; - } - } - else - { - audio_entity_start( src ); - } - } - - vg_audio.queue_len = wr; - - /* Localize others memory */ - for( int i=0; iactive ) - continue; - - if( aap->ent.player ) - { - /* Only copy information in whilst not requeing */ - if( aap->ent.player->enqued == 0 ) - { - aap->ent.info = aap->ent.player->info; - - if( (aap->ent.info.flags & AUDIO_FLAG_KILL) && !aap->ent.fadeout ) - { - aap->ent.fadeout = FADEOUT_LENGTH; - aap->ent.fadeout_current = FADEOUT_LENGTH; - } - } - } + ch = audio_channel_edit_volume( ch, volume, 1 ); + ch = audio_relinquish_channel( ch ); } - - audio_unlock(); + + return ch; } -/* - * Redistribute sound systems - */ -VG_STATIC void audio_system_cleanup(void) +static void audio_set_lfo_wave( int id, enum lfo_wave_type type, + float coefficient ) { - audio_lock(); + audio_lfo *lfo = &vg_audio.oscillators[ id ]; + lfo->editable_state.polynomial_coefficient = coefficient; + lfo->editable_state.wave_type = type; - for( int i=0; iactive ) - { - audio_entity *src = &aap->ent; - if( src->cur < src->length || (src->info.flags & AUDIO_FLAG_LOOP )) - { - /* Good to keep */ - } - else - { - audio_entity_free_internal( i ); - } - } - } + lfo->editble_state_write_mask |= AUDIO_EDIT_LFO_WAVE; +} - audio_unlock(); +static void audio_set_lfo_frequency( int id, float freq ) +{ + audio_lfo *lfo = &vg_audio.oscillators[ id ]; + lfo->editable_state.period = 44100.0f / freq; + lfo->editble_state_write_mask |= AUDIO_EDIT_LFO_PERIOD; } /* - * Get effective volume and pan from this entity + * Committers + * ----------------------------------------------------------------------------- */ -VG_STATIC void audio_entity_spacialize( audio_entity *ent, - float *vol, float *pan ) +static int audio_channel_load_source( audio_channel *ch ) { - if( ent->info.vol < 0.01f ) + if( ch->source->flags & AUDIO_FLAG_VORBIS ) { - *vol = ent->info.vol; - *pan = 0.0f; - return; - } + /* Setup vorbis decoder */ + u32 index = ch - vg_audio.channels; - if( !vg_validf(vg_audio.listener_pos[0]) || - !vg_validf(vg_audio.listener_pos[1]) || - !vg_validf(vg_audio.listener_pos[2]) || - !vg_validf(ent->info.world_position[0]) || - !vg_validf(ent->info.world_position[1]) || - !vg_validf(ent->info.world_position[2]) ) - { - vg_error( "NaN listener/world position (%s)\n", ent->name ); - *vol = 0.0f; - *pan = 0.0f; - return; - } + u8 *buf = (u8*)vg_audio.decode_buffer, + *loc = &buf[AUDIO_DECODE_SIZE*index]; - v3f delta; - v3_sub( ent->info.world_position, vg_audio.listener_pos, delta ); + stb_vorbis_alloc alloc = { + .alloc_buffer = (char *)loc, + .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE + }; - float dist2 = v3_length2( delta ); + int err; + stb_vorbis *decoder = stb_vorbis_open_memory( + ch->source->data, + ch->source->size, &err, &alloc ); - if( dist2 < 0.0001f ) + if( !decoder ) + { + vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", + ch->source->path, err ); + return 0; + } + else + { + ch->source_length = stb_vorbis_stream_length_in_samples( decoder ); + ch->vorbis_handle = decoder; + } + } + else if( ch->source->flags & AUDIO_FLAG_STEREO ) { - *pan = 0.0f; - *vol = 1.0f; + ch->source_length = ch->source->size / 2; } else { - float dist = sqrtf( dist2 ), - attn = (dist / ent->info.vol) +1.0f; - - v3_muls( delta, 1.0f/dist, delta ); - *pan = v3_dot( vg_audio.listener_ears, delta ); - *vol = 1.0f/(attn*attn); + ch->source_length = ch->source->size; } + + return 1; } VG_STATIC void audio_decode_uncompressed_mono( i16 *src, u32 count, float *dst ) @@ -616,57 +612,103 @@ stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len ) return n; } -VG_STATIC void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf ) +static float audio_lfo_pull_sample( audio_lfo *lfo ) { - vg_profile_begin( &_vg_prof_audio_decode ); + lfo->time ++; + + if( lfo->time >= lfo->_.period ) + lfo->time = 0; + + float t = lfo->time; + t /= (float)lfo->_.period; + + if( lfo->_.wave_type == k_lfo_polynomial_bipolar ) + { + /* + * # + * # # + * # # + * # # + * ### # ### + * ## # + * # # + * # # + * ## + */ + + t *= 2.0f; + t -= 1.0f; + + return (( 2.0f * lfo->sqrt_polynomial_coefficient * t ) / + /* --------------------------------------- */ + ( 1.0f + lfo->_.polynomial_coefficient * t*t ) + + ) * (1.0f-fabsf(t)); + } + else + { + return 0.0f; + } +} - struct active_audio_player *aap = &vg_audio.active_players[id]; - audio_entity *ent = &aap->ent; +static void audio_channel_get_samples( audio_channel *ch, + u32 count, float *buf ) +{ + vg_profile_begin( &_vg_prof_audio_decode ); u32 remaining = count; - u32 cursor = ent->cur; u32 buffer_pos = 0; while( remaining ) { - u32 samples_this_run = VG_MIN( remaining, ent->length - cursor ); + u32 samples_this_run = VG_MIN( remaining, ch->source_length -ch->cursor ); remaining -= samples_this_run; float *dst = &buf[ buffer_pos * 2 ]; - - int source_mode = ent->info.source->source_mode; - if( source_mode == k_audio_source_mono ) + if( ch->source->flags & AUDIO_FLAG_STEREO ) { - i16 *src_buffer = ent->info.source->data, - *src = &src_buffer[cursor]; - - audio_decode_uncompressed_mono( src, samples_this_run, dst ); + for( int i=0;isource->flags & AUDIO_FLAG_VORBIS ) { int read_samples = stb_vorbis_get_samples_float_interleaved_stereo( - aap->vorbis_handle, + ch->vorbis_handle, dst, - samples_this_run ); + samples_this_run ); if( read_samples != samples_this_run ) { - vg_warn( "Invalid samples read (%s)\n", ent->info.source->path ); + vg_warn( "Invalid samples read (%s)\n", ch->source->path ); + + for( int i=0; isource->data, + *src = &src_buffer[ch->cursor]; - cursor += samples_this_run; + audio_decode_uncompressed_mono( src, samples_this_run, dst ); + } + + ch->cursor += samples_this_run; buffer_pos += samples_this_run; - if( (ent->info.flags & AUDIO_FLAG_LOOP) && remaining ) + if( (ch->flags & AUDIO_FLAG_LOOP) && remaining ) { - if( source_mode == k_audio_source_compressed ) - { - stb_vorbis_seek_start( aap->vorbis_handle ); - } + if( ch->source->flags & AUDIO_FLAG_VORBIS ) + stb_vorbis_seek_start( ch->vorbis_handle ); - cursor = 0; + ch->cursor = 0; continue; } else @@ -682,53 +724,79 @@ VG_STATIC void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf ) remaining --; } - ent->cur = cursor; vg_profile_end( &_vg_prof_audio_decode ); } -VG_STATIC void audio_entity_mix( aatree_ptr id, float *buffer, - u32 frame_count ) +static void audio_channel_mix( audio_channel *ch, + float *buffer, u32 frame_count ) { - audio_entity *ent = &vg_audio.active_players[id].ent; - - u32 cursor = ent->cur, buffer_pos = 0; + u32 buffer_pos = 0; float *pcf = alloca( frame_count * 2 * sizeof(float) ); - u32 frames_write = frame_count; - float fadeout_divisor = 1.0f / (float)ent->fadeout; - float vol = ent->info.vol, - pan = ent->info.pan; + audio_channel_get_samples( ch, frame_count, pcf ); + vg_profile_begin( &_vg_prof_audio_mix ); - audio_entity_get_samples( id, frame_count, pcf ); + if( ch->_.lfo ) + ch->_.lfo->time = ch->_.lfo->time_startframe; - vg_profile_begin( &_vg_prof_audio_mix ); + float framevol_l = 1.0f, + framevol_r = 1.0f; - if( ent->info.flags & AUDIO_FLAG_SPACIAL_3D ) - audio_entity_spacialize( ent, &vol, &pan ); + if( ch->flags & AUDIO_FLAG_SPACIAL_3D ) + { + if( !vg_validf(vg_audio.listener_pos[0]) || + !vg_validf(vg_audio.listener_pos[1]) || + !vg_validf(vg_audio.listener_pos[2]) || + !vg_validf(ch->_.spacial_falloff[0]) || + !vg_validf(ch->_.spacial_falloff[1]) || + !vg_validf(ch->_.spacial_falloff[2]) ) + { + vg_error( "NaN listener/world position (%s)\n", ch->name ); + + framevol_l = 0.0f; + framevol_r = 0.0f; + } + + v3f delta; + v3_sub( ch->_.spacial_falloff, vg_audio.listener_pos, delta ); + + float dist = v3_length( delta ), + vol = vg_maxf( 0.0f, 1.0f - ch->_.spacial_falloff[3]*dist ); + + v3_muls( delta, 1.0f/dist, delta ); + float pan = v3_dot( vg_audio.listener_ears, delta ); + vol = powf( vol, 5.0f ); + + framevol_l *= (vol * 0.5f) * (1.0f - pan); + framevol_r *= (vol * 0.5f) * (1.0f + pan); + } for( u32 j=0; jfadeout ) + if( ch->volume_movement < ch->_.volume_rate ) { - /* Force this system to be removed now */ - if( ent->fadeout_current == 0 ) - { - ent->info.flags = 0x00; - ent->cur = ent->length; - break; - } + ch->volume_movement ++; + + float movement_t = ch->volume_movement; + movement_t /= (float)ch->_.volume_rate; - frame_vol *= (float)ent->fadeout_current * fadeout_divisor; - ent->fadeout_current --; + ch->_.volume = vg_lerpf( ch->volume_movement_start, + ch->_.volume_target, + movement_t ); } - float sl = 1.0f-pan, - sr = 1.0f+pan; + float vol_norm = ch->_.volume * ch->_.volume; - buffer[ buffer_pos*2+0 ] += pcf[ buffer_pos*2+0 ] * frame_vol * sl; - buffer[ buffer_pos*2+1 ] += pcf[ buffer_pos*2+1 ] * frame_vol * sr; + if( ch->_.lfo ) + vol_norm *= 1.0f + audio_lfo_pull_sample( ch->_.lfo ) + * ch->_.lfo_amount; + + float vol_l = vol_norm * framevol_l, + vol_r = vol_norm * framevol_r; + + buffer[ buffer_pos*2+0 ] += pcf[ buffer_pos*2+0 ] * vol_l; + buffer[ buffer_pos*2+1 ] += pcf[ buffer_pos*2+1 ] * vol_r; buffer_pos ++; } @@ -738,8 +806,150 @@ VG_STATIC void audio_entity_mix( aatree_ptr id, float *buffer, VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count ) { - audio_system_enque(); + /* + * Copy data and move edit flags to commit flags + * ------------------------------------------------------------- */ + audio_lock(); + for( int i=0; iallocated ) + continue; + + /* process relinquishments */ + if( (ch->activity != k_channel_activity_reset) && ch->_.relinquished ) + { + if( (ch->cursor >= ch->source_length && !(ch->flags & AUDIO_FLAG_LOOP)) + || (ch->_.volume == 0.0f) + || (ch->activity == k_channel_activity_error) ) + { + ch->_.relinquished = 0; + ch->allocated = 0; + ch->activity = k_channel_activity_reset; + continue; + } + } + + /* process new channels */ + if( ch->activity == k_channel_activity_reset ) + { + ch->_ = ch->editable_state; + ch->cursor = 0; + ch->source_length = 0; + ch->activity = k_channel_activity_wake; + } + + if( ch->editble_state_write_mask & AUDIO_EDIT_OWNERSHIP ) + ch->_.relinquished = ch->editable_state.relinquished; + else + ch->editable_state.relinquished = ch->_.relinquished; + + if( ch->editble_state_write_mask & AUDIO_EDIT_VOLUME ) + ch->_.volume = ch->editable_state.volume; + else + ch->editable_state.volume = ch->_.volume; + + + if( ch->editble_state_write_mask & AUDIO_EDIT_VOLUME_SLOPE ) + { + ch->volume_movement_start = ch->_.volume; + ch->volume_movement = 0; + + ch->_.volume_target = ch->editable_state.volume_target; + ch->_.volume_rate = ch->editable_state.volume_rate; + } + else + { + ch->editable_state.volume_target = ch->_.volume_target; + ch->editable_state.volume_rate = ch->_.volume_rate; + } + + + if( ch->editble_state_write_mask & AUDIO_EDIT_LFO_ATTACHMENT ) + { + ch->_.lfo = ch->editable_state.lfo; + ch->_.lfo_amount = ch->editable_state.lfo_amount; + } + else + { + ch->editable_state.lfo = ch->_.lfo; + ch->editable_state.lfo_amount = ch->_.lfo_amount; + } + + + if( ch->editble_state_write_mask & AUDIO_EDIT_SPACIAL ) + v4_copy( ch->editable_state.spacial_falloff,ch->_.spacial_falloff ); + else + v4_copy( ch->_.spacial_falloff,ch->editable_state.spacial_falloff ); + + + /* currently readonly, i guess */ + ch->editable_state.pan_target = ch->_.pan_target; + ch->editable_state.pan = ch->_.pan; + ch->editble_state_write_mask = 0x00; + } + + for( int i=0; ieditble_state_write_mask & AUDIO_EDIT_LFO_WAVE ) + { + lfo->_.wave_type = lfo->editable_state.wave_type; + + if( lfo->_.wave_type == k_lfo_polynomial_bipolar ) + { + lfo->_.polynomial_coefficient = + lfo->editable_state.polynomial_coefficient; + lfo->sqrt_polynomial_coefficient = + sqrtf(lfo->_.polynomial_coefficient); + } + } + + if( lfo->editble_state_write_mask & AUDIO_EDIT_LFO_PERIOD ) + { + if( lfo->_.period ) + { + float t = lfo->time; + t/= (float)lfo->_.period; + + lfo->_.period = lfo->editable_state.period; + lfo->time = lfo->_.period * t; + } + else + { + lfo->time = 0; + lfo->_.period = lfo->editable_state.period; + } + } + + lfo->editble_state_write_mask = 0x00; + } + + + audio_unlock(); + + /* + * Process spawns + * ------------------------------------------------------------- */ + for( int i=0; iactivity == k_channel_activity_wake ) + { + if( audio_channel_load_source( ch ) ) + ch->activity = k_channel_activity_alive; + else + ch->activity = k_channel_activity_error; + } + } + + /* + * Mix everything + * -------------------------------------------------------- */ int frame_count = byte_count/(2*sizeof(float)); /* Clear buffer */ @@ -747,39 +957,34 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count ) for( int i=0; itime_startframe = lfo->time; + } - /* Mix all sounds */ - for( int i=0; iactive ) - audio_entity_mix( i, pOut32F, frame_count ); + if( ch->activity == k_channel_activity_alive ) + audio_channel_mix( ch, pOut32F, frame_count ); } - float vol_diff = vg_audio.volume_target_internal - vg_audio.volume, - vol_rate = 1.0f / (44100.0f*0.25f), - vol_chg = frame_count * vol_rate; - - if( vol_chg > fabsf( vol_diff ) ) - vg_audio.volume = vg_audio.volume_target_internal; - else - vg_audio.volume += vg_signf( vol_diff ) * vol_chg; - - /* redistribute */ - audio_system_cleanup(); - + /* + * Relinquishing conditions + * ------------------------------------------------------------------ + */ audio_lock(); + /* Profiling information + * ----------------------------------------------- */ vg_profile_increment( &_vg_prof_audio_decode ); vg_profile_increment( &_vg_prof_audio_mix ); - vg_prof_audio_mix = _vg_prof_audio_mix; vg_prof_audio_decode = _vg_prof_audio_decode; - vg_audio.samples_last = frame_count; + audio_unlock(); } @@ -788,7 +993,25 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) if( lin_alloc == NULL ) lin_alloc = vg_audio.audio_pool; - if( clip->source_mode == k_audio_source_mono ) + + /* load in directly */ + if( clip->flags & AUDIO_FLAG_VORBIS ) + { + audio_lock(); + clip->data = vg_file_read( lin_alloc, clip->path, &clip->size ); + audio_unlock(); + + if( !clip->data ) + vg_fatal_exit_loop( "Audio failed to load" ); + + float mb = (float)(clip->size) / (1024.0f*1024.0f); + vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); + } + else if( clip->flags & AUDIO_FLAG_STEREO ) + { + vg_fatal_exit_loop( "Unsupported format (Stereo uncompressed)" ); + } + else { vg_linear_clear( vg_mem.scratch ); u32 fsize; @@ -830,20 +1053,6 @@ VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc ) vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb, length_samples ); } - - /* load in directly */ - else if( clip->source_mode == k_audio_source_compressed ) - { - audio_lock(); - clip->data = vg_file_read( lin_alloc, clip->path, &clip->size ); - audio_unlock(); - - if( !clip->data ) - vg_fatal_exit_loop( "Audio failed to load" ); - - float mb = (float)(clip->size) / (1024.0f*1024.0f); - vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb ); - } } VG_STATIC void audio_clip_loadn( audio_clip *arr, int count, void *lin_alloc ) @@ -852,38 +1061,6 @@ VG_STATIC void audio_clip_loadn( audio_clip *arr, int count, void *lin_alloc ) audio_clip_load( &arr[i], lin_alloc ); } -/* Mark change to be uploaded through queue system */ -VG_STATIC void audio_player_commit( audio_player *sys ) -{ - audio_require_lock(); - - if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) ) - { - vg_warn( "Audio commit queue full\n" ); - return; - } - - if( sys->enqued ) - { - vg_warn( "[2] Audio commit spamming; already enqued (%s)\n", sys->name ); - return; - } - - sys->enqued = 1; - audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ]; - ent->info = sys->info; - ent->player = sys; -} - -VG_STATIC void audio_require_init( audio_player *player ) -{ - if( player->init ) - return; - - audio_unlock(); - vg_fatal_exit_loop( "Must init audio player before playing! \n" ); -} - VG_STATIC void audio_require_clip_loaded( audio_clip *clip ) { if( clip->data && clip->size ) @@ -893,143 +1070,6 @@ VG_STATIC void audio_require_clip_loaded( audio_clip *clip ) vg_fatal_exit_loop( "Must load audio clip before playing! \n" ); } -/* Play a clip using player. If its already playing something, it will - * fadeout quickly and start the next sound */ -VG_STATIC void audio_player_playclip( audio_player *player, audio_clip *clip ) -{ - audio_require_lock(); - audio_require_init( player ); - audio_require_clip_loaded( clip ); - - if( player->info.flags & AUDIO_FLAG_KILL ) - { - vg_error( "Can't start audio clip on player that is/has disconnected" ); - return; - } - - if( player->enqued ) - { - vg_warn( "[1] Audio commit spamming; already enqued (%s)\n", - player->name ); - return; - } - - player->info.source = clip; - audio_player_commit( player ); -} - -VG_STATIC void audio_play_oneshot( audio_clip *clip, float volume ) -{ - audio_require_lock(); - audio_require_clip_loaded( clip ); - - if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) ) - { - vg_warn( "Audio commit queue full\n" ); - return; - } - - audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ]; - - ent->info.flags = AUDIO_FLAG_ONESHOT; - ent->info.pan = 0.0f; - ent->info.source = clip; - ent->info.vol = volume; - ent->player = NULL; -} - -VG_STATIC void audio_player_init( audio_player *player ) -{ - player->active_entity = AATREE_PTR_NIL; - player->init = 1; -} - -/* - * Effects - */ - -/* - * Safety enforced Get/set attributes - */ - -VG_STATIC int audio_player_is_playing( audio_player *sys ) -{ - audio_require_lock(); - - if( sys->active_entity != AATREE_PTR_NIL ) - return 1; - else - return 0; -} - -VG_STATIC void audio_player_set_position( audio_player *sys, v3f pos ) -{ - audio_require_lock(); - v3_copy( pos, sys->info.world_position ); -} - -VG_STATIC void audio_player_set_vol( audio_player *sys, float vol ) -{ - audio_require_lock(); - - if( !vg_validf(vol) ) - { - vg_warn( "NaN volume (%s)\n", sys->name ); - vol = 0.0f; - } - - if( (vol < 0.0f) || (vol > 100.0f) ) - { - vg_warn( "Invalid volume (%s: %f)\n", sys->name, vol ); - vol = 0.0f; - } - - sys->info.vol = vol; -} - -VG_STATIC float audio_player_get_vol( audio_player *sys ) -{ - audio_require_lock(); - return sys->info.vol; -} - -VG_STATIC void audio_player_set_pan( audio_player *sys, float pan ) -{ - audio_require_lock(); - sys->info.pan = pan; -} - -VG_STATIC float audio_player_get_pan( audio_player *sys ) -{ - audio_require_lock(); - return sys->info.pan; -} - -VG_STATIC void audio_player_set_flags( audio_player *sys, u32 flags ) -{ - audio_require_lock(); - sys->info.flags = flags; -} - -VG_STATIC u32 audio_player_get_flags( audio_player *sys ) -{ - audio_require_lock(); - return sys->info.flags; -} - -VG_STATIC void audio_set_master_vol( float vol ) -{ - audio_require_lock(); - vg_audio.volume_target = vol; -} - -VG_STATIC void audio_push_console_vol(void) -{ - audio_lock(); - audio_set_master_vol( vg_audio.volume_console ); - audio_unlock(); -} - /* * Debugging */ @@ -1039,36 +1079,12 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) if( !vg_audio.debug_ui ) return; - /* Get data */ - struct sound_info - { - const char *name; - u32 cursor, flags, length; - v3f pos; - float vol; - } - infos[ SFX_MAX_SYSTEMS ]; - int num_systems = 0; - audio_lock(); - - for( int i=0; iactive ) - continue; - - audio_entity *ent = &aap->ent; - struct sound_info *snd = &infos[ num_systems ++ ]; - - snd->name = ent->name; - snd->cursor = ent->cur; - snd->flags = ent->info.flags; - snd->length = ent->length; - snd->vol = ent->info.vol*100.0f; - v3_copy( ent->info.world_position, snd->pos ); - } + /* + * Profiler + * ----------------------------------------------------------------------- + */ float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0; vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode, @@ -1076,7 +1092,6 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8, 250, 0 }, 3 ); - audio_unlock(); char perf[128]; @@ -1096,47 +1111,80 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) ui_text( vg_uictx.cursor, perf, 1, 0 ); vg_uictx.cursor[1] += 20; - ui_rect overlap_buffer[ SFX_MAX_SYSTEMS ]; + ui_rect overlap_buffer[ AUDIO_CHANNELS ]; u32 overlap_length = 0; /* Draw audio stack */ - for( int i=0; icursor / (float)inf->length) * w; - - /* cursor */ - vg_uictx.cursor[2] = 2; - vg_uictx.cursor[0] = c; - ui_fill_rect( vg_uictx.cursor, 0xffffffff ); - - vg_uictx.cursor[0] = baseline + 2; - vg_uictx.cursor[1] += 2; - snprintf( perf, 127, "%s %.1f%%", infos[i].name, infos[i].vol ); - ui_text( vg_uictx.cursor, perf, 1, 0 ); - - if( inf->flags & AUDIO_FLAG_SPACIAL_3D ) - { - v4f wpos; - v3_copy( inf->pos, wpos ); - wpos[3] = 1.0f; - m4x4_mulv( mtx_pv, wpos, wpos ); - if( wpos[3] <= 0.0f ) - goto projected_behind; + if( !ch->allocated ) + { + ui_fill_rect( vg_uictx.cursor, 0x50333333 ); + ui_end_down(); + vg_uictx.cursor[1] += 1; + continue; + } + + const char *formats[] = + { + "------", + "Mono ", + "Stereo", + "Vorbis" + }; + + int format_index = 0; + + if( ch->source->flags & AUDIO_FLAG_STEREO ) + format_index = 2; + else if( ch->source->flags & AUDIO_FLAG_VORBIS ) + format_index = 3; + else + format_index = 1; + + snprintf( perf, 127, "%02d %c%c%cD %s %4.2fv'%s'", + i, + (ch->editable_state.relinquished)? 'r': ' ', + 0? 'r': ' ', + 0? '3': '2', + formats[format_index], + ch->editable_state.volume, + ch->name ); + + if( format_index == 0 ) + { + ui_fill_rect( vg_uictx.cursor, 0xa00000ff ); + } + else + { + ui_fill_rect( vg_uictx.cursor, 0xa0333333 ); + } + + vg_uictx.cursor[0] += 2; + vg_uictx.cursor[1] += 2; + ui_text( vg_uictx.cursor, perf, 1, 0 ); + + ui_end_down(); + vg_uictx.cursor[1] += 1; + + if( AUDIO_FLAG_SPACIAL_3D ) + { + v4f wpos; + v3_copy( ch->editable_state.spacial_falloff, wpos ); + + wpos[3] = 1.0f; + m4x4_mulv( mtx_pv, wpos, wpos ); + + if( wpos[3] > 0.0f ) + { v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos ); v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos ); @@ -1170,13 +1218,10 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) ui_rect_copy( wr, overlap_buffer[ overlap_length ++ ] ); } - } - -projected_behind: - - ui_end_down(); - vg_uictx.cursor[1] += 1; + } } + + audio_unlock(); } #endif /* VG_AUDIO_H */ diff --git a/vg_lines.h b/vg_lines.h index 9300445..02d7a07 100644 --- a/vg_lines.h +++ b/vg_lines.h @@ -133,12 +133,12 @@ VG_STATIC void vg_lines_init(void) vg_lines.allow_input = 1; } -VG_STATIC void vg_lines_drawall( float* projection ) +VG_STATIC void vg_lines_drawall( void ) { glUseProgram( _shader_lines.id ); - glUniformMatrix4fv - ( glGetUniformLocation( _shader_lines.id, "uPv" ), 1, GL_FALSE, projection ); + glUniformMatrix4fv( glGetUniformLocation( _shader_lines.id, "uPv" ), + 1, GL_FALSE, (float *)vg.pv ); glBindVertexArray( vg_lines.vao ); glBindBuffer( GL_ARRAY_BUFFER, vg_lines.vbo ); diff --git a/vg_m.h b/vg_m.h index 3c78466..6542605 100644 --- a/vg_m.h +++ b/vg_m.h @@ -269,6 +269,11 @@ static inline void v3_add( v3f a, v3f b, v3f d ) d[0] = a[0]+b[0]; d[1] = a[1]+b[1]; d[2] = a[2]+b[2]; } +static inline void v3i_add( v3i a, v3i b, v3i d ) +{ + d[0] = a[0]+b[0]; d[1] = a[1]+b[1]; d[2] = a[2]+b[2]; +} + static inline void v4_add( v4f a, v4f b, v4f d ) { d[0] = a[0]+b[0]; @@ -282,6 +287,11 @@ static inline void v3_sub( v3f a, v3f b, v3f d ) d[0] = a[0]-b[0]; d[1] = a[1]-b[1]; d[2] = a[2]-b[2]; } +static inline void v3i_sub( v3i a, v3i b, v3i d ) +{ + d[0] = a[0]-b[0]; d[1] = a[1]-b[1]; d[2] = a[2]-b[2]; +} + static inline void v3_mul( v3f a, v3f b, v3f d ) { d[0] = a[0]*b[0]; d[1] = a[1]*b[1]; d[2] = a[2]*b[2];