Selaa lähdekoodia

Use `qoa.c` and custom compress procedure

DeeJayLSP 8 kuukautta sitten
vanhempi
commit
afd68d785b

+ 7 - 1
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()

+ 16 - 28
scene/resources/audio_stream_wav.cpp

@@ -1142,13 +1142,13 @@ Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_fi
 		is16 = false;
 	}
 
-	Vector<uint8_t> pcm_data;
+	Vector<uint8_t> 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<float> left;
@@ -1170,9 +1170,9 @@ Ref<AudioStreamWAV> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &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> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &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> AudioStreamWAV::load_from_buffer(const Vector<uint8_t> &p_fi
 		}
 	}
 
-	Vector<uint8_t> 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<AudioStreamWAV> sample;
 	sample.instantiate();
 	sample->set_data(dst_data);

+ 28 - 3
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<float> &p_data, Vector<uint8_t> &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<int16_t> 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();
 };

+ 6 - 0
servers/audio/effects/audio_effect_record.cpp

@@ -250,6 +250,12 @@ Ref<AudioStreamWAV> 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.");
 	}

+ 2 - 2
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

+ 0 - 53
thirdparty/misc/patches/qoa-min-fix.patch

@@ -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;

+ 4 - 0
thirdparty/misc/qoa.c

@@ -0,0 +1,4 @@
+#define QOA_IMPLEMENTATION
+#define QOA_NO_STDIO
+
+#include "qoa.h"

+ 13 - 11
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;