simplify gitignore
[vg.git] / src / vg / vg_audio.h
index bb4d0920feb889361506d16db83393fefd861138..80307b85951810fc49c6d2d929d5515c94120dbc 100644 (file)
@@ -6,7 +6,15 @@
 #define MA_NO_GENERATION
 #define MA_NO_DECODING
 #define MA_NO_ENCODING
+#define MA_NO_WAV
+#define MA_NO_FLAC
+#define MA_NO_MP3
+#define MA_NO_ENGINE
+#define MA_NO_NODE_GRAPH
+#define MA_NO_RESOURCE_MANAGER
+
 #include "dr_soft/miniaudio.h"
+
 #include "vg/vg.h"
 #include "vg/vg_stdint.h"
 #include "vg/vg_platform.h"
 #include "vg/vg_console.h"
 #include "vg/vg_store.h"
 
-#include <time.h>
+#include <sys/time.h>
+
+#ifdef __GNUC__
+  #ifndef __clang__
+    #pragma GCC push_options
+    #pragma GCC optimize ("O3")
+    #pragma GCC diagnostic push
+    #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+  #endif
+#endif
 
 #define STB_VORBIS_MAX_CHANNELS 2
 #include "stb/stb_vorbis.h"
 
+#ifdef __GNUC__
+  #ifndef __clang__
+    #pragma GCC pop_options
+    #pragma GCC diagnostic pop 
+  #endif
+#endif
+
 #define SFX_MAX_SYSTEMS       32
 #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 FADEOUT_LENGTH        4410
+#define FADEOUT_LENGTH        1100
 #define FADEOUT_DIVISOR       (1.0f/(float)FADEOUT_LENGTH)
 
 #define AUDIO_DECODE_SIZE     (1024*256)  /* 256 kb decoding buffers */
@@ -34,8 +60,7 @@
 enum audio_source_mode
 {
    k_audio_source_mono,
-   k_audio_source_mono_compressed,
-   k_audio_source_stereo_compressed
+   k_audio_source_compressed,
 };
 
 typedef struct audio_clip audio_clip;
@@ -44,10 +69,8 @@ struct audio_clip
    const char *path;
    enum audio_source_mode source_mode;
 
-   /* result */
+   u32 size;
    void *data;
-   u32   len;  /* decompressed: sample count,
-                  compressed:   file size      */
 };
 
 typedef struct audio_mix_info audio_mix_info;
@@ -65,7 +88,7 @@ struct audio_player
 {
    aatree_ptr active_entity;  /* non-nil if currently playing */
    audio_mix_info info;
-   int enqued;
+   int enqued, init;
    
    /* Diagnostic */
    const char *name;
@@ -97,14 +120,15 @@ static struct vg_audio_system
    ma_device         miniaudio_device;
    ma_device_config  miniaudio_dconfig;
 
-   void             *mem, *decode_mem;
-   u32               mem_current,
-                     mem_total;
+   void             *audio_pool, 
+                    *decode_buffer;
+   u32               samples_last;
 
    /* synchro */
    int               sync_locked;
-   MUTEX_TYPE        mutex_checker;
-   MUTEX_TYPE        mutex_sync;
+
+   vg_mutex          mux_checker,
+                     mux_sync;
 
    /* Audio engine, thread 1 */
    struct active_audio_player
@@ -127,78 +151,72 @@ static struct vg_audio_system
    /* System queue, and access from thread 0 */
    audio_entity      entity_queue[SFX_MAX_SYSTEMS];
    int               queue_len;
-
-   char              performance_info[128];
-   int               debug_ui;
+   int               debug_ui, debug_ui_3d;
 
    v3f               listener_pos,
                      listener_ears;
 }
 vg_audio;
 
-static void *audio_alloc( u32 size )
-{
-   u32 new_current = vg_audio.mem_current + size;
-   if( new_current > vg_audio.mem_total )
-   {
-      vg_error( "audio pool over budget!\n" );
-      free( vg_audio.mem );
-      return NULL;
-   }
-
-   void *ptr = vg_audio.mem + vg_audio.mem_current;
-   vg_audio.mem_current = new_current;
-
-   return ptr;
-}
-
+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_audio_decode,
+   vg_prof_audio_mix;
 
 /* 
  * These functions are called from the main thread and used to prevent bad 
  * access. TODO: They should be no-ops in release builds.
  */
-static int audio_lock_checker_load(void)
+VG_STATIC int audio_lock_checker_load(void)
 {
    int value;
-   MUTEX_LOCK( vg_audio.mutex_checker );
+   vg_mutex_lock( &vg_audio.mux_checker );
    value = vg_audio.sync_locked;
-   MUTEX_UNLOCK( vg_audio.mutex_checker );
+   vg_mutex_unlock( &vg_audio.mux_checker );
    return value;
 }
 
