Quiblet turns any file into a chord progression and back again
Quiblet is a Rust codec that maps 13-bit chunks through a lookup table to tones, plays ten at once, and decodes with one FFT per frame. Why every bit length survives, and why the default file is 250 kHz.
- Published
- 3 min
- To read
- Other builds
- Project
Quiblet is a codec between binary data and audio, written in Rust. A message goes in as text, a file, an integer or raw bits, and comes out as a WAV file of tones. The WAV decodes back to exactly the message. Its design is small enough to describe completely, and each choice in it has a reason.
A frequency is a slot and a payload
A frequency in Quiblet is a five digit number from 00000 to 99999. The digit count is a setting. The first digit is the slot, from 0 to 9. The last four digits are the payload.
The slot says where in the frame a chunk of data belongs, and the payload says what the chunk is. A tone at 10201 Hz is slot 1 with payload 0201.
From bits to tones
- The message becomes a bit string
- Integers are written as their binary form, with zero as
0. Text and files go in as their bytes. Raw bits can be given directly. - It is padded to whole chunks
- A single 1 bit and then zeros are added until the length is a whole number of 13 bit chunks. The decoder strips the trailing zeros and that one 1. This is how any bit length survives the round trip, leading zeros included, which would otherwise be lost.
- Each chunk goes through a lookup table
- A 13 bit chunk has 8,192 possible values, and a four digit payload has 10,000, so every chunk fits. In sequential mode, which is the written one,
0000000000001maps to0001and0000000000010to0002. Spread mode stretches the 8,192 values over the whole range from 0000 to 9999. - Position becomes the slot
- The chunk's place in its frame gives it a slot, which becomes the prefix digit. Ten tones play at once, so a frame carries 130 bits.
A frame such as 10201, 01129, 29222, 34551 and 41214 is a chord whose order does not matter. The decoder sorts the tones by prefix and replaces each payload with its chunk. Stereo mode carries ten tones on each channel, with the left channel first in the bit order, for 260 bits per frame.
Why the tones do not leak into each other
Every frame is an exact number of samples, and every tone sits on a bin of the Fourier transform of that length. A tone that fits the window exactly puts its energy in one bin and none in the bins beside it. The decoder takes one FFT per channel per frame and reads the loudest bin in each slot's band. A slot with no tone is silent, and that is how the last frame ends.
The default file cannot be played
At 1 Hz per step the top tone is 99,999 Hz, so the default WAV is written at a 250 kHz sample rate. It is correct and decodes perfectly, and it is beyond what a speaker can reproduce. The Audible scale button changes the step to 0.2 Hz and the frame to 5 seconds, which keeps every tone under 20 kHz.
Frequency 00000 is 0 Hz, a constant level, and audio chains that are AC coupled drop it.
Using it
quiblet encode --text "hi" -o hi.wav
quiblet decode hi.wav
quiblet lut
With no arguments it opens a window. A WAV that Quiblet writes carries its settings in an extra QLET chunk, so it decodes without being told them. Files from elsewhere are read with the settings you pass.
Limits
- The WAV must start on frame 0. There is no search for the start, so a recording with leading silence will not decode.
- One WAV holds up to 4 GiB, and the in-memory buffer is capped at 1 GiB of 32 bit float samples.
- The digit count must be between 2 and 6.
These limits follow from a design with no synchronisation. Quiblet is a codec, and getting a signal through a speaker and a microphone is a separate problem. The Other builds page lists Quiblet with the rest of the smaller projects, with downloads for 64 bit Windows.
Written by Caisson AI. Questions or corrections go to hello@caissonai.co.