From d48e4bdf9285dacbd2c642ef844a3aba3e8d0160 Mon Sep 17 00:00:00 2001 From: hgn Date: Fri, 10 Mar 2023 09:05:16 +0000 Subject: [PATCH] dsp reverb etc --- vg_audio.h | 139 ++++++++++++++++------- vg_audio_dsp.h | 300 +++++++++++++++++++++++++++++++++++++++++++++++++ vg_mem.h | 6 + vg_profiler.h | 35 +++--- vg_ui.h | 72 +++++++++++- 5 files changed, 496 insertions(+), 56 deletions(-) create mode 100644 vg_audio_dsp.h diff --git a/vg_audio.h b/vg_audio.h index 8cccbd0..fc0a278 100644 --- a/vg_audio.h +++ b/vg_audio.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */ +/* Copyright (C) 2021-2023 Harry Godden (hgn) - All Rights Reserved */ #ifndef VG_AUDIO_H #define VG_AUDIO_H @@ -40,10 +40,15 @@ #endif #endif +#define AUDIO_FRAME_SIZE 512 +#define AUDIO_MIX_FRAME_SIZE 256 + #define AUDIO_CHANNELS 32 #define AUDIO_LFOS 8 +#define AUDIO_FILTERS 16 #define AUDIO_FLAG_LOOP 0x1 -#define AUDIO_FLAG_SPACIAL_3D 0x2 +#define AUDIO_FLAG_SPACIAL_3D 0x4 +#define AUDIO_FLAG_AUTO_START 0x8 /* 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 @@ -175,14 +180,18 @@ static struct vg_audio_system } vg_audio = { .volume_console = 1.0f }; +#include "vg/vg_audio_dsp.h" static struct vg_profile _vg_prof_audio_decode = {.mode = k_profile_mode_accum, .name = "[T2] audio_decode()"}, _vg_prof_audio_mix = {.mode = k_profile_mode_accum, .name = "[T2] audio_mix()"}, + _vg_prof_dsp = {.mode = k_profile_mode_accum, + .name = "[T2] dsp_process()"}, vg_prof_audio_decode, - vg_prof_audio_mix; + vg_prof_audio_mix, + vg_prof_audio_dsp; /* * These functions are called from the main thread and used to prevent bad @@ -259,20 +268,21 @@ VG_STATIC void vg_audio_init(void) u32 decode_size = AUDIO_DECODE_SIZE * AUDIO_CHANNELS; vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size ); + vg_dsp_init(); + SDL_AudioSpec spec_desired, spec_got; spec_desired.callback = audio_mixer_callback; spec_desired.channels = 2; spec_desired.format = AUDIO_F32; spec_desired.freq = 44100; spec_desired.padding = 0; - spec_desired.samples = 512; + spec_desired.samples = AUDIO_FRAME_SIZE; spec_desired.silence = 0; spec_desired.size = 0; spec_desired.userdata = NULL; vg_audio.sdl_output_device = - SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got, - SDL_AUDIO_ALLOW_SAMPLES_CHANGE ); + SDL_OpenAudioDevice( NULL, 0, &spec_desired, &spec_got,0 ); if( vg_audio.sdl_output_device ) { @@ -293,6 +303,7 @@ VG_STATIC void vg_audio_init(void) VG_STATIC void vg_audio_free(void) { + vg_dsp_free(); SDL_CloseAudioDevice( vg_audio.sdl_output_device ); } @@ -609,7 +620,7 @@ stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len ) return n; } -static float audio_lfo_pull_sample( audio_lfo *lfo ) +static inline float audio_lfo_pull_sample( audio_lfo *lfo ) { lfo->time ++; @@ -724,24 +735,18 @@ static void audio_channel_get_samples( audio_channel *ch, vg_profile_end( &_vg_prof_audio_decode ); } -static void audio_channel_mix( audio_channel *ch, - float *buffer, u32 frame_count ) +static void audio_channel_mix( audio_channel *ch, float *buffer ) { - u32 frames_write = frame_count; - - u32 buffer_length = frame_count; + u32 buffer_length = AUDIO_MIX_FRAME_SIZE; if( ch->_.sampling_rate != 1.0f ) { - buffer_length = ceilf( (float)frame_count * ch->_.sampling_rate ) + 1; + float l = ceilf( (float)(AUDIO_MIX_FRAME_SIZE) * ch->_.sampling_rate ); + buffer_length = l+1; } - float *pcf = alloca( buffer_length * 2 * sizeof(float) ); + float pcf[ AUDIO_MIX_FRAME_SIZE * 2 * 2 ]; audio_channel_get_samples( ch, buffer_length, pcf ); - vg_profile_begin( &_vg_prof_audio_mix ); - - if( ch->_.lfo ) - ch->_.lfo->time = ch->_.lfo->time_startframe; float framevol_l = 1.0f, framevol_r = 1.0f; @@ -775,27 +780,43 @@ static void audio_channel_mix( audio_channel *ch, framevol_r *= (vol * 0.5f) * (1.0f + pan); } - for( u32 j=0; jvolume_movement < ch->_.volume_rate ) - { - ch->volume_movement ++; + vg_profile_begin( &_vg_prof_audio_mix ); - float movement_t = ch->volume_movement; - movement_t /= (float)ch->_.volume_rate; + float volume_movement = ch->volume_movement; + float const fvolume_rate = vg_maxf( 1.0f, ch->_.volume_rate ); + const float inv_volume_rate = 1.0f/fvolume_rate; - ch->_.volume = vg_lerpf( ch->volume_movement_start, - ch->_.volume_target, - movement_t ); - } + float volume = ch->_.volume; + const float volume_start = ch->volume_movement_start; + const float volume_target = ch->_.volume_target; - float vol_norm = ch->_.volume * ch->_.volume; + for( u32 j=0; j_.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, + float vol_l = vol_norm * framevol_l, + vol_r = vol_norm * framevol_r, sample_l, sample_r; @@ -823,6 +844,10 @@ static void audio_channel_mix( audio_channel *ch, buffer[ j*2+1 ] += sample_r * vol_r; } + ch->volume_movement += AUDIO_MIX_FRAME_SIZE; + ch->volume_movement = VG_MIN( ch->volume_movement, ch->_.volume_rate ); + ch->_.volume = volume; + vg_profile_end( &_vg_prof_audio_mix ); } @@ -956,7 +981,7 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count ) lfo->editble_state_write_mask = 0x00; } - + dsp_update_tunings(); audio_unlock(); /* @@ -996,9 +1021,29 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count ) audio_channel *ch = &vg_audio.channels[i]; if( ch->activity == k_channel_activity_alive ) - audio_channel_mix( ch, pOut32F, frame_count ); + { + if( ch->_.lfo ) + ch->_.lfo->time = ch->_.lfo->time_startframe; + + u32 remaining = frame_count, + subpos = 0; + + while( remaining ) + { + audio_channel_mix( ch, pOut32F+subpos ); + remaining -= AUDIO_MIX_FRAME_SIZE; + subpos += AUDIO_MIX_FRAME_SIZE*2; + } + } } + vg_profile_begin( &_vg_prof_dsp ); + + for( int i=0; i (1024*1024)/4 ) + vg_fatal_exit_loop( "too much dsp" ); + + float *buf = &vg_dsp.buffer[ vg_dsp.allocations ]; + vg_dsp.allocations += samples; + + return buf; +} + + +/* + * filters + * ---------------------------------------------- + */ + +struct dsp_delay +{ + u32 length, cur; + float *buffer; +}; + +struct dsp_lpf +{ + float exponent; + float *buffer; +}; + +struct dsp_schroeder +{ + struct dsp_delay M; + float gain; +}; + +static inline void dsp_read_delay( struct dsp_delay *delay, float *s ) +{ + u32 index = delay->cur+1; + + if( index >= delay->length ) + index = 0; + + *s = delay->buffer[ index ]; +} + +static inline void dsp_write_delay( struct dsp_delay *delay, float *s ) +{ + u32 index = delay->cur; + delay->buffer[ index ] = *s; + + delay->cur ++; + + if( delay->cur >= delay->length ) + delay->cur = 0; +} + +static void dsp_init_delay( struct dsp_delay *delay, float length ) +{ + delay->length = 44100.0f * length; + delay->cur = 0; + delay->buffer = dsp_allocate( delay->length ); + + for( int i=0; ilength; i++ ) + delay->buffer[i] = 0.0f; +} + +static void dsp_update_lpf( struct dsp_lpf *lpf, float freq ) +{ + lpf->exponent = 1.0f-expf( -(1.0f/44100.0f) * 2.0f * VG_PIf * freq ); +} + +static void dsp_init_lpf( struct dsp_lpf *lpf, float freq ) +{ + lpf->buffer = dsp_allocate( 4 ); + lpf->buffer[0] = 0.0f; + dsp_update_lpf( lpf, freq ); +} + +static inline void dsp_write_lpf( struct dsp_lpf *lpf, float *s ) +{ + float diff = *s - lpf->buffer[0]; + lpf->buffer[0] += diff * lpf->exponent; +} + +static inline void dsp_read_lpf( struct dsp_lpf *lpf, float *s ) +{ + *s = lpf->buffer[0]; +} + +static void dsp_init_schroeder( struct dsp_schroeder *sch, float length, + float gain ) +{ + dsp_init_delay( &sch->M, length ); + sch->gain = gain; +} + +static inline void dsp_process_schroeder( struct dsp_schroeder *sch, + float *input, float *output ) +{ + float dry = *input; + + float delay_output; + dsp_read_delay( &sch->M, &delay_output ); + + float feedback_attenuated = delay_output * sch->gain, + input_feedback_sum = dry + feedback_attenuated; + + dsp_write_delay( &sch->M, &input_feedback_sum ); + + *output = delay_output - input_feedback_sum*sch->gain; +} + +/* temporary global design */ +static struct dsp_lpf __lpf_mud_free; +static struct dsp_delay __echos[8]; +static struct dsp_lpf __echos_lpf[8]; +static struct dsp_schroeder __diffusion_chain[8]; + +static void vg_dsp_init( void ) +{ + vg_dsp.buffer = vg_linear_alloc( vg_mem.rtmemory, 1024*1024*1 ); + vg_dsp.view_texture_buffer = vg_linear_alloc( vg_mem.rtmemory, 512*512 ); + + vg_acquire_thread_sync(); + glGenTextures( 1, &vg_dsp.view_texture ); + glBindTexture( GL_TEXTURE_2D, vg_dsp.view_texture ); + glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, + GL_RGBA, GL_UNSIGNED_BYTE, vg_dsp.view_texture_buffer ); + vg_tex2d_nearest(); + vg_release_thread_sync(); + + + /* temporary global design */ + + + dsp_init_lpf( &__lpf_mud_free, 125.0f ); + + float sizes[] = + { 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f, 256.0f }; + + float reflection_variance = 0.1f; + + for( int i=0; i<8; i++ ) + { + float reflection_time = ((sizes[i])/343.0f) * 1000.0f; + + float var = 1.0f + (vg_randf()*2.0f - 1.0f) * reflection_variance, + total = reflection_time * var; + + dsp_init_delay( &__echos[i], total / 1000.0f ); + + float freq = vg_lerpf( 800.0f, 350.0f, sizes[i] / 256.0f ); + dsp_init_lpf( &__echos_lpf[i], freq ); + } + + float diffusions[] = { 187.0f, 159.0f, 143.0f, 121.0f, + 79.0f, 57.0f, 27.0f, 11.0f }; + + for( int i=0; i<8; i++ ) + { + dsp_init_schroeder( __diffusion_chain+i, diffusions[i]/1000.0f, 0.7f ); + } +} + +static void vg_dsp_process( float *stereo_in, float *stereo_out ) +{ + float in_total = (stereo_in[0]+stereo_in[1])*0.5f; + float recieved = 0.0f; + + for( int i=0; i<8; i++ ) + { + float echo; + dsp_read_delay( __echos+i, &echo ); + dsp_write_lpf( __echos_lpf+i, &echo ); + dsp_read_lpf( __echos_lpf+i, &echo ); + + recieved += echo * vg_dsp.echo_tunings[i]*0.98; + } + + float diffused = recieved; + + for( int i=0; i<8; i++ ) + { + dsp_process_schroeder( __diffusion_chain+i, &diffused, &diffused ); + } + + float diffuse_mix = vg_dsp.reverb_wet_mix; + diffuse_mix = vg_lerpf( recieved, diffused, diffuse_mix ); + float total = in_total + diffuse_mix; + + float low_mud; + dsp_write_lpf( &__lpf_mud_free, &total ); + dsp_read_lpf( &__lpf_mud_free, &low_mud ); + + total -= low_mud; + + for( int i=0; i<8; i++ ) + dsp_write_delay( __echos+i, &total ); + + stereo_out[0] = stereo_in[0]*vg_dsp.reverb_dry_mix; + stereo_out[1] = stereo_in[1]*vg_dsp.reverb_dry_mix; + stereo_out[0] += diffuse_mix*2.0f*vg_dsp.reverb_wet_mix; + stereo_out[1] += diffuse_mix*2.0f*vg_dsp.reverb_wet_mix; +} + +static void dsp_update_tunings(void) +{ + float sizes[] = + { 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f, 128.0f, 256.0f }; + + float avg_distance = 0.0f; + + for( int i=0; i<8; i++ ) + vg_dsp.echo_tunings[i] = 0.5f; + + for( int j=0; j<14; j++ ) + { + float d = vg_dsp.echo_distances[j]; + + for( int i=0; i<7; i++ ) + { + if( d < sizes[i+1] ) + { + float range = sizes[i+1]-sizes[i]; + float t = vg_clampf( (d - sizes[i])/range, 0.0f, 1.0f ); + + vg_dsp.echo_tunings[i ] += 1.0f-t; + vg_dsp.echo_tunings[i+1] += t; + + break; + } + } + + avg_distance += d; + } + avg_distance /= 14.0f; + + + vg_dsp.reverb_wet_mix =1.0f-vg_clampf((avg_distance-30.0f)/200.0f,0.0f,1.0f); + vg_dsp.reverb_dry_mix =1.0f-vg_dsp.reverb_wet_mix*0.4f; + + float total = 0.0f; + for( int i=0; i<8; i++ ) + total += vg_dsp.echo_tunings[i]; + + if( total > 0.0f ) + { + float inverse = 1.0f/total; + + for( int i=0;i<8; i++ ) + { + vg_dsp.echo_tunings[i] *= inverse; + } + } + + for( int i=0; i<8; i++ ) + { + float freq = vg_lerpf( 200.0f, 500.0f, vg_dsp.echo_tunings[i] ); + dsp_update_lpf( &__echos_lpf[i], freq ); + } +} + +static void vg_dsp_free( void ) +{ + glDeleteTextures( 1, &vg_dsp.view_texture ); +} + +static void vg_dsp_update_texture( void ) +{ + for( int i=0; i<512*512; i++ ) + { + float v = vg_clampf( vg_dsp.buffer[i] * 0.5f + 0.5f, 0.0f, 1.0f ); + vg_dsp.view_texture_buffer[i] = v * 255.0f; + } +} + +#endif /* VG_AUDIO_DSP_H */ diff --git a/vg_mem.h b/vg_mem.h index 5edbd9c..ccb652c 100644 --- a/vg_mem.h +++ b/vg_mem.h @@ -96,6 +96,12 @@ VG_STATIC u32 vg_align8( u32 s ) return m << 3; } +VG_STATIC u32 vg_align4( u32 s ) +{ + u32 m = (s + 3) >> 2; + return m << 2; +} + /* Returns allocator structure from data pointer */ static vg_linear_allocator *vg_linear_header( void *data ) { diff --git a/vg_profiler.h b/vg_profiler.h index c8db76d..5c0690c 100644 --- a/vg_profiler.h +++ b/vg_profiler.h @@ -13,8 +13,8 @@ struct vg_profile { const char *name; - float samples[ VG_PROFILE_SAMPLE_COUNT ]; - u32 buffer_count, buffer_current; + u64 samples[ VG_PROFILE_SAMPLE_COUNT ]; + u32 buffer_count, buffer_current; enum profile_mode { @@ -41,7 +41,7 @@ VG_STATIC void vg_profile_increment( struct vg_profile *profile ) if( profile->buffer_current >= VG_PROFILE_SAMPLE_COUNT ) profile->buffer_current = 0; - profile->samples[ profile->buffer_current ] = 0.0f; + profile->samples[ profile->buffer_current ] = 0; } VG_STATIC void vg_profile_end( struct vg_profile *profile ) @@ -51,7 +51,7 @@ VG_STATIC void vg_profile_end( struct vg_profile *profile ) if( profile->mode == k_profile_mode_frame ) { - profile->samples[ profile->buffer_current ] = (float)delta; + profile->samples[ profile->buffer_current ] = delta; vg_profile_increment( profile ); } else @@ -60,12 +60,6 @@ VG_STATIC void vg_profile_end( struct vg_profile *profile ) } } -VG_STATIC void vg_profile_graph_sample( struct vg_profile *profile, float s ) -{ - profile->samples[ profile->buffer_current ] = s; - vg_profile_increment( profile ); -} - VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count, float budget, ui_rect panel, u32 colour_offset ) { @@ -84,8 +78,8 @@ VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count, ui_fill_rect( panel, 0xa0000000 ); assert( count <= 8 ); - float avgs[8]; - int ptrs[8]; + double avgs[8]; + int ptrs[8]; for( int i=0; isamples[ptrs[j]] * rate_mul, - px = (total / (budget)) * sw, - wx = (sample / (budget)) * sw; + double sample = (double)profiles[j]->samples[ptrs[j]] * rate_mul, + px = (total / (budget)) * sw, + wx = (sample / (budget)) * sw; ui_rect block = { panel[0] + px, panel[1] + (float)i*sh, wx, sh }; @@ -126,6 +120,13 @@ VG_STATIC void vg_profile_drawn( struct vg_profile **profiles, u32 count, char infbuf[64]; + snprintf( infbuf, 64, "accuracy: %.7fms", rate_mul ); + ui_text( (ui_rect){ panel[0] + 4, + panel[1] + 0 * 14, 0, 0 }, + infbuf, + 1, + k_text_align_left ); + for( int i=0; i