-static void audio_lock_checker_store( int value )
+VG_STATIC void audio_lock_checker_store( int value )
 {
-   MUTEX_LOCK( vg_audio.mutex_checker );
+   vg_mutex_lock( &vg_audio.mux_checker );
    vg_audio.sync_locked = value;
-   MUTEX_UNLOCK( vg_audio.mutex_checker );
+   vg_mutex_unlock( &vg_audio.mux_checker );
 }
 
-static void audio_require_lock(void)
+VG_STATIC void audio_require_lock(void)
 {
    if( audio_lock_checker_load() )
       return;
 
-   vg_exiterr( "Modifying sound effects systems requires locking\n" );
+   vg_error( "Modifying sound effects systems requires locking\n" );
+   abort();
 }
 
-static void audio_lock(void)
+VG_STATIC void audio_lock(void)
 {
-   MUTEX_LOCK( vg_audio.mutex_sync );
+   vg_mutex_lock( &vg_audio.mux_sync );
    audio_lock_checker_store(1);
 }
 
-static void audio_unlock(void)
+VG_STATIC void audio_unlock(void)
 {
    audio_lock_checker_store(0);
-   MUTEX_UNLOCK( vg_audio.mutex_sync );
+   vg_mutex_unlock( &vg_audio.mux_sync );
 }
 
 
-static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf,
+VG_STATIC void audio_mixer_callback( ma_device *pDevice, void *pOutBuf,
                                     const void *pInput, ma_uint32 frameCount );
 
-static int vg_audio_init(void)
+VG_STATIC void vg_audio_init(void)
 {
+   vg_mutex_init( &vg_audio.mux_checker );
+   vg_mutex_init( &vg_audio.mux_sync );
+
+   /* TODO: Move here? */
    vg_convar_push( (struct vg_convar){
       .name = "debug_audio",
       .data = &vg_audio.debug_ui,
@@ -207,11 +225,16 @@ static int vg_audio_init(void)
       .persistent = 1
    });
 
-   u32 decode_region    = AUDIO_DECODE_SIZE * SFX_MAX_SYSTEMS;
-   vg_audio.mem_total   = 1024*1024*32;
-   vg_audio.mem_current = 0;
-   vg_audio.mem         = malloc( vg_audio.mem_total + decode_region );
-   vg_audio.decode_mem  = &((u8 *)vg_audio.mem)[vg_audio.mem_total];
+   /* allocate memory */
+
+   /* 32mb fixed */
+   vg_audio.audio_pool = 
+      vg_create_linear_allocator( vg_mem.rtmemory, 1024*1024*32, 
+                                  VG_MEMORY_SYSTEM );
+
+   /* fixed */
+   u32 decode_size = AUDIO_DECODE_SIZE * SFX_MAX_SYSTEMS;
+   vg_audio.decode_buffer = vg_linear_alloc( vg_mem.rtmemory, decode_size );
 
    /* setup pool */
    vg_audio.active_pool_info.base = vg_audio.active_players;
@@ -224,11 +247,12 @@ static int vg_audio_init(void)
    ma_device_config *dconf  = &vg_audio.miniaudio_dconfig;
    ma_device        *device = &vg_audio.miniaudio_device;
 
-    *dconf = ma_device_config_init( ma_device_type_playback );
+   *dconf = ma_device_config_init( ma_device_type_playback );
    dconf->playback.format    = ma_format_f32;
    dconf->playback.channels  = 2;
    dconf->sampleRate         = 44100;
    dconf->dataCallback       = audio_mixer_callback;
+   dconf->periodSizeInFrames = 441;
 
    dconf->pUserData = NULL;
 
@@ -236,28 +260,29 @@ static int vg_audio_init(void)
    
    if( ma_device_init( NULL, dconf, device) != MA_SUCCESS )
    {
-      vg_error( "ma_device failed to initialize" );
-      return 0;
+      vg_fatal_exit_loop( "(audio) ma_device failed to initialize" );
    } 
    else 
    {   
       if( ma_device_start( device ) != MA_SUCCESS )
       {
          ma_device_uninit( device );
-         vg_error( "ma_device failed to start" );
-         return 0;
+         vg_fatal_exit_loop( "(audio) ma_device failed to start" );
       }
    }
-
-   return 1;
+   
+   vg_success( "Ready\n" );
 }
 
