From: hgn Date: Mon, 26 Sep 2022 00:56:34 +0000 (+0100) Subject: audio rework pt 1 X-Git-Url: https://harrygodden.com/git/?p=vg.git;a=commitdiff_plain;h=94b61bed97af43179b37593f4453a05d03687cbc audio rework pt 1 --- diff --git a/dep/stb/stb_vorbis.h b/dep/stb/stb_vorbis.h index 2f62db2..3e5c250 100644 --- a/dep/stb/stb_vorbis.h +++ b/dep/stb/stb_vorbis.h @@ -1,4 +1,4 @@ -// Ogg Vorbis audio decoder - v1.19 - public domain +// Ogg Vorbis audio decoder - v1.22 - public domain // http://nothings.org/stb_vorbis/ // // Original version written by Sean Barrett in 2007. @@ -29,11 +29,16 @@ // Bernhard Wodo Evan Balster github:alxprd // Tom Beaumont Ingo Leitgeb Nicolas Guillemot // Phillip Bennefall Rohit Thiago Goulart -// github:manxorist saga musix github:infatum +// github:manxorist Saga Musix github:infatum // Timur Gagiev Maxwell Koo Peter Waller -// github:audinowho Dougall Johnson +// github:audinowho Dougall Johnson David Reid +// github:Clownacy Pedro J. Estebanez Remi Verschelde +// AnthoFoxo github:morlat Gabriel Ravier // // Partial history: +// 1.22 - 2021-07-11 - various small fixes +// 1.21 - 2021-07-02 - fix bug for files with no comments +// 1.20 - 2020-07-11 - several small fixes // 1.19 - 2020-02-05 - warnings // 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc. // 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure) @@ -218,6 +223,12 @@ extern int stb_vorbis_decode_frame_pushdata( // channel. In other words, (*output)[0][0] contains the first sample from // the first channel, and (*output)[1][0] contains the first sample from // the second channel. +// +// *output points into stb_vorbis's internal output buffer storage; these +// buffers are owned by stb_vorbis and application code should not free +// them or modify their contents. They are transient and will be overwritten +// once you ask for more data to get decoded, so be sure to grab any data +// you need before then. extern void stb_vorbis_flush_pushdata(stb_vorbis *f); // inform stb_vorbis that your next datablock will not be contiguous with @@ -577,7 +588,7 @@ enum STBVorbisError #if defined(_MSC_VER) || defined(__MINGW32__) #include #endif - #if defined(__linux__) || defined(__linux) || defined(__EMSCRIPTEN__) + #if defined(__linux__) || defined(__linux) || defined(__sun__) || defined(__EMSCRIPTEN__) || defined(__NEWLIB__) #include #endif #else // STB_VORBIS_NO_CRT @@ -599,10 +610,9 @@ enum STBVorbisError #undef __forceinline #endif #define __forceinline - #ifdef alloca - #undef alloca - #endif + #ifndef alloca #define alloca __builtin_alloca + #endif #elif !defined(_MSC_VER) #if __GNUC__ #define __forceinline inline @@ -645,6 +655,12 @@ typedef signed int int32; typedef float codetype; +#ifdef _MSC_VER +#define STBV_NOTUSED(v) (void)(v) +#else +#define STBV_NOTUSED(v) (void)sizeof(v) +#endif + // @NOTE // // Some arrays below are tagged "//varies", which means it's actually @@ -964,7 +980,7 @@ static void *setup_temp_malloc(vorb *f, int sz) static void setup_temp_free(vorb *f, void *p, int sz) { if (f->alloc.alloc_buffer) { - f->temp_offset += (sz+3)&~3; + f->temp_offset += (sz+7)&~7; return; } free(p); @@ -1045,7 +1061,7 @@ static float float32_unpack(uint32 x) uint32 sign = x & 0x80000000; uint32 exp = (x & 0x7fe00000) >> 21; double res = sign ? -(double)mantissa : (double)mantissa; - return (float) ldexp((float)res, exp-788); + return (float) ldexp((float)res, (int)exp-788); } @@ -1076,6 +1092,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) // find the first entry for (k=0; k < n; ++k) if (len[k] < NO_CODE) break; if (k == n) { assert(c->sorted_entries == 0); return TRUE; } + assert(len[k] < 32); // no error return required, code reading lens checks this // add to the list add_entry(c, 0, k, m++, len[k], values); // add all available leaves @@ -1089,6 +1106,7 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) uint32 res; int z = len[i], y; if (z == NO_CODE) continue; + assert(z < 32); // no error return required, code reading lens checks this // find lowest available leaf (should always be earliest, // which is what the specification calls for) // note that this property, and the fact we can never have @@ -1098,12 +1116,10 @@ static int compute_codewords(Codebook *c, uint8 *len, int n, uint32 *values) while (z > 0 && !available[z]) --z; if (z == 0) { return FALSE; } res = available[z]; - assert(z >= 0 && z < 32); available[z] = 0; add_entry(c, bit_reverse(res), i, m++, len[i], values); // propagate availability up the tree if (z != len[i]) { - assert(len[i] >= 0 && len[i] < 32); for (y=len[i]; y > z; --y) { assert(available[y] == 0); available[y] = res + (1 << (32-y)); @@ -1603,7 +1619,8 @@ static uint32 get_bits(vorb *f, int n) f->valid_bits += 8; } } - if (f->valid_bits < 0) return 0; + + assert(f->valid_bits >= n); z = f->acc & ((1 << n)-1); f->acc >>= n; f->valid_bits -= n; @@ -2575,34 +2592,33 @@ static void imdct_step3_inner_s_loop_ld654(int n, float *e, int i_off, float *A, while (z > base) { float k00,k11; - - k00 = z[-0] - z[-8]; - k11 = z[-1] - z[-9]; - z[-0] = z[-0] + z[-8]; - z[-1] = z[-1] + z[-9]; - z[-8] = k00; - z[-9] = k11 ; - - k00 = z[ -2] - z[-10]; - k11 = z[ -3] - z[-11]; - z[ -2] = z[ -2] + z[-10]; - z[ -3] = z[ -3] + z[-11]; - z[-10] = (k00+k11) * A2; - z[-11] = (k11-k00) * A2; - - k00 = z[-12] - z[ -4]; // reverse to avoid a unary negation + float l00,l11; + + k00 = z[-0] - z[ -8]; + k11 = z[-1] - z[ -9]; + l00 = z[-2] - z[-10]; + l11 = z[-3] - z[-11]; + z[ -0] = z[-0] + z[ -8]; + z[ -1] = z[-1] + z[ -9]; + z[ -2] = z[-2] + z[-10]; + z[ -3] = z[-3] + z[-11]; + z[ -8] = k00; + z[ -9] = k11; + z[-10] = (l00+l11) * A2; + z[-11] = (l11-l00) * A2; + + k00 = z[ -4] - z[-12]; k11 = z[ -5] - z[-13]; + l00 = z[ -6] - z[-14]; + l11 = z[ -7] - z[-15]; z[ -4] = z[ -4] + z[-12]; z[ -5] = z[ -5] + z[-13]; - z[-12] = k11; - z[-13] = k00; - - k00 = z[-14] - z[ -6]; // reverse to avoid a unary negation - k11 = z[ -7] - z[-15]; z[ -6] = z[ -6] + z[-14]; z[ -7] = z[ -7] + z[-15]; - z[-14] = (k00+k11) * A2; - z[-15] = (k00-k11) * A2; + z[-12] = k11; + z[-13] = -k00; + z[-14] = (l11-l00) * A2; + z[-15] = (l00+l11) * -A2; iter_54(z); iter_54(z-8); @@ -3067,6 +3083,7 @@ static int do_floor(vorb *f, Mapping *map, int i, int n, float *target, YTYPE *f for (q=1; q < g->values; ++q) { j = g->sorted_order[q]; #ifndef STB_VORBIS_NO_DEFER_FLOOR + STBV_NOTUSED(step2_flag); if (finalY[j] >= 0) #else if (step2_flag[j]) @@ -3169,6 +3186,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // WINDOWING + STBV_NOTUSED(left_end); n = f->blocksize[m->blockflag]; map = &f->mapping[m->mapping]; @@ -3366,7 +3384,7 @@ static int vorbis_decode_packet_rest(vorb *f, int *len, Mode *m, int left_start, // this isn't to spec, but spec would require us to read ahead // and decode the size of all current frames--could be done, // but presumably it's not a commonly used feature - f->current_loc = -n2; // start of first frame is positioned for discard + f->current_loc = 0u - n2; // start of first frame is positioned for discard (NB this is an intentional unsigned overflow/wrap-around) // we might have to discard samples "from" the next frame too, // if we're lapping a large block then a small at the start? f->discard_samples_deferred = n - right_end; @@ -3633,17 +3651,24 @@ static int start_decoder(vorb *f) //file vendor len = get32_packet(f); f->vendor = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->vendor == NULL) return error(f, VORBIS_outofmem); for(i=0; i < len; ++i) { f->vendor[i] = get8_packet(f); } f->vendor[len] = (char)'\0'; //user comments f->comment_list_length = get32_packet(f); - f->comment_list = (char**)setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + f->comment_list = NULL; + if (f->comment_list_length > 0) + { + f->comment_list = (char**) setup_malloc(f, sizeof(char*) * (f->comment_list_length)); + if (f->comment_list == NULL) return error(f, VORBIS_outofmem); + } for(i=0; i < f->comment_list_length; ++i) { len = get32_packet(f); f->comment_list[i] = (char*)setup_malloc(f, sizeof(char) * (len+1)); + if (f->comment_list[i] == NULL) return error(f, VORBIS_outofmem); for(j=0; j < len; ++j) { f->comment_list[i][j] = get8_packet(f); @@ -3860,8 +3885,7 @@ static int start_decoder(vorb *f) unsigned int div=1; for (k=0; k < c->dimensions; ++k) { int off = (z / div) % c->lookup_values; - float val = mults[off]; - val = mults[off]*c->delta_value + c->minimum_value + last; + float val = mults[off]*c->delta_value + c->minimum_value + last; c->multiplicands[j*c->dimensions + k] = val; if (c->sequence_p) last = val; @@ -3944,7 +3968,7 @@ static int start_decoder(vorb *f) if (g->class_masterbooks[j] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } for (k=0; k < 1 << g->class_subclasses[j]; ++k) { - g->subclass_books[j][k] = get_bits(f,8)-1; + g->subclass_books[j][k] = (int16)get_bits(f,8)-1; if (g->subclass_books[j][k] >= f->codebook_count) return error(f, VORBIS_invalid_setup); } } @@ -4256,7 +4280,7 @@ static void vorbis_init(stb_vorbis *p, const stb_vorbis_alloc *z) memset(p, 0, sizeof(*p)); // NULL out all malloc'd pointers to start if (z) { p->alloc = *z; - p->alloc.alloc_buffer_length_in_bytes = (p->alloc.alloc_buffer_length_in_bytes+3) & ~3; + p->alloc.alloc_buffer_length_in_bytes &= ~7; p->temp_offset = p->alloc.alloc_buffer_length_in_bytes; } p->eof = 0; @@ -4502,6 +4526,7 @@ stb_vorbis *stb_vorbis_open_pushdata( *error = VORBIS_need_more_data; else *error = p.error; + vorbis_deinit(&p); return NULL; } f = vorbis_alloc(&p); @@ -4559,7 +4584,7 @@ static uint32 vorbis_find_page(stb_vorbis *f, uint32 *end, uint32 *last) header[i] = get8(f); if (f->eof) return 0; if (header[4] != 0) goto invalid; - goal = header[22] + (header[23] << 8) + (header[24]<<16) + (header[25]<<24); + goal = header[22] + (header[23] << 8) + (header[24]<<16) + ((uint32)header[25]<<24); for (i=22; i < 26; ++i) header[i] = 0; crc = 0; @@ -4963,7 +4988,7 @@ unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f) // set. whoops! break; } - previous_safe = last_page_loc+1; + //previous_safe = last_page_loc+1; // NOTE: not used after this point, but note for debugging last_page_loc = stb_vorbis_get_file_offset(f); } @@ -5074,7 +5099,10 @@ stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const st stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc) { stb_vorbis *f, p; - if (data == NULL) return NULL; + if (!data) { + if (error) *error = VORBIS_unexpected_eof; + return NULL; + } vorbis_init(&p, alloc); p.stream = (uint8 *) data; p.stream_end = (uint8 *) data + len; @@ -5149,11 +5177,11 @@ static void copy_samples(short *dest, float *src, int len) static void compute_samples(int mask, short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE; check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE) { + for (o = 0; o < len; o += STB_BUFFER_SIZE) { memset(buffer, 0, sizeof(buffer)); if (o + n > len) n = len - o; for (j=0; j < num_c; ++j) { @@ -5170,16 +5198,17 @@ static void compute_samples(int mask, short *output, int num_c, float **data, in output[o+i] = v; } } + #undef STB_BUFFER_SIZE } static void compute_stereo_samples(short *output, int num_c, float **data, int d_offset, int len) { - #define BUFFER_SIZE 32 - float buffer[BUFFER_SIZE]; - int i,j,o,n = BUFFER_SIZE >> 1; + #define STB_BUFFER_SIZE 32 + float buffer[STB_BUFFER_SIZE]; + int i,j,o,n = STB_BUFFER_SIZE >> 1; // o is the offset in the source data check_endianness(); - for (o = 0; o < len; o += BUFFER_SIZE >> 1) { + for (o = 0; o < len; o += STB_BUFFER_SIZE >> 1) { // o2 is the offset in the output data int o2 = o << 1; memset(buffer, 0, sizeof(buffer)); @@ -5209,6 +5238,7 @@ static void compute_stereo_samples(short *output, int num_c, float **data, int d output[o2+i] = v; } } + #undef STB_BUFFER_SIZE } static void convert_samples_short(int buf_c, short **buffer, int b_offset, int data_c, float **data, int d_offset, int samples) @@ -5281,8 +5311,6 @@ int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short float **outputs; int len = num_shorts / channels; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; @@ -5301,8 +5329,6 @@ int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, in { float **outputs; int n=0; - int z = f->channels; - if (z > channels) z = channels; while (n < len) { int k = f->channel_buffer_end - f->channel_buffer_start; if (n+k >= len) k = len - n; diff --git a/src/shader.c b/src/shader.c index f5e7b36..9dee03e 100644 --- a/src/shader.c +++ b/src/shader.c @@ -8,6 +8,8 @@ struct uniform { char name[30]; char type[20]; + + int array; } uniform_buffer[100]; static int uniform_count; @@ -54,6 +56,13 @@ static int compile_subshader( FILE *header, char *name ) start[i] = '\0'; strncpy( uf->name, start, sizeof(uf->name) ); } + + if( start[i] == '[' ) + { + start[i] = '\0'; + strncpy( uf->name, start, sizeof(uf->name) ); + uf->array = 1; + } if( start[i] == ' ' ) { @@ -143,6 +152,8 @@ int main( int argc, char *argv[] ) for( int i=0; iarray ) continue; + if( !strcmp(uf->type,"vec2") ) { fprintf( header, "static void shader_%s_%s(v2f v){\n" diff --git a/src/vg/vg.h b/src/vg/vg.h index a2a85fd..fc6e0af 100644 --- a/src/vg/vg.h +++ b/src/vg/vg.h @@ -1,5 +1,8 @@ /* Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved */ +static void vg_exiterr( const char *err ); +static void vg_exit(void); + #ifndef VG_HEADER_H #define VG_HEADER_H @@ -30,7 +33,6 @@ #include "vg_platform.h" void vg_register_exit( void( *funcptr )(void), const char *name ); -void vg_exiterr( const char *strErr ); #include "vg_m.h" #include "vg_io.h" @@ -108,7 +110,7 @@ void vg_register_exit( void( *funcptr )(void), const char *name ) vg_on_exit[ vg_exit_count ++ ] = funcptr; } -void vg_exit(void) +static void vg_exit(void) { for( int i = vg_exit_count-1; i >= 0; i -- ) { @@ -117,13 +119,13 @@ void vg_exit(void) } vg_info( "done\n" ); + exit(0); } -void vg_exiterr( const char *strErr ) +static void vg_exiterr( const char *err ) { - vg_error( "Engine Fatal: %s\n", strErr ); + vg_error( "Engine Fatal: %s\n", err ); vg_exit(); - exit(0); } void vg_mouse_callback( GLFWwindow* ptrW, double xpos, double ypos ) @@ -241,16 +243,14 @@ static void vg_init( int argc, char *argv[], const char *window_name ) .function = vg_shaders_recompile }); - + if( !vg_audio_init() ) + vg_exit(); + vg_register_exit( &vg_audio_free, "vg_audio_free" ); + vg_start(); vg_console_init(); vg_register_exit( &vg_console_free, "Console" ); - - vg_audio_init(); - vg_register_exit( &vg_audio_free, "vg_audio_free" ); - - vg_debugtools_setup(); /* * Main gameloop @@ -280,9 +280,9 @@ static void vg_init( int argc, char *argv[], const char *window_name ) ui_set_mouse( &ui_global_ctx, vg_mouse[0], vg_mouse[1], vg_get_button_state( "primary" ) ); + audio_debug_ui(); vg_ui(); vg_console_draw(); - vg_debugtools_draw(); ui_resolve( &ui_global_ctx ); ui_draw( &ui_global_ctx, NULL ); diff --git a/src/vg/vg_audio.h b/src/vg/vg_audio.h index 024d87c..bb4d092 100644 --- a/src/vg/vg_audio.h +++ b/src/vg/vg_audio.h @@ -1,563 +1,968 @@ -/* Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved */ +/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */ + +#ifndef VG_AUDIO_H +#define VG_AUDIO_H #define MA_NO_GENERATION #define MA_NO_DECODING #define MA_NO_ENCODING #include "dr_soft/miniaudio.h" +#include "vg/vg.h" +#include "vg/vg_stdint.h" +#include "vg/vg_platform.h" +#include "vg/vg_io.h" +#include "vg/vg_m.h" +#include "vg/vg_ui.h" +#include "vg/vg_console.h" +#include "vg/vg_store.h" + +#include #define STB_VORBIS_MAX_CHANNELS 2 #include "stb/stb_vorbis.h" -#define SFX_MAX_SYSTEMS 32 -#define SFX_FLAG_STEREO 0x2 -#define SFX_FLAG_REPEAT 0x4 -#define SFX_FLAG_PERSISTENT 0x8 -#define FADEOUT_LENGTH 4410 -#define FADEOUT_DIVISOR (1.f/(float)FADEOUT_LENGTH) +#define SFX_MAX_SYSTEMS 32 +#define AUDIO_FLAG_LOOP 0x1 +#define AUDIO_FLAG_ONESHOT 0x2 +#define AUDIO_FLAG_SPACIAL_3D 0x4 -typedef struct sfx_vol_control sfx_vol_control; -typedef struct sfx_system sfx_system; +#define FADEOUT_LENGTH 4410 +#define FADEOUT_DIVISOR (1.0f/(float)FADEOUT_LENGTH) -struct sfx_vol_control +#define AUDIO_DECODE_SIZE (1024*256) /* 256 kb decoding buffers */ + +enum audio_source_mode { - float val; - const char *name; + k_audio_source_mono, + k_audio_source_mono_compressed, + k_audio_source_stereo_compressed }; -struct sfx_system +typedef struct audio_clip audio_clip; +struct audio_clip { - sfx_system *persisitent_source; - int in_queue; + const char *path; + enum audio_source_mode source_mode; - /* Source buffer start */ - float *source, *replacement; - - u32 clip_start, clip_end, buffer_length; - - /* Modifiers */ - sfx_vol_control *vol_src; - float vol, cvol, pan; + /* result */ + void *data; + u32 len; /* decompressed: sample count, + compressed: file size */ +}; - u32 delay; - - /* Info */ - u32 ch, end, cur; - u32 flags; - - /* Effects */ - u32 fadeout, fadeout_current; - - /* Diagnostic */ - const char *name; +typedef struct audio_mix_info audio_mix_info; +struct audio_mix_info +{ + audio_clip *source; + v3f world_position; + + float vol, pan; + u32 flags; }; -/* Set of up to 8 sound effects packed into one */ -typedef struct sfx_set sfx_set; -struct sfx_set +typedef struct audio_player audio_player; +struct audio_player { - float *main; - char *sources; - - u32 segments[32]; /* from->to,from->to ... */ - u32 numsegments; - u32 ch; - u32 flags; + aatree_ptr active_entity; /* non-nil if currently playing */ + audio_mix_info info; + int enqued; + + /* Diagnostic */ + const char *name; }; -ma_device g_aud_device; -ma_device_config g_aud_dconfig; +typedef struct audio_entity audio_entity; +struct audio_entity +{ + audio_player *player; + audio_mix_info info; + + u32 length, cur; + + /* Effects */ + u32 fadeout, fadeout_current; + const char *name; +}; -/* - * Thread 1 - audio engine ( spawned from miniaudio.h ) - * ====================================================== +/* + * TODO list sunday + * + * play again: if already playing, leave in queue while it fadeouts + * oneshot: create a ghost entity + * */ -sfx_system sfx_sys[SFX_MAX_SYSTEMS]; -int sfx_sys_len = 0; + +static struct vg_audio_system +{ + ma_device miniaudio_device; + ma_device_config miniaudio_dconfig; + + void *mem, *decode_mem; + u32 mem_current, + mem_total; + + /* synchro */ + int sync_locked; + MUTEX_TYPE mutex_checker; + MUTEX_TYPE mutex_sync; + + /* Audio engine, thread 1 */ + struct active_audio_player + { + int active; + union + { + audio_entity ent; + aatree_pool_node pool_node; + }; + + stb_vorbis *vorbis_handle; + stb_vorbis_alloc vorbis_alloc; + } + active_players[ SFX_MAX_SYSTEMS ]; + + aatree active_pool_info; /* note: just using the pool */ + aatree_ptr active_pool_head; + + /* System queue, and access from thread 0 */ + audio_entity entity_queue[SFX_MAX_SYSTEMS]; + int queue_len; + + char performance_info[128]; + int debug_ui; + + 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; +} + /* - * Thread 0 - Critical transfer section - * ====================================================== + * These functions are called from the main thread and used to prevent bad + * access. TODO: They should be no-ops in release builds. */ -MUTEX_TYPE sfx_mux_t01; /* Resources share: 0 & 1 */ +static int audio_lock_checker_load(void) +{ + int value; + MUTEX_LOCK( vg_audio.mutex_checker ); + value = vg_audio.sync_locked; + MUTEX_UNLOCK( vg_audio.mutex_checker ); + return value; +} -sfx_system *sfx_q[SFX_MAX_SYSTEMS]; /* Stuff changed */ -int sfx_q_len = 0; +static void audio_lock_checker_store( int value ) +{ + MUTEX_LOCK( vg_audio.mutex_checker ); + vg_audio.sync_locked = value; + MUTEX_UNLOCK( vg_audio.mutex_checker ); +} -float g_master_volume = 1.f; +static void audio_require_lock(void) +{ + if( audio_lock_checker_load() ) + return; -/* Decompress entire vorbis stream into buffer */ -static float *sfx_vorbis_stream( const unsigned char *data, int len, - int channels, u32 *samples ) + vg_exiterr( "Modifying sound effects systems requires locking\n" ); +} + +static void audio_lock(void) { - 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 ); - float *buffer = (float *)malloc( length_samples * channels * sizeof(float)); - - if( !buffer ) - { - stb_vorbis_close( pv ); - vg_error( "out of memory while allocating sound resource\n" ); - return NULL; - } - - int read_samples = stb_vorbis_get_samples_float_interleaved( - pv, channels, buffer, length_samples * channels ); + MUTEX_LOCK( vg_audio.mutex_sync ); + audio_lock_checker_store(1); +} - 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; +static void audio_unlock(void) +{ + audio_lock_checker_store(0); + MUTEX_UNLOCK( vg_audio.mutex_sync ); } -static float *sfx_vorbis( const char *strFileName, int channels, u32 *samples ) + +static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, + const void *pInput, ma_uint32 frameCount ); + +static int vg_audio_init(void) { - i64 len; - void *filedata = vg_asset_read_s( strFileName, &len ); - - if( filedata ) - { - float *wav = sfx_vorbis_stream( filedata, len, channels, samples ); - free( filedata ); - return wav; - } - else - { - vg_error( "OGG load failed\n" ); - return NULL; - } + vg_convar_push( (struct vg_convar){ + .name = "debug_audio", + .data = &vg_audio.debug_ui, + .data_type = k_convar_dtype_i32, + .opt_i32 = { .min=0, .max=1, .clamp=1 }, + .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]; + + /* setup pool */ + vg_audio.active_pool_info.base = vg_audio.active_players; + vg_audio.active_pool_info.offset = offsetof(struct active_audio_player, + pool_node ); + vg_audio.active_pool_info.stride = sizeof(struct active_audio_player); + vg_audio.active_pool_info.p_cmp = NULL; + aatree_init_pool( &vg_audio.active_pool_info, SFX_MAX_SYSTEMS ); + + 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->playback.format = ma_format_f32; + dconf->playback.channels = 2; + dconf->sampleRate = 44100; + dconf->dataCallback = audio_mixer_callback; + + dconf->pUserData = NULL; + + vg_info( "Starting audio engine\n" ); + + if( ma_device_init( NULL, dconf, device) != MA_SUCCESS ) + { + vg_error( "ma_device failed to initialize" ); + return 0; + } + else + { + if( ma_device_start( device ) != MA_SUCCESS ) + { + ma_device_uninit( device ); + vg_error( "ma_device failed to start" ); + return 0; + } + } + + return 1; +} + +static void vg_audio_free(void) +{ + ma_device *device = &vg_audio.miniaudio_device; + ma_device_uninit( device ); + + free( vg_audio.mem ); } /* - * thread 0 / client code + * thread 1 */ -static int sfx_begin_edit( sfx_system *sys ) + +static aatree_ptr audio_alloc_entity_internal(void) { - MUTEX_LOCK( sfx_mux_t01 ); - - if( sfx_q_len >= SFX_MAX_SYSTEMS && !sys->in_queue ) - { - MUTEX_UNLOCK( sfx_mux_t01 ); - vg_warn( "Warning: No free space in sound queue\n" ); - return 0; - } - - return 1; + aatree_ptr playerid = aatree_pool_alloc( &vg_audio.active_pool_info, + &vg_audio.active_pool_head ); + + if( playerid == AATREE_PTR_NIL ) + return AATREE_PTR_NIL; + + struct active_audio_player *aap = &vg_audio.active_players[ playerid ]; + aap->active = 1; + + return playerid; } -static void sfx_end_edit( sfx_system *sys ) +static void audio_entity_free_internal( aatree_ptr id ) { - MUTEX_UNLOCK( sfx_mux_t01 ); + struct active_audio_player *aap = &vg_audio.active_players[ id ]; + aap->active = 0; + + /* Notify player that we've finished */ + if( aap->ent.player ) + aap->ent.player->active_entity = AATREE_PTR_NIL; + + /* delete */ + aatree_pool_free( &vg_audio.active_pool_info, id, + &vg_audio.active_pool_head ); } -/* Mark change to be uploaded to queue system */ -static int sfx_push( sfx_system *sys ) +static void *audio_entity_vorbis_ptr( aatree_ptr entid ) { - if( !sys->in_queue ) - { - sfx_q[ sfx_q_len ++ ] = sys; - sys->in_queue = 1; - } - - MUTEX_UNLOCK( sfx_mux_t01 ); - - return 1; + u8 *buf = (u8*)vg_audio.decode_mem, + *loc = &buf[AUDIO_DECODE_SIZE*entid]; + + return (void *)loc; } -/* Edit a volume float, has to be function wrapped because of mutex */ -static void sfx_vol_fset( sfx_vol_control *src, float to ) +static void audio_entity_start( audio_entity *src ) { - MUTEX_LOCK( sfx_mux_t01 ); + aatree_ptr entid = audio_alloc_entity_internal(); + if( entid == AATREE_PTR_NIL ) + return; + + audio_entity *ent = &vg_audio.active_players[ entid ].ent; + + ent->info = src->info; + ent->name = "todo"; + ent->cur = 0; + ent->player = src->player; + + ent->fadeout = 0; + ent->fadeout_current = 0; + + /* Notify main player we are dequeud and playing */ + if( src->player ) + { + src->player->enqued = 0; + 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 ) + { + /* Setup vorbis decoder */ + struct active_audio_player *aap = &vg_audio.active_players[ entid ]; + + stb_vorbis_alloc alloc = { + .alloc_buffer = (char *)audio_entity_vorbis_ptr( entid ), + .alloc_buffer_length_in_bytes = AUDIO_DECODE_SIZE + }; + + int err; + stb_vorbis *decoder = stb_vorbis_open_memory( + src->info.source->data, src->info.source->len, &err, &alloc ); + + if( !decoder ) + { + vg_error( "stb_vorbis_open_memory failed on '%s' (%d)\n", + src->info.source->path, err ); + + audio_entity_free_internal( entid ); + return; + } + else + { + ent->length = stb_vorbis_stream_length_in_samples( decoder ); + } + + aap->vorbis_handle = decoder; + } + else + { + ent->length = src->info.source->len; + } +} - src->val = to; +/* + * Read everything from the queue + */ +static void audio_system_enque(void) +{ + /* Process incoming sound queue */ + audio_lock(); + + int wr = 0; + for( int i=0; iplayer ) + { + /* Start new */ + if( src->player->active_entity == AATREE_PTR_NIL ) + { + audio_entity_start( src ); + } + else + { + /* Otherwise try start fadeout but dont remove from queue */ + + aatree_ptr entid = src->player->active_entity; + audio_entity *ent = &vg_audio.active_players[ entid ].ent; + if( !ent->fadeout ) + { + ent->fadeout = 1; + ent->fadeout_current = FADEOUT_LENGTH; + } + + vg_audio.entity_queue[ wr ++ ] = *src; + } + } + else + { + audio_entity_start( src ); + } + } + + vg_audio.queue_len = wr; + + /* Localize others memory */ + for( int i=0; iactive ) + continue; + + if( aap->ent.player ) + { + /* Only copy information in whilst not requeing */ + if( aap->ent.player->enqued == 0 ) + { + aap->ent.info = aap->ent.player->info; + } + } + } + + audio_unlock(); +} - MUTEX_UNLOCK( sfx_mux_t01 ); +/* + * Redistribute sound systems + */ +static void audio_system_cleanup(void) +{ + audio_lock(); + + for( int i=0; iactive ) + { + audio_entity *src = &aap->ent; + if( src->cur < src->length || (src->info.flags & AUDIO_FLAG_LOOP )) + { + /* Good to keep */ + } + else + { + audio_entity_free_internal( i ); + } + } + } + + audio_unlock(); } -/* thread-safe get volume value */ -static float sfx_vol_fget( sfx_vol_control *src ) +/* + * Get effective volume and pan from this entity + */ +static void audio_entity_spacialize( audio_entity *ent, float *vol, float *pan ) { - float val; - - MUTEX_LOCK( sfx_mux_t01 ); - - val = src->val; - - MUTEX_UNLOCK( sfx_mux_t01 ); - - return val; + 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; + + v3_muls( delta, 1.0f/dist, delta ); + + *pan = v3_dot( vg_audio.listener_ears, delta ); + *vol = 1.0f/(attn*attn); } -/* thread-safe set master volume */ -static void sfx_set_master( float to ) +static void audio_decode_uncompressed_mono( float *src, u32 count, float *dst ) { - MUTEX_LOCK( sfx_mux_t01 ); - - g_master_volume = to; - - MUTEX_UNLOCK( sfx_mux_t01 ); + for( u32 i=0; ient; + + u32 remaining = count; + u32 cursor = ent->cur; + u32 buffer_pos = 0; + + while( remaining ) + { + u32 samples_this_run = VG_MIN( remaining, ent->length - cursor ); + remaining -= samples_this_run; + + float *dst = &buf[ buffer_pos * 2 ]; + + if( ent->info.source->source_mode == k_audio_source_mono ) + { + float *src = &((float *)ent->info.source->data)[ cursor ]; + audio_decode_uncompressed_mono( src, samples_this_run, dst ); + } + else if( ent->info.source->source_mode == k_audio_source_mono_compressed ) + { + int read_samples = stb_vorbis_get_samples_float_interleaved( + aap->vorbis_handle, + 2, + dst, + samples_this_run * 2 ); + + if( read_samples != samples_this_run ) + { + vg_warn( "Invalid samples read (%s)\n", ent->info.source->path ); + } + } + + cursor += samples_this_run; + buffer_pos += samples_this_run; + + 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 ) + { + stb_vorbis_seek_start( aap->vorbis_handle ); + } + + cursor = 0; + continue; + } + else + break; + } + + while( remaining ) + { + buf[ buffer_pos*2 + 0 ] = 0.0f; + buf[ buffer_pos*2 + 1 ] = 0.0f; + buffer_pos ++; + + remaining --; + } + + ent->cur = cursor; +} - MUTEX_LOCK( sfx_mux_t01 ); - - val = g_master_volume; - - MUTEX_UNLOCK( sfx_mux_t01 ); - - return val; +static void audio_entity_mix( aatree_ptr id, float *buffer, + u32 frame_count ) +{ + audio_entity *ent = &vg_audio.active_players[id].ent; + + u32 cursor = ent->cur, buffer_pos = 0; + float *pcf = alloca( frame_count * 2 * sizeof(float) ); + + u32 frames_write = frame_count; + float fadeout_divisor = 1.0f / (float)ent->fadeout; + + float vol = ent->info.vol, + pan = ent->info.pan; + + audio_entity_get_samples( id, frame_count, pcf ); + + if( ent->info.flags & AUDIO_FLAG_SPACIAL_3D ) + audio_entity_spacialize( ent, &vol, &pan ); + + for( u32 j=0; jfadeout ) + { + /* Force this system to be removed now */ + if( ent->fadeout_current == 0 ) + { + ent->info.flags = 0x00; + ent->cur = ent->length; + break; + } + + frame_vol *= (float)ent->fadeout_current * fadeout_divisor; + ent->fadeout_current --; + } + + float sl = 1.0f-pan, + sr = 1.0f+pan; + + buffer[ buffer_pos*2+0 ] += pcf[ buffer_pos*2+0 ] * frame_vol * sl; + buffer[ buffer_pos*2+1 ] += pcf[ buffer_pos*2+1 ] * frame_vol * sr; + + buffer_pos ++; + } } -void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, - const void *pInput, ma_uint32 frameCount ); +static void vg_sleep_ms( long msec ) +{ + struct timespec ts; -static void vg_audio_init(void) + ts.tv_sec = msec / 1000; + ts.tv_nsec = (msec % 1000) * 1000000; + nanosleep( &ts, &ts ); +} + +/* + * callback from miniaudio.h interface + */ +static void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, + const void *pInput, ma_uint32 frame_count ) { - 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; - g_aud_dconfig.sampleRate = 44100; - g_aud_dconfig.dataCallback = audio_mixer_callback; - - g_aud_dconfig.pUserData = NULL; - - vg_info( "Starting audio engine\n" ); - - if( ma_device_init( NULL, &g_aud_dconfig, &g_aud_device ) != MA_SUCCESS ) - { - vg_exiterr( "ma_device failed to initialize" ); - } - else - { - if( ma_device_start( &g_aud_device ) != MA_SUCCESS ) - { - ma_device_uninit( &g_aud_device ); - vg_exiterr( "ma_device failed to start" ); - } - } + struct timespec time_start, time_end; + clock_gettime( CLOCK_REALTIME, &time_start ); + + audio_system_enque(); + + /* Clear buffer */ + float *pOut32F = (float *)pOutBuf; + for( int i=0; iactive ) + audio_entity_mix( i, pOut32F, frame_count ); + } + +#if 0 + vg_sleep_ms( 20 ); +#endif + + /* redistribute */ + audio_system_cleanup(); + + /* 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; + + snprintf( vg_audio.performance_info, 127, + "%.1fms/%.1fms (%.1f%%) (%u frames)", + elapsed, budget, percent, frame_count ); } -static void vg_audio_free(void) +/* Decompress entire vorbis stream into buffer */ +static float *audio_decompress_vorbis( const unsigned char *data, int len, + int channels, u32 *samples ) { - ma_device_uninit( &g_aud_device ); + 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 ); + + 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 ) + { + stb_vorbis_close( pv ); + vg_exit(); + } + + int read_samples = stb_vorbis_get_samples_float_interleaved( + pv, channels, buffer, length_samples * channels ); + + 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; } -/* - * thread 1 +static int audio_clip_load( audio_clip *clip ) +{ + /* Load and decompress */ + i64 file_len; + void *filedata = vg_asset_read_s( clip->path, &file_len ); + + 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; + + float seconds = (float)samples / 44100.0f, + mb = (float)(samples*4) / (1024.0f*1024.0f); + + 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 ); + + clip->data = data; + clip->len = file_len; + + 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 ) + { + /* ... */ + + clip->data = NULL; + clip->len = 0; + + vg_error( "Source mode (%u) currently unsupported\n", clip->source_mode ); + return 0; + } + else + { + /* ... */ + + clip->data = NULL; + clip->len = 0; + + vg_error( "Unkown source mode (%u)\n", clip->source_mode ); + return 0; + } + + return 1; +} + +static void audio_clip_loadn( audio_clip *arr, int count ) +{ + for( int i=0; ifadeout = 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 ); +} -static sfx_system *sfx_alloc(void) +#endif + +/* Mark change to be uploaded through queue system */ +static void audio_player_commit( audio_player *sys ) { - if( sfx_sys_len >= SFX_MAX_SYSTEMS ) - return NULL; - - /* - * A conditional is done against this in localization step, - * Needs to be initialized. - */ - sfx_sys[ sfx_sys_len ].source = NULL; - - return sfx_sys + (sfx_sys_len++); + audio_require_lock(); + + if( vg_audio.queue_len >= vg_list_size( vg_audio.entity_queue ) ) + { + vg_warn( "Audio commit queue full\n" ); + return; + } + + if( sys->enqued ) + { + vg_warn( "Audio commit spamming; already enqued (%s)\n", sys->name ); + return; + } + + sys->enqued = 1; + audio_entity *ent = &vg_audio.entity_queue[ vg_audio.queue_len ++ ]; + ent->info = sys->info; + ent->player = sys; + sys->active_entity = AATREE_PTR_NIL; } -/* Fetch samples into pcf */ -static void audio_mixer_getsamples( float *pcf, float *source, u32 cur, u32 ch ) +/* 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 ) { - if( ch == 2 ) - { - pcf[0] = source[ cur*2+0 ]; - pcf[1] = source[ cur*2+1 ]; - } - else - { - pcf[0] = source[ cur ]; - pcf[1] = source[ cur ]; - } + audio_require_lock(); + + player->info.source = clip; + audio_player_commit( player ); +} + +static void audio_player_playoneshot( audio_player *player, audio_clip *clip ) +{ + } /* - * callback from miniaudio.h interface + * Effects */ -void audio_mixer_callback( ma_device *pDevice, void *pOutBuf, - const void *pInput, ma_uint32 frameCount ) + +/* + * Safety enforced Get/set attributes + */ + +static void audio_player_set_position( audio_player *sys, v3f pos ) { - /* Process incoming sound queue */ - MUTEX_LOCK( sfx_mux_t01 ); - - while( sfx_q_len --> 0 ) - { - sfx_system *src = sfx_q[sfx_q_len]; - sfx_system *clone; - - src->in_queue = 0; - - clone = sfx_alloc(); - *clone = *src; - - /* Links need to exist on persistent sounds */ - clone->persisitent_source = src->flags & SFX_FLAG_PERSISTENT? src: NULL; - } - - sfx_q_len = 0; - - /* Volume modifiers */ - for( int i = 0; i < sfx_sys_len; i ++ ) - { - sfx_system *sys = sfx_sys + i; - - /* Apply persistent volume if linked */ - if( sys->flags & SFX_FLAG_PERSISTENT ) - { - sys->vol = sys->persisitent_source->vol * g_master_volume; - sys->pan = sys->persisitent_source->pan; - - /* Fadeout effect ( + remove ) */ - if( sys->persisitent_source->fadeout ) - { - sys->fadeout_current = sys->persisitent_source->fadeout_current; - sys->fadeout = sys->persisitent_source->fadeout; - - sys->persisitent_source = NULL; - sys->flags &= ~SFX_FLAG_PERSISTENT; - } - } - - /* Apply volume slider if it has one linked */ - if( sys->vol_src ) - sys->cvol = sys->vol * sys->vol_src->val; - else - sys->cvol = sys->vol; - } - - MUTEX_UNLOCK( sfx_mux_t01 ); - - /* Clear buffer */ - float *pOut32F = (float *)pOutBuf; - for( int i = 0; i < frameCount * 2; i ++ ){ - pOut32F[i] = 0.f; - } + audio_require_lock(); + v3_copy( pos, sys->info.world_position ); +} - for( int i = 0; i < sfx_sys_len; i ++ ) - { - sfx_system *sys = sfx_sys + i; - - u32 cursor = sys->cur, buffer_pos = 0; - float pcf[2] = { 0.f, 0.0f }; - - u32 frames_write = frameCount; - float fadeout_divisor = 1.0f / (float)sys->fadeout; - - while( frames_write ) - { - u32 samples_this_run = VG_MIN( frames_write, sys->end - cursor ); - - if( sys->fadeout ) - { - /* Force this system to be removed now */ - if( sys->fadeout_current == 0 ) - { - sys->flags &= 0x00000000; - sys->cur = sys->end; - break; - } - - samples_this_run = VG_MIN( samples_this_run, sys->fadeout_current ); - } - - for( u32 j=0; jsource, cursor, sys->ch ); - - float vol = sys->cvol; - - if( sys->fadeout ) - { - vol *= (float)sys->fadeout_current * fadeout_divisor; - sys->fadeout_current --; - } - - if( buffer_pos >= frameCount ) - { - break; - } - - float sl = 1.0f-sys->pan, - sr = 1.0f+sys->pan; - - pOut32F[ buffer_pos*2+0 ] += pcf[0] * vol * sl; - pOut32F[ buffer_pos*2+1 ] += pcf[1] * vol * sr; - - cursor ++; - buffer_pos ++; - } - - frames_write -= samples_this_run; - - if( sys->flags & SFX_FLAG_REPEAT ) - { - if( frames_write ) - { - cursor = sys->clip_start; - continue; - } - } - - sys->cur = cursor; - break; - } - } +static void audio_player_set_vol( audio_player *sys, float vol ) +{ + audio_require_lock(); + sys->info.vol = vol; +} - /* Redistribute sound systems */ - MUTEX_LOCK( sfx_mux_t01 ); +static float audio_player_get_vol( audio_player *sys ) +{ + audio_require_lock(); + return sys->info.vol; +} - u32 idx = 0, wr = 0; - while( idx != sfx_sys_len ) - { - sfx_system *src = sfx_sys + idx; - - /* Keep only if cursor is before end or repeating */ - if( src->cur < src->end || (src->flags & SFX_FLAG_REPEAT) ) - { - sfx_sys[ wr ++ ] = sfx_sys[ idx ]; - } - - idx ++ ; - } - sfx_sys_len = wr; - - MUTEX_UNLOCK( sfx_mux_t01 ); - - (void)pInput; +static void audio_player_set_pan( audio_player *sys, float pan ) +{ + audio_require_lock(); + sys->info.pan = pan; } -/* - * Load strings into sfx_set's memory - * String layout: "sounda.ogg\0soundb.ogg\0soundc.ogg\0\0" - */ -static void sfx_set_strings( sfx_set *dest, char *strSources, - u32 flags, int bAsync ) +static float audio_player_get_pan( audio_player *sys ) { - dest->ch = (flags & SFX_FLAG_STEREO)? 2: 1; - - dest->main = NULL; - dest->numsegments = 0; - char *source = strSources; - - u32 total = 0; - int len; - while( (len = strlen( source )) ) - { - u32 samples; - float *sound = sfx_vorbis( source, dest->ch, &samples ); - - if( !sound ) - { - free( dest->main ); - dest->numsegments = 0; - return; - } - - total += samples; - - float *nbuf = realloc( dest->main, total * dest->ch * sizeof(float) ); - - if( nbuf ) - { - dest->main = nbuf; - memcpy( dest->main + (total-samples)*dest->ch, - sound, samples*dest->ch*sizeof(float) ); - free( sound ); - - dest->segments[ dest->numsegments*2+0 ] = total-samples; - dest->segments[ dest->numsegments*2+1 ] = total; - } - else - { - vg_error( "realloc() failed\n" ); - free( sound ); - return; - } - - source += len +1; - dest->numsegments ++; - } + audio_require_lock(); + return sys->info.pan; } -static void sfx_set_init( sfx_set *dest, char *sources ) +static void audio_player_set_flags( audio_player *sys, u32 flags ) { - if( !sources ) - sfx_set_strings( dest, dest->sources, dest->flags, 0 ); - else - sfx_set_strings( dest, sources, dest->flags, 0 ); + audio_require_lock(); + sys->info.flags = flags; } -static void sfx_set_play( sfx_set *source, sfx_system *sys, int id ) +static u32 audio_player_get_flags( audio_player *sys ) { - if( sfx_begin_edit( sys ) ) - { - sys->fadeout = 0; - sys->fadeout_current = 0; - sys->source = source->main; - sys->cur = source->segments[ id*2 + 0 ]; - sys->end = source->segments[ id*2 + 1 ]; - sys->ch = source->ch; - - /* for diagnostics */ - sys->clip_start = sys->cur; - sys->clip_end = sys->end; - sys->buffer_length = source->segments[ (source->numsegments-1)*2 + 1 ]; - - sfx_push( sys ); - } + audio_require_lock(); + return sys->info.flags; } -/* Pick a random sound from the buffer and play it into system */ -static void sfx_set_playrnd( sfx_set *source, sfx_system *sys, - int min_id, int max_id ) + +/* + * Debugging + */ + +static void audio_debug_ui(void) { - if( !source->numsegments ) - return; + if( !vg_audio.debug_ui ) + return; - if( max_id > source->numsegments ) + /* Get data */ + struct sound_info { - vg_error( "Max ID out of range for playrnd\n" ); - return; + const char *name; + u32 cursor, flags, length; + float vol; } + infos[ SFX_MAX_SYSTEMS ]; + int num_systems = 0; - int pick = (rand() % (max_id-min_id)) + min_id; + char perf[128]; - sfx_set_play( source, sys, pick ); -} + audio_lock(); + + for( int i=0; iactive ) + continue; + + audio_entity *ent = &aap->ent; + struct sound_info *snd = &infos[ num_systems ++ ]; + + snd->name = ent->name; + snd->cursor = ent->cur; + snd->flags = ent->info.flags; + snd->length = ent->length; + snd->vol = ent->info.vol*100.0f; + } + strcpy( perf, vg_audio.performance_info ); + audio_unlock(); + + /* 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 ); + + 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; + 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_global_ctx.cursor[1] += 20; + + /* Draw audio stack */ + for( int i=0; ifadeout_current = length_samples; - sys->fadeout = length_samples; + struct sound_info *inf = &infos[i]; + + ui_global_ctx.cursor[2] = 150; + ui_global_ctx.cursor[3] = 12; - sfx_end_edit( sys ); + u32 alpha = 0xa0000000; + + ui_new_node( &ui_global_ctx ); + { + ui_fill_rect( &ui_global_ctx, ui_global_ctx.cursor, 0x00333333|alpha ); + + ui_px baseline = ui_global_ctx.cursor[0], + w = 150, + 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 ); + + ui_global_ctx.cursor[0] = baseline + 2; + ui_global_ctx.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_end_down( &ui_global_ctx ); + ui_global_ctx.cursor[1] += 1; } } -static void sfx_set_free( sfx_set *set ) -{ - free( set->main ); -} +#endif /* VG_AUDIO_H */ diff --git a/src/vg/vg_console.h b/src/vg/vg_console.h index 304de0f..8f9020c 100644 --- a/src/vg/vg_console.h +++ b/src/vg/vg_console.h @@ -1,5 +1,8 @@ /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */ +#ifndef VG_CONSOLE_H +#define VG_CONSOLE_H + struct vg_console { struct vg_convar @@ -673,3 +676,5 @@ static void console_proc_wchar( GLFWwindow* ptrW, u32 uWchar ) console_put_char((char)uWchar); } } + +#endif /* VG_CONSOLE_H */ diff --git a/src/vg/vg_debug.h b/src/vg/vg_debug.h index 1360548..67557b7 100644 --- a/src/vg/vg_debug.h +++ b/src/vg/vg_debug.h @@ -1,5 +1,5 @@ +#if 0 int debug_sfx = 0; - struct vg_convar debug_cvars[] = { { .name = "debug_sfx", .data = &debug_sfx, .data_type = k_convar_dtype_i32 } @@ -21,9 +21,10 @@ static void sfx_internal_debug_overlay(void) } infos[ SFX_MAX_SYSTEMS ]; int num_systems; + + char perf[128]; - MUTEX_LOCK( sfx_mux_t01 ); - + sfx_lock(); num_systems = sfx_sys_len; for( int i = 0; i < sfx_sys_len; i ++ ) @@ -39,11 +40,18 @@ static void sfx_internal_debug_overlay(void) snd->clip_end = sys->clip_end; snd->buffer_length = sys->buffer_length; } - - MUTEX_UNLOCK( sfx_mux_t01 ); + strcpy( perf, sfx_performance_info ); + sfx_unlock(); // UI part // ======== + 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 ); + ui_global_ctx.cursor[1] += 20; + // Draw audio stack for( int i = 0; i < num_systems; i ++ ) @@ -95,3 +103,4 @@ static void vg_debugtools_setup(void) vg_convar_push( debug_cvars[i] ); } } +#endif diff --git a/src/vg/vg_m.h b/src/vg/vg_m.h index a97101e..5bbb467 100644 --- a/src/vg/vg_m.h +++ b/src/vg/vg_m.h @@ -1,4 +1,11 @@ -/* Copyright (C) 2021 Harry Godden (hgn) - All Rights Reserved */ +/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */ + +#ifndef VG_M_H +#define VG_M_H + +#include "vg_platform.h" +#include +#include #define VG_PIf 3.14159265358979323846264338327950288f #define VG_TAUf 6.28318530717958647692528676655900576f @@ -28,16 +35,6 @@ static inline float vg_fractf( float a ) return a - floorf( a ); } -static inline float vg_randf(void) -{ - return (float)rand()/(float)(RAND_MAX); -} - -static inline int vg_randint(int max) -{ - return rand()%max; -} - static float stable_force( float current, float diff ) { float fnew = current + diff; @@ -1174,6 +1171,12 @@ static inline void q_inv( v4f q, v4f d ) d[3] = q[3]*s; } +static inline void q_nlerp( v4f a, v4f b, float t, v4f d ) +{ + v4_lerp( a, b, t, d ); + q_normalize( d ); +} + static inline void q_m3x3( v4f q, m3x3f d ) { float @@ -1282,3 +1285,33 @@ static int ray_tri( v3f tri[3], v3f co, v3f dir, float *dist ) } else return 0; } + +static inline float vg_randf(void) +{ + return (float)rand()/(float)(RAND_MAX); +} + +static inline void vg_rand_dir(v3f dir) +{ + dir[0] = vg_randf(); + dir[1] = vg_randf(); + dir[2] = vg_randf(); + + v3_muls( dir, 2.0f, dir ); + v3_sub( dir, (v3f){1.0f,1.0f,1.0f}, dir ); + + v3_normalize( dir ); +} + +static inline void vg_rand_sphere( v3f co ) +{ + vg_rand_dir(co); + v3_muls( co, cbrtf( vg_randf() ), co ); +} + +static inline int vg_randint(int max) +{ + return rand()%max; +} + +#endif /* VG_M_H */ diff --git a/src/vg/vg_shader.h b/src/vg/vg_shader.h index 6007a42..3cad235 100644 --- a/src/vg/vg_shader.h +++ b/src/vg/vg_shader.h @@ -1,4 +1,8 @@ -/* TODO: TUrn off in release */ +/* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */ + +#ifndef VG_SHADER_H +#define VG_SHADER_H + #define STB_INCLUDE_IMPLEMENTATION #define STB_INCLUDE_LINE_GLSL #include "stb/stb_include.h" @@ -178,3 +182,5 @@ static void vg_shader_register( struct vg_shader *shader ) shader->id = 0; /* TODO: make this an error shader */ arrpush( vg_shaders_active, shader ); } + +#endif /* VG_SHADER_H */ diff --git a/src/vg/vg_store.h b/src/vg/vg_store.h index 075695e..00a8540 100644 --- a/src/vg/vg_store.h +++ b/src/vg/vg_store.h @@ -9,6 +9,8 @@ * parents are kept track of * duplicates are allowed * data is never allocated or destroyed here + * + * TODO: seperate offset,stride,base into 'generic array', seperate pool */ typedef struct aatree aatree; diff --git a/src/vg/vg_tex.h b/src/vg/vg_tex.h index 33e074e..71c79c0 100644 --- a/src/vg/vg_tex.h +++ b/src/vg/vg_tex.h @@ -1,4 +1,6 @@ /* Copyright (C) 2021-2022 Harry Godden (hgn) - All Rights Reserved */ +#ifndef VG_TEX_H +#define VG_TEX_H #define VG_TEXTURE_NO_MIP 0x1 #define VG_TEXTURE_REPEAT 0x2 @@ -135,3 +137,5 @@ static void vg_tex2d_free( vg_tex2d *textures[], int num ) glDeleteTextures( 1, &textures[i]->name ); } } + +#endif /* VG_TEX_H */ diff --git a/src/vg/vg_ui.h b/src/vg/vg_ui.h index 917b5e6..fa4d9cd 100644 --- a/src/vg/vg_ui.h +++ b/src/vg/vg_ui.h @@ -3,6 +3,10 @@ #ifndef VG_UI_H #define VG_UI_H +#include "vg/vg.h" +#include "vg/vg_tex.h" +#include "vg/vg_shader.h" + static struct vg_shader _shader_ui = { .name = "[vg] ui", @@ -743,6 +747,8 @@ static int ui_button( ui_ctx *ctx ) return k_button_click; else if( ctx->capture_lock && ctx->click_state == 2 ) return k_button_hold; + + return k_button_click; } else ui_fill_rect( ctx, ctx->cursor, ctx->colours->main ); @@ -942,4 +948,4 @@ static void ui_checkbox( ui_ctx *ctx, struct ui_checkbox *cb ) #define gui_push_image(...) ui_push_image( &ui_global_ctx, __VA_ARGS__ ) #define gui_reset_colours(...) ui_reset_colours( &ui_global_ctx ) -#endif +#endif /* VG_UI_H */