Compressed Delta (`cMdT`) Format
Binary container for regularly-sampled signed-integer multichannel signal data. A file is a fixed 28-byte header followed by an encoded samples block.
The samples block goes through two optional, independent transforms before storage:
- Coding — per-sample delta encoding (1st or 2nd order) followed by zig-zag, mapping signed deltas onto small unsigned integers.
- Compression — generic compression (Zstandard or zlib) over the coded bytes.
Both stages are selected per file via header fields.
Wire format
Section titled “Wire format”All multi-byte fields are little-endian. The header is packed (no padding) and must be exactly 28 bytes:
| Off | Size | Field | C type | Constraint |
|---|---|---|---|---|
| 0 | 4 | magic |
uint32 |
0x54644D63 (ASCII c M d T) |
| 4 | 8 | payload_size |
uint64 |
size of samples block in bytes |
| 12 | 1 | total_channels |
uint8 |
≥ 1 |
| 13 | 4 | total_samples |
uint32 |
≥ 1 per channel; ≥ 2 when coding = 2 |
| 17 | 8 | sample_rate |
float64 |
finite, > 0 |
| 25 | 1 | bits_per_sample |
uint8 |
8, 16, 24, or 32 |
| 26 | 1 | coding |
uint8 |
see Coding |
| 27 | 1 | compression |
uint8 |
see Compression |
The samples block immediately follows the header and spans payload_size bytes.
Sample layout
Section titled “Sample layout”Samples are stored channel-major (non-interleaved). After full decoding — reversing compression then reversing coding — the layout is:
c0[0] c0[1] ... c0[M-1] c1[0] c1[1] ... c1[M-1] ... c(N-1)[M-1]where N = total_channels and M = total_samples.
Each sample is a signed integer of bits_per_sample bits, stored little-endian.
24-bit samples occupy three consecutive bytes; readers sign-extend from bit 23
when widening to 32 bits.
The fully-decoded payload size equals
total_channels × total_samples × (bits_per_sample / 8) bytes.
Coding
Section titled “Coding”The coding field selects per-sample preprocessing applied independently per
channel before cross-channel concatenation.
| Value | Symbol | Per-channel transform |
|---|---|---|
| 0 | TWOS_COMPLEMENT |
none — samples stored as raw signed ints |
| 1 | DELTA_ZIGZAG |
1st-order delta, then zig-zag |
| 2 | DOUBLE_DELTA_ZIGZAG |
2nd-order delta, then zig-zag |
Storage is in bits_per_sample-wide slots in all three modes; the bit-width
never changes.
Zig-zag
Section titled “Zig-zag”Zig-zag maps signed integers onto unsigned integers placing small absolute values near zero. For an N-bit signed integer:
encode(n) = (n << 1) ^ (n >> (N - 1)) // N = 8/16/24/32, arithmetic right shiftdecode(z) = (z >> 1) ^ -(z & 1) // z is unsigned, logical right shiftDELTA_ZIGZAG (1st order)
Section titled “DELTA_ZIGZAG (1st order)”For a channel with raw samples x[0..M-1], the encoder produces deltas
d[0..M-1]:
d[0] = x[0]d[i] = x[i] - x[i-1] for i ≥ 1Then writes y[i] = zigzag(d[i]) to the output slot for sample i. The first
sample is the raw seed; subsequent samples are first-order differences.
DOUBLE_DELTA_ZIGZAG (2nd order)
Section titled “DOUBLE_DELTA_ZIGZAG (2nd order)”Two raw seed samples, then second-order differences:
d[0] = x[0]d[1] = x[1]d[i] = (x[i] - x[i-1]) - (x[i-1] - x[i-2]) for i ≥ 2Then writes y[i] = zigzag(d[i]) for each i. Note both d[0] and d[1]
are raw samples — not first-order deltas — because the decoder needs two
consecutive raw samples to seed the second-order recurrence. Consequently
coding = 2 requires total_samples ≥ 2.
Sample arithmetic is in bits_per_sample-wide signed two’s-complement; overflow
wraps modulo 2^bits_per_sample and the inverse transform on decode recovers
the original x[i].
Compression
Section titled “Compression”The compression field selects how the coded bytes are stored.
| Value | Symbol | Storage |
|---|---|---|
| 0 | NO_COMPRESSION |
coded bytes stored verbatim |
| 1 | ZSTD |
coded bytes compressed with Zstandard |
| 2 | ZLIB |
coded bytes compressed with zlib (deflate) |
For NO_COMPRESSION, payload_size equals the raw size
total_channels × total_samples × (bits_per_sample / 8).
For ZSTD and ZLIB, payload_size is the compressor’s output size; the
decompressor must yield exactly the raw size.
Validation
Section titled “Validation”A conforming reader must reject inputs that violate any of the following:
- Header reads less than 28 bytes.
magic != 0x54644D63.bits_per_sampleis not 8, 16, 24, or 32.coding > 2.compression > 2.total_channels == 0.total_samples == 0.total_samples < 2whilecodingisDOUBLE_DELTA_ZIGZAG.sample_rateis NaN, ±Inf, or≤ 0.- For
NO_COMPRESSION: fewer bytes than the raw size remain after the header. - For
ZSTD/ZLIB: fewer thanpayload_sizebytes remain after the header. - For
ZSTD: payload does not start with a valid Zstandard frame header. - For
ZLIB: payload does not start with a valid zlib stream header. - For
ZSTD/ZLIB: the decompressed samples block size differs from the raw size.
The validation order is at the reader’s discretion, but cheap checks (header size, magic, enum ranges) should run before stream-position-dependent checks (payload size, frame headers).
Reference encoder (ISO C17)
Section titled “Reference encoder (ISO C17)”Single-call encoder for any valid combination of coding, compression, and
bits_per_sample. Allocates the result on the heap; caller frees with free.
Returns NULL on invalid arguments, compression failure, or allocation
failure.
Compile: cc -std=c17 -O3 cmdt_encode.c -lzstd -lz -o cmdt_encode
#include <stdint.h>#include <stdlib.h>#include <string.h>#include <math.h>#include <zstd.h>#include <zlib.h>
#define CMDT_MAGIC 0x54644D63u#define CMDT_CODING_TWOS 0#define CMDT_CODING_DELTA 1#define CMDT_CODING_DOUBLE 2#define CMDT_COMPRESS_NONE 0#define CMDT_COMPRESS_ZSTD 1#define CMDT_COMPRESS_ZLIB 2
#pragma pack(push, 1)typedef struct { uint32_t magic; uint64_t payload_size; uint8_t total_channels; uint32_t total_samples; double sample_rate; uint8_t bits_per_sample; uint8_t coding; uint8_t compression;} CmdtHeader;#pragma pack(pop)
// Unsigned sign-fill mask keeps every step fully defined; a sign-extended N-bit input yields an N-bit resultstatic inline uint32_t zigzag_encode(int32_t value) { const uint32_t sign_mask = 0u - ((uint32_t)value >> 31); return ((uint32_t)value << 1) ^ sign_mask;}
// Reinterpret the low bits of value as a signed bits-wide integer (modular two's-complement)static inline int32_t truncate_signed(uint32_t value, uint8_t bits) { if (bits == 32) { return (value & 0x80000000u) ? (int32_t)(value - 0x80000000u) + INT32_MIN : (int32_t)value; } const uint32_t sign_bit = 1u << (bits - 1); const uint32_t low = value & ((1u << bits) - 1); return (int32_t)(low ^ sign_bit) - (int32_t)sign_bit;}
// Byte-wise little-endian access: no alignment, aliasing, or host byte-order assumptionsstatic int32_t read_sample(const uint8_t* src, uint32_t sample_index, uint8_t bits_per_sample) { const size_t bytes_per_sample = bits_per_sample / 8u; const uint8_t* sample_data = src + (size_t)sample_index * bytes_per_sample; uint32_t raw = 0;
for (size_t byte_index = 0; byte_index < bytes_per_sample; ++byte_index) { raw |= (uint32_t)sample_data[byte_index] << (8u * byte_index); }
return truncate_signed(raw, bits_per_sample);}
static void write_sample(uint8_t* dst, uint32_t sample_index, uint8_t bits_per_sample, uint32_t value) { const size_t bytes_per_sample = bits_per_sample / 8u; uint8_t* sample_data = dst + (size_t)sample_index * bytes_per_sample;
for (size_t byte_index = 0; byte_index < bytes_per_sample; ++byte_index) { sample_data[byte_index] = (uint8_t)(value >> (8u * byte_index)); }}
uint8_t* cmdt_encode(const void* input_samples, uint8_t total_channels, uint32_t total_samples, double sample_rate, uint8_t bits_per_sample, uint8_t coding, uint8_t compression, size_t* output_size) { if (!input_samples || !output_size || !total_channels || !total_samples) { return NULL; } if (!isfinite(sample_rate) || sample_rate <= 0.0) { return NULL; } if (bits_per_sample != 8 && bits_per_sample != 16 && bits_per_sample != 24 && bits_per_sample != 32) { return NULL; } if (coding > CMDT_CODING_DOUBLE || compression > CMDT_COMPRESS_ZLIB) { return NULL; } if (coding == CMDT_CODING_DOUBLE && total_samples < 2) { return NULL; }
const size_t bytes_per_sample = bits_per_sample / 8; const size_t raw_size = (size_t)total_channels * total_samples * bytes_per_sample;
// Apply per-channel coding into a contiguous channel-major buffer uint8_t* coded = (uint8_t*)malloc(raw_size);
if (!coded) { return NULL; }
for (uint8_t channel = 0; channel < total_channels; ++channel) { const uint8_t* src = (const uint8_t*)input_samples + (size_t)channel * total_samples * bytes_per_sample; uint8_t* dst = coded + (size_t)channel * total_samples * bytes_per_sample;
for (uint32_t sample_index = 0; sample_index < total_samples; ++sample_index) { const int32_t x = read_sample(src, sample_index, bits_per_sample); uint32_t delta = (uint32_t)x;
// Deltas stay in unsigned arithmetic, so wraparound is fully defined at every sample width if (coding == CMDT_CODING_DELTA && sample_index >= 1) { const int32_t x_prev = read_sample(src, sample_index - 1, bits_per_sample); delta = (uint32_t)x - (uint32_t)x_prev;
} else if (coding == CMDT_CODING_DOUBLE && sample_index >= 2) { const int32_t x_prev1 = read_sample(src, sample_index - 1, bits_per_sample); const int32_t x_prev2 = read_sample(src, sample_index - 2, bits_per_sample); delta = ((uint32_t)x - (uint32_t)x_prev1) - ((uint32_t)x_prev1 - (uint32_t)x_prev2); }
// Wrap the delta to bits_per_sample width before zigzag const int32_t d = truncate_signed(delta, bits_per_sample);
const uint32_t y = (coding == CMDT_CODING_TWOS) ? (uint32_t)d : zigzag_encode(d);
write_sample(dst, sample_index, bits_per_sample, y); } }
// Optionally compress the coded buffer const uint8_t* final_data = coded; size_t final_size = raw_size; uint8_t* compressed_buffer = NULL;
if (compression == CMDT_COMPRESS_ZSTD) { const size_t bound = ZSTD_compressBound(raw_size); compressed_buffer = (uint8_t*)malloc(bound);
if (!compressed_buffer) { free(coded); return NULL; }
const size_t result = ZSTD_compress(compressed_buffer, bound, coded, raw_size, 3);
if (ZSTD_isError(result)) { free(compressed_buffer); free(coded); return NULL; }
final_data = compressed_buffer; final_size = result;
} else if (compression == CMDT_COMPRESS_ZLIB) { uLong bound = compressBound((uLong)raw_size); compressed_buffer = (uint8_t*)malloc(bound);
if (!compressed_buffer) { free(coded); return NULL; }
if (compress(compressed_buffer, &bound, coded, (uLong)raw_size) != Z_OK) { free(compressed_buffer); free(coded); return NULL; }
final_data = compressed_buffer; final_size = (size_t)bound; }
// Build the final blob: header followed by samples block const size_t blob_size = sizeof(CmdtHeader) + final_size; uint8_t* blob = (uint8_t*)malloc(blob_size);
if (!blob) { free(compressed_buffer); free(coded); return NULL; }
CmdtHeader* header = (CmdtHeader*)blob; header->magic = CMDT_MAGIC; header->payload_size = final_size; header->total_channels = total_channels; header->total_samples = total_samples; header->sample_rate = sample_rate; header->bits_per_sample = bits_per_sample; header->coding = coding; header->compression = compression;
memcpy(blob + sizeof(CmdtHeader), final_data, final_size);
free(compressed_buffer); free(coded);
*output_size = blob_size; return blob;}- The encoder stores the header in host byte order. On big-endian platforms the multi-byte header fields must be byte-swapped to little-endian before the bytes are written out. The samples block is assembled byte-wise, so it is little-endian on any host and needs no particular input alignment.
- For brevity, this encoder reads each input sample twice when computing
deltas. A production encoder would maintain
prev1/prev2running variables to make the inner loop a single read per sample.