-static void vg_audio_free(void)
+VG_STATIC void vg_audio_free(void * nothing)
 {
    ma_device        *device = &vg_audio.miniaudio_device;
    ma_device_uninit( device );
 
-   free( vg_audio.mem );
+#if 0
+   vg_free( vg_audio.mem );
+   vg_audio.mem = NULL;
+#endif
 }
 
 /* 
@@ -278,7 +303,7 @@ static aatree_ptr audio_alloc_entity_internal(void)
    return playerid;
 }
 
-static void audio_entity_free_internal( aatree_ptr id )
+VG_STATIC void audio_entity_free_internal( aatree_ptr id )
 {
    struct active_audio_player *aap = &vg_audio.active_players[ id ];
    aap->active = 0;
@@ -292,15 +317,15 @@ static void audio_entity_free_internal( aatree_ptr id )
                      &vg_audio.active_pool_head );
 }
 
-static void *audio_entity_vorbis_ptr( aatree_ptr entid )
+VG_STATIC void *audio_entity_vorbis_ptr( aatree_ptr entid )
 {
-   u8 *buf = (u8*)vg_audio.decode_mem,
+   u8 *buf = (u8*)vg_audio.decode_buffer,
       *loc = &buf[AUDIO_DECODE_SIZE*entid];
 
    return (void *)loc;
 }
 
-static void audio_entity_start( audio_entity *src )
+VG_STATIC void audio_entity_start( audio_entity *src )
 {
    aatree_ptr entid = audio_alloc_entity_internal();
    if( entid == AATREE_PTR_NIL )
@@ -309,7 +334,7 @@ static void audio_entity_start( audio_entity *src )
    audio_entity *ent = &vg_audio.active_players[ entid ].ent;
 
    ent->info = src->info;
-   ent->name = "todo";
+   ent->name = src->info.source->path;
    ent->cur  = 0;
    ent->player = src->player;
 
@@ -323,8 +348,7 @@ static void audio_entity_start( audio_entity *src )
       src->player->active_entity = entid;
    }
 
-   if( src->info.source->source_mode == k_audio_source_mono_compressed ||
-       src->info.source->source_mode == k_audio_source_stereo_compressed )
+   if( src->info.source->source_mode == k_audio_source_compressed )
    {
       /* Setup vorbis decoder */
       struct active_audio_player *aap = &vg_audio.active_players[ entid ];
@@ -336,7 +360,8 @@ static void audio_entity_start( audio_entity *src )
 
       int err;
       stb_vorbis *decoder = stb_vorbis_open_memory( 
-            src->info.source->data, src->info.source->len, &err, &alloc );
+            src->info.source->data,
+            src->info.source->size, &err, &alloc );
 
       if( !decoder )
       {
@@ -355,14 +380,14 @@ static void audio_entity_start( audio_entity *src )
    }
    else
    {
-      ent->length = src->info.source->len;
+      ent->length = src->info.source->size;
    }
 }
 
 /*
  * Read everything from the queue 
  */
-static void audio_system_enque(void)
+VG_STATIC void audio_system_enque(void)
 {
    /* Process incoming sound queue */
    audio_lock();
@@ -387,7 +412,7 @@ static void audio_system_enque(void)
             audio_entity *ent = &vg_audio.active_players[ entid ].ent;
             if( !ent->fadeout )
             {
-               ent->fadeout = 1;
+               ent->fadeout = FADEOUT_LENGTH;
                ent->fadeout_current = FADEOUT_LENGTH;
             }
 
@@ -415,6 +440,12 @@ static void audio_system_enque(void)
          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;
+            }
          }
       }
    }
@@ -425,7 +456,7 @@ static void audio_system_enque(void)
 /* 
  * Redistribute sound systems 
  */
