Skip to content

Instantly share code, notes, and snippets.

@jakubtomsu
Created February 26, 2026 16:08
Show Gist options
  • Select an option

  • Save jakubtomsu/ec8675231e6cf19e04bfaa20cc7e19f8 to your computer and use it in GitHub Desktop.

Select an option

Save jakubtomsu/ec8675231e6cf19e04bfaa20cc7e19f8 to your computer and use it in GitHub Desktop.
Odin QOA encoding/decoding test https://github.com/phoboslab/qoa
// Bindings for qoa.h
package cqoa
foreign import lib "qoa.lib"
RECORD_TOTAL_ERROR :: true
MIN_FILESIZE :: 16
MAX_CHANNELS :: 8
SLICE_LEN :: 20
SLICES_PER_FRAME :: 256
FRAME_LEN :: (SLICES_PER_FRAME * SLICE_LEN)
LMS_LEN :: 4
MAGIC :: 0x716f6166 /* 'qoaf' */
LMS :: struct {
history: [LMS_LEN]i32,
weights: [LMS_LEN]i32,
}
Desc :: struct {
channels: u32,
samplerate: u32,
samples: u32,
lms: [MAX_CHANNELS]LMS,
error: (f64 when RECORD_TOTAL_ERROR else struct {}),
}
@(link_prefix = "qoa_", default_calling_convention = "c")
foreign lib {
max_frame_size :: proc(qoa: ^Desc) -> u32 ---
encode_header :: proc(qoa: ^Desc, bytes: [^]byte) -> u32 ---
encode_frame :: proc(sample_data: [^]i16, qoa: ^Desc, frame_len: u32, bytes: [^]byte) -> u32 ---
encode :: proc(sample_data: [^]i16, qoa: ^Desc, out_len: ^u32) -> [^]byte ---
decode_header :: proc(bytes: [^]byte, size: i32, qoa: ^Desc) -> u32 ---
decode_frame :: proc(bytes: [^]byte, size: u32, qoa: ^Desc, sample_data: [^]i16, frame_len: ^u32) -> u32 ---
decode :: proc(bytes: [^]byte, size: i32, file: ^Desc) -> [^]u16 ---
}
package qoa
import "core:slice"
import "base:runtime"
import "core:testing"
import "core:log"
import "core:strings"
import "../wav"
import "cqoa"
// Try a different linker if the files bloat compile times too much.
// The samples are from:
// https://qoaformat.org/samples/
// Unzip and put them into a qoa_test_samples directory.
@(private)
_wav_data := [][]runtime.Load_Directory_File{
// #load_directory("qoa_test_samples/oculus_audio_pack"),
// #load_directory("qoa_test_samples/sqam"),
#load_directory("qoa_test_samples/bandcamp"),
}
@(private)
_qoa_data := [][]runtime.Load_Directory_File{
// #load_directory("qoa_test_samples/oculus_audio_pack/qoa"),
// #load_directory("qoa_test_samples/sqam/qoa"),
#load_directory("qoa_test_samples/bandcamp/qoa"),
}
@(private)
_qoa_wav_data := [][]runtime.Load_Directory_File{
// #load_directory("qoa_test_samples/oculus_audio_pack/qoa_wav"),
// #load_directory("qoa_test_samples/sqam/qoa"),
#load_directory("qoa_test_samples/bandcamp/qoa"),
}
@(test)
file_sanity_test :: proc(t: ^testing.T) {
testing.expect(t, len(_wav_data) == len(_qoa_data))
testing.expect(t, len(_wav_data) == len(_qoa_wav_data))
for dir, i in _wav_data {
testing.expect(t, len(dir) == len(_qoa_data[i]))
testing.expect(t, len(dir) == len(_qoa_wav_data[i]))
for file, j in dir {
testing.expect(t, len(strings.common_prefix(file.name, _qoa_data[i][j].name)) > 5)
testing.expect(t, len(strings.common_prefix(file.name, _qoa_wav_data[i][j].name)) > 5)
}
}
}
@(test)
encode_test_cqoa :: proc(t: ^testing.T) {
for dir, i in _wav_data {
for file, j in dir {
defer free_all(context.temp_allocator)
wav_header, wav_data, wav_ok := wav.decode_header(file.data)
testing.expect(t, wav_ok)
assert(wav_header.format.bits_per_sample == 16)
samples := wav.reinterpret_bytes(i16, wav_data)
num_samples := len(samples) / int(wav_header.format.num_channels)
// log.infof("Encode %s: %i samples, %i channels, %ihz", file.name, num_samples, wav_header.format.num_channels, wav_header.format.sample_rate)
desc := Desc{
samplerate = wav_header.format.sample_rate,
channels = u32(wav_header.format.num_channels),
}
qoa_enc, qoa_ok := encode(&desc, samples, context.temp_allocator)
testing.expect(t, qoa_ok)
cqoa_len: u32
cqoa_buf := cqoa.encode(&samples[0], &cqoa.Desc{
samplerate = wav_header.format.sample_rate,
samples = u32(num_samples),
channels = u32(wav_header.format.num_channels),
}, &cqoa_len)
cqoa_enc := cqoa_buf[:cqoa_len]
testing.expect(t, len(qoa_enc) == len(cqoa_enc))
testing.expectf(t, slice.equal(qoa_enc, cqoa_enc), "%s: data mismatch", file.name)
}
}
}
// @(test)
// test_cqoa_encode_vs_test_sample_data :: proc(t: ^testing.T) {
// for dir, i in _wav_data {
// for file, j in dir {
// defer free_all(context.temp_allocator)
// wav_header, wav_data, wav_ok := wav.decode_header(file.data)
// testing.expect(t, wav_ok)
// assert(wav_header.format.bits_per_sample == 16)
// samples := wav.reinterpret_bytes(i16, wav_data)
// num_samples := len(samples) / int(wav_header.format.num_channels)
// // log.infof("CQOA Encode %s: %i samples, %i channels, %ihz", file.name, num_samples, wav_header.format.num_channels, wav_header.format.sample_rate)
// cqoa_len: u32
// cqoa_buf := cqoa.encode(&samples[0], &cqoa.Desc{
// samplerate = wav_header.format.sample_rate,
// samples = u32(num_samples),
// channels = u32(wav_header.format.num_channels),
// }, &cqoa_len)
// cqoa_enc := cqoa_buf[:cqoa_len]
// qoa_enc := _qoa_data[i][j].data
// testing.expect(t, len(qoa_enc) == len(cqoa_enc))
// testing.expectf(t, slice.equal(qoa_enc, cqoa_enc), "%s: data mismatch", file.name)
// }
// }
// }
// @(test)
// encode_test :: proc(t: ^testing.T) {
// for dir, i in _wav_data {
// for file, j in dir {
// defer free_all(context.temp_allocator)
// wav_header, wav_data, wav_ok := wav.decode_header(file.data)
// testing.expect(t, wav_ok)
// assert(wav_header.format.bits_per_sample == 16)
// samples := wav.reinterpret_bytes(i16, wav_data)
// log.infof("Encode %s: %i samples, %i channels, %ihz", file.name, len(samples) / int(wav_header.format.num_channels), wav_header.format.num_channels, wav_header.format.sample_rate)
// desc := Desc{
// samplerate = wav_header.format.sample_rate,
// channels = u32(wav_header.format.num_channels),
// }
// qoa_enc, qoa_ok := encode(&desc, samples, context.temp_allocator)
// testing.expect(t, qoa_ok)
// qoa_src := _qoa_data[i][j].data
// testing.expect(t, len(qoa_enc) == len(qoa_src))
// if !testing.expectf(t, slice.equal(qoa_enc, qoa_src), "%s: data mismatch", file.name) {
// n := 0
// for i in 0..<len(qoa_enc) {
// if qoa_enc[i] != qoa_src[i] {
// n += 1
// // log.infof("%s: %i: %i != %i", file.name, i, qoa_enc[i], qoa_src[i])
// }
// }
// log.errorf("\tnum different samples: %i", n)
// }
// }
// }
// }
@(test)
decode_test :: proc(t: ^testing.T) {
for dir, i in _wav_data {
for file, j in dir {
defer free_all(context.temp_allocator)
wav_header, wav_data, wav_ok := wav.decode_header(file.data)
testing.expect(t, wav_ok)
assert(wav_header.format.bits_per_sample == 16)
orig_samples := wav.reinterpret_bytes(i16, wav_data)
qoa_src := _qoa_data[i][j].data
qoa_wav_src := _qoa_wav_data[i][j].data
// log.infof("Decode %s: %i samples, %i channels, %ihz", file.name, len(orig_samples) / int(wav_header.format.num_channels), wav_header.format.num_channels, wav_header.format.sample_rate)
qoa_wav_header, qoa_wav_data, qoa_wav_ok := wav.decode_header(qoa_wav_src)
testing.expect(t, qoa_wav_ok)
assert(qoa_wav_header.format.bits_per_sample == 16)
expected_samples := wav.reinterpret_bytes(i16, qoa_wav_data)
desc, decoded_samples, decode_ok := decode(qoa_src, context.temp_allocator)
testing.expect(t, decode_ok)
testing.expect(t, desc.samples > 0)
testing.expect(t, desc.channels > 0)
testing.expect(t, desc.samplerate > 0)
testing.expect(t, desc.channels == u32(wav_header.format.num_channels))
testing.expect(t, desc.channels == u32(qoa_wav_header.format.num_channels))
testing.expect(t, desc.samplerate == wav_header.format.sample_rate)
testing.expect(t, desc.samplerate == qoa_wav_header.format.sample_rate)
testing.expect(t, len(decoded_samples) == len(orig_samples))
testing.expect(t, len(decoded_samples) == len(expected_samples))
testing.expect(t, slice.equal(decoded_samples, expected_samples))
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment