RAU-26
Format definition for the PureBasic module and the C and JavaScript implementations. The file signatures are RA26 and RC26. See the development history for the codec's milestones.
1. Stream header (RA26)
17 bytes, little-endian integers. Sample counts are per channel.
| Offset | Bytes | Contents |
|---|---|---|
| 0 | 4 | ASCII RA26 |
| 4 | 1 | Flags: bit 0 = 0 for 8 bits, 1 for 16 bits; bit 1 = 0 for mono, 1 for stereo; bits 2–5 = fuzzy index; bits 6–7 = 0. |
| 5 | 4 | Sample rate, U32, 1–200,000 Hz |
| 9 | 8 | Samples per channel, U64; implementation limit: 1–16,777,216 |
| 17 | variable | Frame bitstream |
Fuzzy table for indices 0–15: 0, 8, 13, 21, 34, 55, 89, 144, 233, 288, 377, 610, 850, 987, 1597, 2584. For 8-bit signals with a nonzero value, f = max(1, floor(value / 256)); otherwise f = value. The quantization step is 2*f + 1.
2. Bitstream and frames
Fields and bytes are written least significant bit first (LSB first). Frames follow without byte alignment. Each frame contains 1,024 samples per channel; the final frame length follows from the total sample count in the header. The last byte is padded with zero bits.
Every frame starts with 3 in 2 bits, followed by a 5-bit subtype:
- 1 – Constant: one signed value per channel, using the header bit depth and two’s complement. Repeat this value for every sample in the frame.
- 2 – Repeat: no payload. Copy the first N samples per channel from the previous reconstructed frame. Invalid for the first frame.
- 0 – Prediction: the fields and residuals described below.
Other frame types and subtypes are invalid. Constant and Repeat do not modify the LMS state.
Prediction frame
- Order minus 1: 2 bits, representing orders 1–4. The frame length must exceed the order.
- Skip LMS: 1 bit. Apply LMS decoding only when
f = 0and this bit is 0. - Stereo only: 2-bit mode, 0 = L/R, 1 = Mid/Side, 2 = weighted difference; 3 is invalid. Mode 2 adds a signed 5-bit alpha (−16 to 15).
- Seeds: the first
ordersamples, interleaved by channel. Mono uses the header bit depth. For stereo, channel 0 uses that depth and channel 1 always uses one extra bit, including in mode 0. - Rice block mode: 2 bits; 0/1/2/3 mean 64/128/256/512 residuals per block.
(frame length − order) * channelsresiduals, interleaved by channel. The final block may be shorter.
Rice codes
Each block starts with a 4-bit parameter k (0–15). Signed zigzag maps r ≥ 0 to z = 2*r, otherwise to z = −2*r−1. For q = floor(z / 2^k) < 48: write q one bits, one zero bit, then k remainder bits. For larger q: exactly 48 one bits, one zero bit and the complete z value in 32 bits. More than 48 one bits are invalid.
Reconstruction
First undo LMS if enabled, then multiply residuals by 2*f+1 and add the predictor. Per channel, the predictor uses previously reconstructed samples a/b/c/d, from newest to oldest:
Order 1: a Order 2: 2*a - b Order 3: 3*a - 3*b + c Order 4: 4*a - 6*b + 4*c - d
Stereo mode 1 stores M = floor((L+R)/2) and S = L−R. Reconstruct with R = M−floor(S/2), L = R+S. In mode 2, channel 0 is L and channel 1 is R−floor(alpha*L/8); add the weighted prediction to recover R.
LMS maintains a weight w and previous residual v per channel, both initially zero. For every coded residual e: r = e + floor(w*v/256). Then w = clamp(w + sign(e)*sign(v), −2048, 2047) and v = r. State persists across prediction frames. Samples, residuals and LMS state are stored as signed 32-bit values; intermediate calculations use wider values and stored overflows wrap to 32 bits.
PCM16 output clamps values to −32,768 through 32,767. Multiply 8-bit signals by 256 first. Internal frame history retains reconstructed values before this clamping.
3. Packet container (RC26)
"RC26" U32 packet length in bytes + complete RA26 stream U32 packet length in bytes + complete RA26 stream … until end of file
No packet count or offset table. At least one packet is required. All packets must share sample rate, channels, bit depth and fuzzy index. The total sample count per channel must not exceed the implementation limit. Each packet starts with fresh frame history and LMS state. Encoders use approximately one second per packet; decoders may accept other packet lengths.
4. Encoder profiles
| Profile | Target rate / bits | Fuzzy mono / stereo |
|---|---|---|
| Lossless | Source rate / source bit depth (8 or 16) | 0 / 0 |
| High | 32 kHz / 16 | 34 / 89 |
| Mid | 24 kHz / 16 | 144 / 233 |
| Low | 16 kHz / 16 | 377 / 610 |
| Crappy | 8 kHz / 8 | 850 / 1597 |
Never upsample beyond the source rate. The reference resamples the entire track before splitting packets: linear interpolation with a Q16 position and a 1:2:1 smoothing filter around each adjacent sample. Round the output length up and extend boundary samples. For 8-bit output, shift PCM16 values right by 8 bits arithmetically.
Encoders select stereo mode and predictor order by the smallest sum of absolute quantized residuals. Lossless considers orders 1–3; lossy profiles consider 1–2. They then find the cheapest Rice block mode and evaluate LMS, Constant and Repeat. Residual quantization feeds reconstructed samples back into the predictor: q = sign(r)*floor((abs(r)+floor(step/2))/step). This search strategy describes the reference encoder; a compatible decoder does not depend on it.
5. Using the source code
rau.pbi is the self-contained PureBasic module. It includes the reference codec's public API and implementation, with no Retro project dependencies. It accepts interleaved signed PCM16 and provides inspection, encoding, whole-buffer decoding and incremental decoding. File access and playback belong to the calling application. The module includes a memory-only example when compiled as the main file; it is not executed when included.
XIncludeFile "codecs/rau.pbi"
; pcm16 and samplesPerChannel are supplied by the calling application.
Define audio.RAU::RAUAudio, decoded.RAU::RAUAudio, encodedSize.Integer
audio\pcm = pcm16
audio\samples = samplesPerChannel
audio\channels = 2
audio\samplerate = 44100
audio\bits = 16
Define *encoded = RAU::EncodeRAUChunked(@audio, @encodedSize, 0, RAU::#Profile_Mid)
If *encoded
If RAU::CatchRAU(@decoded, *encoded, encodedSize\i)
; Consume decoded\pcm before releasing it.
RAU::FreeAudio(@decoded)
EndIf
FreeMemory(*encoded)
EndIf
The caller owns encoder output and frees it with FreeMemory; decoded audio is released with RAU::FreeAudio. Streaming uses BeginDecode, ReadDecode and EndDecode; keep the encoded input memory alive until decoding ends. Compile with thread safety enabled (-t) for parallel packet encoding. The distributable module is generated from the existing reference, so codec changes remain in one source.
rau.c and rau.h form a standalone C99 codec. Input and output use memory only. The caller owns successful outputs and releases them with free(). A return value of 0 means success.
#include "rau.h"
#include <stdlib.h>
rau_audio input = {pcm16, 44100, samples_per_channel, 2, 16};
uint8_t *encoded = NULL;
size_t encoded_size = 0;
if (rau_encode(&input, RAU_MID, &encoded, &encoded_size) == RAU_OK) {
rau_audio output;
if (rau_decode(encoded, encoded_size, &output) == RAU_OK)
free(output.pcm);
free(encoded);
}
rau.js exposes RAU in the browser and a CommonJS module in Node.js. The codec has no DOM or file operations. encode returns a Promise and yields execution between packets. The page handles file selection and playback. WAV import supports uncompressed 8/16-bit PCM, mono/stereo; WAV export produces PCM16.
const audio = RAU.decodeWav(await file.arrayBuffer());
const encoded = await RAU.encode(audio, 'mid');
const decoded = RAU.decode(encoded);
const wav = RAU.encodeWav(decoded);
const url = URL.createObjectURL(new Blob([wav], {type: 'audio/wav'}));
player.src = url;
// URL.revokeObjectURL(url) when the player no longer needs it.
Additional APIs: RAU.inspect(bytes) and rau_inspect(data, size, &info). Both ports support all five profiles. The browser interface offers Lossless, Mid and Crappy.