Sold my soul to play monlight sonata
Audio sources & decoding are pretty much done and working well. Playback is not really implemented yet, I'm just hacking in a way to output an audio source. - Seriously microsoft wtf
This commit is contained in:
parent
e1aafd8220
commit
2b335aee35
19 changed files with 1102 additions and 198 deletions
5
TODO
Normal file
5
TODO
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
- Audio
|
||||||
|
- Optimize
|
||||||
|
- Spam simd
|
||||||
|
- Concurrent jobs for players?
|
|
@ -6,6 +6,6 @@ mkdir build
|
||||||
|
|
||||||
pushd build
|
pushd build
|
||||||
|
|
||||||
clang -g -o cgame.exe ../build.c -O0 -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lkernel32 -lgdi32 -luser32 -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -femit-all-decls
|
clang -g -o cgame.exe ../build.c -O0 -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -lkernel32 -lgdi32 -luser32 -lruntimeobject -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -lole32 -lavrt -lksuser -femit-all-decls
|
||||||
|
|
||||||
popd
|
popd
|
5
build.c
5
build.c
|
@ -3,6 +3,8 @@
|
||||||
///
|
///
|
||||||
// Build config stuff
|
// Build config stuff
|
||||||
|
|
||||||
|
#define OOGABOOGA_DEV 1
|
||||||
|
|
||||||
#define INITIAL_PROGRAM_MEMORY_SIZE MB(5)
|
#define INITIAL_PROGRAM_MEMORY_SIZE MB(5)
|
||||||
|
|
||||||
typedef struct Context_Extra {
|
typedef struct Context_Extra {
|
||||||
|
@ -26,12 +28,13 @@ 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"
|
||||||
// #include "oogabooga/examples/custom_logger.c"
|
// #include "oogabooga/examples/custom_logger.c"
|
||||||
// #include "oogabooga/examples/renderer_stress_test.c"
|
// #include "oogabooga/examples/renderer_stress_test.c"
|
||||||
// #include "oogabooga/examples/tile_game.c"
|
// #include "oogabooga/examples/tile_game.c"
|
||||||
|
#include "oogabooga/examples/audio_test.c"
|
||||||
|
|
||||||
// This is where you swap in your own project!
|
// This is where you swap in your own project!
|
||||||
// #include "entry_yourepicgamename.c"
|
// #include "entry_yourepicgamename.c"
|
||||||
|
|
|
@ -9,7 +9,7 @@ pushd build
|
||||||
mkdir release
|
mkdir release
|
||||||
pushd release
|
pushd release
|
||||||
|
|
||||||
clang -o cgame.exe ../../build.c -Ofast -DNDEBUG -std=c11 -D_CRT_SECURE_NO_WARNINGS -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
|
clang -o cgame.exe ../../build.c -Ofast -DNDEBUG -std=c11 -D_CRT_SECURE_NO_WARNINGS -Wextra -Wno-incompatible-library-redeclaration -Wno-sign-compare -Wno-unused-parameter -Wno-builtin-requires-header -Wno-deprecated-declarations -lkernel32 -lgdi32 -luser32 -lruntimeobject -lwinmm -ld3d11 -ldxguid -ld3dcompiler -lshlwapi -lole32 -lavrt -lksuser -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
|
|
@ -1,3 +1,21 @@
|
||||||
|
|
||||||
|
## v0.01.001 - AUDIO!
|
||||||
|
- Added audio sources
|
||||||
|
- File stream sources & Fully decoded sources
|
||||||
|
- 16-bit int & 32-bit float support
|
||||||
|
- WAV & OGG support
|
||||||
|
- Usage:
|
||||||
|
bool audio_source_init_file_stream(*src, path, bit_width, allocator)
|
||||||
|
bool audio_source_init_file_decode(*src, path, bit_width, allocator)
|
||||||
|
void audio_source_destroy(*src)
|
||||||
|
- Misc
|
||||||
|
- Win32 audio impl
|
||||||
|
- Make default logger thread safe
|
||||||
|
- Rename tm_scope_cycles & tm_scope_cycles_xxx -> tm_scope & tm_scope_xxx
|
||||||
|
- Minor cleanups
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## v0.00.005 - Z layers
|
## v0.00.005 - Z layers
|
||||||
|
|
||||||
Renderer:
|
Renderer:
|
||||||
|
|
|
@ -1,37 +1,308 @@
|
||||||
|
|
||||||
/*
|
|
||||||
bool check_wav_header(string data) {
|
bool check_wav_header(string data) {
|
||||||
return string_starts_with(data, STR("RIFFWAVE"));
|
return string_starts_with(data, STR("RIFF"));
|
||||||
}
|
}
|
||||||
bool check_ogg_header(string data) {
|
bool check_ogg_header(string data) {
|
||||||
return string_starts_with(data, STR("oggs"));
|
return string_starts_with(data, STR("OggS"));
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum Audio_Format_Type {
|
// Supporting more than s16 and f32
|
||||||
AUDIO_FORMAT_TYPE_UNKNOWN,
|
// If it's a real thing that there's audio devices which support neither then I will be surprised
|
||||||
AUDIO_FORMAT_TYPE_U8,
|
// The only format I might consider adding is S32 if it turns out people want VERY detailed audio
|
||||||
AUDIO_FORMAT_TYPE_S16,
|
typedef enum Audio_Format_Bits {
|
||||||
AUDIO_FORMAT_TYPE_S24,
|
AUDIO_BITS_16, // this will be s16
|
||||||
AUDIO_FORMAT_TYPE_S32,
|
AUDIO_BITS_32, // this will be f32
|
||||||
AUDIO_FORMAT_TYPE_F32
|
} Audio_Format_Bits;
|
||||||
} Audio_Format_Type;
|
u64 get_audio_bit_width_byte_size(Audio_Format_Bits b) {
|
||||||
|
switch (b) {
|
||||||
|
case AUDIO_BITS_32: return 4; break;
|
||||||
|
case AUDIO_BITS_16: return 2; break;
|
||||||
|
}
|
||||||
|
panic("");
|
||||||
|
}
|
||||||
typedef struct Audio_Format {
|
typedef struct Audio_Format {
|
||||||
Audio_Format_Type type;
|
Audio_Format_Bits bit_width;
|
||||||
int channels;
|
int channels;
|
||||||
int sample_rate;
|
int sample_rate;
|
||||||
} Audio_Format;
|
} Audio_Format;
|
||||||
|
|
||||||
u64 get_audio_format_component_byte_size(Audio_Format_Type format) {
|
// I don't see a big reason for you to use anything else than WAV and OGG.
|
||||||
switch (format) {
|
// If you use mp3 that's just not very smart.
|
||||||
case AUDIO_FORMAT_TYPE_F32: return 4; break;
|
// Ogg has better quality AND better compression AND you don't need any licensing (which you need for mp3)
|
||||||
case AUDIO_FORMAT_TYPE_S32: return 4; break;
|
// https://convertio.co/mp3-ogg/
|
||||||
case AUDIO_FORMAT_TYPE_S24: return 3; break;
|
// I will probably add mp3 support at some point for compatibility reasonavg.
|
||||||
case AUDIO_FORMAT_TYPE_S16: return 2; break;
|
// - Charlie 2024-07-11
|
||||||
case AUDIO_FORMAT_TYPE_U8: return 1; break;
|
typedef enum Audio_Decoder_Kind {
|
||||||
case AUDIO_FORMAT_TYPE_UNKNOWN: return 0; break;
|
AUDIO_DECODER_WAV,
|
||||||
}
|
AUDIO_DECODER_OGG
|
||||||
panic("");
|
} Audio_Decoder_Kind;
|
||||||
|
|
||||||
|
typedef enum Audio_Source_Kind {
|
||||||
|
AUDIO_SOURCE_FILE_STREAM,
|
||||||
|
AUDIO_SOURCE_MEMORY, // Raw pcm frames
|
||||||
|
} Audio_Source_Kind;
|
||||||
|
|
||||||
|
typedef struct Audio_Source {
|
||||||
|
|
||||||
|
Audio_Source_Kind kind;
|
||||||
|
Audio_Format format;
|
||||||
|
u64 number_of_frames;
|
||||||
|
Allocator allocator;
|
||||||
|
string compressed_data;
|
||||||
|
|
||||||
|
// For file stream
|
||||||
|
Audio_Decoder_Kind decoder;
|
||||||
|
union {
|
||||||
|
drwav wav;
|
||||||
|
stb_vorbis *ogg;
|
||||||
|
};
|
||||||
|
|
||||||
|
// For memory source
|
||||||
|
void *pcm_frames;
|
||||||
|
|
||||||
|
} Audio_Source;
|
||||||
|
|
||||||
|
int
|
||||||
|
_audio_file_stream_sample_frames(Audio_Source *src, u64 first_frame_index,
|
||||||
|
u64 number_of_frames, void *output_buffer);
|
||||||
|
|
||||||
|
bool
|
||||||
|
audio_source_init_file_stream(Audio_Source *src, string path, Audio_Format_Bits bit_width,
|
||||||
|
Allocator allocator) {
|
||||||
|
*src = ZERO(Audio_Source);
|
||||||
|
|
||||||
|
src->allocator = allocator;
|
||||||
|
src->kind = AUDIO_SOURCE_FILE_STREAM;
|
||||||
|
|
||||||
|
string data;
|
||||||
|
bool read_ok = os_read_entire_file(path, &data, allocator);
|
||||||
|
src->compressed_data = data;
|
||||||
|
|
||||||
|
third_party_allocator = allocator;
|
||||||
|
|
||||||
|
if (!read_ok) {
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (check_wav_header(data)) {
|
||||||
|
drwav_bool32 init_ok = drwav_init_memory(&src->wav, data.data, data.count, null);
|
||||||
|
if (!init_ok) {
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
src->decoder = AUDIO_DECODER_WAV;
|
||||||
|
|
||||||
|
src->format.channels = src->wav.fmt.channels;
|
||||||
|
src->format.sample_rate = src->wav.fmt.sampleRate;
|
||||||
|
src->number_of_frames = src->wav.totalPCMFrameCount;
|
||||||
|
} else if (check_ogg_header(data)) {
|
||||||
|
int err;
|
||||||
|
src->ogg = stb_vorbis_open_memory(data.data, data.count, &err, null);
|
||||||
|
if (!src->ogg) {
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
src->decoder = AUDIO_DECODER_OGG;
|
||||||
|
|
||||||
|
stb_vorbis_info info = stb_vorbis_get_info(src->ogg);
|
||||||
|
src->format.channels = info.channels;
|
||||||
|
src->format.sample_rate = info.sample_rate;
|
||||||
|
src->number_of_frames = stb_vorbis_stream_length_in_samples(src->ogg);
|
||||||
|
} else {
|
||||||
|
log_error("Error in init_audio_source_file_stream(): Unrecognized audio format in file '%s'. We currently support WAV and OGG (Vorbis).", path);
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
src->format.bit_width = bit_width;
|
||||||
|
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
audio_source_init_file_decode(Audio_Source *src, string path, Audio_Format_Bits bit_width,
|
||||||
|
Allocator allocator) {
|
||||||
|
if (!audio_source_init_file_stream(src, path, bit_width, allocator)) return false;
|
||||||
|
src->kind = AUDIO_SOURCE_MEMORY;
|
||||||
|
|
||||||
|
u64 comp_size = get_audio_bit_width_byte_size(src->format.bit_width);
|
||||||
|
u64 total_size = src->number_of_frames * src->format.channels * comp_size;
|
||||||
|
src->pcm_frames = alloc(allocator, total_size);
|
||||||
|
|
||||||
|
int num_retrieved = _audio_file_stream_sample_frames(src, 0, src->number_of_frames, src->pcm_frames);
|
||||||
|
assert(num_retrieved == src->number_of_frames, "decoder failed failed");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
audio_source_destroy(Audio_Source *src) {
|
||||||
|
switch (src->kind) {
|
||||||
|
case AUDIO_SOURCE_FILE_STREAM: {
|
||||||
|
if (src->pcm_frames) dealloc(src->allocator, src->pcm_frames);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AUDIO_SOURCE_MEMORY: {
|
||||||
|
dealloc(src->allocator, src->pcm_frames);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
third_party_allocator = src->allocator;
|
||||||
|
switch (src->decoder) {
|
||||||
|
case AUDIO_DECODER_WAV: {
|
||||||
|
drwav_uninit(&src->wav);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AUDIO_DECODER_OGG: {
|
||||||
|
stb_vorbis_close(src->ogg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
|
||||||
|
dealloc_string(src->allocator, src->compressed_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
_audio_file_stream_sample_frames(Audio_Source *src, u64 first_frame_index,
|
||||||
|
u64 number_of_frames, void *output_buffer) {
|
||||||
|
third_party_allocator = src->allocator;
|
||||||
|
int retrieved = 0;
|
||||||
|
switch (src->decoder) {
|
||||||
|
case AUDIO_DECODER_WAV:
|
||||||
|
bool seek_ok = drwav_seek_to_pcm_frame(&src->wav, first_frame_index);
|
||||||
|
assert(seek_ok);
|
||||||
|
switch(src->format.bit_width) {
|
||||||
|
case AUDIO_BITS_32: {
|
||||||
|
retrieved = drwav_read_pcm_frames_f32(
|
||||||
|
&src->wav,
|
||||||
|
number_of_frames,
|
||||||
|
(f32*)output_buffer
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AUDIO_BITS_16: {
|
||||||
|
retrieved = drwav_read_pcm_frames_s16(
|
||||||
|
&src->wav,
|
||||||
|
number_of_frames,
|
||||||
|
(s16*)output_buffer
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: panic("Invalid bits value");
|
||||||
|
} break; // case AUDIO_DECODER_WAV:
|
||||||
|
case AUDIO_DECODER_OGG:
|
||||||
|
seek_ok = stb_vorbis_seek(src->ogg, first_frame_index);
|
||||||
|
assert(seek_ok);
|
||||||
|
switch(src->format.bit_width) {
|
||||||
|
case AUDIO_BITS_32: {
|
||||||
|
retrieved = stb_vorbis_get_samples_float_interleaved(
|
||||||
|
src->ogg,
|
||||||
|
src->format.channels,
|
||||||
|
(f32*)output_buffer,
|
||||||
|
number_of_frames * src->format.channels
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AUDIO_BITS_16: {
|
||||||
|
retrieved = stb_vorbis_get_samples_short_interleaved(
|
||||||
|
src->ogg,
|
||||||
|
src->format.channels,
|
||||||
|
(s16*)output_buffer,
|
||||||
|
number_of_frames * src->format.channels
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: panic("Invalid bits value");
|
||||||
|
} break; // case AUDIO_DECODER_OGG:
|
||||||
|
default: panic("Invalid decoder value");
|
||||||
|
}
|
||||||
|
|
||||||
|
third_party_allocator = ZERO(Allocator);
|
||||||
|
return retrieved;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 // New frame index
|
||||||
|
audio_source_sample_frames(Audio_Source *src, u64 first_frame_index, u64 number_of_frames,
|
||||||
|
void *output_buffer, bool looping) {
|
||||||
|
|
||||||
|
u64 comp_size = get_audio_bit_width_byte_size(src->format.bit_width);
|
||||||
|
u64 frame_size = comp_size * src->format.channels;
|
||||||
|
u64 output_size = number_of_frames * frame_size;
|
||||||
|
|
||||||
|
if (first_frame_index == src->number_of_frames) {
|
||||||
|
return first_frame_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(first_frame_index < src->number_of_frames, "Invalid first_frame_index");
|
||||||
|
|
||||||
|
u64 new_index = first_frame_index;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int num_retrieved;
|
||||||
|
switch (src->kind) {
|
||||||
|
case AUDIO_SOURCE_FILE_STREAM: {
|
||||||
|
|
||||||
|
num_retrieved = _audio_file_stream_sample_frames(
|
||||||
|
src,
|
||||||
|
first_frame_index,
|
||||||
|
number_of_frames,
|
||||||
|
output_buffer
|
||||||
|
);
|
||||||
|
new_index += num_retrieved;
|
||||||
|
|
||||||
|
assert(num_retrieved <= number_of_frames);
|
||||||
|
|
||||||
|
if (num_retrieved < number_of_frames) {
|
||||||
|
void *dst_remain = ((u8*)output_buffer) + num_retrieved*frame_size;
|
||||||
|
if (looping) {
|
||||||
|
num_retrieved = _audio_file_stream_sample_frames(
|
||||||
|
src,
|
||||||
|
0,
|
||||||
|
number_of_frames-num_retrieved,
|
||||||
|
dst_remain
|
||||||
|
);
|
||||||
|
new_index = number_of_frames-num_retrieved;
|
||||||
|
} else {
|
||||||
|
memset(dst_remain, 0, frame_size * (number_of_frames - num_retrieved));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break; // case AUDIO_SOURCE_FILE_STREAM
|
||||||
|
}
|
||||||
|
case AUDIO_SOURCE_MEMORY: {
|
||||||
|
s64 first_number_of_frames = min(number_of_frames, src->number_of_frames-first_frame_index);
|
||||||
|
void *src_pcm_start = (u8*)src->pcm_frames + first_frame_index*frame_size;
|
||||||
|
|
||||||
|
memcpy(output_buffer, src_pcm_start, first_number_of_frames*frame_size);
|
||||||
|
new_index += first_number_of_frames;
|
||||||
|
|
||||||
|
s64 remainder = number_of_frames-first_number_of_frames;
|
||||||
|
if (remainder > 0) {
|
||||||
|
void *dst_remain = (u8*)output_buffer + first_number_of_frames*frame_size;
|
||||||
|
|
||||||
|
if (looping) {
|
||||||
|
memcpy(dst_remain, src->pcm_frames, frame_size*remainder);
|
||||||
|
new_index = remainder;
|
||||||
|
} else {
|
||||||
|
memset(dst_remain, 0, frame_size*remainder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_index;
|
||||||
|
}
|
||||||
|
|
||||||
#define U8_MAX 255
|
#define U8_MAX 255
|
||||||
#define S16_MIN -32768
|
#define S16_MIN -32768
|
||||||
#define S16_MAX 32767
|
#define S16_MAX 32767
|
||||||
|
@ -40,8 +311,9 @@ u64 get_audio_format_component_byte_size(Audio_Format_Type format) {
|
||||||
#define S32_MIN -2147483648
|
#define S32_MIN -2147483648
|
||||||
#define S32_MAX 2147483647
|
#define S32_MAX 2147483647
|
||||||
|
|
||||||
void mix_frames(void *dst, void *src, u64 frame_count, Audio_Format format) {
|
void
|
||||||
u64 comp_size = get_audio_format_component_byte_size(format.type);
|
mix_frames(void *dst, void *src, u64 frame_count, Audio_Format format) {
|
||||||
|
u64 comp_size = get_audio_bit_width_byte_size(format.bit_width);
|
||||||
u64 frame_size = comp_size * format.channels;
|
u64 frame_size = comp_size * format.channels;
|
||||||
u64 output_size = frame_count * frame_size;
|
u64 output_size = frame_count * frame_size;
|
||||||
|
|
||||||
|
@ -57,54 +329,300 @@ void mix_frames(void *dst, void *src, u64 frame_count, Audio_Format format) {
|
||||||
void *src_sample = (u8*)src + frame*frame_size + c*comp_size;
|
void *src_sample = (u8*)src + frame*frame_size + c*comp_size;
|
||||||
void *dst_sample = (u8*)dst + frame*frame_size + c*comp_size;
|
void *dst_sample = (u8*)dst + frame*frame_size + c*comp_size;
|
||||||
|
|
||||||
switch (format.type) {
|
switch (format.bit_width) {
|
||||||
case AUDIO_FORMAT_TYPE_F32: {
|
case AUDIO_BITS_32: {
|
||||||
*((f32*)dst_sample) += *((f32*)src_sample);
|
*((f32*)dst_sample) += *((f32*)src_sample);
|
||||||
}
|
}
|
||||||
case AUDIO_FORMAT_TYPE_S32: {
|
case AUDIO_BITS_16: {
|
||||||
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 dst_int = *((s16*)dst_sample);
|
||||||
s16 src_int = *((s16*)src_sample);
|
s16 src_int = *((s16*)src_sample);
|
||||||
*((s16*)dst_sample) = (s16)clamp((s64)(dst_int + src_int), S16_MIN, S16_MAX);
|
*((s16*)dst_sample) = (s16)clamp((s64)(dst_int + src_int), S16_MIN, S16_MAX);
|
||||||
break;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
void
|
||||||
|
convert_one_component(void *dst, Audio_Format_Bits dst_bits,
|
||||||
|
void *src, Audio_Format_Bits src_bits) {
|
||||||
|
switch (dst_bits) {
|
||||||
|
case AUDIO_BITS_32: {
|
||||||
|
switch (src_bits) {
|
||||||
|
case AUDIO_BITS_32:
|
||||||
|
memcpy(dst, src, get_audio_bit_width_byte_size(dst_bits)); break;
|
||||||
|
case AUDIO_BITS_16:
|
||||||
|
// #Simd
|
||||||
|
*(f32*)dst = (f64)((f32)*((s16*)src) * ((f64)1.0 / (f64)32768.0));
|
||||||
|
break;
|
||||||
|
default: panic("Unhandled bits");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AUDIO_BITS_16: {
|
||||||
|
switch (src_bits) {
|
||||||
|
case AUDIO_BITS_32:
|
||||||
|
// #Simd
|
||||||
|
*(s16*)dst = (s16)(*((f32*)src) * 32768.0f);
|
||||||
|
break;
|
||||||
|
case AUDIO_BITS_16:
|
||||||
|
memcpy(dst, src, get_audio_bit_width_byte_size(dst_bits));
|
||||||
|
break;
|
||||||
|
default: panic("Unhandled bits");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: panic("Unhandled bits");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assume dst buffer is large enough
|
||||||
|
// in-place conversion is OK
|
||||||
|
void
|
||||||
|
resample_frames(void *dst, Audio_Format dst_format,
|
||||||
|
void *src, Audio_Format src_format, u64 src_frame_count) {
|
||||||
|
assert(dst_format.channels == src_format.channels, "Channel count must be the same for sample rate conversion");
|
||||||
|
assert(dst_format.bit_width == src_format.bit_width, "Types must be the same for sample rate conversion");
|
||||||
|
|
||||||
|
f32 src_ratio = (f32)src_format.sample_rate / (f32)dst_format.sample_rate;
|
||||||
|
u64 dst_frame_count = (u64)round(src_frame_count / src_ratio);
|
||||||
|
u64 dst_comp_size = get_audio_bit_width_byte_size(dst_format.bit_width);
|
||||||
|
u64 dst_frame_size = dst_comp_size * dst_format.channels;
|
||||||
|
u64 src_comp_size = get_audio_bit_width_byte_size(src_format.bit_width);
|
||||||
|
u64 src_frame_size = src_comp_size * src_format.channels;
|
||||||
|
|
||||||
|
// Reverse in case dst == src (so we can do in-place conversion)
|
||||||
|
for (s64 dst_frame_index = dst_frame_count - 1; dst_frame_index >= 1; dst_frame_index--) {
|
||||||
|
f32 src_frame_index_f = dst_frame_index * src_ratio;
|
||||||
|
u64 src_frame_index_1 = (u64)src_frame_index_f;
|
||||||
|
u64 src_frame_index_2 = src_frame_index_1 + 1;
|
||||||
|
if (src_frame_index_2 >= src_frame_count) src_frame_index_2 = src_frame_count - 1;
|
||||||
|
|
||||||
|
f32 lerp_factor = src_frame_index_f - (f32)src_frame_index_1;
|
||||||
|
|
||||||
|
void *src_frame_1 = (u8*)src + src_frame_index_1 * src_frame_size;
|
||||||
|
void *src_frame_2 = (u8*)src + src_frame_index_2 * src_frame_size;
|
||||||
|
void *dst_frame = (u8*)dst + dst_frame_index * dst_frame_size;
|
||||||
|
|
||||||
|
for (int c = 0; c < src_format.channels; c++) {
|
||||||
|
union {
|
||||||
|
s16 s16_sample;
|
||||||
|
f32 f32_sample;
|
||||||
|
u8 data[4];
|
||||||
|
} sample_dst;
|
||||||
|
|
||||||
|
void *src_comp_1 = (u8*)src_frame_1 + c * src_comp_size;
|
||||||
|
void *src_comp_2 = (u8*)src_frame_2 + c * src_comp_size;
|
||||||
|
void *dst_comp = (u8*)dst_frame + c * dst_comp_size;
|
||||||
|
|
||||||
|
if (src_format.bit_width == AUDIO_BITS_32) {
|
||||||
|
float sample_1 = *((f32*)src_comp_1);
|
||||||
|
float sample_2 = *((f32*)src_comp_2);
|
||||||
|
sample_dst.f32_sample = sample_1 + lerp_factor * (sample_2 - sample_1);
|
||||||
|
} else if (src_format.bit_width == AUDIO_BITS_16) {
|
||||||
|
s16 sample_1 = *((s16*)src_comp_1);
|
||||||
|
s16 sample_2 = *((s16*)src_comp_2);
|
||||||
|
sample_dst.s16_sample = (s16)((f32)sample_1 + lerp_factor * ((f32)sample_2 - (f32)sample_1));
|
||||||
|
} else {
|
||||||
|
panic("Unhandled bit width");
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dst_comp, sample_dst.data, dst_comp_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Correct padding if we downscaled (since we coverted in reverse)
|
||||||
|
if (src == dst && dst_format.sample_rate < src_format.sample_rate) {
|
||||||
|
void *dst_after_pad = (u8*)dst + (src_frame_count - dst_frame_count) * dst_frame_size;
|
||||||
|
u64 padding = (u64)dst_after_pad - (u64)dst;
|
||||||
|
memcpy(
|
||||||
|
dst,
|
||||||
|
dst_after_pad,
|
||||||
|
dst_frame_count * dst_frame_size
|
||||||
|
);
|
||||||
|
memset((u8*)dst+dst_frame_count * dst_frame_size, 0, padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assumes dst buffer is large enough
|
||||||
|
void
|
||||||
|
convert_frames(void *dst, Audio_Format dst_format,
|
||||||
|
void *src, Audio_Format src_format, u64 src_frame_count) {
|
||||||
|
|
||||||
|
u64 dst_comp_size = get_audio_bit_width_byte_size(dst_format.bit_width);
|
||||||
|
u64 dst_frame_size = dst_comp_size * dst_format.channels;
|
||||||
|
u64 src_comp_size = get_audio_bit_width_byte_size(src_format.bit_width);
|
||||||
|
u64 src_frame_size = src_comp_size * src_format.channels;
|
||||||
|
|
||||||
|
if (dst_format.sample_rate != src_format.sample_rate) {
|
||||||
|
f32 ratio = (f32)src_format.sample_rate/(f32)dst_format.sample_rate;
|
||||||
|
src_frame_count = (u64)round((f32)src_frame_count*ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bytes_match(&dst_format, &src_format, sizeof(Audio_Format))) {
|
||||||
|
memcpy(dst, src, src_frame_count*src_frame_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 output_frame_count = src_frame_count;
|
||||||
|
|
||||||
|
// #Speed #Simd
|
||||||
|
if (dst_format.channels != src_format.channels || dst_format.bit_width != src_format.bit_width) {
|
||||||
|
for (u64 src_frame_index = 0; src_frame_index < src_frame_count; src_frame_index++) {
|
||||||
|
void *src_frame = ((u8*)src) + src_frame_index*src_frame_size;
|
||||||
|
void *dst_frame = ((u8*)dst) + src_frame_index*dst_frame_size;
|
||||||
|
|
||||||
|
// For getting average src sample
|
||||||
|
union {
|
||||||
|
s16 s16_sample;
|
||||||
|
f32 f32_sample;
|
||||||
|
u8 data[4];
|
||||||
|
} avg;
|
||||||
|
if (src_format.channels != dst_format.channels) {
|
||||||
|
// This is where we get the average src sample
|
||||||
|
f32 sum = 0;
|
||||||
|
for (int c = 0; c < src_format.channels; c++) {
|
||||||
|
avg.s16_sample = 0;
|
||||||
|
void *src_comp = (u8*)src_frame + c * src_comp_size;
|
||||||
|
convert_one_component(
|
||||||
|
avg.data, dst_format.bit_width,
|
||||||
|
src_comp, src_format.bit_width
|
||||||
|
);
|
||||||
|
if (dst_format.bit_width == AUDIO_BITS_32) sum += avg.f32_sample;
|
||||||
|
else if (dst_format.bit_width == AUDIO_BITS_16) sum += (f32)avg.s16_sample;
|
||||||
|
else panic("Unhandled bit width");
|
||||||
|
}
|
||||||
|
if (dst_format.bit_width == AUDIO_BITS_32) {
|
||||||
|
avg.f32_sample = sum/(f32)src_format.channels;
|
||||||
|
} else if (dst_format.bit_width == AUDIO_BITS_16) {
|
||||||
|
avg.s16_sample = (s16)round(sum/(f32)src_format.channels);
|
||||||
|
} else panic("Unhandled bit width");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_format.channels > dst_format.channels) {
|
||||||
|
|
||||||
|
// #Limitation #Audioquality
|
||||||
|
// Here we are down-scaling the channel count.
|
||||||
|
// So what we do is we get the average sample for all channels in src and then
|
||||||
|
// set all channels in dst to that. This is fine for mono to stereo, but will
|
||||||
|
// be a loss for example for surround to mono. But I'm not sure we will ever
|
||||||
|
// care about non-stereo/mono audio.
|
||||||
|
|
||||||
|
for (int c = 0; c < dst_format.channels; c++) {
|
||||||
|
void *dst_comp = (u8*)dst_frame + c * dst_comp_size;
|
||||||
|
memcpy(dst_comp, avg.data, dst_comp_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (dst_format.channels > src_format.channels) {
|
||||||
|
|
||||||
|
// Here, we are upscaling to a higher channel count.
|
||||||
|
// I'm not sure what the best way to do this is, but for now I will try to just
|
||||||
|
// get the average in src and set that to the extra channels in dst.
|
||||||
|
// This is obviously fine for mono -> stereo but might be a problem for surround.
|
||||||
|
// Again, I'm not sure if surround will ever be on our list of worries.
|
||||||
|
|
||||||
|
for (int c = 0; c < dst_format.channels; c++) {
|
||||||
|
void *dst_comp = (u8*)dst_frame + c * dst_comp_size;
|
||||||
|
void *src_comp = (u8*)src_frame + c * src_comp_size;
|
||||||
|
|
||||||
|
if (c < src_format.channels)
|
||||||
|
convert_one_component(dst_comp, dst_format.bit_width,
|
||||||
|
src_comp, src_format.bit_width);
|
||||||
|
else
|
||||||
|
memcpy(dst_comp, avg.data, dst_comp_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Same channel count, just copy components over
|
||||||
|
for (int c = 0; c < dst_format.channels; c++) {
|
||||||
|
void *dst_comp = (u8*)dst_frame + c * dst_comp_size;
|
||||||
|
void *src_comp = (u8*)src_frame + c * src_comp_size;
|
||||||
|
convert_one_component(dst_comp, dst_format.bit_width, src_comp, src_format.bit_width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dst_format.sample_rate != src_format.sample_rate) {
|
||||||
|
resample_frames(
|
||||||
|
dst,
|
||||||
|
(Audio_Format){dst_format.bit_width, dst_format.channels, dst_format.sample_rate},
|
||||||
|
dst,
|
||||||
|
(Audio_Format){dst_format.bit_width, dst_format.channels, src_format.sample_rate},
|
||||||
|
src_frame_count
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// #Temporary this is jsut for testing
|
||||||
|
Audio_Source *current_source = 0;
|
||||||
|
u64 current_index = 0;
|
||||||
|
|
||||||
|
// This is supposed to be called by OS layer audio thread whenever it wants more audio samples
|
||||||
|
void do_program_audio_sample(u64 number_of_output_frames, Audio_Format out_format,
|
||||||
|
void *output) {
|
||||||
|
u64 out_comp_size = get_audio_bit_width_byte_size(out_format.bit_width);
|
||||||
|
u64 out_frame_size = out_comp_size * out_format.channels;
|
||||||
|
u64 output_size = number_of_output_frames * out_frame_size;
|
||||||
|
|
||||||
|
memset(output, 0, output_size);
|
||||||
|
if (current_source) {
|
||||||
|
|
||||||
|
bool need_convert = !bytes_match(&out_format, ¤t_source->format, sizeof(Audio_Format));
|
||||||
|
|
||||||
|
u64 in_comp_size = get_audio_bit_width_byte_size(current_source->format.bit_width);
|
||||||
|
u64 in_frame_size = in_comp_size * current_source->format.channels;
|
||||||
|
u64 input_size = number_of_output_frames * in_frame_size;
|
||||||
|
|
||||||
|
void *target_buffer = output;
|
||||||
|
u64 number_of_sample_frames = number_of_output_frames;
|
||||||
|
|
||||||
|
thread_local local_persist void *convert_buffer = 0;
|
||||||
|
thread_local local_persist u64 convert_buffer_size;
|
||||||
|
if (need_convert) {
|
||||||
|
if (current_source->format.sample_rate != out_format.sample_rate) {
|
||||||
|
f32 src_ratio
|
||||||
|
= (f32)current_source->format.sample_rate / (f32)out_format.sample_rate;
|
||||||
|
number_of_sample_frames = round(number_of_output_frames * src_ratio);
|
||||||
|
input_size = number_of_sample_frames * in_frame_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 biggest_size = max(input_size, output_size);
|
||||||
|
|
||||||
|
if (!convert_buffer || convert_buffer_size < biggest_size) {
|
||||||
|
// #Speed
|
||||||
|
if (convert_buffer) dealloc(get_heap_allocator(), convert_buffer);
|
||||||
|
convert_buffer = alloc(get_heap_allocator(), biggest_size);
|
||||||
|
convert_buffer_size = biggest_size;
|
||||||
|
}
|
||||||
|
target_buffer = convert_buffer;
|
||||||
|
memset(convert_buffer, 0, biggest_size);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
current_index = audio_source_sample_frames(
|
||||||
|
current_source,
|
||||||
|
current_index,
|
||||||
|
number_of_sample_frames,
|
||||||
|
target_buffer,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
if (need_convert) {
|
||||||
|
convert_frames(
|
||||||
|
output,
|
||||||
|
out_format,
|
||||||
|
convert_buffer,
|
||||||
|
current_source->format,
|
||||||
|
number_of_output_frames
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,13 @@ typedef struct Spinlock Spinlock;
|
||||||
typedef struct Mutex Mutex;
|
typedef struct Mutex Mutex;
|
||||||
typedef struct Binary_Semaphore Binary_Semaphore;
|
typedef struct Binary_Semaphore Binary_Semaphore;
|
||||||
|
|
||||||
|
// These are probably your best friend for sync-free multi-processing.
|
||||||
|
inline bool compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old);
|
||||||
|
inline bool compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old);
|
||||||
|
inline bool compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old);
|
||||||
|
inline bool compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old);
|
||||||
|
inline bool compare_and_swap_bool(bool *a, bool b, bool old);
|
||||||
|
|
||||||
///
|
///
|
||||||
// Spinlock "primitive"
|
// Spinlock "primitive"
|
||||||
// Like a mutex but it eats up the entire core while waiting.
|
// Like a mutex but it eats up the entire core while waiting.
|
||||||
|
|
|
@ -29,7 +29,8 @@ typedef struct Cpu_Capabilities {
|
||||||
#define inline __forceinline
|
#define inline __forceinline
|
||||||
#define alignat(x) __declspec(align(x))
|
#define alignat(x) __declspec(align(x))
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
||||||
inline void crash() {
|
inline void
|
||||||
|
crash() {
|
||||||
__debugbreak();
|
__debugbreak();
|
||||||
volatile int *a = 0;
|
volatile int *a = 0;
|
||||||
*a = 5;
|
*a = 5;
|
||||||
|
@ -38,7 +39,8 @@ typedef struct Cpu_Capabilities {
|
||||||
}
|
}
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#pragma intrinsic(__rdtsc)
|
#pragma intrinsic(__rdtsc)
|
||||||
inline u64 rdtsc() {
|
inline u64
|
||||||
|
rdtsc() {
|
||||||
return __rdtsc();
|
return __rdtsc();
|
||||||
}
|
}
|
||||||
inline Cpu_Info_X86 cpuid(u32 function_id) {
|
inline Cpu_Info_X86 cpuid(u32 function_id) {
|
||||||
|
@ -77,23 +79,28 @@ typedef struct Cpu_Capabilities {
|
||||||
#pragma intrinsic(_InterlockedCompareExchange)
|
#pragma intrinsic(_InterlockedCompareExchange)
|
||||||
#pragma intrinsic(_InterlockedCompareExchange64)
|
#pragma intrinsic(_InterlockedCompareExchange64)
|
||||||
|
|
||||||
inline bool compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old) {
|
inline bool
|
||||||
|
compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old) {
|
||||||
return _InterlockedCompareExchange8((volatile char*)a, (char)b, (char)old) == old;
|
return _InterlockedCompareExchange8((volatile char*)a, (char)b, (char)old) == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old) {
|
inline bool
|
||||||
|
compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old) {
|
||||||
return _InterlockedCompareExchange16((volatile short*)a, (short)b, (short)old) == old;
|
return _InterlockedCompareExchange16((volatile short*)a, (short)b, (short)old) == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old) {
|
inline bool
|
||||||
|
compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old) {
|
||||||
return _InterlockedCompareExchange((volatile long*)a, (long)b, (long)old) == old;
|
return _InterlockedCompareExchange((volatile long*)a, (long)b, (long)old) == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old) {
|
inline bool
|
||||||
|
compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old) {
|
||||||
return _InterlockedCompareExchange64((volatile long long*)a, (long long)b, (long long)old) == old;
|
return _InterlockedCompareExchange64((volatile long long*)a, (long long)b, (long long)old) == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_bool(bool *a, bool b, bool old) {
|
inline bool
|
||||||
|
compare_and_swap_bool(bool *a, bool b, bool old) {
|
||||||
return compare_and_swap_8((uint8_t*)a, (uint8_t)b, (uint8_t)old);
|
return compare_and_swap_8((uint8_t*)a, (uint8_t)b, (uint8_t)old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,20 +110,26 @@ typedef struct Cpu_Capabilities {
|
||||||
#define inline __attribute__((always_inline)) inline
|
#define inline __attribute__((always_inline)) inline
|
||||||
#define alignat(x) __attribute__((aligned(x)))
|
#define alignat(x) __attribute__((aligned(x)))
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 1
|
||||||
inline void crash() {
|
|
||||||
|
inline void
|
||||||
|
crash() {
|
||||||
__builtin_trap();
|
__builtin_trap();
|
||||||
volatile int *a = 0;
|
volatile int *a = 0;
|
||||||
*a = 5;
|
*a = 5;
|
||||||
a = (int*)0xDEADBEEF;
|
a = (int*)0xDEADBEEF;
|
||||||
*a = 5;
|
*a = 5;
|
||||||
}
|
}
|
||||||
inline u64 rdtsc() {
|
|
||||||
|
inline u64
|
||||||
|
rdtsc() {
|
||||||
unsigned int lo, hi;
|
unsigned int lo, hi;
|
||||||
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
__asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi));
|
||||||
return ((u64)hi << 32) | lo;
|
return ((u64)hi << 32) | lo;
|
||||||
}
|
}
|
||||||
inline Cpu_Info_X86 cpuid(u32 function_id) {
|
|
||||||
Cpu_Info_X86 info;
|
inline
|
||||||
|
Cpu_Info_X86 cpuid(u32 function_id) {
|
||||||
|
Cpu_Info_X86 info;
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"cpuid"
|
"cpuid"
|
||||||
: "=a"(info.eax), "=b"(info.ebx), "=c"(info.ecx), "=d"(info.edx)
|
: "=a"(info.eax), "=b"(info.ebx), "=c"(info.ecx), "=d"(info.edx)
|
||||||
|
@ -152,7 +165,8 @@ typedef struct Cpu_Capabilities {
|
||||||
|
|
||||||
#define DEPRECATED(proc, msg) proc __attribute__((deprecated(msg)))
|
#define DEPRECATED(proc, msg) proc __attribute__((deprecated(msg)))
|
||||||
|
|
||||||
inline bool compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old) {
|
inline bool
|
||||||
|
compare_and_swap_8(uint8_t *a, uint8_t b, uint8_t old) {
|
||||||
unsigned char result;
|
unsigned char result;
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"lock; cmpxchgb %2, %1"
|
"lock; cmpxchgb %2, %1"
|
||||||
|
@ -163,7 +177,8 @@ typedef struct Cpu_Capabilities {
|
||||||
return result == old;
|
return result == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old) {
|
inline bool
|
||||||
|
compare_and_swap_16(uint16_t *a, uint16_t b, uint16_t old) {
|
||||||
unsigned short result;
|
unsigned short result;
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"lock; cmpxchgw %2, %1"
|
"lock; cmpxchgw %2, %1"
|
||||||
|
@ -174,7 +189,8 @@ typedef struct Cpu_Capabilities {
|
||||||
return result == old;
|
return result == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old) {
|
inline bool
|
||||||
|
compare_and_swap_32(uint32_t *a, uint32_t b, uint32_t old) {
|
||||||
unsigned int result;
|
unsigned int result;
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"lock; cmpxchgl %2, %1"
|
"lock; cmpxchgl %2, %1"
|
||||||
|
@ -185,7 +201,8 @@ typedef struct Cpu_Capabilities {
|
||||||
return result == old;
|
return result == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old) {
|
inline bool
|
||||||
|
compare_and_swap_64(uint64_t *a, uint64_t b, uint64_t old) {
|
||||||
unsigned long long result;
|
unsigned long long result;
|
||||||
__asm__ __volatile__(
|
__asm__ __volatile__(
|
||||||
"lock; cmpxchgq %2, %1"
|
"lock; cmpxchgq %2, %1"
|
||||||
|
@ -196,7 +213,8 @@ typedef struct Cpu_Capabilities {
|
||||||
return result == old;
|
return result == old;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool compare_and_swap_bool(bool *a, bool b, bool old) {
|
inline bool
|
||||||
|
compare_and_swap_bool(bool *a, bool b, bool old) {
|
||||||
return compare_and_swap_8((uint8_t*)a, (uint8_t)b, (uint8_t)old);
|
return compare_and_swap_8((uint8_t*)a, (uint8_t)b, (uint8_t)old);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +224,8 @@ typedef struct Cpu_Capabilities {
|
||||||
#define inline inline
|
#define inline inline
|
||||||
#define COMPILER_HAS_MEMCPY_INTRINSICS 0
|
#define COMPILER_HAS_MEMCPY_INTRINSICS 0
|
||||||
|
|
||||||
inline u64 rdtsc() { return 0; }
|
inline u64
|
||||||
|
rdtsc() { return 0; }
|
||||||
inline Cpu_Info_X86 cpuid(u32 function_id) {return (Cpu_Info_X86){0};}
|
inline Cpu_Info_X86 cpuid(u32 function_id) {return (Cpu_Info_X86){0};}
|
||||||
#define COMPILER_CAN_DO_SSE2 0
|
#define COMPILER_CAN_DO_SSE2 0
|
||||||
#define COMPILER_CAN_DO_AVX 0
|
#define COMPILER_CAN_DO_AVX 0
|
||||||
|
@ -220,7 +239,8 @@ typedef struct Cpu_Capabilities {
|
||||||
#warning "Compiler is not explicitly supported, some things will probably not work as expected"
|
#warning "Compiler is not explicitly supported, some things will probably not work as expected"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Cpu_Capabilities query_cpu_capabilities() {
|
Cpu_Capabilities
|
||||||
|
query_cpu_capabilities() {
|
||||||
Cpu_Capabilities result = {0};
|
Cpu_Capabilities result = {0};
|
||||||
|
|
||||||
Cpu_Info_X86 info = cpuid(1);
|
Cpu_Info_X86 info = cpuid(1);
|
||||||
|
|
41
oogabooga/examples/audio_test.c
Normal file
41
oogabooga/examples/audio_test.c
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int entry(int argc, char **argv) {
|
||||||
|
|
||||||
|
window.title = STR("Audio test");
|
||||||
|
window.scaled_width = 1280; // We need to set the scaled size if we want to handle system scaling (DPI)
|
||||||
|
window.scaled_height = 720;
|
||||||
|
window.x = 200;
|
||||||
|
window.y = 90;
|
||||||
|
window.clear_color = hex_to_rgba(0x6495EDff);
|
||||||
|
|
||||||
|
Allocator heap = get_heap_allocator();
|
||||||
|
|
||||||
|
Audio_Source bruh, song;
|
||||||
|
bool bruh_ok = audio_source_init_file_decode(&bruh, STR("oogabooga/examples/bruh.wav"), AUDIO_BITS_32, heap);
|
||||||
|
assert(bruh_ok, "Could not load bruh.wav");
|
||||||
|
|
||||||
|
bool song_ok = audio_source_init_file_stream(&song, STR("oogabooga/examples/song.ogg"), AUDIO_BITS_16, heap);
|
||||||
|
assert(bruh_ok, "Could not load song.ogg");
|
||||||
|
|
||||||
|
// #Temporary This is not actually how it will work, I'm just testing audio source output.
|
||||||
|
current_source = &song;
|
||||||
|
|
||||||
|
while (!window.should_close) {
|
||||||
|
reset_temporary_storage();
|
||||||
|
|
||||||
|
float64 now = os_get_current_time_in_seconds();
|
||||||
|
Matrix4 rect_xform = m4_scalar(1.0);
|
||||||
|
rect_xform = m4_rotate_z(rect_xform, (f32)now);
|
||||||
|
rect_xform = m4_translate(rect_xform, v3(-.25f, -.25f, 0));
|
||||||
|
draw_rect_xform(rect_xform, v2(.5f, .5f), COLOR_GREEN);
|
||||||
|
|
||||||
|
draw_rect(v2(sin(now), -.8), v2(.5, .25), COLOR_RED);
|
||||||
|
|
||||||
|
os_update();
|
||||||
|
gfx_update();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Binary file not shown.
|
@ -5,6 +5,10 @@
|
||||||
and in-game logger.
|
and in-game logger.
|
||||||
|
|
||||||
We also have log levels to be able to disable/enable the respective levels.
|
We also have log levels to be able to disable/enable the respective levels.
|
||||||
|
|
||||||
|
BEWARE!!
|
||||||
|
This logger is not thread-safe. If multiple threads call log(), then nobody knows
|
||||||
|
what might happen. If you need to make it thread-safe, check out concurrency.c.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ int entry(int argc, char **argv) {
|
||||||
Matrix4 camera_view = m4_scalar(1.0);
|
Matrix4 camera_view = m4_scalar(1.0);
|
||||||
|
|
||||||
float64 last_time = os_get_current_time_in_seconds();
|
float64 last_time = os_get_current_time_in_seconds();
|
||||||
while (!window.should_close) tm_scope_cycles("Frame") {
|
while (!window.should_close) tm_scope("Frame") {
|
||||||
reset_temporary_storage();
|
reset_temporary_storage();
|
||||||
|
|
||||||
float64 now = os_get_current_time_in_seconds();
|
float64 now = os_get_current_time_in_seconds();
|
||||||
|
@ -47,7 +47,7 @@ int entry(int argc, char **argv) {
|
||||||
delta = now - last_time;
|
delta = now - last_time;
|
||||||
}
|
}
|
||||||
last_time = now;
|
last_time = now;
|
||||||
tm_scope_cycles("os_update") {
|
tm_scope("os_update") {
|
||||||
os_update();
|
os_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ int entry(int argc, char **argv) {
|
||||||
|
|
||||||
if (show) draw_image(atlas->image, v2(-1.6, -1), v2(4, 4), COLOR_WHITE);
|
if (show) draw_image(atlas->image, v2(-1.6, -1), v2(4, 4), COLOR_WHITE);
|
||||||
|
|
||||||
tm_scope_cycles("gfx_update") {
|
tm_scope("gfx_update") {
|
||||||
gfx_update();
|
gfx_update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ int entry(int argc, char **argv) {
|
||||||
|
|
||||||
const u32 font_height = 48;
|
const u32 font_height = 48;
|
||||||
|
|
||||||
while (!window.should_close) tm_scope_cycles("Frame") {
|
while (!window.should_close) tm_scope("Frame") {
|
||||||
reset_temporary_storage();
|
reset_temporary_storage();
|
||||||
|
|
||||||
// Text is easiest to deal with if our projection matches window pixel size, because
|
// Text is easiest to deal with if our projection matches window pixel size, because
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #Cleanup apparently there are C macros for these (COBJMACROS)
|
||||||
#define D3D11Release(x) x->lpVtbl->Release(x)
|
#define D3D11Release(x) x->lpVtbl->Release(x)
|
||||||
#define VTABLE(proc, ...) FIRST_ARG(__VA_ARGS__)->lpVtbl->proc(__VA_ARGS__)
|
#define VTABLE(proc, ...) FIRST_ARG(__VA_ARGS__)->lpVtbl->proc(__VA_ARGS__)
|
||||||
|
|
||||||
|
@ -114,28 +115,30 @@ void CALLBACK d3d11_debug_callback(D3D11_MESSAGE_CATEGORY category, D3D11_MESSAG
|
||||||
|
|
||||||
#define win32_check_hr(hr) win32_check_hr_impl(hr, __LINE__, __FILE__);
|
#define win32_check_hr(hr) win32_check_hr_impl(hr, __LINE__, __FILE__);
|
||||||
void win32_check_hr_impl(HRESULT hr, u32 line, const char* file_name) {
|
void win32_check_hr_impl(HRESULT hr, u32 line, const char* file_name) {
|
||||||
if (FAILED(hr)) {
|
if (hr != S_OK) {
|
||||||
LPVOID errorMsg;
|
|
||||||
|
LPVOID errorMsg;
|
||||||
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS;
|
FORMAT_MESSAGE_IGNORE_INSERTS;
|
||||||
|
|
||||||
DWORD messageLength = FormatMessage(
|
DWORD messageLength = FormatMessageW(
|
||||||
dwFlags,
|
dwFlags,
|
||||||
NULL,
|
NULL,
|
||||||
hr,
|
hr,
|
||||||
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
|
||||||
(LPTSTR) &errorMsg,
|
(LPWSTR) &errorMsg,
|
||||||
0,
|
0,
|
||||||
NULL );
|
NULL );
|
||||||
|
|
||||||
if (messageLength > 0) {
|
if (messageLength > 0) {
|
||||||
MessageBox(NULL, (LPCTSTR)errorMsg, TEXT("Error"), MB_OK | MB_ICONERROR);
|
MessageBoxW(NULL, (LPWSTR)errorMsg, L"Error", MB_OK | MB_ICONERROR);
|
||||||
} else {
|
} else {
|
||||||
MessageBox(NULL, TEXT("Failed to retrieve error message."), TEXT("Error"), MB_OK | MB_ICONERROR);
|
MessageBoxW(NULL, L"Failed to retrieve error message.", L"Error", MB_OK | MB_ICONERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
panic("win32 hr failed in file %cs on line %d", file_name, line);
|
panic("win32 hr failed in file %cs on line %d, hr was %d", file_name, line, hr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,8 +179,8 @@ void d3d11_update_swapchain() {
|
||||||
|
|
||||||
|
|
||||||
// Obtain DXGI factory from device
|
// Obtain DXGI factory from device
|
||||||
IDXGIDevice *dxgi_device;
|
IDXGIDevice *dxgi_device = 0;
|
||||||
hr = VTABLE(QueryInterface, d3d11_device, &IID_IDXGIDevice, cast(void**)&dxgi_device);
|
hr = ID3D11Device_QueryInterface(d3d11_device, &IID_IDXGIDevice, cast(void**)&dxgi_device);
|
||||||
win32_check_hr(hr);
|
win32_check_hr(hr);
|
||||||
|
|
||||||
IDXGIAdapter *adapter;
|
IDXGIAdapter *adapter;
|
||||||
|
@ -599,8 +602,8 @@ void d3d11_process_draw_frame() {
|
||||||
D3D11_Vertex* pointer = head;
|
D3D11_Vertex* pointer = head;
|
||||||
u64 number_of_rendered_quads = 0;
|
u64 number_of_rendered_quads = 0;
|
||||||
|
|
||||||
tm_scope_cycles("Quad processing") {
|
tm_scope("Quad processing") {
|
||||||
if (draw_frame.enable_z_sorting) tm_scope_cycles("Z sorting") {
|
if (draw_frame.enable_z_sorting) tm_scope("Z sorting") {
|
||||||
if (!sort_quad_buffer || (sort_quad_buffer_size < allocated_quads*sizeof(Draw_Quad))) {
|
if (!sort_quad_buffer || (sort_quad_buffer_size < allocated_quads*sizeof(Draw_Quad))) {
|
||||||
// #Memory #Heapalloc
|
// #Memory #Heapalloc
|
||||||
if (sort_quad_buffer) dealloc(get_heap_allocator(), sort_quad_buffer);
|
if (sort_quad_buffer) dealloc(get_heap_allocator(), sort_quad_buffer);
|
||||||
|
@ -721,23 +724,23 @@ void d3d11_process_draw_frame() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tm_scope_cycles("Write to gpu") {
|
tm_scope("Write to gpu") {
|
||||||
D3D11_MAPPED_SUBRESOURCE buffer_mapping;
|
D3D11_MAPPED_SUBRESOURCE buffer_mapping;
|
||||||
tm_scope_cycles("The Map call") {
|
tm_scope("The Map call") {
|
||||||
hr = VTABLE(Map, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0, D3D11_MAP_WRITE_DISCARD, 0, &buffer_mapping);
|
hr = VTABLE(Map, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0, D3D11_MAP_WRITE_DISCARD, 0, &buffer_mapping);
|
||||||
win32_check_hr(hr);
|
win32_check_hr(hr);
|
||||||
}
|
}
|
||||||
tm_scope_cycles("The memcpy") {
|
tm_scope("The memcpy") {
|
||||||
memcpy(buffer_mapping.pData, d3d11_staging_quad_buffer, number_of_rendered_quads*sizeof(D3D11_Vertex)*6);
|
memcpy(buffer_mapping.pData, d3d11_staging_quad_buffer, number_of_rendered_quads*sizeof(D3D11_Vertex)*6);
|
||||||
}
|
}
|
||||||
tm_scope_cycles("The Unmap call") {
|
tm_scope("The Unmap call") {
|
||||||
VTABLE(Unmap, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0);
|
VTABLE(Unmap, d3d11_context, (ID3D11Resource*)d3d11_quad_vbo, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
// Draw call
|
// Draw call
|
||||||
tm_scope_cycles("Draw call") d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
|
tm_scope("Draw call") d3d11_draw_call(number_of_rendered_quads, textures, num_textures);
|
||||||
}
|
}
|
||||||
|
|
||||||
reset_draw_frame(&draw_frame);
|
reset_draw_frame(&draw_frame);
|
||||||
|
@ -762,7 +765,7 @@ void gfx_update() {
|
||||||
|
|
||||||
d3d11_process_draw_frame();
|
d3d11_process_draw_frame();
|
||||||
|
|
||||||
tm_scope_cycles("Present") {
|
tm_scope("Present") {
|
||||||
VTABLE(Present, d3d11_swap_chain, window.enable_vsync, window.enable_vsync ? 0 : DXGI_PRESENT_ALLOW_TEARING);
|
VTABLE(Present, d3d11_swap_chain, window.enable_vsync, window.enable_vsync ? 0 : DXGI_PRESENT_ALLOW_TEARING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,16 +98,16 @@
|
||||||
|
|
||||||
Note:
|
Note:
|
||||||
See timing macros in profile.c
|
See timing macros in profile.c
|
||||||
tm_scope_cycles
|
tm_scope
|
||||||
tm_scope_cycles_var
|
tm_scope_var
|
||||||
tm_scope_cycles_accum
|
tm_scope_accum
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define OGB_VERSION_MAJOR 0
|
#define OGB_VERSION_MAJOR 0
|
||||||
#define OGB_VERSION_MINOR 0
|
#define OGB_VERSION_MINOR 1
|
||||||
#define OGB_VERSION_PATCH 5
|
#define OGB_VERSION_PATCH 0
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
|
@ -212,6 +212,7 @@ typedef u8 bool;
|
||||||
#define MACOS 2
|
#define MACOS 2
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#define COBJMACROS
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#define TARGET_OS WINDOWS
|
#define TARGET_OS WINDOWS
|
||||||
#define OS_PATHS_HAVE_BACKSLASH 1
|
#define OS_PATHS_HAVE_BACKSLASH 1
|
||||||
|
@ -314,6 +315,26 @@ typedef u8 bool;
|
||||||
#define malloc please_use_alloc_for_memory_allocations_instead_of_malloc
|
#define malloc please_use_alloc_for_memory_allocations_instead_of_malloc
|
||||||
#define free please_use_dealloc_for_memory_deallocations_instead_of_free
|
#define free please_use_dealloc_for_memory_deallocations_instead_of_free
|
||||||
|
|
||||||
|
Mutex _default_logger_mutex;
|
||||||
|
bool _default_logger_mutex_initted = false;
|
||||||
|
void default_logger(Log_Level level, string s) {
|
||||||
|
|
||||||
|
if (!_default_logger_mutex_initted) {
|
||||||
|
mutex_init(&_default_logger_mutex);
|
||||||
|
_default_logger_mutex_initted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_acquire_or_wait(&_default_logger_mutex);
|
||||||
|
switch (level) {
|
||||||
|
case LOG_VERBOSE: print("[VERBOSE]: %s\n", s); break;
|
||||||
|
case LOG_INFO: print("[INFO]: %s\n", s); break;
|
||||||
|
case LOG_WARNING: print("[WARNING]: %s\n", s); break;
|
||||||
|
case LOG_ERROR: print("[ERROR]: %s\n", s); break;
|
||||||
|
case LOG_LEVEL_COUNT: break;
|
||||||
|
}
|
||||||
|
mutex_release(&_default_logger_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
void oogabooga_init(u64 program_memory_size) {
|
void oogabooga_init(u64 program_memory_size) {
|
||||||
context.logger = default_logger;
|
context.logger = default_logger;
|
||||||
temp = get_initialization_allocator();
|
temp = get_initialization_allocator();
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
|
|
||||||
|
#define CINTERFACE
|
||||||
#include <Shlwapi.h>
|
#include <Shlwapi.h>
|
||||||
|
#include <audioclient.h>
|
||||||
|
#include <audiopolicy.h>
|
||||||
|
#include <mmdeviceapi.h>
|
||||||
|
#include <initguid.h>
|
||||||
|
#include <avrt.h>
|
||||||
|
|
||||||
#define VIRTUAL_MEMORY_BASE ((void*)0x0000690000000000ULL)
|
#define VIRTUAL_MEMORY_BASE ((void*)0x0000690000000000ULL)
|
||||||
|
|
||||||
|
@ -132,12 +138,72 @@ LRESULT CALLBACK win32_window_proc(HWND passed_window, UINT message, WPARAM wpar
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void win32_audio_thread(Thread *t);
|
||||||
|
void
|
||||||
|
win32_audio_init();
|
||||||
|
void win32_init_window() {
|
||||||
|
memset(&window, 0, sizeof(window));
|
||||||
|
|
||||||
|
window.title = STR("Unnamed Window");
|
||||||
|
window.width = 1280;
|
||||||
|
window.height = 720;
|
||||||
|
window.x = 0;
|
||||||
|
window.y = 0;
|
||||||
|
window.should_close = false;
|
||||||
|
window._initialized = false;
|
||||||
|
window.clear_color.r = 0.392f;
|
||||||
|
window.clear_color.g = 0.584f;
|
||||||
|
window.clear_color.b = 0.929f;
|
||||||
|
window.clear_color.a = 1.0f;
|
||||||
|
|
||||||
|
WNDCLASSEX wc = (WNDCLASSEX){0};
|
||||||
|
MSG msg;
|
||||||
|
HINSTANCE instance = GetModuleHandle(0);
|
||||||
|
assert(instance != INVALID_HANDLE_VALUE, "Failed getting current HINSTANCE");
|
||||||
|
|
||||||
|
wc.cbSize = sizeof(WNDCLASSEX);
|
||||||
|
wc.style = CS_OWNDC;
|
||||||
|
wc.lpfnWndProc = win32_window_proc;
|
||||||
|
wc.hInstance = instance;
|
||||||
|
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
|
||||||
|
wc.hCursor = LoadCursor(0, IDC_ARROW);
|
||||||
|
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||||
|
wc.lpszClassName = "sigma balls";
|
||||||
|
wc.hIconSm = LoadIcon(0, IDI_APPLICATION);
|
||||||
|
|
||||||
|
BOOL ok = RegisterClassEx(&wc);
|
||||||
|
assert(ok, "Failed registering window class (error code %lu)", GetLastError());
|
||||||
|
|
||||||
|
RECT rect = {0, 0, window.width, window.height};
|
||||||
|
DWORD style = WS_OVERLAPPEDWINDOW;
|
||||||
|
DWORD ex_style = WS_EX_CLIENTEDGE;
|
||||||
|
ok = AdjustWindowRectEx(&rect, style, FALSE, ex_style);
|
||||||
|
assert(ok != 0, "AdjustWindowRectEx failed with error code %lu", GetLastError());
|
||||||
|
|
||||||
|
u32 actual_window_width = rect.right - rect.left;
|
||||||
|
u32 actual_window_height = rect.bottom - rect.top;
|
||||||
|
// Create the window
|
||||||
|
window._os_handle = CreateWindowEx(
|
||||||
|
ex_style,
|
||||||
|
"sigma balls",
|
||||||
|
temp_convert_to_null_terminated_string(window.title),
|
||||||
|
style,
|
||||||
|
CW_USEDEFAULT, CW_USEDEFAULT, actual_window_width, actual_window_height,
|
||||||
|
0, 0, instance, 0);
|
||||||
|
assert(window._os_handle != 0, "Window creation failed, error: %lu", GetLastError());
|
||||||
|
window._initialized = true;
|
||||||
|
ShowWindow(window._os_handle, SW_SHOWDEFAULT);
|
||||||
|
UpdateWindow(window._os_handle);
|
||||||
|
}
|
||||||
|
|
||||||
void os_init(u64 program_memory_size) {
|
void os_init(u64 program_memory_size) {
|
||||||
|
|
||||||
|
HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
context.thread_id = GetCurrentThreadId();
|
context.thread_id = GetCurrentThreadId();
|
||||||
|
|
||||||
memset(&window, 0, sizeof(window));
|
|
||||||
|
|
||||||
#if CONFIGURATION == RELEASE
|
#if CONFIGURATION == RELEASE
|
||||||
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);
|
||||||
|
@ -159,7 +225,7 @@ void os_init(u64 program_memory_size) {
|
||||||
unsigned char* addr = 0;
|
unsigned char* addr = 0;
|
||||||
while (VirtualQuery(addr, &mbi, sizeof(mbi))) {
|
while (VirtualQuery(addr, &mbi, sizeof(mbi))) {
|
||||||
if (mbi.Type == MEM_IMAGE) {
|
if (mbi.Type == MEM_IMAGE) {
|
||||||
if (os.static_memory_start == NULL) {
|
if (os.static_memory_start == 0) {
|
||||||
os.static_memory_start = mbi.BaseAddress;
|
os.static_memory_start = mbi.BaseAddress;
|
||||||
}
|
}
|
||||||
os.static_memory_end = (unsigned char*)mbi.BaseAddress + mbi.RegionSize;
|
os.static_memory_end = (unsigned char*)mbi.BaseAddress + mbi.RegionSize;
|
||||||
|
@ -171,7 +237,6 @@ void os_init(u64 program_memory_size) {
|
||||||
program_memory_mutex = os_make_mutex();
|
program_memory_mutex = os_make_mutex();
|
||||||
os_grow_program_memory(program_memory_size);
|
os_grow_program_memory(program_memory_size);
|
||||||
|
|
||||||
|
|
||||||
heap_init();
|
heap_init();
|
||||||
|
|
||||||
os.crt = os_load_dynamic_library(STR("msvcrt.dll"));
|
os.crt = os_load_dynamic_library(STR("msvcrt.dll"));
|
||||||
|
@ -189,61 +254,8 @@ void os_init(u64 program_memory_size) {
|
||||||
os.crt_memset = (Crt_Memset_Proc)os_dynamic_library_load_symbol(os.crt, STR("memset"));
|
os.crt_memset = (Crt_Memset_Proc)os_dynamic_library_load_symbol(os.crt, STR("memset"));
|
||||||
assert(os.crt_memset, "Missing memset in crt");
|
assert(os.crt_memset, "Missing memset in crt");
|
||||||
|
|
||||||
window.title = STR("Unnamed Window");
|
win32_init_window();
|
||||||
window.width = 1280;
|
os_start_thread(os_make_thread(win32_audio_thread, get_heap_allocator()));
|
||||||
window.height = 720;
|
|
||||||
window.x = 0;
|
|
||||||
window.y = 0;
|
|
||||||
window.should_close = false;
|
|
||||||
window._initialized = false;
|
|
||||||
window.clear_color.r = 0.392f;
|
|
||||||
window.clear_color.g = 0.584f;
|
|
||||||
window.clear_color.b = 0.929f;
|
|
||||||
window.clear_color.a = 1.0f;
|
|
||||||
|
|
||||||
WNDCLASSEX wc = (WNDCLASSEX){0};
|
|
||||||
MSG msg;
|
|
||||||
HINSTANCE instance = GetModuleHandle(NULL);
|
|
||||||
assert(instance != INVALID_HANDLE_VALUE, "Failed getting current HINSTANCE");
|
|
||||||
|
|
||||||
wc.cbSize = sizeof(WNDCLASSEX);
|
|
||||||
wc.style = CS_OWNDC;
|
|
||||||
wc.lpfnWndProc = win32_window_proc;
|
|
||||||
wc.hInstance = instance;
|
|
||||||
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
|
|
||||||
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
|
|
||||||
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
||||||
wc.lpszClassName = "sigma balls";
|
|
||||||
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
|
|
||||||
|
|
||||||
BOOL ok = RegisterClassEx(&wc);
|
|
||||||
assert(ok, "Failed registering window class (error code %lu)", GetLastError());
|
|
||||||
|
|
||||||
RECT rect = {0, 0, window.width, window.height};
|
|
||||||
DWORD style = WS_OVERLAPPEDWINDOW;
|
|
||||||
DWORD ex_style = WS_EX_CLIENTEDGE;
|
|
||||||
ok = AdjustWindowRectEx(&rect, style, FALSE, ex_style);
|
|
||||||
assert(ok != 0, "AdjustWindowRectEx failed with error code %lu", GetLastError());
|
|
||||||
|
|
||||||
u32 actual_window_width = rect.right - rect.left;
|
|
||||||
u32 actual_window_height = rect.bottom - rect.top;
|
|
||||||
// Create the window
|
|
||||||
window._os_handle = CreateWindowEx(
|
|
||||||
ex_style,
|
|
||||||
"sigma balls",
|
|
||||||
temp_convert_to_null_terminated_string(window.title),
|
|
||||||
style,
|
|
||||||
CW_USEDEFAULT, CW_USEDEFAULT, actual_window_width, actual_window_height,
|
|
||||||
NULL, NULL, instance, NULL);
|
|
||||||
assert(window._os_handle != NULL, "Window creation failed, error: %lu", GetLastError());
|
|
||||||
window._initialized = true;
|
|
||||||
ShowWindow(window._os_handle, SW_SHOWDEFAULT);
|
|
||||||
UpdateWindow(window._os_handle);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void s64_to_null_terminated_string_reverse(char str[], int length)
|
void s64_to_null_terminated_string_reverse(char str[], int length)
|
||||||
|
@ -573,7 +585,7 @@ void os_write_string_to_stdout(string s) {
|
||||||
HANDLE win32_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
HANDLE win32_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
if (win32_stdout == INVALID_HANDLE_VALUE) return;
|
if (win32_stdout == INVALID_HANDLE_VALUE) return;
|
||||||
|
|
||||||
WriteFile(win32_stdout, s.data, s.count, 0, NULL);
|
WriteFile(win32_stdout, s.data, s.count, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8, Allocator allocator) {
|
u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8, Allocator allocator) {
|
||||||
|
@ -591,7 +603,7 @@ u16 *win32_fixed_utf8_to_null_terminated_wide(string utf8, Allocator allocator)
|
||||||
int result = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, utf16_str, utf16_length);
|
int result = MultiByteToWideChar(CP_UTF8, 0, (LPCCH)utf8.data, (int)utf8.count, utf16_str, utf16_length);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
dealloc(allocator, utf16_str);
|
dealloc(allocator, utf16_str);
|
||||||
return NULL;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
utf16_str[utf16_length] = 0;
|
utf16_str[utf16_length] = 0;
|
||||||
|
@ -646,7 +658,7 @@ File os_file_open_s(string path, Os_Io_Open_Flags flags) {
|
||||||
|
|
||||||
u16 *wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
u16 *wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
||||||
|
|
||||||
return CreateFileW(wide, access, FILE_SHARE_READ, NULL, creation, FILE_ATTRIBUTE_NORMAL, NULL);
|
return CreateFileW(wide, access, FILE_SHARE_READ, 0, creation, FILE_ATTRIBUTE_NORMAL, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void os_file_close(File f) {
|
void os_file_close(File f) {
|
||||||
|
@ -672,7 +684,7 @@ bool os_make_directory_s(string path, bool recursive) {
|
||||||
wchar_t *sep = wcschr(wide_path + 1, L'\\');
|
wchar_t *sep = wcschr(wide_path + 1, L'\\');
|
||||||
while (sep) {
|
while (sep) {
|
||||||
*sep = 0;
|
*sep = 0;
|
||||||
if (!CreateDirectoryW(wide_path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
if (!CreateDirectoryW(wide_path, 0) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*sep = L'\\';
|
*sep = L'\\';
|
||||||
|
@ -680,7 +692,7 @@ bool os_make_directory_s(string path, bool recursive) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CreateDirectoryW(wide_path, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
if (!CreateDirectoryW(wide_path, 0) && GetLastError() != ERROR_ALREADY_EXISTS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,19 +745,19 @@ bool os_delete_directory_s(string path, bool recursive) {
|
||||||
|
|
||||||
bool os_file_write_string(File f, string s) {
|
bool os_file_write_string(File f, string s) {
|
||||||
DWORD written;
|
DWORD written;
|
||||||
BOOL result = WriteFile(f, s.data, s.count, &written, NULL);
|
BOOL result = WriteFile(f, s.data, s.count, &written, 0);
|
||||||
return result && (written == s.count);
|
return result && (written == s.count);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes) {
|
bool os_file_write_bytes(File f, void *buffer, u64 size_in_bytes) {
|
||||||
DWORD written;
|
DWORD written;
|
||||||
BOOL result = WriteFile(f, buffer, (DWORD)size_in_bytes, &written, NULL);
|
BOOL result = WriteFile(f, buffer, (DWORD)size_in_bytes, &written, 0);
|
||||||
return result && (written == size_in_bytes);
|
return result && (written == size_in_bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_bytes) {
|
bool os_file_read(File f, void* buffer, u64 bytes_to_read, u64 *actual_read_bytes) {
|
||||||
DWORD read;
|
DWORD read;
|
||||||
BOOL result = ReadFile(f, buffer, (DWORD)bytes_to_read, &read, NULL);
|
BOOL result = ReadFile(f, buffer, (DWORD)bytes_to_read, &read, 0);
|
||||||
if (actual_read_bytes) {
|
if (actual_read_bytes) {
|
||||||
*actual_read_bytes = read;
|
*actual_read_bytes = read;
|
||||||
}
|
}
|
||||||
|
@ -799,7 +811,7 @@ bool os_read_entire_file_s(string path, string *result, Allocator allocator) {
|
||||||
bool os_is_file_s(string path) {
|
bool os_is_file_s(string path) {
|
||||||
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
||||||
assert(path_wide, "Invalid path string");
|
assert(path_wide, "Invalid path string");
|
||||||
if (path_wide == NULL) {
|
if (path_wide == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -815,7 +827,7 @@ bool os_is_file_s(string path) {
|
||||||
bool os_is_directory_s(string path) {
|
bool os_is_directory_s(string path) {
|
||||||
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
u16 *path_wide = temp_win32_fixed_utf8_to_null_terminated_wide(path);
|
||||||
assert(path_wide, "Invalid path string");
|
assert(path_wide, "Invalid path string");
|
||||||
if (path_wide == NULL) {
|
if (path_wide == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,10 +913,10 @@ bool os_do_paths_match(string a, string b) {
|
||||||
wchar_t full_path_b[MAX_PATH];
|
wchar_t full_path_b[MAX_PATH];
|
||||||
|
|
||||||
// Get the full path for both paths
|
// Get the full path for both paths
|
||||||
if (!GetFullPathNameW(wide_path_a, MAX_PATH, full_path_a, NULL)) {
|
if (!GetFullPathNameW(wide_path_a, MAX_PATH, full_path_a, 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!GetFullPathNameW(wide_path_b, MAX_PATH, full_path_b, NULL)) {
|
if (!GetFullPathNameW(wide_path_b, MAX_PATH, full_path_b, 0)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -950,9 +962,270 @@ void* os_get_stack_limit() {
|
||||||
return tib->StackLimit;
|
return tib->StackLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Actually fuck you bill gates
|
||||||
|
const GUID CLSID_MMDeviceEnumerator = {0xbcde0395, 0xe52f, 0x467c, {0x8e,0x3d, 0xc4,0x57,0x92,0x91,0x69,0x2e}};
|
||||||
|
const GUID IID_IMMDeviceEnumerator = {0xa95664d2, 0x9614, 0x4f35, {0xa7,0x46, 0xde,0x8d,0xb6,0x36,0x17,0xe6}};
|
||||||
|
const GUID IID_IAudioClient = {0x1cb9ad4c, 0xdbfa, 0x4c32, {0xb1,0x78, 0xc2,0xf5,0x68,0xa7,0x03,0xb2}};
|
||||||
|
const GUID IID_IAudioRenderClient = {0xf294acfc, 0x3146, 0x4483, {0xa7,0xbf, 0xad,0xdc,0xa7,0xc2,0x60,0xe2}};
|
||||||
|
DEFINE_GUID(IID_ISimpleAudioVolume,
|
||||||
|
0x87CE5498, 0x68D6, 0x44E5, 0x92, 0x15, 0x6D, 0xA4, 0x7E, 0xF8, 0x83, 0xD8);
|
||||||
|
|
||||||
|
IAudioClient* win32_audio_client;
|
||||||
|
IAudioRenderClient* win32_render_client;
|
||||||
|
bool win32_audio_deactivated = false;
|
||||||
|
Audio_Format win32_output_format;
|
||||||
|
IMMDevice* win32_audio_device = 0;
|
||||||
|
IMMDeviceEnumerator* win32_device_enumerator = 0;
|
||||||
|
ISimpleAudioVolume* win32_audio_volume = 0;
|
||||||
|
|
||||||
|
void
|
||||||
|
win32_audio_init() {
|
||||||
|
|
||||||
|
win32_audio_client = 0;
|
||||||
|
win32_render_client = 0;
|
||||||
|
win32_audio_deactivated = 0;
|
||||||
|
win32_audio_device = 0;
|
||||||
|
win32_device_enumerator = 0;
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
WAVEFORMATEX* device_base_format = 0;
|
||||||
|
WAVEFORMATEX* output_format = 0;
|
||||||
|
|
||||||
|
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, 0, CLSCTX_ALL, &IID_IMMDeviceEnumerator, (void**)&win32_device_enumerator);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(win32_device_enumerator, eRender, eConsole, &win32_audio_device);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
hr = IMMDevice_Activate(
|
||||||
|
win32_audio_device,
|
||||||
|
&IID_IAudioClient,
|
||||||
|
CLSCTX_ALL, 0,
|
||||||
|
(void**)&win32_audio_client
|
||||||
|
);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
hr = IAudioClient_GetMixFormat(win32_audio_client, &device_base_format);
|
||||||
|
|
||||||
|
WAVEFORMATEXTENSIBLE *format_f32
|
||||||
|
= (WAVEFORMATEXTENSIBLE*)CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
|
WAVEFORMATEXTENSIBLE *format_s16
|
||||||
|
= (WAVEFORMATEXTENSIBLE*)CoTaskMemAlloc(sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
|
|
||||||
|
memcpy(format_f32, device_base_format, sizeof(WAVEFORMATEX));
|
||||||
|
|
||||||
|
format_f32->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
|
||||||
|
format_f32->Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
|
||||||
|
format_f32->Samples.wValidBitsPerSample = format_f32->Format.wBitsPerSample;
|
||||||
|
format_f32->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
|
||||||
|
|
||||||
|
memcpy(format_s16, format_f32, sizeof(WAVEFORMATEXTENSIBLE));
|
||||||
|
|
||||||
|
format_f32->SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
|
||||||
|
format_f32->Format.wBitsPerSample = 32;
|
||||||
|
format_f32->Format.nBlockAlign
|
||||||
|
= format_f32->Format.nChannels * format_f32->Format.wBitsPerSample / 8;
|
||||||
|
format_f32->Format.nAvgBytesPerSec
|
||||||
|
= format_f32->Format.nSamplesPerSec * format_f32->Format.nBlockAlign;
|
||||||
|
|
||||||
|
format_s16->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
|
||||||
|
format_s16->Format.wBitsPerSample = 16;
|
||||||
|
format_s16->Format.nBlockAlign = format_s16->Format.nChannels * format_s16->Format.wBitsPerSample / 8;
|
||||||
|
format_s16->Format.nAvgBytesPerSec = format_s16->Format.nSamplesPerSec * format_s16->Format.nBlockAlign;
|
||||||
|
|
||||||
|
// First look for f32 support
|
||||||
|
WAVEFORMATEX *closest_match = NULL;
|
||||||
|
hr = IAudioClient_IsFormatSupported(
|
||||||
|
win32_audio_client,
|
||||||
|
AUDCLNT_SHAREMODE_SHARED,
|
||||||
|
(WAVEFORMATEX *)format_f32,
|
||||||
|
&closest_match
|
||||||
|
);
|
||||||
|
|
||||||
|
// If f32 fails, look for s16
|
||||||
|
if (hr != S_OK) {
|
||||||
|
hr = IAudioClient_IsFormatSupported(
|
||||||
|
win32_audio_client,
|
||||||
|
AUDCLNT_SHAREMODE_SHARED,
|
||||||
|
(WAVEFORMATEX *)format_s16,
|
||||||
|
&closest_match
|
||||||
|
);
|
||||||
|
if (hr != S_OK) {
|
||||||
|
win32_audio_deactivated = true;
|
||||||
|
log_error("Default audio output device is not supported.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
output_format = (WAVEFORMATEX*)format_s16;
|
||||||
|
} else {
|
||||||
|
output_format = (WAVEFORMATEX*)format_f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
const s64 BUFFER_DURATION_MS = 40;
|
||||||
|
hr = IAudioClient_Initialize(
|
||||||
|
win32_audio_client,
|
||||||
|
AUDCLNT_SHAREMODE_SHARED,
|
||||||
|
0,
|
||||||
|
BUFFER_DURATION_MS*10000ll, 0,
|
||||||
|
output_format, 0
|
||||||
|
);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
hr = IAudioClient_GetService(win32_audio_client, &IID_IAudioRenderClient, (void**)&win32_render_client);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
win32_output_format.channels = output_format->nChannels;
|
||||||
|
win32_output_format.sample_rate = output_format->nSamplesPerSec;
|
||||||
|
if (output_format == (WAVEFORMATEX*)format_s16) {
|
||||||
|
win32_output_format.bit_width = AUDIO_BITS_16;
|
||||||
|
} else if (output_format == (WAVEFORMATEX*)format_f32) {
|
||||||
|
win32_output_format.bit_width = AUDIO_BITS_32;
|
||||||
|
} else {
|
||||||
|
panic("What");
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD task_index;
|
||||||
|
AvSetMmThreadCharacteristics(TEXT("Pro Audio"), &task_index);
|
||||||
|
|
||||||
|
hr = IAudioClient_GetService(win32_audio_client, &IID_ISimpleAudioVolume, (void**)&win32_audio_volume);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
hr = ISimpleAudioVolume_SetMasterVolume(win32_audio_volume, 1.0f, 0);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
ISimpleAudioVolume_Release(win32_audio_volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info("Successfully initialized default audio device. Channels: %d, sample_rate: %d, bits: %d", win32_output_format.channels, win32_output_format.sample_rate, get_audio_bit_width_byte_size(win32_output_format.bit_width)*8);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
win32_audio_thread(Thread *t) {
|
||||||
|
|
||||||
|
win32_audio_init();
|
||||||
|
|
||||||
|
timeBeginPeriod(1);
|
||||||
|
|
||||||
|
u32 buffer_frame_count;
|
||||||
|
HRESULT hr = IAudioClient_GetBufferSize(win32_audio_client, &buffer_frame_count);
|
||||||
|
if (FAILED(hr)) win32_audio_deactivated = true;
|
||||||
|
|
||||||
|
bool started = false;
|
||||||
|
|
||||||
|
while (!window.should_close) tm_scope("Audio update") {
|
||||||
|
if (win32_audio_deactivated) tm_scope("Retry audio device") {
|
||||||
|
os_sleep(100);
|
||||||
|
win32_audio_init();
|
||||||
|
started = false;
|
||||||
|
if (win32_audio_deactivated) {
|
||||||
|
hr = IAudioClient_GetBufferSize(win32_audio_client, &buffer_frame_count);
|
||||||
|
if (FAILED(hr)) win32_audio_deactivated = true;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We gotta check if there is a new default endpoint (#Speed ?)
|
||||||
|
|
||||||
|
IMMDevice *now_default = 0;
|
||||||
|
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(win32_device_enumerator, eRender, eConsole, &now_default);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
WCHAR *now_default_id = NULL;
|
||||||
|
hr = IMMDevice_GetId(now_default, &now_default_id);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
WCHAR *previous_id = NULL;
|
||||||
|
hr = IMMDevice_GetId(win32_audio_device, &previous_id);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
|
||||||
|
if (wcscmp(now_default_id, previous_id) != 0) {
|
||||||
|
win32_audio_deactivated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoTaskMemFree(now_default_id);
|
||||||
|
CoTaskMemFree(previous_id);
|
||||||
|
|
||||||
|
IMMDevice_Release(now_default);
|
||||||
|
|
||||||
|
if (win32_audio_deactivated) continue;
|
||||||
|
|
||||||
|
BYTE *buffer = 0;
|
||||||
|
|
||||||
|
u32 num_frames_available = 0;
|
||||||
|
hr = IAudioClient_GetCurrentPadding(win32_audio_client, &num_frames_available);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
win32_audio_deactivated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
u32 num_frames_to_write = buffer_frame_count - num_frames_available;
|
||||||
|
|
||||||
|
if (!started) {
|
||||||
|
hr = IAudioClient_Start(win32_audio_client);
|
||||||
|
win32_check_hr(hr);
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (num_frames_to_write == 0) tm_scope("Chill") {
|
||||||
|
// We yield & sleep until we have any work to do
|
||||||
|
os_yield_thread();
|
||||||
|
os_sleep(1);
|
||||||
|
|
||||||
|
hr = IAudioClient_GetCurrentPadding(win32_audio_client, &num_frames_available);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
win32_audio_deactivated = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
num_frames_to_write = buffer_frame_count - num_frames_available;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (win32_audio_deactivated) continue;
|
||||||
|
|
||||||
|
|
||||||
|
if (num_frames_to_write > 0) tm_scope("Output frames") {
|
||||||
|
hr = IAudioRenderClient_GetBuffer(
|
||||||
|
win32_render_client,
|
||||||
|
num_frames_to_write,
|
||||||
|
&buffer
|
||||||
|
);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
win32_audio_deactivated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
do_program_audio_sample(num_frames_to_write, win32_output_format, buffer);
|
||||||
|
//f32 s = 0.5;
|
||||||
|
//for (u32 i = 0; i < num_frames_to_write * win32_output_format.channels; ++i) {
|
||||||
|
// ((f32*)buffer)[i] = s;
|
||||||
|
//}
|
||||||
|
/*float64 time = 0;
|
||||||
|
float *fbuffer = (float *)buffer;
|
||||||
|
for (UINT32 frameIndex = 0; frameIndex < num_frames_to_write; frameIndex++) {
|
||||||
|
float amplitude = (float)(sin(time)*0.2);
|
||||||
|
|
||||||
|
*fbuffer++ = amplitude; // left
|
||||||
|
*fbuffer++ = amplitude; // right
|
||||||
|
|
||||||
|
time += 0.05;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
|
||||||
|
//for (u64 i = 0; i < num_frames_to_write; i++) {
|
||||||
|
// f32 s = *(((f32*)buffer)+i*win32_output_format.channels);
|
||||||
|
// print("%f ", s);
|
||||||
|
//}
|
||||||
|
hr = IAudioRenderClient_ReleaseBuffer(
|
||||||
|
win32_render_client,
|
||||||
|
num_frames_to_write,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
win32_audio_deactivated = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void os_update() {
|
void os_update() {
|
||||||
|
|
||||||
|
@ -989,7 +1262,7 @@ void os_update() {
|
||||||
u32 actual_x = update_rect.left;
|
u32 actual_x = update_rect.left;
|
||||||
u32 actual_y = screen_height - update_rect.top - (update_rect.bottom - update_rect.top);
|
u32 actual_y = screen_height - update_rect.top - (update_rect.bottom - update_rect.top);
|
||||||
|
|
||||||
SetWindowPos(window._os_handle, NULL, actual_x, actual_y, actual_width, actual_height, SWP_NOZORDER | SWP_NOACTIVATE);
|
SetWindowPos(window._os_handle, 0, actual_x, actual_y, actual_width, actual_height, SWP_NOZORDER | SWP_NOACTIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
RECT client_rect;
|
RECT client_rect;
|
||||||
|
@ -1050,7 +1323,7 @@ void os_update() {
|
||||||
|
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (input_frame.number_of_events < MAX_EVENTS_PER_FRAME
|
while (input_frame.number_of_events < MAX_EVENTS_PER_FRAME
|
||||||
&& PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
&& PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
|
||||||
if (msg.message == WM_QUIT) {
|
if (msg.message == WM_QUIT) {
|
||||||
window.should_close = true;
|
window.should_close = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -101,6 +101,7 @@ typedef struct Thread {
|
||||||
|
|
||||||
///
|
///
|
||||||
// Thread primitive
|
// Thread primitive
|
||||||
|
// #Cleanup this shouldn't be allocating just for the pointer!! Just do os_thread_init(*)
|
||||||
Thread* os_make_thread(Thread_Proc proc, Allocator allocator);
|
Thread* os_make_thread(Thread_Proc proc, Allocator allocator);
|
||||||
void os_destroy_thread(Thread *t);
|
void os_destroy_thread(Thread *t);
|
||||||
void os_start_thread(Thread* t);
|
void os_start_thread(Thread* t);
|
||||||
|
|
|
@ -29,20 +29,20 @@ void _profiler_report_time_cycles(string name, u64 count, u64 start) {
|
||||||
spinlock_release(&_profiler_lock);
|
spinlock_release(&_profiler_lock);
|
||||||
}
|
}
|
||||||
#if ENABLE_PROFILING
|
#if ENABLE_PROFILING
|
||||||
#define tm_scope_cycles(name) \
|
#define tm_scope(name) \
|
||||||
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
|
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
|
||||||
elapsed_time == 0; \
|
elapsed_time == 0; \
|
||||||
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, _profiler_report_time_cycles(STR(name), elapsed_time, start_time))
|
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, _profiler_report_time_cycles(STR(name), elapsed_time, start_time))
|
||||||
#define tm_scope_cycles_var(name, var) \
|
#define tm_scope_var(name, var) \
|
||||||
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
|
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
|
||||||
elapsed_time == 0; \
|
elapsed_time == 0; \
|
||||||
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, var=elapsed_time)
|
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, var=elapsed_time)
|
||||||
#define tm_scope_cycles_accum(name, var) \
|
#define tm_scope_accum(name, var) \
|
||||||
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
|
for (u64 start_time = os_get_current_cycle_count(), end_time = start_time, elapsed_time = 0; \
|
||||||
elapsed_time == 0; \
|
elapsed_time == 0; \
|
||||||
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, var+=elapsed_time)
|
elapsed_time = (end_time = os_get_current_cycle_count()) - start_time, var+=elapsed_time)
|
||||||
#else
|
#else
|
||||||
#define tm_scope_cycles(...)
|
#define tm_scope(...)
|
||||||
#define tm_scope_cycles_var(...)
|
#define tm_scope_var(...)
|
||||||
#define tm_scope_cycles_accum(...)
|
#define tm_scope_accum(...)
|
||||||
#endif
|
#endif
|
|
@ -223,16 +223,6 @@ typedef void(*Logger_Proc)(Log_Level level, string s);
|
||||||
|
|
||||||
#define log(...) LOG_BASE(LOG_INFO, __VA_ARGS__)
|
#define log(...) LOG_BASE(LOG_INFO, __VA_ARGS__)
|
||||||
|
|
||||||
void default_logger(Log_Level level, string s) {
|
|
||||||
switch (level) {
|
|
||||||
case LOG_VERBOSE: print("[VERBOSE]: %s\n", s); break;
|
|
||||||
case LOG_INFO: print("[INFO]: %s\n", s); break;
|
|
||||||
case LOG_WARNING: print("[WARNING]: %s\n", s); break;
|
|
||||||
case LOG_ERROR: print("[ERROR]: %s\n", s); break;
|
|
||||||
case LOG_LEVEL_COUNT: break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct String_Builder {
|
typedef struct String_Builder {
|
||||||
union {
|
union {
|
||||||
|
|
Reference in a new issue