bird
authorhgn <hgodden00@gmail.com>
Mon, 13 Mar 2023 16:16:13 +0000 (16:16 +0000)
committerhgn <hgodden00@gmail.com>
Mon, 13 Mar 2023 16:16:13 +0000 (16:16 +0000)
projects/birds.c [new file with mode: 0644]
vg_audio.h
vg_m.h
vg_mem.h

diff --git a/projects/birds.c b/projects/birds.c
new file mode 100644 (file)
index 0000000..95d9249
--- /dev/null
@@ -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; 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;
+}
+
index fc0a278facf8c8c8756b866c8dab4c8a0110511a..0968957a64dbe7983f9e7d629960647bf3b83a0b 100644 (file)
@@ -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; 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 );
@@ -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 65426053001da34bf4c1dccb8e90fe7b8fa7cab0..ee8b1074ce93bad2bdc96682544225198bd350e0 100644 (file)
--- 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);
 }
 
index ccb652c70bc424c9457258651fe5452ef2de5dcd..666a4c65cdea9d9aa99b5e38fe9764fb3d69100a 100644 (file)
--- 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 )