X-Git-Url: https://harrygodden.com/git/?a=blobdiff_plain;f=vg%2Fvg_audio.h;h=0c9a666691e64f62f4926041d41f4fc0679e33cf;hb=3a55f20f6b627db61294523f7bf34344514e7166;hp=6d797717f5c7bf77987e8b011645707a9a93b40a;hpb=def3a94bd35b2f134ecedb4149eae0bb6821ecd8;p=fishladder.git diff --git a/vg/vg_audio.h b/vg/vg_audio.h index 6d79771..0c9a666 100644 --- a/vg/vg_audio.h +++ b/vg/vg_audio.h @@ -6,79 +6,75 @@ #define STB_VORBIS_MAX_CHANNELS 2 #include "stb/stb_vorbis.h" -#define SFX_MAX_SYSTEMS 16 -#define SFX_FLAG_ONESHOT 0x1 -#define SFX_FLAG_STEREO 0x2 -#define FADEOUT_LENGTH 441 -#define FADEOUT_DIVISOR (1.f/(float)FADEOUT_LENGTH) - -typedef struct sfx_vol sfx_vol_t; -typedef struct sfx_system sfx_system_t; - -struct sfx_vol +#define SFX_MAX_SYSTEMS 32 +#define SFX_FLAG_ONESHOT 0x1 +#define SFX_FLAG_STEREO 0x2 +#define SFX_FLAG_REPEAT 0x4 +#define SFX_FLAG_GHOST 0x8 +#define FADEOUT_LENGTH 4410 +#define FADEOUT_DIVISOR (1.f/(float)FADEOUT_LENGTH) + +typedef struct sfx_vol_control sfx_vol_control; +typedef struct sfx_system sfx_system; + +struct sfx_vol_control { float val; - float cmp; + const char *name; }; struct sfx_system { // Source buffer start - float *source; - float *replacement; + float *source, *replacement; + + u32 clip_start, clip_end, buffer_length; // Modifiers - sfx_vol_t *vol_src; + sfx_vol_control *vol_src; float vol; - float spd; - // Info - int ch; // channels - u32 end; // buffer end - u32 cur; // cursor position + u32 ch, end, cur; u32 flags; // Effects - u32 snh; - u32 fadeout; + u32 fadeout, fadeout_current; - // The 'Opposite' pointer - sfx_system_t *optr; + sfx_system *thread_clone; // Memory of this structure is copied into thread 2 // Diagnostic - float cvol; // Current signal volume + float signal_average; // Current signal volume const char *name; }; -// 0 -int sfx_save( sfx_system_t *sys ); // Mark change to be uploaded to queue system -void sfx_sys_init(void); // Miniaudio.h init -void sfx_sys_free(void); // Shutdown audio device -sfx_system_t *sfx_alloc(void); // Create and return slot for a sound - -// 1 -void sfx_localize(void); // Copy in data from all queued -void sfx_redist(void); // Send out updates to sources -void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, const void *pInput, ma_uint32 frameCount ); // miniaudio.h interface +// Set of up to 8 sound effects packed into one +typedef struct sfx_set sfx_set; +struct sfx_set +{ + float *main; + char *sources; + + u32 segments[16]; //from->to,from->to ... + u32 numsegments; + u32 ch; + u32 flags; +}; ma_device g_aud_device; ma_device_config g_aud_dconfig; -// Thread 2 - background loader ( to be moved ) -// ====================================================== - // Thread 1 - audio engine ( spawned from miniaudio.h ) // ====================================================== -sfx_system_t sfx_sys[SFX_MAX_SYSTEMS]; -int sfx_sys_len = 0; +sfx_system sfx_sys[SFX_MAX_SYSTEMS]; +int sfx_sys_len = 0; // Thread 0 - Critical transfer section // ====================================================== -MUTEX_TYPE sfx_mux_t01; // Resources share: 0 & 1 +MUTEX_TYPE sfx_mux_t01; // Resources share: 0 & 1 -sfx_system_t *sfx_q[SFX_MAX_SYSTEMS]; // Stuff changed -int sfx_q_len = 0; // How much +sfx_system *sfx_q[SFX_MAX_SYSTEMS]; // Stuff changed +int sfx_q_len = 0; // How much // x / 2 // ====================================================== @@ -86,14 +82,8 @@ int sfx_q_len = 0; // How much // g_vol_master is never directly acessed by users float g_master_volume = 1.f; -sfx_vol_t g_vol_music; -sfx_vol_t g_vol_sfx; - -#define SFX_NUM_VOLUMES 2 -sfx_vol_t *g_volumes[] = { &g_vol_music, &g_vol_sfx }; - // Decompress entire vorbis stream into buffer -float *sfx_vorbis_stream( const unsigned char *data, int len, int channels, uint32_t *samples ) +static float *sfx_vorbis_stream( const unsigned char *data, int len, int channels, u32 *samples ) { int err; stb_vorbis *pv = stb_vorbis_open_memory( data, len, &err, NULL ); @@ -126,7 +116,7 @@ float *sfx_vorbis_stream( const unsigned char *data, int len, int channels, uint return buffer; } -float *sfx_vorbis( const char *strFileName, int channels, u32 *samples ) +static float *sfx_vorbis( const char *strFileName, int channels, u32 *samples ) { i64 len; void *filedata = vg_asset_read_s( strFileName, &len ); @@ -188,13 +178,16 @@ int sfx_vorbis_a( const char *path, int channels, void(*OnComplete)(sfx_bgload_t // Asynchronous load-to-system callback struct sfx_vorbis_a_to_inf { - sfx_system_t *sys; - u32 flags; + sfx_system *sys; + u32 flags; }; #define SFX_A_FLAG_AUTOSTART 0x1 #define SFX_A_FLAG_AUTOFREE 0x2 +/* +static int sfx_save( sfx_system *sys ); + // Asynchronous load-to-system callback void sfx_vorbis_a_to_c( sfx_bgload_t *loadinf ) { @@ -202,20 +195,14 @@ void sfx_vorbis_a_to_c( sfx_bgload_t *loadinf ) // Mark buffer for deallocation if autofree is set if( inf->flags & SFX_A_FLAG_AUTOFREE ) - { inf->sys->replacement = loadinf->buffer; - } else - { inf->sys->source = loadinf->buffer; - } inf->sys->end = loadinf->samples; if( inf->flags & SFX_A_FLAG_AUTOSTART ) - { sfx_save( inf->sys ); - } free( loadinf->path ); free( loadinf ); @@ -223,7 +210,7 @@ void sfx_vorbis_a_to_c( sfx_bgload_t *loadinf ) } // Asynchronous vorbis load into audio system -void sfx_vorbis_a_to( sfx_system_t *sys, const char *strFileName, int channels, uint32_t flags ) +void sfx_vorbis_a_to( sfx_system *sys, const char *strFileName, int channels, u32 flags ) { struct sfx_vorbis_a_to_inf *inf = malloc( sizeof( struct sfx_vorbis_a_to_inf ) ); inf->flags = flags; @@ -232,27 +219,29 @@ void sfx_vorbis_a_to( sfx_system_t *sys, const char *strFileName, int channels, sys->ch = channels; if( !sfx_vorbis_a( strFileName, channels, sfx_vorbis_a_to_c, inf ) ) - { free( inf ); - } -} +}*/ // 0 // ====================================================== -// Mark change to be uploaded to queue system -int sfx_save( sfx_system_t *sys ) +static int sfx_begin_edit( sfx_system *sys ) { MUTEX_LOCK( sfx_mux_t01 ); - + if( sfx_q_len >= SFX_MAX_SYSTEMS ) { - vg_error( "Warning: No free space in sound queue\n" ); - MUTEX_UNLOCK( sfx_mux_t01 ); + vg_warn( "Warning: No free space in sound queue\n" ); return 0; } + return 1; +} + +// Mark change to be uploaded to queue system +static int sfx_save( sfx_system *sys ) +{ // Mark change in queue sfx_q[ sfx_q_len ++ ] = sys; @@ -261,20 +250,18 @@ int sfx_save( sfx_system_t *sys ) return 1; } -// Edit a volume float, has to be function round-tripped -// because of mutex -void sfx_vol_fset( sfx_vol_t *src, float to ) +// Edit a volume float, has to be function wrapped because of mutex +static void sfx_vol_fset( sfx_vol_control *src, float to ) { MUTEX_LOCK( sfx_mux_t01 ); - + src->val = to; - src->cmp = g_master_volume * to; - + MUTEX_UNLOCK( sfx_mux_t01 ); } // thread-safe get volume value -float sfx_vol_fget( sfx_vol_t *src ) +static float sfx_vol_fget( sfx_vol_control *src ) { float val; @@ -288,22 +275,17 @@ float sfx_vol_fget( sfx_vol_t *src ) } // thread-safe set master volume -void sfx_set_master( float to ) +static void sfx_set_master( float to ) { MUTEX_LOCK( sfx_mux_t01 ); g_master_volume = to; - for( int i = 0; i < SFX_NUM_VOLUMES; i ++ ) - { - g_volumes[ i ]->cmp = g_volumes[ i ]->val * g_master_volume; - } - MUTEX_UNLOCK( sfx_mux_t01 ); } // thread-safe get master volume -float sfx_get_master(void) +static float sfx_get_master(void) { float val; @@ -316,15 +298,11 @@ float sfx_get_master(void) return val; } +void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, const void *pInput, ma_uint32 frameCount ); + // Miniaudio.h init -void vg_audio_init(void) +static void vg_audio_init(void) { - // Setup volume values - // Todo: load these from config - g_vol_sfx.val = 1.f; - g_vol_music.val = 1.f; - sfx_set_master( 1.f ); - g_aud_dconfig = ma_device_config_init( ma_device_type_playback ); g_aud_dconfig.playback.format = ma_format_f32; g_aud_dconfig.playback.channels = 2; @@ -349,39 +327,20 @@ void vg_audio_init(void) } } -#ifndef VYGER_RELEASE -uint32_t num_sfx_sets = 0; -#endif - // Shutdown audio device -void vg_audio_free(void) +static void vg_audio_free(void) { ma_device_uninit( &g_aud_device ); } -// (debug) make sure we are shutting down safely -void sfx_sys_chkerr(void) -{ -#ifndef VYGER_RELEASE - if( num_sfx_sets ) - { - vg_error( "Leaked %u sfx sets\n", num_sfx_sets ); - } -#endif -} - // 1 // ====================================================== // Create and return slot for a sound -sfx_system_t *sfx_alloc(void) +static sfx_system *sfx_alloc(void) { if( sfx_sys_len >= SFX_MAX_SYSTEMS ) - { - vg_error( "Warning: No free space in sound system\n" ); - return NULL; - } // A conditional is done against this in localization step, // Needs to be initialized. @@ -390,209 +349,222 @@ sfx_system_t *sfx_alloc(void) return sfx_sys + (sfx_sys_len++); } -// Copy in data from all queued -void sfx_localize(void) +// Fetch samples into pcf +static void audio_mixer_getsamples( float *pcf, float *source, u32 cur, u32 ch ) { + if( ch == 2 ) + { + pcf[0] = source[ cur*2+0 ]; + pcf[1] = source[ cur*2+1 ]; + } + else + { + pcf[0] = source[ cur ]; + pcf[1] = source[ cur ]; + } +} + +// miniaudio.h interface +void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, const void *pInput, ma_uint32 frameCount ) +{ + // Process incoming sound queue MUTEX_LOCK( sfx_mux_t01 ); while( sfx_q_len --> 0 ) { - sfx_system_t *src = sfx_q[sfx_q_len]; + sfx_system *src = sfx_q[sfx_q_len]; + sfx_system *clone; - // This is a 'new' sound if optr not set. - if( !src->optr || src->flags & SFX_FLAG_ONESHOT ) + // This is a 'new' sound if thread_clone not set. + if( !src->thread_clone || (src->flags & SFX_FLAG_ONESHOT) ) { - src->optr = sfx_alloc(); + clone = sfx_alloc(); + if( !clone ) + break; + + src->thread_clone = clone; } - - // run replacement routine if one is waiting - if( src->replacement ) + else { - if( src->source ) + clone = src->thread_clone; + + // Modifying an active system's cursor spawns a small fadeout ghost system + if( clone->cur != src->cur ) { - printf( "Deallocating previous source buffer\n" ); + sfx_system *ghost_system = sfx_alloc(); + + if( !ghost_system ) + break; + + ghost_system->source = clone->source; + ghost_system->ch = clone->ch; + ghost_system->end = clone->end; + ghost_system->cur = clone->cur; + ghost_system->flags = SFX_FLAG_GHOST; + ghost_system->fadeout = FADEOUT_LENGTH; + ghost_system->fadeout_current = FADEOUT_LENGTH; + ghost_system->vol_src = clone->vol_src; + ghost_system->name = clone->name; + ghost_system->thread_clone = src; } - + } + + // run replacement routine if one is waiting (todo: what is this?) + if( src->replacement ) + { free( src->source ); src->source = src->replacement; src->replacement = NULL; } - - src->optr->source = src->source; // Localize data to thread 1's memory pool - // memcpy( src->optr, src, sizeof( sfx_system_t ) ); - - src->optr->spd = src->spd; - src->optr->ch = src->ch; - src->optr->end = src->end; - src->optr->cur = src->cur; - src->optr->flags = src->flags; - // src->optr->sng = src->snh; - src->optr->fadeout = src->fadeout; - // src->optr->optr = src->optr; - // src->optr->cvol = src->cvol; - src->optr->vol_src = src->vol_src; - src->optr->name = src->name; - - // loopback pointer on system system - src->optr->optr = src; + clone->source = src->source; + clone->ch = src->ch; + clone->end = src->end; + clone->cur = src->cur; + clone->flags = src->flags; + clone->vol_src = src->vol_src; + clone->name = src->name; + clone->fadeout = src->fadeout; + clone->fadeout_current = src->fadeout_current; + + // loopback pointer, mainly used for persistent sound handles + clone->thread_clone = src; } + sfx_q_len = 0; // Pull in volume sliders for( int i = 0; i < sfx_sys_len; i ++ ) { - sfx_system_t *sys = sfx_sys + i; - sys->vol = sys->optr->vol; - if( sys->vol_src ) { sys->vol *= sys->vol_src->cmp; }; - } - - MUTEX_UNLOCK( sfx_mux_t01 ); -} - -// Send out updates to sources -void sfx_redist(void) -{ - MUTEX_LOCK( sfx_mux_t01 ); - - unsigned int idx = 0, wr = 0; - while( idx != sfx_sys_len ) - { - sfx_system_t *src = sfx_sys + idx; - - // Keep only if cursor is before end - if( src->cur < src->end ) - { - if( !(src->flags & SFX_FLAG_ONESHOT) ) - { - // Correct source pointer - src->optr->optr = sfx_sys + wr; - } - - sfx_sys[ wr ++ ] = sfx_sys[ idx ]; - } - else - { - if( !(src->flags & SFX_FLAG_ONESHOT) ) - { - // Clear link on source - src->optr->optr = NULL; - } - } + sfx_system *sys = sfx_sys + i; + sys->vol = sys->thread_clone->vol * g_master_volume; - idx ++ ; + if( sys->vol_src ) + sys->vol *= sys->vol_src->val; } - sfx_sys_len = wr; MUTEX_UNLOCK( sfx_mux_t01 ); -} - -// Fetch samples into pcf -void audio_mixer_getsamples( float *pcf, float *source, uint32_t cur, uint32_t ch ) -{ - if( ch == 2 ) - { - pcf[0] = source[ cur*2+0 ]; - pcf[1] = source[ cur*2+1 ]; - } - else - { - pcf[0] = source[ cur ]; - pcf[1] = source[ cur ]; - } -} - -// miniaudio.h interface -void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, const void *pInput, ma_uint32 frameCount ) -{ - sfx_localize(); - + // Clear buffer ( is necessary ? ) float *pOut32F = (float *)pOutBuf; for( int i = 0; i < frameCount * 2; i ++ ){ pOut32F[i] = 0.f; } - + // Do something with local.. for( int i = 0; i < sfx_sys_len; i ++ ) { - sfx_system_t *sys = sfx_sys + i; - - uint32_t cursor = sys->cur; - uint32_t bpos = 0; + sfx_system *sys = sfx_sys + i; + u32 cursor = sys->cur, buffer_pos = 0; float avgvol = 0.f; + float pcf[2] = { 0.f, 0.0f }; - float pcf[2] = { 0.f }; + u32 frames_write = frameCount; + float fadeout_divisor = 1.0f / (float)sys->fadeout; - while( cursor < vg_min( sys->cur + frameCount, sys->end ) ) + while( frames_write ) { - audio_mixer_getsamples( pcf, sys->source, cursor, sys->ch ); - - avgvol += fabs( pcf[0] * sys->vol ); - avgvol += fabs( pcf[1] * sys->vol ); - - pOut32F[ bpos*2+0 ] += pcf[0] * sys->vol; - pOut32F[ bpos*2+1 ] += pcf[1] * sys->vol; - - // Blend the fadeout cursor in to prevent popping - // This lasts 441 samples + u32 samples_this_run = VG_MIN( frames_write, sys->end - cursor ); + if( sys->fadeout ) { - if( sys->snh < sys->end ) + // Force this system to be removed + if( sys->fadeout_current == 0 ) { - audio_mixer_getsamples( pcf, sys->source, sys->snh, sys->ch ); - - float mul = (float)sys->fadeout * FADEOUT_DIVISOR; - - pOut32F[ bpos*2+0 ] += pcf[0] * sys->vol * mul; - pOut32F[ bpos*2+1 ] += pcf[1] * sys->vol * mul; - - sys->snh ++; - sys->fadeout --; + sys->flags = SFX_FLAG_GHOST; + sys->cur = sys->end; + break; } - else + + samples_this_run = VG_MIN( samples_this_run, sys->fadeout_current ); + } + + for( u32 j = 0; j < samples_this_run; j ++ ) + { + audio_mixer_getsamples( pcf, sys->source, cursor, sys->ch ); + + float vol = sys->vol; + + if( sys->fadeout ) + { + vol *= (float)sys->fadeout_current * fadeout_divisor; + sys->fadeout_current --; + } + + if( buffer_pos >= frameCount ) + { + break; + } + + pOut32F[ buffer_pos*2+0 ] += pcf[0] * vol; + pOut32F[ buffer_pos*2+1 ] += pcf[1] * vol; + + avgvol += fabs( pcf[0] * vol ); + avgvol += fabs( pcf[1] * vol ); + + cursor ++; + buffer_pos ++; + } + + frames_write -= samples_this_run; + + if( sys->flags & SFX_FLAG_REPEAT ) + { + if( frames_write ) { - sys->fadeout = 0; + cursor = 0; + continue; } } + + sys->cur = cursor; + sys->signal_average = avgvol / (float)(buffer_pos*2); - cursor ++; - bpos ++; + break; } + } + + // Redistribute sound systems + MUTEX_LOCK( sfx_mux_t01 ); + + u32 idx = 0, wr = 0; + while( idx != sfx_sys_len ) + { + sfx_system *src = sfx_sys + idx; - if( !sys->fadeout ) + // Keep only if cursor is before end or repeating + if( src->cur < src->end || (src->flags & SFX_FLAG_REPEAT) ) { - sys->snh = cursor; + // Correct source pointer on persisitent originals since we shifted ID's + if( !(src->flags & (SFX_FLAG_ONESHOT|SFX_FLAG_GHOST)) ) + { + src->thread_clone->thread_clone = sfx_sys + wr; + } + + sfx_sys[ wr ++ ] = sfx_sys[ idx ]; + } + else + { + // Clear link on persistent sources (done playing) + if( !(src->flags & (SFX_FLAG_ONESHOT|SFX_FLAG_GHOST)) ) + src->thread_clone->thread_clone = NULL; } - sys->cvol = avgvol / (float)(bpos*2); - sys->cur += frameCount; + idx ++ ; } - - sfx_redist(); + sfx_sys_len = wr; + + MUTEX_UNLOCK( sfx_mux_t01 ); (void)pInput; } -// Set of up to 8 sound effects packed into one -typedef struct sfx_set sfx_set_t; -struct sfx_set -{ - float *main; - char *sources; - - uint32_t segments[16]; //from->to,from->to ... - uint32_t numsegments; - uint32_t ch; - uint32_t flags; -}; - // Load strings into sfx_set's memory // String layout: "sounda.ogg\0soundb.ogg\0soundc.ogg\0\0" -void sfx_set_strings( sfx_set_t *dest, char *strSources, uint32_t flags, int bAsync ) +static void sfx_set_strings( sfx_set *dest, char *strSources, u32 flags, int bAsync ) { printf( "Init sfx set\n| start | end | length | name \n" ); @@ -602,11 +574,11 @@ void sfx_set_strings( sfx_set_t *dest, char *strSources, uint32_t flags, int bAs dest->numsegments = 0; char *source = strSources; - uint32_t total = 0; + u32 total = 0; int len; while( (len = strlen( source )) ) { - uint32_t samples; + u32 samples; float *sound = sfx_vorbis( source, dest->ch, &samples ); if( !sound ) @@ -645,51 +617,63 @@ void sfx_set_strings( sfx_set_t *dest, char *strSources, uint32_t flags, int bAs vg_info( "finished, numsegments: %u\n", dest->numsegments ); } - -// If sources is non-null then it will try to pull from -// internally set string -// -// internal set string should be literal, otherwise leak if not -// handled correctly -void sfx_set_init( sfx_set_t *dest, char *sources ) +static void sfx_set_init( sfx_set *dest, char *sources ) { -#ifndef VYGER_RELEASE - num_sfx_sets ++; -#endif - if( !sources ) - { sfx_set_strings( dest, dest->sources, dest->flags, 0 ); - } else - { sfx_set_strings( dest, sources, dest->flags, 0 ); - } } // Pick a random sound from the buffer and play it into system -void sfx_set_playrnd( sfx_set_t *source, sfx_system_t *sys, int min_id, int max_id ) +static void sfx_set_playrnd( sfx_set *source, sfx_system *sys, int min_id, int max_id ) { if( !source->numsegments ) - { return; - } int pick = (rand() % (max_id-min_id)) + min_id; - sys->source = source->main; - sys->cur = source->segments[ pick*2 + 0 ]; - sys->end = source->segments[ pick*2 + 1 ]; - sys->ch = source->ch; - - sfx_save( sys ); + if( sfx_begin_edit( sys ) ) + { + sys->fadeout = 0; + sys->source = source->main; + sys->cur = source->segments[ pick*2 + 0 ]; + sys->end = source->segments[ pick*2 + 1 ]; + sys->ch = source->ch; + + // Diagnostics + sys->clip_start = sys->cur; + sys->clip_end = sys->end; + sys->buffer_length = source->segments[ (source->numsegments-1)*2 + 1 ]; + + sfx_save( sys ); + } +} + +static void sfx_system_fadeout( sfx_system *sys, u32 length_samples ) +{ + if( sfx_begin_edit( sys ) ) + { + if( sys->end ) + { + sys->fadeout_current = length_samples; + sys->fadeout = length_samples; + + if( sys->thread_clone ) + sys->cur = sys->thread_clone->cur; + + sfx_save( sys ); + } + else + { + // Sound was not initialized + MUTEX_UNLOCK( sfx_mux_t01 ); + } + } } // Free set resources -void sfx_set_free( sfx_set_t *set ) +static void sfx_set_free( sfx_set *set ) { -#ifndef VYGER_RELEASE - num_sfx_sets --; -#endif free( set->main ); }