Note
Try it out in your browser: you can do an ABX test with your own files at feelfreelinux.github.io/hqlc
A low-complexity MDCT audio codec targeting 64 kbps+* stereo at 48 kHz, designed to run comfortably on embedded targets like the ESP32 while staying competitive with established codecs in audio quality.
The codec supports 48 kHz sample rate only, with PCM16 or PCM24 input, at a fixed frame size of 512 samples (~10.67 ms).
See HQLC_DESIGN.md for a detailed write-up of the codec internals and design rationale. This readme only covers the build instructions & some benchmarks.
* While 64kbps+ is tested as a stable operating point, I recommend 128kbps+ for high quality use cases.
You can build the C encoder / decoder CLI via CMake:
mkdir build && cd build
cmake .. -DHQLC_TOOLS=ON -DCMAKE_BUILD_TYPE=Release -GNinja && ninja
This produces hqlc, the CLI encoder / decoder tool.
hqlc input.wav output.wav -b 96000 # roundtrip / encode and decode at 96kbps
hqlc -e input.flac output.hqlc -b 128000 # encode from FLAC
hqlc -e input.wav output.hqlc -t Title="..." # encoe from WAV, adding apev2 tags
hqlc -d output.hqlc decoded.wav [-s 12.5] # decode, optionally from 12.5 s
hqlc -i output.hqlc # print stream info and tags
ffmpeg -i in.m4a -ar 48000 -f wav - | hqlc -e - out.hqlc # pipe from ffmpeg
hqlc -e "music/*.flac" -b 128000 # batch, writes music/*.hqlc
The cli tool handles resampling from other sample rates automatically, using speex resampler.
The .hqlc container spec is described in HQLC_CONTAINER.md.
The codec API is in include/hqlc.h and should be self-explanatory. The container optional, included as s a separate library, include/hqlc_container.h.
For ESP-IDF integration, there's a sample component and linker script in misc/esp-benchmark/hqlc.
There’s a Python/NumPy implementation in misc/python. It can be run directly from the repo root:
python -m misc.python input.wav output.wav -b 96000
Take care that it might be out of sync with the C version, and is not optimized.
https://www.foobar2000.org/components/view/foo_input_hqlc
You can decode HQLC files in foobar2000, through a plugin maintained by Case.
Measured on an ESP32 at 240 MHz, stereo 48 kHz, ~2.1s of audio. Other codecs measured via espressif/esp_audio_codec@2.4.1.
| Codec | kbps | Enc (ms) | Dec (ms) |
|---|---|---|---|
| SBC | 357 | 202 | 161 |
| HQLC | 96 | 364 | 308 |
| AAC | 129 | 1318 | 621 |
| Opus (c1) | 96 | 1379 | 855 |
| Opus (c5) | 96 | 1787 | 917 |
| LC3 | 96 | 2195 | 710 |
SBC is included as a baseline - it's a subband codec, hence simpler and faster than any MDCT-based design, but at the cost of much worse compression efficiency (357 kbps vs 96 kbps).
The current implementation has no SIMD/NEON optimizations since ESP32 was the primary target, but the codec's straightforward structure (radix-4 FFT, lattice filters, integer rANS) should make vectorization easy on other platforms.
The encoder needs ~13.5 KB of RAM (~3.2 KB state + ~10.2 KB scratch) and the decoder ~10 KB (~2 KB state + ~8.2 KB scratch). The compiled library is about 27 KB on ESP32 (-Os), keeping the overall footprint small enough for memory-constrained targets.
All codecs at 96 kbps stereo 48 kHz, scored with ViSQOL and Zimtohrli. Both are MOS-like metrics, higher is better. Rows are sorted by ViSQOL mean.
MUSDB18 (50 tracks, real mixed-style music):
| Codec | ViSQOL | (min–max) | Zim | (min–max) |
|---|---|---|---|---|
| HQLC | 4.649 | 4.533–4.725 | 4.807 | 4.739–4.893 |
| LC3 | 4.522 | 4.051–4.705 | 4.751 | 4.638–4.867 |
| Opus | 4.506 | 4.111–4.711 | 4.850 | 4.755–4.930 |
| AAC | 4.439 | 4.082–4.726 | 4.804 | 4.612–4.946 |
| MP3 | 4.107 | 3.390–4.724 | 4.412 | 3.729–4.904 |
SQAM (70 tracks, harder recordings):
| Codec | ViSQOL | (min–max) | Zim | (min–max) |
|---|---|---|---|---|
| HQLC | 4.658 | 4.207–4.732 | 4.882 | 4.751–4.999 |
| Opus | 4.570 | 4.061–4.732 | 4.895 | 4.642–5.000 |
| LC3 | 4.516 | 3.972–4.732 | 4.778 | 2.893–4.995 |
| AAC | 4.410 | 3.637–4.732 | 4.882 | 4.688–5.000 |
| MP3 | 4.210 | 3.082–4.732 | 4.690 | 4.032–5.000 |
Both ViSQOL and Zim are just rough proxies for audio quality, and shouldn't be interpreted as HQLC being better than those codecs. All scored on v0.1.1 release.
The test-clips/ directory contains short audio clips under open licenses, intended as freely redistributable replacements for the hard-case tracks from SQAM (which can't be included in the repo). These are used for CI regression tests with ViSQOL.
bass_guitar.wav: Serolillo, CC BY 2.5, via Wikimedia Commonsflamenco_percussion.wav: from Freesound- Remaining clips: classical recordings from Musopen (public domain)
Apache 2.0. See LICENSE for details.