Skip to content

Commit 0eb9500

Browse files
committed
Add QOA (Quite OK Audio) as a WAV compression mode
1 parent 658e97c commit 0eb9500

File tree

5 files changed

+1045
-17
lines changed

5 files changed

+1045
-17
lines changed

editor/import/resource_importer_wav.cpp

+28-8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
2929
/**************************************************************************/
3030

31+
#define QOA_IMPLEMENTATION
32+
#define QOA_NO_STDIO
33+
3134
#include "resource_importer_wav.h"
3235

3336
#include "core/io/file_access.h"
@@ -90,7 +93,7 @@ void ResourceImporterWAV::get_import_options(const String &p_path, List<ImportOp
9093
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_mode", PROPERTY_HINT_ENUM, "Detect From WAV,Disabled,Forward,Ping-Pong,Backward", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), 0));
9194
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_begin"), 0));
9295
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "edit/loop_end"), -1));
93-
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Disabled,RAM (Ima-ADPCM)"), 0));
96+
r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "compress/mode", PROPERTY_HINT_ENUM, "Disabled,RAM (Ima-ADPCM),QOA (Quite OK Audio)"), 0));
9497
}
9598

9699
Error ResourceImporterWAV::import(const String &p_source_file, const String &p_save_path, const HashMap<StringName, Variant> &p_options, List<String> *r_platform_variants, List<String> *r_gen_files, Variant *r_metadata) {
@@ -456,13 +459,13 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
456459
is16 = false;
457460
}
458461

459-
Vector<uint8_t> dst_data;
462+
Vector<uint8_t> pcm_data;
460463
AudioStreamWAV::Format dst_format;
461464

462465
if (compression == 1) {
463466
dst_format = AudioStreamWAV::FORMAT_IMA_ADPCM;
464467
if (format_channels == 1) {
465-
_compress_ima_adpcm(data, dst_data);
468+
_compress_ima_adpcm(data, pcm_data);
466469
} else {
467470
//byte interleave
468471
Vector<float> left;
@@ -484,9 +487,9 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
484487
_compress_ima_adpcm(right, bright);
485488

486489
int dl = bleft.size();
487-
dst_data.resize(dl * 2);
490+
pcm_data.resize(dl * 2);
488491

489-
uint8_t *w = dst_data.ptrw();
492+
uint8_t *w = pcm_data.ptrw();
490493
const uint8_t *rl = bleft.ptr();
491494
const uint8_t *rr = bright.ptr();
492495

@@ -498,13 +501,13 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
498501

499502
} else {
500503
dst_format = is16 ? AudioStreamWAV::FORMAT_16_BITS : AudioStreamWAV::FORMAT_8_BITS;
501-
dst_data.resize(data.size() * (is16 ? 2 : 1));
504+
pcm_data.resize(data.size() * (is16 ? 2 : 1));
502505
{
503-
uint8_t *w = dst_data.ptrw();
506+
uint8_t *w = pcm_data.ptrw();
504507

505508
int ds = data.size();
506509
for (int i = 0; i < ds; i++) {
507-
if (is16) {
510+
if (is16 || compression == 2) {
508511
int16_t v = CLAMP(data[i] * 32768, -32768, 32767);
509512
encode_uint16(v, &w[i * 2]);
510513
} else {
@@ -515,6 +518,23 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
515518
}
516519
}
517520

521+
Vector<uint8_t> dst_data;
522+
if (compression == 2) { // QOA
523+
dst_format = AudioStreamWAV::FORMAT_QOA;
524+
qoa_desc desc;
525+
uint32_t qoa_len;
526+
527+
desc.samplerate = rate;
528+
desc.samples = frames;
529+
desc.channels = format_channels;
530+
531+
void *encoded = qoa_encode((short *)pcm_data.ptrw(), &desc, &qoa_len);
532+
dst_data.resize(qoa_len);
533+
memcpy(dst_data.ptrw(), encoded, qoa_len);
534+
} else {
535+
dst_data = pcm_data;
536+
}
537+
518538
Ref<AudioStreamWAV> sample;
519539
sample.instantiate();
520540
sample->set_data(dst_data);

scene/resources/audio_stream_wav.cpp

+30-8
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@
2828
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
2929
/**************************************************************************/
3030

31+
#define QOA_IMPLEMENTATION
32+
#define QOA_NO_STDIO
33+
3134
#include "audio_stream_wav.h"
3235

3336
#include "core/io/file_access.h"
@@ -235,6 +238,7 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
235238
len /= 1;
236239
break;
237240
case AudioStreamWAV::FORMAT_16_BITS:
241+
case AudioStreamWAV::FORMAT_QOA:
238242
len /= 2;
239243
break;
240244
case AudioStreamWAV::FORMAT_IMA_ADPCM:
@@ -373,7 +377,8 @@ int AudioStreamPlaybackWAV::mix(AudioFrame *p_buffer, float p_rate_scale, int p_
373377
do_resample<int8_t, false, false>((int8_t *)data, dst_buff, offset, increment, target, ima_adpcm);
374378
}
375379
} break;
376-
case AudioStreamWAV::FORMAT_16_BITS: {
380+
case AudioStreamWAV::FORMAT_16_BITS:
381+
case AudioStreamWAV::FORMAT_QOA: {
377382
if (is_stereo) {
378383
do_resample<int16_t, true, false>((int16_t *)data, dst_buff, offset, increment, target, ima_adpcm);
379384
} else {
@@ -470,6 +475,7 @@ double AudioStreamWAV::get_length() const {
470475
len /= 1;
471476
break;
472477
case AudioStreamWAV::FORMAT_16_BITS:
478+
case AudioStreamWAV::FORMAT_QOA:
473479
len /= 2;
474480
break;
475481
case AudioStreamWAV::FORMAT_IMA_ADPCM:
@@ -499,12 +505,25 @@ void AudioStreamWAV::set_data(const Vector<uint8_t> &p_data) {
499505
int datalen = p_data.size();
500506
if (datalen) {
501507
const uint8_t *r = p_data.ptr();
502-
int alloc_len = datalen + DATA_PAD * 2;
503-
data = memalloc(alloc_len); //alloc with some padding for interpolation
504-
memset(data, 0, alloc_len);
505-
uint8_t *dataptr = (uint8_t *)data;
506-
memcpy(dataptr + DATA_PAD, r, datalen);
507-
data_bytes = datalen;
508+
qoa_desc qoad;
509+
int alloc_len = 0;
510+
if (qoa_decode_header(r, datalen, &qoad) == 8) { // QOA
511+
int16_t *sample_data = qoa_decode(r, datalen, &qoad);
512+
int qoa_datalen = qoad.samples * qoad.channels * sizeof(int16_t);
513+
alloc_len = qoa_datalen + DATA_PAD * 2;
514+
data = memalloc(alloc_len);
515+
memset(data, 0, alloc_len);
516+
int16_t *dataptr = (int16_t *)data;
517+
memcpy(dataptr + DATA_PAD, sample_data, qoa_datalen);
518+
data_bytes = qoa_datalen;
519+
} else {
520+
alloc_len = datalen + DATA_PAD * 2;
521+
data = memalloc(alloc_len); //alloc with some padding for interpolation
522+
memset(data, 0, alloc_len);
523+
uint8_t *dataptr = (uint8_t *)data;
524+
memcpy(dataptr + DATA_PAD, r, datalen);
525+
data_bytes = datalen;
526+
}
508527
}
509528

510529
AudioServer::get_singleton()->unlock();
@@ -548,6 +567,7 @@ Error AudioStreamWAV::save_to_wav(const String &p_path) {
548567
byte_pr_sample = 1;
549568
break;
550569
case AudioStreamWAV::FORMAT_16_BITS:
570+
case AudioStreamWAV::FORMAT_QOA:
551571
byte_pr_sample = 2;
552572
break;
553573
case AudioStreamWAV::FORMAT_IMA_ADPCM:
@@ -590,6 +610,7 @@ Error AudioStreamWAV::save_to_wav(const String &p_path) {
590610
}
591611
break;
592612
case AudioStreamWAV::FORMAT_16_BITS:
613+
case AudioStreamWAV::FORMAT_QOA:
593614
for (unsigned int i = 0; i < data_bytes / 2; i++) {
594615
uint16_t data_point = decode_uint16(&read_data[i * 2]);
595616
file->store_16(data_point);
@@ -639,7 +660,7 @@ void AudioStreamWAV::_bind_methods() {
639660
ClassDB::bind_method(D_METHOD("save_to_wav", "path"), &AudioStreamWAV::save_to_wav);
640661

641662
ADD_PROPERTY(PropertyInfo(Variant::PACKED_BYTE_ARRAY, "data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_data", "get_data");
642-
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM"), "set_format", "get_format");
663+
ADD_PROPERTY(PropertyInfo(Variant::INT, "format", PROPERTY_HINT_ENUM, "8-Bit,16-Bit,IMA-ADPCM,QOA"), "set_format", "get_format");
643664
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_mode", PROPERTY_HINT_ENUM, "Disabled,Forward,Ping-Pong,Backward"), "set_loop_mode", "get_loop_mode");
644665
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_begin"), "set_loop_begin", "get_loop_begin");
645666
ADD_PROPERTY(PropertyInfo(Variant::INT, "loop_end"), "set_loop_end", "get_loop_end");
@@ -649,6 +670,7 @@ void AudioStreamWAV::_bind_methods() {
649670
BIND_ENUM_CONSTANT(FORMAT_8_BITS);
650671
BIND_ENUM_CONSTANT(FORMAT_16_BITS);
651672
BIND_ENUM_CONSTANT(FORMAT_IMA_ADPCM);
673+
BIND_ENUM_CONSTANT(FORMAT_QOA);
652674

653675
BIND_ENUM_CONSTANT(LOOP_DISABLED);
654676
BIND_ENUM_CONSTANT(LOOP_FORWARD);

scene/resources/audio_stream_wav.h

+3-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#define AUDIO_STREAM_WAV_H
3333

3434
#include "servers/audio/audio_stream.h"
35+
#include "thirdparty/misc/qoa.h"
3536

3637
class AudioStreamWAV;
3738

@@ -88,7 +89,8 @@ class AudioStreamWAV : public AudioStream {
8889
enum Format {
8990
FORMAT_8_BITS,
9091
FORMAT_16_BITS,
91-
FORMAT_IMA_ADPCM
92+
FORMAT_IMA_ADPCM,
93+
FORMAT_QOA
9294
};
9395

9496
// Keep the ResourceImporterWAV `edit/loop_mode` enum hint in sync with these options.

0 commit comments

Comments
 (0)