-static void audio_system_cleanup(void)
+VG_STATIC void audio_system_cleanup(void)
 {
    audio_lock();
 
@@ -452,31 +483,123 @@ static void audio_system_cleanup(void)
 /* 
  * Get effective volume and pan from this entity
  */
-static void audio_entity_spacialize( audio_entity *ent, float *vol, float *pan )
+VG_STATIC void audio_entity_spacialize( audio_entity *ent, float *vol, float *pan )
 {
+   if( ent->info.vol < 0.01f )
+   {
+      *vol = ent->info.vol;
+      *pan = 0.0f;
+      return;
+   }
+
    v3f delta;
    v3_sub( ent->info.world_position, vg_audio.listener_pos, delta );
 
-   float dist = v3_length( delta ),
-         attn = (dist / ent->info.vol) +1.0f;
+   float dist2 = v3_length2( delta );
 
-   v3_muls( delta, 1.0f/dist, delta );
+   if( dist2 < 0.0001f )
+   {
+      *pan = 0.0f;
+      *vol = 1.0f;
+   }
+   else
+   {
+      float dist = sqrtf( dist2 ),
+            attn = (dist / ent->info.vol) +1.0f;
 
-   *pan = v3_dot( vg_audio.listener_ears, delta );
-   *vol = 1.0f/(attn*attn);
+      v3_muls( delta, 1.0f/dist, delta );
+      *pan = v3_dot( vg_audio.listener_ears, delta );
+      *vol = 1.0f/(attn*attn);
+   }
 }
 
-static void audio_decode_uncompressed_mono( float *src, u32 count, float *dst )
+VG_STATIC void audio_decode_uncompressed_mono( i16 *src, u32 count, float *dst )
 {
    for( u32 i=0; i<count; i++ )
    {
-      dst[ i*2 + 0 ] = src[i];
-      dst[ i*2 + 1 ] = src[i];
+      dst[ i*2 + 0 ] = ((float)src[i]) * (1.0f/32767.0f);
+      dst[ i*2 + 1 ] = ((float)src[i]) * (1.0f/32767.0f);
+   }
+}
+
+/* 
+ * adapted from stb_vorbis.h, since the original does not handle mono->stereo
+ */
+VG_STATIC int 
+stb_vorbis_get_samples_float_interleaved_stereo( stb_vorbis *f, float *buffer, 
+                                                 int len )
+{
+   int n = 0,
+       c = VG_MIN( 1, f->channels - 1 );
+
+   while( n < len ) 
+   {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+
+      if( n+k >= len ) 
+         k = len - n;
+
+      for( int j=0; j < k; ++j ) 
+      {
+         *buffer++ = f->channel_buffers[ 0 ][f->channel_buffer_start+j];
+         *buffer++ = f->channel_buffers[ c ][f->channel_buffer_start+j];
+      }
+
+      n += k;
+      f->channel_buffer_start += k;
+
+      if( n == len )
+         break;
+
+      if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
+         break;
+   }
+
+   return n;
+}
+
+/* 
+ * ........ more wrecked code sorry!
+ */
+VG_STATIC int 
+stb_vorbis_get_samples_i16_downmixed( stb_vorbis *f, i16 *buffer, int len )
+{
+   int n = 0,
+       c = VG_MIN( 1, f->channels - 1 );
+
+   while( n < len ) 
+   {
+      int k = f->channel_buffer_end - f->channel_buffer_start;
+
+      if( n+k >= len ) 
+         k = len - n;
+
+      for( int j=0; j < k; ++j ) 
+      {
+         float sl = f->channel_buffers[ 0 ][f->channel_buffer_start+j],
+               sr = f->channel_buffers[ c ][f->channel_buffer_start+j];
+
+         *buffer++ = vg_clampf( 0.5f*(sl+sr), -1.0f, 1.0f ) * 32767.0f;
+         //*buffer++ = vg_clampf( sr, -1.0f, 1.0f ) * 32767.0f;
+      }
+
+      n += k;
+      f->channel_buffer_start += k;
+
+      if( n == len )
+         break;
+
+      if( !stb_vorbis_get_frame_float( f, NULL, NULL ))
+         break;
    }
+
+   return n;
 }
 
-static void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
+VG_STATIC void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
 {
+   vg_profile_begin( &_vg_prof_audio_decode );
+
    struct active_audio_player *aap = &vg_audio.active_players[id];
    audio_entity *ent = &aap->ent;
 
@@ -490,19 +613,22 @@ static void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
       remaining -= samples_this_run;
 
       float *dst = &buf[ buffer_pos * 2 ]; 
+
+      int source_mode = ent->info.source->source_mode;
       
-      if( ent->info.source->source_mode == k_audio_source_mono )
+      if( source_mode == k_audio_source_mono )
       {
-         float *src = &((float *)ent->info.source->data)[ cursor ];
+         i16 *src_buffer = ent->info.source->data,
+             *src = &src_buffer[cursor];
+
          audio_decode_uncompressed_mono( src, samples_this_run, dst );
       }
-      else if( ent->info.source->source_mode == k_audio_source_mono_compressed )
+      else if( source_mode == k_audio_source_compressed )
       {
-         int read_samples = stb_vorbis_get_samples_float_interleaved( 
+         int read_samples = stb_vorbis_get_samples_float_interleaved_stereo
                aap->vorbis_handle,
-               2,
                dst,
-               samples_this_run * 2 );
+               samples_this_run  );
 
          if( read_samples != samples_this_run )
          {
@@ -515,8 +641,7 @@ static void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
       
       if( (ent->info.flags & AUDIO_FLAG_LOOP) && remaining )
       {
-         if( ent->info.source->source_mode == k_audio_source_mono_compressed ||
-             ent->info.source->source_mode == k_audio_source_stereo_compressed )
+         if( source_mode == k_audio_source_compressed )
          {
             stb_vorbis_seek_start( aap->vorbis_handle );
          }
@@ -538,10 +663,11 @@ static void audio_entity_get_samples( aatree_ptr id, u32 count, float *buf )
    }
 
    ent->cur = cursor;
+   vg_profile_end( &_vg_prof_audio_decode );
 }
 
-static void audio_entity_mix( aatree_ptr id, float *buffer, 
-                              u32 frame_count )
+VG_STATIC void audio_entity_mix( aatree_ptr id, float *buffer, 
+                                u32 frame_count )
 {
    audio_entity *ent = &vg_audio.active_players[id].ent;
 
@@ -556,6 +682,8 @@ static void audio_entity_mix( aatree_ptr id, float *buffer,
 
    audio_entity_get_samples( id, frame_count, pcf );
 
+   vg_profile_begin( &_vg_prof_audio_mix );
+
    if( ent->info.flags & AUDIO_FLAG_SPACIAL_3D )
       audio_entity_spacialize( ent, &vol, &pan );
 
@@ -585,21 +713,14 @@ static void audio_entity_mix( aatree_ptr id, float *buffer,
       
       buffer_pos ++;
    }
-}
 
-static void vg_sleep_ms( long msec )
-{
-    struct timespec ts;
-
-    ts.tv_sec = msec / 1000;
-    ts.tv_nsec = (msec % 1000) * 1000000;
-    nanosleep( &ts, &ts );
+   vg_profile_end( &_vg_prof_audio_mix );
 }
 
 /*
  * callback from miniaudio.h interface
  */
-static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, 
+VG_STATIC void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, 
                                   const void *pInput, ma_uint32 frame_count )
 {
    struct timespec time_start, time_end;
@@ -618,12 +739,10 @@ static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf,
       struct active_audio_player *aap = &vg_audio.active_players[i];
 
       if( aap->active )
+      {
          audio_entity_mix( i, pOut32F, frame_count );
+      }
    }
-   
-#if 0
-   vg_sleep_ms( 20 );
-#endif
 
    /* redistribute */
    audio_system_cleanup();
@@ -631,159 +750,89 @@ static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf,
    /* TODO: what the hell is this? */
    (void)pInput;
    
-   /*
-    * Debug information
-    */
-   clock_gettime( CLOCK_REALTIME, &time_end );
 
-   double elapsed = 1000.0*time_end.tv_sec + 1e-6*time_end.tv_nsec
-                       - (1000.0*time_start.tv_sec + 1e-6*time_start.tv_nsec),
-          budget  = ((double)frame_count / 44100.0) * 1000.0,
-          percent = (elapsed/budget) * 100.0;
+   audio_lock();
+   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;
 
-   snprintf( vg_audio.performance_info, 127, 
-                  "%.1fms/%.1fms (%.1f%%) (%u frames)",
-                  elapsed, budget, percent, frame_count );
+   vg_audio.samples_last = frame_count;
+   audio_unlock();
 }
 
-/* Decompress entire vorbis stream into buffer */
-static float *audio_decompress_vorbis( const unsigned char *data, int len, 
-                                       int channels, u32 *samples )
+VG_STATIC void audio_clip_load( audio_clip *clip, void *lin_alloc )
 {
-   int err;
-   stb_vorbis *pv = stb_vorbis_open_memory( data, len, &err, NULL );
-   
-   if( !pv )
-   {
-      vg_error( "stb_vorbis_open_memory() failed with error code: %i\n", err );
-      return NULL;
-   }
-      
-   u32 length_samples = stb_vorbis_stream_length_in_samples( pv );
+   if( lin_alloc == NULL )
+      lin_alloc = vg_audio.audio_pool;
 
-   vg_info( "decompress_vorbis: %u samples (%.2fs), %.1fkb\n",
-               length_samples, 
-               (float)length_samples / (44100.0f*(float)channels),
-               (float)(length_samples*4*channels) / 1024.0f );
-   
-   float *buffer = audio_alloc( length_samples * channels * sizeof(float) );
-   if( !buffer )
+   if( clip->source_mode == k_audio_source_mono )
    {
-      stb_vorbis_close( pv );
-      vg_exit();
-   }
-   
-   int read_samples = stb_vorbis_get_samples_float_interleaved( 
-                           pv, channels, buffer, length_samples * channels );
+      vg_linear_clear( vg_mem.scratch );
+      u32 fsize;
 
-   if( read_samples != length_samples )
-   {
-      vg_warn( "| warning: sample count mismatch. Expected %u got %i\n", 
-                  length_samples, read_samples );
-      length_samples = read_samples;
-   }
-   
-   stb_vorbis_close( pv );
-   *samples = length_samples;
-   return buffer;
-}
+      stb_vorbis_alloc alloc = {
+         .alloc_buffer = vg_linear_alloc( vg_mem.scratch, AUDIO_DECODE_SIZE ),
+         .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE
+      };
 
-static int audio_clip_load( audio_clip *clip )
-{
-   /* Load and decompress */
-   i64 file_len;
-   void *filedata = vg_asset_read_s( clip->path, &file_len );
+      void *filedata = vg_file_read( vg_mem.scratch, clip->path, &fsize );
 
-   if( !filedata )
-   {
-      vg_error( "OGG load failed (%s)\n", clip->path );
-      return 0;
-   }
-
-   if( clip->source_mode == k_audio_source_mono )
-   {
-      u32 samples;
-      float *sound = audio_decompress_vorbis( filedata, file_len, 1, &samples );
-      clip->data = sound;
-      clip->len = samples;
+      int err;
+      stb_vorbis *decoder = stb_vorbis_open_memory( 
+                            filedata, fsize, &err, &alloc );
 
-      float seconds = (float)samples / 44100.0f,
-            mb      = (float)(samples*4) / (1024.0f*1024.0f);
+      if( !decoder )
+      {
+         vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", 
+                     clip->path, err );
+         vg_fatal_exit_loop( "Vorbis decode error" );
+      }
 
-      vg_info( "Loaded audio clip[mono] '%s' (%.1fs, %.1fmb)\n", 
-                                 clip->path, seconds, mb );
-   }
-   else if( clip->source_mode == k_audio_source_mono_compressed )
-   {
-      void *data = audio_alloc( file_len );
-      memcpy( data, filedata, file_len );
+      /* only mono is supported in uncompressed */
+      u32 length_samples = stb_vorbis_stream_length_in_samples( decoder ),
+          data_size      = length_samples * sizeof(i16);
 
-      clip->data = data;
-      clip->len = file_len;
+      audio_lock();
+      clip->data = vg_linear_alloc( lin_alloc, data_size );
+      clip->size = length_samples;
+      audio_unlock();
 
-      float mb = (float)(file_len) / (1024.0f*1024.0f);
-      vg_info( "Loaded audio clip[mono_compressed] '%s' (%.1fmb)\n", 
-                                          clip->path, mb );
-   }
-   else if( clip->source_mode == k_audio_source_stereo_compressed )
-   {
-      /* ... */
+      int read_samples = stb_vorbis_get_samples_i16_downmixed( 
+                              decoder, clip->data, length_samples );
 
-      clip->data = NULL;
-      clip->len = 0;
+      if( read_samples != length_samples )
+         vg_fatal_exit_loop( "Decode error" );
 
-      vg_error( "Source mode (%u) currently unsupported\n", clip->source_mode );
-      return 0;
+      float mb = (float)(data_size) / (1024.0f*1024.0f);
+      vg_info( "Loaded audio clip '%s' (%.1fmb) %u samples\n", clip->path, mb,
+               length_samples );
    }
-   else
+
+   /* 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();
 
-      clip->data = NULL;
-      clip->len = 0;
+      if( !clip->data )
+         vg_fatal_exit_loop( "Audio failed to load" );
 
-      vg_error( "Unkown source mode (%u)\n", clip->source_mode );
-      return 0;
+      float mb = (float)(clip->size) / (1024.0f*1024.0f);
+      vg_info( "Loaded audio clip '%s' (%.1fmb)\n", clip->path, mb );
    }
-
-   return 1;
 }
 
-static void audio_clip_loadn( audio_clip *arr, int count )
+VG_STATIC void audio_clip_loadn( audio_clip *arr, int count, void *lin_alloc )
 {
    for( int i=0; i<count; i++ )
-      audio_clip_load( &arr[i] );
-}
-
-#if 0
-/*
- * Client code
- */
-static void audio_pack_play( audio_pack *source, audio_player *sys, int id )
-{
-   audio_require_lock();
-
-   sys->fadeout = 0;
-   sys->fadeout_current = 0;
-   sys->source = source->data;
-   sys->cur    = source->segments[ id*2 + 0 ];
-   sys->end    = source->segments[ id*2 + 1 ];
-   sys->ch     = source->ch;
-   sys->source_mode = source->source_mode;
-   
-   /* for diagnostics */
-   sys->clip_start = sys->cur;
-   sys->clip_end = sys->end;
-   sys->buffer_length = source->segments[ (source->numsegments-1)*2 + 1 ];
-   sys->is_playing = 1;
-   
-   audio_player_push( sys );
+      audio_clip_load( &arr[i], lin_alloc );
 }
 
-#endif
-
 /* Mark change to be uploaded through queue system */
-static void audio_player_commit( audio_player *sys )
+VG_STATIC void audio_player_commit( audio_player *sys )
 {
    audio_require_lock();
    
@@ -795,7 +844,7 @@ static void audio_player_commit( audio_player *sys )
 
    if( sys->enqued )
    {
-      vg_warn( "Audio commit spamming; already enqued (%s)\n", sys->name );
+      vg_warn( "[2] Audio commit spamming; already enqued (%s)\n", sys->name );
       return;
    }
    
@@ -803,22 +852,81 @@ static void audio_player_commit( audio_player *sys )
    audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ];
    ent->info = sys->info;
    ent->player = sys;
-   sys->active_entity = AATREE_PTR_NIL;
+}
+
+VG_STATIC void audio_require_init( audio_player *player )
+{
+   if( player->init )
+      return;
+
+   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 )
+      return;
+
+   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 */
-static void audio_player_playclip( audio_player *player, audio_clip *clip )
+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 );
 }
 
