From e939a3f4a617c4d4c8a9e2aa7883773651a7c28b Mon Sep 17 00:00:00 2001 From: Dennis Eichhorn Date: Sat, 1 Jun 2024 23:53:22 +0200 Subject: [PATCH] update --- Compression/LZP.h | 101 +++++++++++++++++++++++++++++++++++++ Sound/DirectSound.h | 98 +++++++++++++++++++++--------------- Sound/XAudio2.h | 119 +++++++++++++++++++++++++++----------------- Stdlib/Types.h | 68 +++++++++++++++++++++++++ Utils/TestUtils.h | 10 ++++ 5 files changed, 312 insertions(+), 84 deletions(-) create mode 100644 Compression/LZP.h diff --git a/Compression/LZP.h b/Compression/LZP.h new file mode 100644 index 0000000..2692536 --- /dev/null +++ b/Compression/LZP.h @@ -0,0 +1,101 @@ +/** + * Jingga + * + * @package Models + * @copyright Dennis Eichhorn + * @license OMS License 1.0 + * @version 1.0.0 + * @link https://jingga.app + */ +#ifndef COMPRESSION_LZP_H +#define COMPRESSION_LZP_H + +#include + +#include "../Stdlib/Types.h" + +uint32 encode_lzp(const byte* in, uint32 length, byte* out) +{ + byte buf[9]; + byte table[1 << 16] = {0}; + + uint16 hash = 0; + int32 mask, i, j, c, in_pos = 0, out_pos = 0; + + while(true) { + j = 1; + mask = 0; + for (i = 0; i < 8; ++i) { + if (in_pos == length) { + break; + } + + c = in[in_pos++]; + if (c == table[hash]) { + mask |= 1 << i; + } else { + table[hash] = c; + buf[j++] = c; + } + + hash = (hash << 4) ^ c; + } + + if (i > 0) { + buf[0] = mask; + for (i = 0; i < j; ++i) { + out[out_pos++] = buf[i]; + } + } + + if (in_pos == length) { + break; + } + } + + return out_pos; +} + +uint32 decode_lzp(const byte* in, uint32 length, byte* out) +{ + byte buf[8]; + byte table[1 << 16] = {0}; + + uint16 hash = 0; + int mask, i, j, c, in_pos = 0, out_pos = 0; + + while (true) { + j = 0; + if (in_pos == length) { + break; + } + + mask = in[in_pos++]; + for (i = 0; i < 8; ++i) { + if ((mask & (1 << i)) != 0) { + c = table[hash]; + } else { + if (in_pos == length) { + break; + } + + c = in[in_pos++]; + table[hash] = c; + } + + buf[j++] = c; + + hash = (hash << 4) ^ c; + } + + if (j > 0) { + for (i = 0; i < j; ++i) { + out[out_pos++] = buf[i]; + } + } + } + + return out_pos; +} + +#endif \ No newline at end of file diff --git a/Sound/DirectSound.h b/Sound/DirectSound.h index cee60ad..6caf974 100644 --- a/Sound/DirectSound.h +++ b/Sound/DirectSound.h @@ -25,8 +25,14 @@ namespace Sound { uint32 sample_rate; uint32 sample_size; uint32 sample_index; + int16 volume; + uint32 buffer_size; + + // Actual samples inside the buffer + // The buffer could be larger than the data to output + uint32 sample_buffer_size; int16* buffer; bool playing = false; @@ -61,6 +67,8 @@ namespace Sound { if(!SUCCEEDED(setting->direct_sound->SetCooperativeLevel(w->hwnd, DSSCL_PRIORITY))) { // @todo Log + + return; } WAVEFORMATEX wf = {}; @@ -81,13 +89,18 @@ namespace Sound { if(!SUCCEEDED(setting->direct_sound->CreateSoundBuffer(&bufferDesc, &setting->primary_buffer, 0))) { // @todo Log + + return; } if (!SUCCEEDED(setting->primary_buffer->SetFormat(&wf))) { // @todo Log + + return; } setting->buffer_size = setting->sample_rate * setting->sample_size; + setting->buffer = (int16 *) calloc(setting->sample_rate, setting->sample_size); // Create secondary buffer DSBUFFERDESC bufferDesc2; @@ -100,15 +113,19 @@ namespace Sound { if(!SUCCEEDED(setting->direct_sound->CreateSoundBuffer(&bufferDesc2, &setting->secondary_buffer, 0))) { // @todo Log + + return; } } + inline void direct_sound_play(DirectSoundSetting* setting) { setting->secondary_buffer->Play(0, 0, DSBPLAY_LOOPING); setting->playing = true; } + inline void direct_sound_free(DirectSoundSetting* setting) { if (setting->direct_sound) { @@ -124,31 +141,19 @@ namespace Sound { } } - bool direct_sound_should_update(DirectSoundSetting* setting) - { - return true; - } - - int16 *direct_sound_return_buffer(DirectSoundSetting* setting) - { - - } - - void direct_sound_play_buffer(DirectSoundSetting* setting) + /** + * Calculates the samples in bytes to generate for the buffer + */ + inline + uint32 direct_sound_sample_buffer_size(const DirectSoundSetting* setting) { DWORD player_cursor; DWORD write_cursor; if (!SUCCEEDED(setting->secondary_buffer->GetCurrentPosition(&player_cursor, &write_cursor))) { // @todo Log - return; + return 0; } - void *region1; - DWORD region1_size; - - void *region2; - DWORD region2_size; - DWORD bytes_to_lock = (setting->sample_index * setting->sample_size) % setting->buffer_size; DWORD bytes_to_write = 0; @@ -161,6 +166,24 @@ namespace Sound { bytes_to_write = player_cursor - bytes_to_lock; } + return bytes_to_write; + } + + inline + void direct_sound_play_buffer(DirectSoundSetting* setting, uint32 bytes_to_write) + { + if (bytes_to_write == 0) { + return; + } + + void *region1; + DWORD region1_size; + + void *region2; + DWORD region2_size; + + DWORD bytes_to_lock = (setting->sample_index * setting->sample_size) % setting->buffer_size; + setting->secondary_buffer->Lock( bytes_to_lock, bytes_to_write, ®ion1, ®ion1_size, @@ -168,31 +191,28 @@ namespace Sound { 0 ); - int16* sample_out = (int16 *) region1; - DWORD region1_sample_count = region1_size / setting->sample_size; + // @question Do we even need to use memcpy? Can't we use the buffer directly? + // Probably depends on what lock actually does to region1/region2 + // Of course we would than need some mechanism to check when we can write into the buffer + // See XAudio2 for this, we would probably need a second buffer as well + memcpy( + (void *) region1, + (void *) setting->buffer, + region1_size + ); - for (DWORD idx = 0; idx < region1_sample_count; ++idx) { - f32 t = ((f32) 1.0f / (f32) (48000 / 256)) * 2.0f * OMS_PI; - f32 sine_value = sinf(t); - int16 sample_value = (int16) (sine_value * setting->volume); - - *sample_out++ = sample_value; - *sample_out++ = sample_value; - } - - sample_out = (int16 *) region2; - DWORD region2_sample_count = region2_size / setting->sample_size; - - for (DWORD idx = 0; idx < region2_sample_count; ++idx) { - f32 t = ((f32) 1.0f / (f32) (48000 / 256)) * 2.0f * OMS_PI; - f32 sine_value = sinf(t); - int16 sample_value = (int16) (sine_value * setting->volume); - - *sample_out++ = sample_value; - *sample_out++ = sample_value; + if (region2_size > 0) { + memcpy( + (void *) region2, + (void *) (setting->buffer + region1_size), + region2_size + ); } setting->secondary_buffer->Unlock(region1, region1_size, region2, region2_size); + + setting->sample_index += bytes_to_write / setting->sample_size; + setting->sample_buffer_size = 0; } } diff --git a/Sound/XAudio2.h b/Sound/XAudio2.h index 08d5f39..f4219e5 100644 --- a/Sound/XAudio2.h +++ b/Sound/XAudio2.h @@ -25,9 +25,16 @@ namespace Sound { uint32 sample_rate; uint32 sample_size; uint32 sample_index; + int16 volume; + uint32 buffer_size; + // Actual samples inside the buffer + // The buffer could be larger than the data to output + uint32 sample_buffer_size; + int16* buffer; + bool playing = false; byte type = SOUND_API_XAUDIO2; @@ -36,7 +43,7 @@ namespace Sound { IXAudio2SourceVoice* source_voice; IXAudio2MasteringVoice* mastering_voice; - XAUDIO2_BUFFER buffer[2]; + XAUDIO2_BUFFER internal_buffer[2]; }; // BEGIN: Dynamically load XAudio2 @@ -64,7 +71,13 @@ namespace Sound { return; } - if (!SUCCEEDED(setting->xaudio2->CreateMasteringVoice(&setting->mastering_voice))) { + if (!SUCCEEDED(setting->xaudio2->CreateMasteringVoice( + &setting->mastering_voice, + XAUDIO2_DEFAULT_CHANNELS, + setting->sample_rate, + 0, + NULL + ))) { // @todo Log return; } @@ -72,10 +85,10 @@ namespace Sound { WAVEFORMATEX wf = {}; wf.wFormatTag = WAVE_FORMAT_PCM; wf.nChannels = 2; - wf.wBitsPerSample = 16; - wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; + wf.wBitsPerSample = (uint16) ((setting->sample_size * 8) / wf.nChannels); // = sample_size per channel + wf.nBlockAlign = (wf.nChannels * wf.wBitsPerSample) / 8; // = sample_szie wf.nSamplesPerSec = setting->sample_rate; - wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; // = buffer_size wf.cbSize = 0; if (!SUCCEEDED(setting->xaudio2->CreateSourceVoice(&setting->source_voice, &wf))) { @@ -83,32 +96,34 @@ namespace Sound { return; } - setting->buffer_size = setting->sample_rate * wf.nBlockAlign; + setting->buffer_size = setting->sample_rate * setting->sample_size; + setting->buffer = (int16 *) calloc(setting->sample_rate, setting->sample_size); // @question Consider to move to the heap? - setting->buffer[0].Flags = 0; - setting->buffer[0].AudioBytes = setting->buffer_size; - setting->buffer[0].pAudioData = (BYTE *) malloc(setting->buffer_size * sizeof(BYTE)); - setting->buffer[0].PlayBegin = 0; - setting->buffer[0].PlayLength = 0; - setting->buffer[0].LoopBegin = 0; - setting->buffer[0].LoopLength = 0; - setting->buffer[0].LoopCount = 0; - setting->buffer[0].pContext = NULL; + setting->internal_buffer[0].Flags = 0; + setting->internal_buffer[0].AudioBytes = setting->buffer_size; + setting->internal_buffer[0].pAudioData = (byte *) malloc(setting->buffer_size * sizeof(byte)); + setting->internal_buffer[0].PlayBegin = 0; + setting->internal_buffer[0].PlayLength = 0; + setting->internal_buffer[0].LoopBegin = 0; + setting->internal_buffer[0].LoopLength = 0; + setting->internal_buffer[0].LoopCount = 0; + setting->internal_buffer[0].pContext = NULL; - setting->buffer[1].Flags = 0; - setting->buffer[1].AudioBytes = setting->buffer_size; - setting->buffer[1].pAudioData = (BYTE *) malloc(setting->buffer_size * sizeof(BYTE)); - setting->buffer[1].PlayBegin = 0; - setting->buffer[1].PlayLength = 0; - setting->buffer[1].LoopBegin = 0; - setting->buffer[1].LoopLength = 0; - setting->buffer[1].LoopCount = 0; - setting->buffer[1].pContext = NULL; + setting->internal_buffer[1].Flags = 0; + setting->internal_buffer[1].AudioBytes = setting->buffer_size; + setting->internal_buffer[1].pAudioData = (byte *) malloc(setting->buffer_size * sizeof(byte)); + setting->internal_buffer[1].PlayBegin = 0; + setting->internal_buffer[1].PlayLength = 0; + setting->internal_buffer[1].LoopBegin = 0; + setting->internal_buffer[1].LoopLength = 0; + setting->internal_buffer[1].LoopCount = 0; + setting->internal_buffer[1].pContext = NULL; setting->sample_index = 0; } + inline void xaudio2_play(XAudio2Setting* setting) { if (!setting->source_voice) { // @todo Log @@ -120,14 +135,19 @@ namespace Sound { setting->playing = true; } + inline void xaudio2_free(XAudio2Setting* setting) { - if (setting->buffer[0].pAudioData) { - free((void *) setting->buffer[0].pAudioData); + if (setting->internal_buffer[0].pAudioData) { + free((void *) setting->internal_buffer[0].pAudioData); } - if (setting->buffer[1].pAudioData) { - free((void *) setting->buffer[1].pAudioData); + if (setting->internal_buffer[1].pAudioData) { + free((void *) setting->internal_buffer[1].pAudioData); + } + + if (setting->buffer) { + free((void *) setting->buffer); } if (setting->source_voice) { @@ -143,46 +163,55 @@ namespace Sound { } } - bool xaudio2_should_update(XAudio2Setting* setting) + /** + * Calculates the samples to generate for the buffer + * + * For XAudio2 we currently always fill the entire buffer size. + * For other audio APIs we maybe have to do something else + */ + inline + uint32 xaudio2_sample_buffer_size(XAudio2Setting* setting) { if (!setting->source_voice) { // @todo Log - return false; + return 0; } XAUDIO2_VOICE_STATE state; setting->source_voice->GetState(&state); if (state.BuffersQueued > 1) { - return false; + return 0; } - return true; + return setting->buffer_size; } - int16* xaudio2_return_buffer(XAudio2Setting* setting) - { - return (int16 *) setting->buffer[setting->sample_index].pAudioData; - } - - int16* xaudio2_fill_buffer(XAudio2Setting* setting, int16* buffer) - { - int16* sample_out = (int16 *) setting->buffer[setting->sample_index].pAudioData; - } - - void xaudio2_play_buffer(XAudio2Setting* setting) { + inline + void xaudio2_play_buffer(XAudio2Setting* setting, uint32 bytes_to_write) { if (!setting->source_voice) { // @todo Log return; } - setting->sample_index = (setting->sample_index + 1) % 2; + if (bytes_to_write == 0) { + return; + } - if (!SUCCEEDED(setting->source_voice->SubmitSourceBuffer(&setting->buffer[setting->sample_index]))) { + memcpy( + (void *) setting->internal_buffer[setting->sample_index].pAudioData, + setting->buffer, + bytes_to_write + ); + + if (!SUCCEEDED(setting->source_voice->SubmitSourceBuffer(&setting->internal_buffer[setting->sample_index]))) { // @todo Log return; } + + setting->sample_index = (setting->sample_index + 1) % 2; + setting->sample_buffer_size = 0; } } diff --git a/Stdlib/Types.h b/Stdlib/Types.h index 6e534ea..2826e19 100644 --- a/Stdlib/Types.h +++ b/Stdlib/Types.h @@ -23,6 +23,7 @@ typedef uint16_t uint16; typedef uint32_t uint32; typedef uint64_t uint64; +typedef uint16_t f16; typedef float f32; typedef double f64; @@ -32,4 +33,71 @@ typedef unsigned char byte; #define local_persist static #define global_persist static +#define HALF_FLOAT_SIGN_MASK 0x8000 +#define HALF_FLOAT_EXP_MASK 0x7C00 +#define HALF_FLOAT_FRAC_MASK 0x03FF + +#define HALF_FLOAT_EXP_SHIFT 10 +#define HALF_FLOAT_EXP_BIAS 15 + +#define FLOAT32_SIGN_MASK 0x80000000 +#define FLOAT32_EXP_MASK 0x7F800000 +#define FLOAT32_FRAC_MASK 0x007FFFFF + +#define FLOAT32_EXP_SHIFT 23 +#define FLOAT32_EXP_BIAS 127 + +uint16 float_to_f16(float f) { + uint32_t f_bits = *((uint32_t*)&f); + uint16_t f16_bits = 0; + + // Extract sign, exponent, and fraction from float + uint16_t sign = (f_bits & FLOAT32_SIGN_MASK) >> 16; + int32_t exponent = ((f_bits & FLOAT32_EXP_MASK) >> FLOAT32_EXP_SHIFT) - FLOAT32_EXP_BIAS + HALF_FLOAT_EXP_BIAS; + uint32_t fraction = (f_bits & FLOAT32_FRAC_MASK) >> (FLOAT32_EXP_SHIFT - HALF_FLOAT_EXP_SHIFT); + + if (exponent <= 0) { + if (exponent < -10) { + fraction = 0; + } else { + fraction = (fraction | 0x0400) >> (1 - exponent); + } + exponent = 0; + } else if (exponent >= 0x1F) { + exponent = 0x1F; + fraction = 0; + } + + f16_bits = sign | (exponent << HALF_FLOAT_EXP_SHIFT) | (fraction & HALF_FLOAT_FRAC_MASK); + + return f16_bits; +} + +float f16_to_float(f16 f) { + uint32_t f_bits = 0; + + uint32_t sign = (f & HALF_FLOAT_SIGN_MASK) << 16; + int32_t exponent = (f & HALF_FLOAT_EXP_MASK) >> HALF_FLOAT_EXP_SHIFT; + uint32_t fraction = (f & HALF_FLOAT_FRAC_MASK) << (FLOAT32_EXP_SHIFT - HALF_FLOAT_EXP_SHIFT); + + if (exponent == 0) { + if (fraction != 0) { + exponent = 1; + while ((fraction & (1 << FLOAT32_EXP_SHIFT)) == 0) { + fraction <<= 1; + exponent--; + } + fraction &= ~FLOAT32_EXP_MASK; + } + } else if (exponent == 0x1F) { + exponent = 0xFF; + } else { + exponent += FLOAT32_EXP_BIAS - HALF_FLOAT_EXP_BIAS; + } + + f_bits = sign | (exponent << FLOAT32_EXP_SHIFT) | fraction; + + return *((float*)&f_bits); +} + #endif diff --git a/Utils/TestUtils.h b/Utils/TestUtils.h index fefdd82..b7859f9 100755 --- a/Utils/TestUtils.h +++ b/Utils/TestUtils.h @@ -27,6 +27,10 @@ namespace Test { double dt = 0.0; }; + struct LogMessages { + char *messages; + }; + void update_timing_stat(TimingStat *stat) { #ifdef _WIN32 @@ -46,6 +50,12 @@ namespace Test { } } +#if DEBUG + #define UPDATE_TIMING_STAT(stat) Test::update_timing_stat(stat); +#else + #define UPDATE_TIMING_STAT(stat) +#endif + #define ASSERT_EQUALS(a, b, t1, t2) \ ({ \ if ((a) == (b)) { \