Add minimal libraries for decoding audio

This commit is contained in:
Charlie 2024-07-08 15:33:01 +02:00
parent 21482f2df2
commit aee3f78bd5
24 changed files with 19492 additions and 9147 deletions

View file

@ -6,6 +6,6 @@ mkdir build
pushd build pushd build
clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lkernel32 -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi clang -g -o cgame.exe ../build.c -O0 -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lkernel32 -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -femit-all-decls
popd popd

View file

@ -36,12 +36,12 @@ typedef struct Context_Extra {
// //
// this is a minimal starting point for new projects. Copy & rename to get started // this is a minimal starting point for new projects. Copy & rename to get started
#include "oogabooga/examples/minimal_game_loop.c" // #include "oogabooga/examples/minimal_game_loop.c"
// #include "oogabooga/examples/text_rendering.c" // #include "oogabooga/examples/text_rendering.c"
// An engine dev stress test for rendering // An engine dev stress test for rendering
// #include "oogabooga/examples/renderer_stress_test.c" #include "oogabooga/examples/renderer_stress_test.c"
// Randy's example game that he's building out as a tutorial for using the engine // Randy's example game that he's building out as a tutorial for using the engine
// #include "entry_randygame.c" // #include "entry_randygame.c"

View file

@ -9,7 +9,7 @@ pushd build
mkdir release mkdir release
pushd release pushd release
clang -o cgame.exe ../../build.c -Ofast -DNDEBUG -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -finline-functions -ffast-math -fno-math-errno -funsafe-math-optimizations -freciprocal-math -ffinite-math-only -fassociative-math -fno-signed-zeros -fno-trapping-math -ftree-vectorize -fomit-frame-pointer -funroll-loops -fno-rtti -fno-exceptions clang -o cgame.exe ../../build.c -Ofast -DNDEBUG -std=c11 -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -finline-functions -finline-hint-functions -ffast-math -fno-math-errno -funsafe-math-optimizations -freciprocal-math -ffinite-math-only -fassociative-math -fno-signed-zeros -fno-trapping-math -ftree-vectorize -fomit-frame-pointer -funroll-loops -fno-rtti -fno-exceptions
popd popd
popd popd

View file

@ -1,3 +1,22 @@
## v0.00.003 -
Random:
- get_random_float64()
- get_random_float32_in_range()
- get_random_float64_in_range()
- get_random_int_in_range()
Third party:
- Added 3 minimal libraries for audio decoding
- dr_mp3.h: MP3 decoding
- dr_wav.h: WAV decoding
- stb_vorbis.c: OGG decoding
- Made a global thread_local "third_party_allocator" which is set when third party libraries are used so all memory still goes through our *program_memory
- Stripped all third party libraries of external dependencies (C headers) & noise
Misc:
- Fix typos in examples/text_rendering.c
## v0.00.002 - Text rendering, image manipulation, hash tables ## v0.00.002 - Text rendering, image manipulation, hash tables
Renderer: Renderer:

110
oogabooga/audio.c Normal file
View file

@ -0,0 +1,110 @@
/*
bool check_wav_header(string data) {
return string_starts_with(data, STR("RIFFWAVE"));
}
bool check_ogg_header(string data) {
return string_starts_with(data, STR("oggs"));
}
typedef enum Audio_Format_Type {
AUDIO_FORMAT_TYPE_UNKNOWN,
AUDIO_FORMAT_TYPE_U8,
AUDIO_FORMAT_TYPE_S16,
AUDIO_FORMAT_TYPE_S24,
AUDIO_FORMAT_TYPE_S32,
AUDIO_FORMAT_TYPE_F32
} Audio_Format_Type;
typedef struct Audio_Format {
Audio_Format_Type type;
int channels;
int sample_rate;
} Audio_Format;
u64 get_audio_format_component_byte_size(Audio_Format_Type format) {
switch (format) {
case AUDIO_FORMAT_TYPE_F32: return 4; break;
case AUDIO_FORMAT_TYPE_S32: return 4; break;
case AUDIO_FORMAT_TYPE_S24: return 3; break;
case AUDIO_FORMAT_TYPE_S16: return 2; break;
case AUDIO_FORMAT_TYPE_U8: return 1; break;
case AUDIO_FORMAT_TYPE_UNKNOWN: return 0; break;
}
panic("");
}
#define U8_MAX 255
#define S16_MIN -32768
#define S16_MAX 32767
#define S24_MIN -8388608
#define S24_MAX 8388607
#define S32_MIN -2147483648
#define S32_MAX 2147483647
void mix_frames(void *dst, void *src, u64 frame_count, Audio_Format format) {
u64 comp_size = get_audio_format_component_byte_size(format.type);
u64 frame_size = comp_size * format.channels;
u64 output_size = frame_count * frame_size;
// #Speed #Simd #Incomplete
// Quality:
// - Dithering
// - Clipping. Dynamic Range Compression?
for (u64 frame = 0; frame < frame_count; frame++) {
for (u64 c = 0; c < format.channels; c++) {
void *src_sample = (u8*)src + frame*frame_size + c*comp_size;
void *dst_sample = (u8*)dst + frame*frame_size + c*comp_size;
switch (format.type) {
case AUDIO_FORMAT_TYPE_F32: {
*((f32*)dst_sample) += *((f32*)src_sample);
}
case AUDIO_FORMAT_TYPE_S32: {
s32 dst_int = *((s32*)dst_sample);
s32 src_int = *((s32*)src_sample);
*((s32*)dst_sample) = (s32)clamp((s64)(dst_int + src_int), S32_MIN, S32_MAX);
break;
}
case AUDIO_FORMAT_TYPE_S24: {
s64 src_int = 0;
memcpy(&src_int, src_sample, 3);
src_int <<= 40;
src_int >>= 40;
s64 dst_int;
memcpy(&dst_int, dst_sample, 3);
dst_int <<= 40;
dst_int >>= 40;
s64 sum = clamp(src_int + dst_int, S24_MIN, S24_MAX);
memcpy(dst_sample, &sum, 3);
break;
}
case AUDIO_FORMAT_TYPE_S16: {
s16 dst_int = *((s16*)dst_sample);
s16 src_int = *((s16*)src_sample);
*((s16*)dst_sample) = (s16)clamp((s64)(dst_int + src_int), S16_MIN, S16_MAX);
break;
}
case AUDIO_FORMAT_TYPE_U8: {
u8 dst_int = *((u8*)dst_sample);
u8 src_int = *((u8*)src_sample);
*((u8*)dst_sample) = (u8)clamp((s64(dst_int + src_int), 0, U8_MAX);
break;
}
case AUDIO_FORMAT_TYPE_UNKNOWN: break;
}
}
}
}
*/

View file

@ -75,6 +75,8 @@ typedef struct Allocator {
void *data; void *data;
} Allocator; } Allocator;
Allocator get_heap_allocator();
typedef struct Context { typedef struct Context {
void *logger; // void(*Logger_Proc)(Log_Level level, string fmt, ...) void *logger; // void(*Logger_Proc)(Log_Level level, string fmt, ...)

BIN
oogabooga/examples/bruh.ogg Normal file

Binary file not shown.

BIN
oogabooga/examples/song.mp3 Normal file

Binary file not shown.

View file

@ -51,7 +51,7 @@ int entry(int argc, char **argv) {
// If for example we wanted to center the text, we would do the same but then add // If for example we wanted to center the text, we would do the same but then add
// the text size divided by two: // the text size divided by two:
// justified = v2_add(justified, v2_subf(hello_metrics.functional_size, 2.0)); // justified = v2_add(justified, v2_divf(hello_metrics.functional_size, 2.0));
local_persist bool show_bounds = false; local_persist bool show_bounds = false;
if (is_key_just_pressed('E')) show_bounds = !show_bounds; if (is_key_just_pressed('E')) show_bounds = !show_bounds;

Binary file not shown.

View file

@ -82,6 +82,7 @@ Gfx_Font *load_font_from_disk(string path, Allocator allocator) {
if (!read_ok) return 0; if (!read_ok) return 0;
third_party_allocator = allocator;
stbtt_fontinfo stbtt_handle; stbtt_fontinfo stbtt_handle;
int result = stbtt_InitFont(&stbtt_handle, font_data.data, stbtt_GetFontOffsetForIndex(font_data.data, 0)); int result = stbtt_InitFont(&stbtt_handle, font_data.data, stbtt_GetFontOffsetForIndex(font_data.data, 0));
@ -94,10 +95,14 @@ Gfx_Font *load_font_from_disk(string path, Allocator allocator) {
font->raw_font_data = font_data; font->raw_font_data = font_data;
font->allocator = allocator; font->allocator = allocator;
third_party_allocator = ZERO(Allocator);
return font; return font;
} }
void destroy_font(Gfx_Font *font) { void destroy_font(Gfx_Font *font) {
third_party_allocator = font->allocator;
for (u64 i = 0; i < MAX_FONT_HEIGHT; i++) { for (u64 i = 0; i < MAX_FONT_HEIGHT; i++) {
Gfx_Font_Variation *variation = &font->variations[i]; Gfx_Font_Variation *variation = &font->variations[i];
if (!variation->initted) continue; if (!variation->initted) continue;
@ -114,6 +119,8 @@ void destroy_font(Gfx_Font *font) {
dealloc_string(font->allocator, font->raw_font_data); dealloc_string(font->allocator, font->raw_font_data);
dealloc(font->allocator, font); dealloc(font->allocator, font);
third_party_allocator = ZERO(Allocator);
} }
void font_variation_init(Gfx_Font_Variation *variation, Gfx_Font *font, u32 font_height) { void font_variation_init(Gfx_Font_Variation *variation, Gfx_Font *font, u32 font_height) {
@ -173,7 +180,7 @@ void font_atlas_init(Gfx_Font_Atlas *atlas, Gfx_Font_Variation *variation, u32 f
u32 cursor_x = 0; u32 cursor_x = 0;
u32 cursor_y = 0; u32 cursor_y = 0;
third_party_allocator = variation->font->allocator;
// Used for flipping bitmaps // Used for flipping bitmaps
u8 *temp_row = (u8 *)talloc(variation->height); u8 *temp_row = (u8 *)talloc(variation->height);
for (u32 c = first_codepoint; c < first_codepoint + variation->codepoint_range_per_atlas; c++) { for (u32 c = first_codepoint; c < first_codepoint + variation->codepoint_range_per_atlas; c++) {
@ -215,6 +222,8 @@ void font_atlas_init(Gfx_Font_Atlas *atlas, Gfx_Font_Variation *variation, u32 f
cursor_x += w; cursor_x += w;
} }
third_party_allocator = ZERO(Allocator);
} }
void render_atlas_if_not_yet_rendered(Gfx_Font *font, u32 font_height, u32 codepoint) { void render_atlas_if_not_yet_rendered(Gfx_Font *font, u32 font_height, u32 codepoint) {

View file

@ -65,8 +65,10 @@ Gfx_Image *load_image_from_disk(string path, Allocator allocator) {
int width, height, channels; int width, height, channels;
stbi_set_flip_vertically_on_load(1); stbi_set_flip_vertically_on_load(1);
third_party_allocator = allocator;
unsigned char* stb_data = stbi_load_from_memory(png.data, png.count, &width, &height, &channels, STBI_rgb_alpha); unsigned char* stb_data = stbi_load_from_memory(png.data, png.count, &width, &height, &channels, STBI_rgb_alpha);
if (!stb_data) { if (!stb_data) {
dealloc(allocator, image); dealloc(allocator, image);
dealloc_string(allocator, png); dealloc_string(allocator, png);
@ -84,6 +86,8 @@ Gfx_Image *load_image_from_disk(string path, Allocator allocator) {
gfx_init_image(image, stb_data); gfx_init_image(image, stb_data);
stbi_image_free(stb_data); stbi_image_free(stb_data);
third_party_allocator = ZERO(Allocator);
return image; return image;
} }

View file

@ -403,4 +403,10 @@ Matrix4 m4_inverse(Matrix4 m) {
} }
return inv; return inv;
} }
// This isn't really linmath but just putting it here for now
#define clamp(x, lo, hi) ((x) < (lo) ? (lo) : ((x) > (hi) ? (hi) : (x)))

View file

@ -240,7 +240,7 @@ void *heap_alloc(u64 size) {
size = (size+HEAP_ALIGNMENT) & ~(HEAP_ALIGNMENT-1); size = (size+HEAP_ALIGNMENT) & ~(HEAP_ALIGNMENT-1);
assert(size < MAX_HEAP_BLOCK_SIZE, "Past Charlie has been lazy and did not handle large allocations like this. I apologize on behalf of past Charlie. A quick fix could be to increase the heap block size for now."); assert(size < MAX_HEAP_BLOCK_SIZE, "Past Charlie has been lazy and did not handle large allocations like this. I apologize on behalf of past Charlie. A quick fix could be to increase the heap block size for now. #Incomplete #Limitation");
Heap_Block *block = heap_head; Heap_Block *block = heap_head;
Heap_Block *last_block = 0; Heap_Block *last_block = 0;

View file

@ -107,7 +107,7 @@
#define OGB_VERSION_MAJOR 0 #define OGB_VERSION_MAJOR 0
#define OGB_VERSION_MINOR 0 #define OGB_VERSION_MINOR 0
#define OGB_VERSION_PATCH 2 #define OGB_VERSION_PATCH 3
#define OGB_VERSION (OGB_VERSION_MAJOR*1000000+OGB_VERSION_MINOR*1000+OGB_VERSION_PATCH) #define OGB_VERSION (OGB_VERSION_MAJOR*1000000+OGB_VERSION_MINOR*1000+OGB_VERSION_PATCH)
@ -274,8 +274,11 @@ typedef u8 bool;
#include "color.c" #include "color.c"
#include "memory.c" #include "memory.c"
#include "input.c" #include "input.c"
#include "drawing.c" #include "drawing.c"
#include "audio.c"
// #Portability // #Portability
#if GFX_RENDERER == GFX_RENDERER_D3D11 #if GFX_RENDERER == GFX_RENDERER_D3D11
#include "gfx_impl_d3d11.c" #include "gfx_impl_d3d11.c"

View file

@ -1,10 +1,11 @@
// This is a naive implementation for now (LCG) // LCG
// Distribution will probably suck. // Meh distribution, but good enough for general purposes
#define RAND_MAX_64 0xFFFFFFFFFFFFFFFFull #define RAND_MAX_64 0xFFFFFFFFFFFFFFFFull
#define MULTIPLIER 6364136223846793005ull #define MULTIPLIER 6364136223846793005ull
#define INCREMENT 1ull #define INCREMENT 1442695040888963407ull
// set this to something like os_get_current_cycle_count() for very randomized seed
u64 seed_for_random = 1; u64 seed_for_random = 1;
u64 get_random() { u64 get_random() {
@ -14,4 +15,19 @@ u64 get_random() {
f32 get_random_float32() { f32 get_random_float32() {
return (float32)get_random()/(float32)UINT64_MAX; return (float32)get_random()/(float32)UINT64_MAX;
}
f64 get_random_float64() {
return (float64)get_random()/(float64)UINT64_MAX;
}
f32 get_random_float32_in_range(f32 min, f32 max) {
return (max-min)*get_random_float32()+min;
}
f64 get_random_float64_in_range(f64 min, f64 max) {
return (max-min)*get_random_float64()+min;
}
s64 get_random_int_in_range(s64 min, s64 max) {
return min + (s64)(get_random() % (max - min + 1));
} }

View file

@ -100,4 +100,10 @@ s64 string_find_from_right(string s, string sub) {
return -1; return -1;
} }
bool string_starts_with(string s, string sub) {
if (s.count < sub.count) return false;
s.count = sub.count;
return strings_match(s, sub);
}

View file

@ -1056,6 +1056,30 @@ void test_hash_table() {
assert(table.count == 0, "Failed: Hash table count should be 0 after destroy"); assert(table.count == 0, "Failed: Hash table count should be 0 after destroy");
assert(table.capacity_count == 0, "Failed: Hash table capacity count should be 0 after destroy"); assert(table.capacity_count == 0, "Failed: Hash table capacity count should be 0 after destroy");
} }
#define NUM_BINS 100
#define NUM_SAMPLES 100000000
void test_random_distribution() {
int bins[NUM_BINS] = {0};
seed_for_random = os_get_current_cycle_count();
for (int i = 0; i < NUM_SAMPLES; i++) {
f32 rand_val = get_random_float32();
int bin = (int)(rand_val * NUM_BINS);
bins[bin]++;
}
int min_bin = INT32_MAX;
int max_bin = 0;
print("Histogram of Random Values:\n");
for (int i = 0; i < NUM_BINS; i++) {
print("Bin %d: %d\n", i, bins[i]);
min_bin = min(min_bin, bins[i]);
max_bin = max(max_bin, bins[i]);
}
print("Min: %d, max: %d\n", min_bin, max_bin);
}
void oogabooga_run_tests() { void oogabooga_run_tests() {
@ -1101,4 +1125,8 @@ void oogabooga_run_tests() {
test_hash_table(); test_hash_table();
print("OK!\n"); print("OK!\n");
print("Testing random distribution... ");
test_random_distribution();
print("OK!\n");
} }

View file

@ -1,34 +1,3 @@
// Custom allocators for lodepng
Allocator get_heap_allocator();
/*Allocator lodepng_allocator = {0};
void* lodepng_malloc(size_t size) {
#ifdef LODEPNG_MAX_ALLOC
if(size > LODEPNG_MAX_ALLOC) return 0;
#endif
return alloc(get_heap_allocator(), size);
}
void* lodepng_realloc(void* ptr, size_t new_size) {
if (!ptr) return lodepng_malloc(new_size);
#ifdef LODEPNG_MAX_ALLOC
if(new_size > LODEPNG_MAX_ALLOC) return 0;
#endif
if (lodepng_allocator.proc) return lodepng_allocator.proc(new_size, ptr, ALLOCATOR_REALLOCATE, lodepng_allocator.data);
return get_heap_allocator().proc(new_size, ptr, ALLOCATOR_REALLOCATE, get_heap_allocator().data);
}
void lodepng_free(void* ptr) {
if (!ptr) return;
if (lodepng_allocator.proc) dealloc(lodepng_allocator, ptr);
else dealloc(get_heap_allocator(), ptr);
}
#define LODEPNG_NO_COMPILE_ALLOCATORS
//#define LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS
#define LODEPNG_NO_COMPILE_DISK
#define LODEPNG_NO_COMPILE_ERROR_TEXT
#define LODEPNG_NO_COMPILE_ENCODER
// One day I might write my own png decoder so we don't even need this
#include "third_party/lodepng.h"
#include "third_party/lodepng.c"*/
#define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
@ -40,22 +9,21 @@ typedef signed short s16;
typedef unsigned int u32; typedef unsigned int u32;
typedef signed int s32; typedef signed int s32;
// #Temporary #Incomplete #Memory thread_local Allocator third_party_allocator = {0};
// This should use the allocator we pass to the gfx font system. void *third_party_malloc(size_t size) {
// Probably just do a thread local global here assert(third_party_allocator.proc, "No third party allocator was set, but it was used!");
void *stbtt_malloc(size_t size) {
if (!size) return 0; if (!size) return 0;
return alloc(get_heap_allocator(), size); return alloc(third_party_allocator, size);
} }
#define STBTT_malloc(x,u) ((void)(u),stbtt_malloc(x)) void third_party_free(void *p) {
void stbtt_free(void *p) { assert(third_party_allocator.proc, "No third party allocator was set, but it was used!");
if (!p) return; if (!p) return;
dealloc(get_heap_allocator(), p); dealloc(third_party_allocator, p);
} }
#define STBTT_free(x,u) ((void)(u),stbtt_free(x))
#define STBTT_malloc(x,u) ((void)(u),third_party_malloc(x))
#define STBTT_free(x,u) ((void)(u),third_party_free(x))
#define STBTT_assert(x) assert(x) #define STBTT_assert(x) assert(x)
size_t stbtt_strlen(const char* str) { size_t stbtt_strlen(const char* str) {
size_t count = 0; size_t count = 0;
while (str[count] != 0) count += 1; while (str[count] != 0) count += 1;
@ -64,14 +32,36 @@ size_t stbtt_strlen(const char* str) {
#define STBTT_strlen(x) stbtt_strlen(x) #define STBTT_strlen(x) stbtt_strlen(x)
#define STBTT_memcpy memcpy #define STBTT_memcpy memcpy
#define STBTT_memset memset #define STBTT_memset memset
#include "third_party/stb_truetype.h"
#define STBI_NO_STDIO #define STBI_NO_STDIO
#define STBI_ASSERT(x) {if (!(x)) *(volatile char*)0 = 0;} #define STBI_ASSERT(x) {if (!(x)) *(volatile char*)0 = 0;}
#define STBI_MALLOC(sz) third_party_malloc(sz)
#define STBI_MALLOC(sz) stbtt_malloc(sz) #define STBI_REALLOC(p,newsz) third_party_allocator.proc(newsz, p, ALLOCATOR_REALLOCATE, 0)
#define STBI_REALLOC(p,newsz) get_heap_allocator().proc(newsz, p, ALLOCATOR_REALLOCATE, 0) #define STBI_FREE(p) third_party_free(p)
#define STBI_FREE(p) stbtt_free(p)
#include "third_party/stb_image.h" #include "third_party/stb_image.h"
#include "third_party/stb_truetype.h"
#define STB_VORBIS_NO_CRT
#include "third_party/stb_vorbis.c"
// Why Sean ?????
#undef R
#undef C
#undef L
#define DR_MP3_NO_STDIO
#define DRMP3_ASSERT(exp) assert(exp, "dr_mp3 assertion failed")
#define DRMP3_MALLOC(sz) third_party_malloc(sz)
#define DRMP3_REALLOC(p,newsz) third_party_allocator.proc(newsz, p, ALLOCATOR_REALLOCATE, 0)
#define DRMP3_FREE(p) third_party_free(p)
#define DR_MP3_IMPLEMENTATION
#include "third_party/dr_mp3.h"
#define DR_WAV_NO_STDIO
#define DR_WAV_NO_WCHAR
#define DRWAV_ASSERT(exp) assert(exp, "dr_mp3 assertion failed")
#define DRWAV_MALLOC(sz) third_party_malloc(sz)
#define DRWAV_REALLOC(p,newsz) third_party_allocator.proc(newsz, p, ALLOCATOR_REALLOCATE, 0)
#define DRWAV_FREE(p) third_party_free(p)
#define DR_WAV_IMPLEMENTATION
#include "third_party/dr_wav.h"

4838
oogabooga/third_party/dr_mp3.h vendored Normal file

File diff suppressed because it is too large Load diff

8816
oogabooga/third_party/dr_wav.h vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

5585
oogabooga/third_party/stb_vorbis.c vendored Normal file

File diff suppressed because it is too large Load diff