-static void audio_player_playoneshot( audio_player *player, audio_clip *clip )
+#if 0
+VG_STATIC void audio_player_playoneshot( audio_player *player, audio_clip *clip )
 {
-   
+   audio_require_lock();
+   audio_require_init( player );
+}
+#endif
+
+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;
 }
 
 /*
@@ -829,43 +937,53 @@ static void audio_player_playoneshot( audio_player *player, audio_clip *clip )
  * Safety enforced Get/set attributes
  */
 
-static void audio_player_set_position( audio_player *sys, v3f pos )
+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 );
 }
 
-static void audio_player_set_vol( audio_player *sys, float vol )
+VG_STATIC void audio_player_set_vol( audio_player *sys, float vol )
 {
    audio_require_lock();
    sys->info.vol = vol;
 }
 
-static float audio_player_get_vol( audio_player *sys )
+VG_STATIC float audio_player_get_vol( audio_player *sys )
 {
    audio_require_lock();
    return sys->info.vol;
 }
 
-static void audio_player_set_pan( audio_player *sys, float pan )
+VG_STATIC void audio_player_set_pan( audio_player *sys, float pan )
 {
    audio_require_lock();
    sys->info.pan = pan;
 }
 
-static float audio_player_get_pan( audio_player *sys )
+VG_STATIC float audio_player_get_pan( audio_player *sys )
 {
    audio_require_lock();
    return sys->info.pan;
 }
 
