--- /dev/null
+#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; i<delay->length; 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;
+}
+
char name[32]; /* only editable while allocated == 0 */
audio_clip *source; /* ... */
u32 flags; /* ... */
+ u32 colour; /* ... */
/* internal non-readable state
* -----------------------------*/
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
{
ch->source = clip;
ch->flags = flags;
+ ch->colour = 0x00333333;
strcpy( ch->name, clip->path );
ch->allocated = 1;
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;
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
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) )
{
vg_profile_end( &_vg_prof_dsp );
- /*
- * Relinquishing conditions
- * ------------------------------------------------------------------
- */
audio_lock();
+ for( int i=0; i<AUDIO_CHANNELS; i ++ )
+ {
+ audio_channel *ch = &vg_audio.channels[i];
+ ch->readable_activity = ch->activity;
+ }
+
/* Profiling information
* ----------------------------------------------- */
vg_profile_increment( &_vg_prof_audio_decode );
"Vorbis"
};
+ const char *activties[] =
+ {
+ "reset",
+ "wake ",
+ "alive",
+ "end ",
+ "error"
+ };
+
int format_index = 0;
if( ch->source->flags & AUDIO_FLAG_STEREO )
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 );
}
else
{
- ui_fill_rect( vg_uictx.cursor, 0xa0333333 );
+ ui_fill_rect( vg_uictx.cursor, 0xa0000000 | ch->colour );
}
vg_uictx.cursor[0] += 2;