diff --git a/scene/resources/SCsub b/scene/resources/SCsub index 46f6251b913..ae2a9b8c3a7 100644 --- a/scene/resources/SCsub +++ b/scene/resources/SCsub @@ -7,7 +7,13 @@ Import("env") thirdparty_obj = [] -thirdparty_sources = "#thirdparty/misc/mikktspace.c" +thirdparty_dir = "#thirdparty/misc/" +thirdparty_sources = [ + "mikktspace.c", + "qoa.c" +] + +thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources] env_thirdparty = env.Clone() env_thirdparty.disable_warnings() diff --git a/scene/resources/audio_stream_wav.cpp b/scene/resources/audio_stream_wav.cpp index cea9af729eb..15e3f0f806b 100644 --- a/scene/resources/audio_stream_wav.cpp +++ b/scene/resources/audio_stream_wav.cpp @@ -1142,13 +1142,13 @@ Ref AudioStreamWAV::load_from_buffer(const Vector &p_fi is16 = false; } - Vector pcm_data; + Vector dst_data; AudioStreamWAV::Format dst_format; if (compression == 1) { dst_format = AudioStreamWAV::FORMAT_IMA_ADPCM; if (format_channels == 1) { - _compress_ima_adpcm(data, pcm_data); + _compress_ima_adpcm(data, dst_data); } else { //byte interleave Vector left; @@ -1170,9 +1170,9 @@ Ref AudioStreamWAV::load_from_buffer(const Vector &p_fi _compress_ima_adpcm(right, bright); int dl = bleft.size(); - pcm_data.resize(dl * 2); + dst_data.resize(dl * 2); - uint8_t *w = pcm_data.ptrw(); + uint8_t *w = dst_data.ptrw(); const uint8_t *rl = bleft.ptr(); const uint8_t *rr = bright.ptr(); @@ -1182,16 +1182,24 @@ Ref AudioStreamWAV::load_from_buffer(const Vector &p_fi } } + } else if (compression == 2) { + dst_format = AudioStreamWAV::FORMAT_QOA; + + qoa_desc desc = {}; + desc.samplerate = rate; + desc.samples = frames; + desc.channels = format_channels; + + _compress_qoa(data, dst_data, &desc); } else { dst_format = is16 ? AudioStreamWAV::FORMAT_16_BITS : AudioStreamWAV::FORMAT_8_BITS; - bool enforce16 = is16 || compression == 2; - pcm_data.resize(data.size() * (enforce16 ? 2 : 1)); + dst_data.resize(data.size() * (is16 ? 2 : 1)); { - uint8_t *w = pcm_data.ptrw(); + uint8_t *w = dst_data.ptrw(); int ds = data.size(); for (int i = 0; i < ds; i++) { - if (enforce16) { + if (is16) { int16_t v = CLAMP(data[i] * 32768, -32768, 32767); encode_uint16(v, &w[i * 2]); } else { @@ -1202,26 +1210,6 @@ Ref AudioStreamWAV::load_from_buffer(const Vector &p_fi } } - Vector dst_data; - if (compression == 2) { - dst_format = AudioStreamWAV::FORMAT_QOA; - qoa_desc desc = {}; - uint32_t qoa_len = 0; - - desc.samplerate = rate; - desc.samples = frames; - desc.channels = format_channels; - - void *encoded = qoa_encode((short *)pcm_data.ptr(), &desc, &qoa_len); - if (encoded) { - dst_data.resize(qoa_len); - memcpy(dst_data.ptrw(), encoded, qoa_len); - QOA_FREE(encoded); - } - } else { - dst_data = pcm_data; - } - Ref sample; sample.instantiate(); sample->set_data(dst_data); diff --git a/scene/resources/audio_stream_wav.h b/scene/resources/audio_stream_wav.h index 269ab1e05f1..e36d33cfa93 100644 --- a/scene/resources/audio_stream_wav.h +++ b/scene/resources/audio_stream_wav.h @@ -31,9 +31,6 @@ #ifndef AUDIO_STREAM_WAV_H #define AUDIO_STREAM_WAV_H -#define QOA_IMPLEMENTATION -#define QOA_NO_STDIO - #include "servers/audio/audio_stream.h" #include "thirdparty/misc/qoa.h" @@ -273,6 +270,34 @@ public: } } + static void _compress_qoa(const Vector &p_data, Vector &dst_data, qoa_desc *p_desc) { + uint32_t frames_len = (p_desc->samples + QOA_FRAME_LEN - 1) / QOA_FRAME_LEN * (QOA_LMS_LEN * 4 * p_desc->channels + 8); + uint32_t slices_len = (p_desc->samples + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN * 8 * p_desc->channels; + dst_data.resize(8 + frames_len + slices_len); + + for (uint32_t c = 0; c < p_desc->channels; c++) { + memset(p_desc->lms[c].history, 0, sizeof(p_desc->lms[c].history)); + memset(p_desc->lms[c].weights, 0, sizeof(p_desc->lms[c].weights)); + p_desc->lms[c].weights[2] = -(1 << 13); + p_desc->lms[c].weights[3] = (1 << 14); + } + + LocalVector data16; + data16.resize(QOA_FRAME_LEN * p_desc->channels); + + uint8_t *dst_ptr = dst_data.ptrw(); + dst_ptr += qoa_encode_header(p_desc, dst_data.ptrw()); + + uint32_t frame_len = QOA_FRAME_LEN; + for (uint32_t s = 0; s < p_desc->samples; s += frame_len) { + frame_len = MIN(frame_len, p_desc->samples - s); + for (uint32_t i = 0; i < frame_len * p_desc->channels; i++) { + data16[i] = CLAMP(p_data[s * p_desc->channels + i] * 32767.0, -32768, 32767); + } + dst_ptr += qoa_encode_frame(data16.ptr(), p_desc, frame_len, dst_ptr); + } + } + AudioStreamWAV(); ~AudioStreamWAV(); }; diff --git a/servers/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp index f82a6fa3afb..b6bc5d00c4f 100644 --- a/servers/audio/effects/audio_effect_record.cpp +++ b/servers/audio/effects/audio_effect_record.cpp @@ -250,6 +250,12 @@ Ref AudioEffectRecord::get_recording() const { w[i * 2 + 0] = rl[i]; w[i * 2 + 1] = rr[i]; } + } else if (dst_format == AudioStreamWAV::FORMAT_QOA) { + qoa_desc desc = {}; + desc.samples = current_instance->recording_data.size() / 2; + desc.samplerate = AudioServer::get_singleton()->get_mix_rate(); + desc.channels = 2; + AudioStreamWAV::_compress_qoa(current_instance->recording_data, dst_data, &desc); } else { ERR_PRINT("Format not implemented."); } diff --git a/thirdparty/README.md b/thirdparty/README.md index 1e11c6c9725..7031a9ddf33 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -716,8 +716,8 @@ Collection of single-file libraries used in Godot components. * License: MIT - `qoa.h` * Upstream: https://github.com/phoboslab/qoa - * Version: git (e0c69447d4d3945c3c92ac1751e4cdc9803a8303, 2024) - * Modifications: Added a few modifiers to comply with C++ nature. + * Version: git (a2d927f8ce78a85e903676a33e0f956e53b89f7d, 2024) + * Modifications: Added implementation through `qoa.c`. * License: MIT - `r128.{c,h}` * Upstream: https://github.com/fahickman/r128 diff --git a/thirdparty/misc/patches/qoa-min-fix.patch b/thirdparty/misc/patches/qoa-min-fix.patch deleted file mode 100644 index 6008b5f8bcd..00000000000 --- a/thirdparty/misc/patches/qoa-min-fix.patch +++ /dev/null @@ -1,53 +0,0 @@ -diff --git a/qoa.h b/qoa.h -index cfed266bef..23612bb0bf 100644 ---- a/qoa.h -+++ b/qoa.h -@@ -140,14 +140,14 @@ typedef struct { - #endif - } qoa_desc; - --unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes); --unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes); --void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len); -+inline unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes); -+inline unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes); -+inline void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len); - --unsigned int qoa_max_frame_size(qoa_desc *qoa); --unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa); --unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len); --short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file); -+inline unsigned int qoa_max_frame_size(qoa_desc *qoa); -+inline unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa); -+inline unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len); -+inline short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file); - - #ifndef QOA_NO_STDIO - -@@ -395,7 +395,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned - qoa_uint64_t best_error = -1; - #endif - qoa_uint64_t best_slice = 0; -- qoa_lms_t best_lms; -+ qoa_lms_t best_lms = {}; - int best_scalefactor = 0; - - for (int sfi = 0; sfi < 16; sfi++) { -@@ -500,7 +500,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) - num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */ - num_slices * 8 * qoa->channels; /* 8 byte slices */ - -- unsigned char *bytes = QOA_MALLOC(encoded_size); -+ unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size); - - for (unsigned int c = 0; c < qoa->channels; c++) { - /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the -@@ -655,7 +655,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) { - - /* Calculate the required size of the sample buffer and allocate */ - int total_samples = qoa->samples * qoa->channels; -- short *sample_data = QOA_MALLOC(total_samples * sizeof(short)); -+ short *sample_data = (short *)QOA_MALLOC(total_samples * sizeof(short)); - - unsigned int sample_index = 0; - unsigned int frame_len; diff --git a/thirdparty/misc/qoa.c b/thirdparty/misc/qoa.c new file mode 100644 index 00000000000..7f7d366dfa1 --- /dev/null +++ b/thirdparty/misc/qoa.c @@ -0,0 +1,4 @@ +#define QOA_IMPLEMENTATION +#define QOA_NO_STDIO + +#include "qoa.h" diff --git a/thirdparty/misc/qoa.h b/thirdparty/misc/qoa.h index 23612bb0bfb..f0f44214d81 100644 --- a/thirdparty/misc/qoa.h +++ b/thirdparty/misc/qoa.h @@ -140,14 +140,14 @@ typedef struct { #endif } qoa_desc; -inline unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes); -inline unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes); -inline void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len); +unsigned int qoa_encode_header(qoa_desc *qoa, unsigned char *bytes); +unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int frame_len, unsigned char *bytes); +void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len); -inline unsigned int qoa_max_frame_size(qoa_desc *qoa); -inline unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa); -inline unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len); -inline short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file); +unsigned int qoa_max_frame_size(qoa_desc *qoa); +unsigned int qoa_decode_header(const unsigned char *bytes, int size, qoa_desc *qoa); +unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa_desc *qoa, short *sample_data, unsigned int *frame_len); +short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *file); #ifndef QOA_NO_STDIO @@ -395,7 +395,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned qoa_uint64_t best_error = -1; #endif qoa_uint64_t best_slice = 0; - qoa_lms_t best_lms = {}; + qoa_lms_t best_lms; int best_scalefactor = 0; for (int sfi = 0; sfi < 16; sfi++) { @@ -500,7 +500,7 @@ void *qoa_encode(const short *sample_data, qoa_desc *qoa, unsigned int *out_len) num_frames * QOA_LMS_LEN * 4 * qoa->channels + /* 4 * 4 bytes lms state per channel */ num_slices * 8 * qoa->channels; /* 8 byte slices */ - unsigned char *bytes = (unsigned char *)QOA_MALLOC(encoded_size); + unsigned char *bytes = QOA_MALLOC(encoded_size); for (unsigned int c = 0; c < qoa->channels; c++) { /* Set the initial LMS weights to {0, 0, -1, 2}. This helps with the @@ -626,12 +626,14 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa qoa_uint64_t slice = qoa_read_u64(bytes, &p); int scalefactor = (slice >> 60) & 0xf; + slice <<= 4; + int slice_start = sample_index * channels + c; int slice_end = qoa_clamp(sample_index + QOA_SLICE_LEN, 0, samples) * channels + c; for (int si = slice_start; si < slice_end; si += channels) { int predicted = qoa_lms_predict(&qoa->lms[c]); - int quantized = (slice >> 57) & 0x7; + int quantized = (slice >> 61) & 0x7; int dequantized = qoa_dequant_tab[scalefactor][quantized]; int reconstructed = qoa_clamp_s16(predicted + dequantized); @@ -655,7 +657,7 @@ short *qoa_decode(const unsigned char *bytes, int size, qoa_desc *qoa) { /* Calculate the required size of the sample buffer and allocate */ int total_samples = qoa->samples * qoa->channels; - short *sample_data = (short *)QOA_MALLOC(total_samples * sizeof(short)); + short *sample_data = QOA_MALLOC(total_samples * sizeof(short)); unsigned int sample_index = 0; unsigned int frame_len;