From d5882ddde922fff8f5164bd6930df7ed35c5d1f9 Mon Sep 17 00:00:00 2001 From: hgn Date: Mon, 13 Mar 2023 16:16:13 +0000 Subject: [PATCH] bird --- projects/birds.c | 464 +++++++++++++++++++++++++++++++++++++++++++++++ vg_audio.h | 60 ++++-- vg_m.h | 1 + vg_mem.h | 3 + 4 files changed, 517 insertions(+), 11 deletions(-) create mode 100644 projects/birds.c diff --git a/projects/birds.c b/projects/birds.c new file mode 100644 index 0000000..95d9249 --- /dev/null +++ b/projects/birds.c @@ -0,0 +1,464 @@ +#include "stdint.h" +#include "stdio.h" +#include "stdlib.h" +#include "math.h" + +/* Birds sounds + * TODO: THis can be embedded using 'song strings' in the path field of a sound + * entity for skaterift/vg. and the generation algorthm be placed in the + * decode section. will need to find a place to store its memory, though. + * + * */ + +#define WRAP1S( X ) (X)%44100 + + +/* + * clang birds.c -lm -o birds && ./birds | aplay -f cd /dev/stdin + * + * Performance measurements: (generate 30seconds audio) + * + * DSP SINE_ACCURATE -O3 x Realtime + * + * on yes 0.182s 164x + * on no 0.134s 223x + * off yes 0.113s 265x + * off no 0.074s 405x + * + */ + +//#define SINE_ACCURATE + +#ifdef SINE_ACCURATE +static float sine_44100_1( int o ) +{ + float t = (float)o*(1.0f/44100.0f); + return sinf(t*6.28318530717958647692528676655900576); +} + +static void sine_44100_4( int o[4], float v[4] ) +{ + v[0] = sine_44100_1( o[0] ); + v[1] = sine_44100_1( o[1] ); + v[2] = sine_44100_1( o[2] ); + v[3] = sine_44100_1( o[3] ); +} +#else + +/* sine functions over the range [0, 44100] : [-pi, pi]. + * Not accurate! */ + +static float sine_44100_1( int o ) +{ + float s = (o<22050)?-1.0f:1.0f; + float t = ((float)o*(1.0f/22050.0f))-1.0f - s*0.5f; + float t2 = t*t; + float t4 = t2*t2; + return s*(5.0f*t2-4.0f*t4-1.0f); +} + +static void sine_44100_4( int o[4], float v[4] ) +{ + float s[4],t[4],t2[4],t4[4]; + s[0] = (o[0]<22050)?-1.0f:1.0f; + s[1] = (o[1]<22050)?-1.0f:1.0f; + s[2] = (o[2]<22050)?-1.0f:1.0f; + s[3] = (o[3]<22050)?-1.0f:1.0f; + + t[0] = ((float)o[0]*(1.0f/22050.0f))-1.0f - s[0]*0.5f; + t[1] = ((float)o[1]*(1.0f/22050.0f))-1.0f - s[1]*0.5f; + t[2] = ((float)o[2]*(1.0f/22050.0f))-1.0f - s[2]*0.5f; + t[3] = ((float)o[3]*(1.0f/22050.0f))-1.0f - s[3]*0.5f; + + t2[0] = t[0]*t[0]; + t2[1] = t[1]*t[1]; + t2[2] = t[2]*t[2]; + t2[3] = t[3]*t[3]; + + t4[0] = t2[0]*t2[0]; + t4[1] = t2[1]*t2[1]; + t4[2] = t2[2]*t2[2]; + t4[3] = t2[3]*t2[3]; + + v[0] = s[0]*(5.0f*t2[0]-4.0f*t4[0]-1.0f); + v[1] = s[1]*(5.0f*t2[1]-4.0f*t4[1]-1.0f); + v[2] = s[2]*(5.0f*t2[2]-4.0f*t4[2]-1.0f); + v[3] = s[3]*(5.0f*t2[3]-4.0f*t4[3]-1.0f); +} +#endif + +static float saw_44100_1( int o ) +{ + float t = ((float)o*(1.0f/22050.0f))-1.0f, + tt = t*t, + ttt = tt*t; + + return -2.5f*ttt+2.5f*t; +} + +static double rand_float( double min, double max ) +{ + double r = (double)(rand()&(4096-1))*(1.0/4096.0); + return min + r*(max-min); +} + +static int rand_seconds( double min, double max ) +{ + return rand_float( min*44100.0, max*44100.0 ); +} + +static void vg_dsp_init( void ); +static void vg_dsp_process( float *stereo_in, float *stereo_out ); + +struct synth_bird +{ + int harmonic_4[4]; + + int frame,length; + struct synth_bird_signature + { + float c0, c1, c2, c3, length, pause; + + int lfo_hz, + fm; /* lfo modulation depth (+/- hz) */ + } + pattern[]; +}; + +static int birdsynth_pattern_count_signatures( const char *pattern ) +{ + /* {200,5000,1000,200,20,10,30,200}, {...} */ + + int signatures = 0; + + const char *c = pattern; + while( *c ) + { + if( *c == '{' ) + signatures ++; + + c ++; + } + + return signatures; +} + +static void birdsynth_pattern_decode( struct synth_bird *bird, + const char *pattern ) +{ + bird->length = 0; + + const char *c = pattern; + while( *c ) + { + if( *c == '{' ) + { + struct synth_bird_signature *sig = &bird->pattern[ bird->length ++ ]; + sig->c0 = 3000.0f; + sig->c1 = -800.0f; + sig->c2 = 1500.0f; + sig->c3 = -860.0f; + sig->length = 0.5f; + sig->pause = 0.1f; + sig->lfo_hz = 30; + sig->fm = 100; + + sscanf( c, "{%f,%f,%f,%f,%f,%f,%d,%d}", + &sig->c0, &sig->c1, &sig->c2, &sig->c3, + &sig->length, &sig->pause, &sig->lfo_hz, &sig->fm ); + } + + c ++; + } +} + +static struct synth_bird *birdsynth_malloc_create( const char *pattern ) +{ + int s = birdsynth_pattern_count_signatures( pattern ); + if( s == 0 ) + s = 1; + + struct synth_bird *bird = malloc( sizeof(struct synth_bird) + + s * sizeof(struct synth_bird_signature) ); + + birdsynth_pattern_decode( bird, pattern ); + return bird; +} + +int main( int argc, char *argv[] ) +{ + vg_dsp_init(); + + struct synth_bird *bird = birdsynth_malloc_create( "" ); + + int f0=2500, o1=0, f1=30; + int adsr=0; + + int o0[4]={0,0,0,0}; + + float c0=0.0f, c1=0.0f, c2=0.0f, c3=0.0f; /* signature coefficients */ + int l=44100/2, /* length of thing */ + x=0, /* frame of thing */ + a=0; /* 0: silence 1: signature */ + + float poly=0.0f; + + for(int _=0;_<44100*30;_++){ + x ++; + if( x >= l ){ /* do new thing */ + if( a ){ + int retrigger = rand() % 4; + a=0; + if( retrigger ) l=rand_seconds(0.03,0.1); + else l=rand_seconds(0.5,10.0); + } + else{ + c0=rand_float(-1.0,1.0); + c1=rand_float(-1.0,1.0); + c2=rand_float(-0.1,0.1); + c3=rand_float(-0.5,1.5); + l=rand_seconds(0.05,0.5); + a=1; + } + x=0; + } + + if(a){ + adsr += 40; + if( adsr > 44100 ) adsr = 44100; + } + else{ + adsr -= 40; + if( adsr < 0 ) adsr = 0; + } + + o1 += f1; + o1 = o1 % 44100; + + float s1 = sine_44100_1( o1 ); + float level = adsr; + level *= (1.0f/44100.0f); + + if( a ) + { + float t = ((float)x * (1.0f/(float)l))*2.0f - 1.0f, + tt = t*t, + ttt = tt*t; + + poly = c0+t*c1+tt*c2+ttt*c3; + } + + int fm = s1*100.0f*(1.0f+poly*0.5f) + poly*500.0f; + + int ff = f0+fm; + o0[0] = WRAP1S( o0[0] + ff*1 ); + o0[1] = WRAP1S( o0[1] + ff*2 ); + o0[2] = WRAP1S( o0[2] + ff*3 ); + o0[3] = WRAP1S( o0[3] + ff*4 ); + + float v[4]; + sine_44100_4( o0, v ); + float s0 = v[0] + + v[1] * 0.5f + + v[2] * 0.25f + + v[3] * 0.125f; + s0 *= level; + + float stereo[2] = { s0, s0 }; + //vg_dsp_process( stereo, stereo ); + + int16_t l = stereo[0] * 10000.0f, + r = stereo[1] * 10000.0f; + + fwrite( &l, 2,1, stdout ); + fwrite( &r, 2,1, stdout ); + } +} + + +static float dsp_buffer[(1024*1024)/4]; +static int dsp_allocations = 0; + +struct dsp_delay +{ + int length, cur; + float *buffer; +}; + +struct dsp_lpf +{ + float exponent; + float *buffer; +}; + +struct dsp_schroeder +{ + struct dsp_delay M; + float gain; +}; + +static float *dsp_allocate( int l ) +{ + float *buf = &dsp_buffer[ dsp_allocations ]; + dsp_allocations += l; + return buf; +} + +static inline void dsp_read_delay( struct dsp_delay *delay, float *s ) +{ + int 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 ) +{ + int 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*3.1415926535897f*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 inline float vg_lerpf( float a, float b, float t ) +{ + return a + t*(b-a); +} + +static void vg_dsp_init( void ) +{ + /* 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 + rand_float(-1.0,1.0) * 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.8f ); + } +} + +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; + + float echo_tunings[] = { 0.05f, 0.05f, 0.1f, 0.1f, + 0.1f, 0.1f, 0.2f, 0.3f }; + + 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 * echo_tunings[i]*0.997; + } + + float diffused = recieved; + + for( int i=0; i<8; i++ ) + { + dsp_process_schroeder( __diffusion_chain+i, &diffused, &diffused ); + } + + float total = in_total + (diffused*0.1f + recieved*0.9f); + + 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]*0.25f; + stereo_out[1] = stereo_in[1]*0.25f; + stereo_out[0] += diffused*0.5f+recieved*0.9f; + stereo_out[1] += diffused*0.5f+recieved*0.9f; +} + diff --git a/vg_audio.h b/vg_audio.h index fc0a278..0968957 100644 --- a/vg_audio.h +++ b/vg_audio.h @@ -116,6 +116,7 @@ static struct vg_audio_system char name[32]; /* only editable while allocated == 0 */ audio_clip *source; /* ... */ u32 flags; /* ... */ + u32 colour; /* ... */ /* internal non-readable state * -----------------------------*/ @@ -135,9 +136,11 @@ static struct vg_audio_system 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_end, k_channel_activity_error } - activity; + activity, + readable_activity; /* * editable structure, can be modified inside _lock and _unlock @@ -330,6 +333,7 @@ static audio_channel *audio_request_channel( audio_clip *clip, u32 flags ) { ch->source = clip; ch->flags = flags; + ch->colour = 0x00333333; strcpy( ch->name, clip->path ); ch->allocated = 1; @@ -353,6 +357,14 @@ static audio_channel *audio_request_channel( audio_clip *clip, u32 flags ) return NULL; } +static int audio_channel_finished( audio_channel *ch ) +{ + if( ch->readable_activity == k_channel_activity_end ) + return 1; + else + return 0; +} + static audio_channel *audio_relinquish_channel( audio_channel *ch ) { ch->editable_state.relinquished = 1; @@ -430,7 +442,12 @@ static void 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; + + if( range == 0.0f ) + ch->editable_state.spacial_falloff[3] = 1.0f; + else + ch->editable_state.spacial_falloff[3] = 1.0f/range; + ch->editble_state_write_mask |= AUDIO_EDIT_SPACIAL; } else @@ -864,10 +881,19 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count ) if( !ch->allocated ) continue; + if( ch->activity == k_channel_activity_alive ) + { + if( (ch->cursor >= ch->source_length) && + !(ch->flags & AUDIO_FLAG_LOOP) ) + { + ch->activity = k_channel_activity_end; + } + } + /* process relinquishments */ if( (ch->activity != k_channel_activity_reset) && ch->_.relinquished ) { - if( (ch->cursor >= ch->source_length && !(ch->flags & AUDIO_FLAG_LOOP)) + if( (ch->activity == k_channel_activity_end) || (ch->_.volume == 0.0f) || (ch->activity == k_channel_activity_error) ) { @@ -1044,12 +1070,14 @@ VG_STATIC void audio_mixer_callback( void *user, u8 *stream, int byte_count ) vg_profile_end( &_vg_prof_dsp ); - /* - * Relinquishing conditions - * ------------------------------------------------------------------ - */ audio_lock(); + for( int i=0; ireadable_activity = ch->activity; + } + /* Profiling information * ----------------------------------------------- */ vg_profile_increment( &_vg_prof_audio_decode ); @@ -1232,6 +1260,15 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) "Vorbis" }; + const char *activties[] = + { + "reset", + "wake ", + "alive", + "end ", + "error" + }; + int format_index = 0; if( ch->source->flags & AUDIO_FLAG_STEREO ) @@ -1241,12 +1278,13 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) else format_index = 1; - snprintf( perf, 127, "%02d %c%c%cD %s %4.2fv'%s'", + snprintf( perf, 127, "%02d %c%c%cD %s [%s] %4.2fv'%s'", i, - (ch->editable_state.relinquished)? 'r': ' ', - 0? 'r': ' ', + (ch->editable_state.relinquished)? 'r': '_', + 0? 'r': '_', 0? '3': '2', formats[format_index], + activties[ch->readable_activity], ch->editable_state.volume, ch->name ); @@ -1256,7 +1294,7 @@ VG_STATIC void audio_debug_ui( m4x4f mtx_pv ) } else { - ui_fill_rect( vg_uictx.cursor, 0xa0333333 ); + ui_fill_rect( vg_uictx.cursor, 0xa0000000 | ch->colour ); } vg_uictx.cursor[0] += 2; diff --git a/vg_m.h b/vg_m.h index 6542605..ee8b107 100644 --- a/vg_m.h +++ b/vg_m.h @@ -2046,6 +2046,7 @@ static int spherecast_triangle( v3f tri[3], static inline float vg_randf(void) { + /* TODO: replace with our own rand */ return (float)rand()/(float)(RAND_MAX); } diff --git a/vg_mem.h b/vg_mem.h index ccb652c..666a4c6 100644 --- a/vg_mem.h +++ b/vg_mem.h @@ -216,6 +216,9 @@ VG_STATIC void vg_linear_del( void *buffer, void *data ) __attribute__((warn_unused_result)) VG_STATIC void *vg_linear_extend( void *buffer, void *data, u32 extra ) { + if( !data ) + return vg_linear_alloc( buffer, extra ); + vg_linear_allocator *alloc = vg_linear_header( buffer ); if( alloc->last_alloc != data ) -- 2.25.1