-static void audio_player_set_flags( audio_player *sys, u32 flags )
+VG_STATIC void audio_player_set_flags( audio_player *sys, u32 flags )
 {
    audio_require_lock();
    sys->info.flags = flags;
 }
 
-static u32 audio_player_get_flags( audio_player *sys )
+VG_STATIC u32 audio_player_get_flags( audio_player *sys )
 {
    audio_require_lock();
    return sys->info.flags;
@@ -876,7 +994,7 @@ static u32 audio_player_get_flags( audio_player *sys )
  * Debugging
  */
 
-static void audio_debug_ui(void)
+VG_STATIC void audio_debug_ui( m4x4f mtx_pv )
 {
    if( !vg_audio.debug_ui )
       return;
@@ -886,13 +1004,12 @@ static void audio_debug_ui(void)
        {
                const char *name;
                u32 cursor, flags, length;
+      v3f pos;
       float vol;
        }
        infos[ SFX_MAX_SYSTEMS ];
        int num_systems = 0;
 
-   char perf[128];
-       
    audio_lock();
        
        for( int i=0; i<SFX_MAX_SYSTEMS; i ++ )
@@ -910,58 +1027,115 @@ static void audio_debug_ui(void)
                snd->flags = ent->info.flags;
                snd->length = ent->length;
       snd->vol = ent->info.vol*100.0f;
+      v3_copy( ent->info.world_position, snd->pos );
        }
-   strcpy( perf, vg_audio.performance_info );
+
+   float budget = ((double)vg_audio.samples_last / 44100.0) * 1000.0;
+   vg_profile_drawn( (struct vg_profile *[]){ &vg_prof_audio_decode,
+                                              &vg_prof_audio_mix }, 2, 
+                     budget, (ui_rect){ 4, VG_PROFILE_SAMPLE_COUNT*2 + 8,
+                                        250, 0 }, 3 );
+
    audio_unlock();
 
+   char perf[128];
+       
    /* Draw UI */
-   ui_global_ctx.cursor[0] = 10;
-   ui_global_ctx.cursor[1] = 10;
-   ui_global_ctx.cursor[2] = 150;
-   ui_global_ctx.cursor[3] = 12;
-   ui_text( &ui_global_ctx, ui_global_ctx.cursor, perf, 1, 0 );
+   vg_uictx.cursor[0] = 258;
+   vg_uictx.cursor[1] = VG_PROFILE_SAMPLE_COUNT*2+8+24+12;
+   vg_uictx.cursor[2] = 150;
+   vg_uictx.cursor[3] = 12;
    
-   float usage = (float)vg_audio.mem_current / (1024.0f*1024.0f),
-         total = (float)vg_audio.mem_total   / (1024.0f*1024.0f),
-         percent = (usage/total) * 100.0f;
+   float mb1      = 1024.0f*1024.0f,
+         usage    = vg_linear_get_cur( vg_audio.audio_pool )      / mb1,
+         total    = vg_linear_get_capacity( vg_audio.audio_pool ) / mb1,
+         percent  = (usage/total) * 100.0f;
+
    snprintf( perf, 127, "Mem: %.1f/%.1fmb (%.1f%%)\n", usage, total, percent );
 
-   ui_global_ctx.cursor[1] += 20;
-   ui_text( &ui_global_ctx, ui_global_ctx.cursor, perf, 1, 0 );
+   ui_text( vg_uictx.cursor, perf, 1, 0 );
+   vg_uictx.cursor[1] += 20;
 
-   ui_global_ctx.cursor[1] += 20;
+   ui_rect overlap_buffer[ SFX_MAX_SYSTEMS ];
+   u32 overlap_length = 0;
 
        /* Draw audio stack */
        for( int i=0; i<num_systems; i ++ )
        {
       struct sound_info *inf = &infos[i];
 
-               ui_global_ctx.cursor[2] = 150;
-               ui_global_ctx.cursor[3] = 12;
+               vg_uictx.cursor[2] = 200;
+               vg_uictx.cursor[3] = 18;
                
                u32 alpha = 0xa0000000;
 
-               ui_new_node( &ui_global_ctx );
+               ui_new_node();
                {               
-                       ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x00333333|alpha );
+                       ui_fill_rect( vg_uictx.cursor, 0x00333333|alpha );
 
-                       ui_px baseline = ui_global_ctx.cursor[0],
-               w  = 150,
+                       ui_px baseline = vg_uictx.cursor[0],
+               w  = 200,
                c  = baseline + ((float)inf->cursor / (float)inf->length) * w;
                        
                        /* cursor */
-                       ui_global_ctx.cursor[2] = 2;
-                       ui_global_ctx.cursor[0] = c;
-                       ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0xffffffff );
+                       vg_uictx.cursor[2] = 2;
+                       vg_uictx.cursor[0] = c;
+                       ui_fill_rect( vg_uictx.cursor, 0xffffffff );
                        
-                       ui_global_ctx.cursor[0] = baseline + 2;
-                       ui_global_ctx.cursor[1] += 2;
+                       vg_uictx.cursor[0] = baseline + 2;
+                       vg_uictx.cursor[1] += 2;
          snprintf( perf, 127, "%s %.1f%%", infos[i].name, infos[i].vol );
-                       ui_text( &ui_global_ctx, ui_global_ctx.cursor, perf, 1, 0 );
+                       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;
+
+            v2_muls( wpos, (1.0f/wpos[3]) * 0.5f, wpos );
+            v2_add( wpos, (v2f){ 0.5f, 0.5f }, wpos );
+            
+            ui_rect wr;
+            wr[0] = wpos[0] * vg.window_x;
+            wr[1] = (1.0f-wpos[1]) * vg.window_y;
+            wr[2] = 100;
+            wr[3] = 17;
+            
+            for( int j=0; j<12; j++ )
+            {
+               int collide = 0;
+               for( int k=0; k<overlap_length; k++ )
+               {
+                  ui_px *wk = overlap_buffer[k];
+                  if( ((wr[0] <= wk[0]+wk[2]) && (wr[0]+wr[2] >= wk[0])) &&
+                      ((wr[1] <= wk[1]+wk[3]) && (wr[1]+wr[3] >= wk[1])) )
+                  {
+                     collide = 1;
+                     break;
+                  }
+               }
+
+               if( !collide )
+                  break;
+               else
+                  wr[1] += 18;
+            }
+
+            ui_text( wr, perf, 1, 0 );
+
+            ui_rect_copy( wr, overlap_buffer[ overlap_length ++ ] );
+         }
                }
 
-               ui_end_down( &ui_global_ctx );
-               ui_global_ctx.cursor[1] += 1;
+projected_behind:
+
+               ui_end_down();
+               vg_uictx.cursor[1] += 1;
        }
 }