Complete reference for every way to use Falcon: the falcon command-line tool,
the Rust library crates, and the C / C++ bindings. For a project overview,
benchmarks, and the bitstream format, see the main README.
- 1. Overview — which surface to use
- 2. Command-Line Interface
- 3. Rust library
- 4. C API (
falcon.h) - 5. C++ API (
falcon.hpp) - 6. Bitstream, versioning, and the v2 stability promise
- 7. Migrating from 1.x
Throughout, PCM is 32-bit float, nominally in the range [-1.0, 1.0], and
the codec is tuned for 48 kHz. One Falcon frame is 960 samples per
channel (20 ms @ 48 kHz). Other sample rates are stored faithfully in the
header, but the band tables assume 48 kHz.
Falcon exposes the same codec through three surfaces. Pick by what you are building:
| You want to… | Use | Entry points |
|---|---|---|
Convert files, inspect a .falcon, or benchmark decode from a shell or script |
CLI (falcon) |
encode, decode, info, bench-decode |
| Encode/decode from a Rust program, with full control over configuration and streaming | Rust crates | falcon_encoder, falcon_decoder, falcon_core |
| Integrate into a C or C++ application (e.g. a game engine) | C ABI / header-only C++ wrapper | falcon.h, falcon.hpp |
All three sit on the same falcon_core kernel, so a file produced by any of
them decodes on any of the others.
- The CLI is the quickest way to try Falcon and is what the benchmarks use.
- The Rust API gives the deepest access: streaming frame-by-frame
encode/decode, custom
EncoderConfig, and direct header inspection. - The C API is a small, stable ABI designed for real-time playback (one 20 ms frame of look-ahead, O(frame) memory). The C++ wrapper adds RAII and typed error codes on top with zero extra dependencies.
Falcon ships a single binary, falcon, with four subcommands. Build it with
cargo build --release -p falcon_cli (the binary is named falcon).
falcon <SUBCOMMAND> [ARGS...] [OPTIONS]
Global behavior (standard clap conventions):
-h, --helpis available on the top-level command and on every subcommand.-V, --versionprints the CLI version (top-level command).
| Situation | Stream | Exit code |
|---|---|---|
| Success | stdout (progress/results) | 0 |
| Runtime error (bad file, I/O, invalid bitstream, …) | Error: <message> on stderr |
1 |
| Usage / argument-parse error | clap usage message on stderr | 2 |
falcon encode <INPUT> <OUTPUT> [OPTIONS]
Reads an integer-PCM WAV file (e.g. 16- or 24-bit) and writes a .falcon
bitstream. Channel count and sample rate are taken from the WAV header; stereo
coupling (M/S) is enabled automatically when the input has ≥ 2 channels.
Positional arguments
| Argument | Description |
|---|---|
<INPUT> |
Input WAV file (path). Required. |
<OUTPUT> |
Output Falcon file (path). Required. |
Options
| Option | Type / default | Description |
|---|---|---|
-b, --bitrate <BITRATE> |
u16, default 128 |
Target bitrate in kbps. This is the default rate-control target when nothing else is given; it is also recorded in the file header. |
--target-bytes <TARGET_BYTES> |
usize, optional |
Rate control: pick the finest quality whose total output ≤ N bytes. Overrides --target-kbps and --bitrate. |
--target-kbps <TARGET_KBPS> |
f32, optional |
Rate control: target a bitrate of R kbps (converted to a byte budget from the clip duration). Overridden by --target-bytes. |
--quality-index <QUALITY_INDEX> |
u8 (0–255), optional |
Directly set the quality index (0 = finest, 255 = coarsest). Bypasses rate control entirely. |
Rate-control resolution (highest priority first — matches the source):
--quality-indexset → rate control is disabled; that fixed index is used.- else
--target-bytes→ exact byte budget. - else
--target-kbps→ byte budget derived from duration. - else
--bitrate(default 128) → byte budget derived from duration.
See also the Rate Control section of the README.
Examples
# Default: constant-quality VBR toward 128 kbps
falcon encode input.wav output.falcon
# Explicit bitrate
falcon encode input.wav output.falcon --bitrate 96
# Exact size target (finest quality that fits 500,000 bytes)
falcon encode input.wav output.falcon --target-bytes 500000
# Fixed quality index, no rate control
falcon encode input.wav output.falcon --quality-index 20On success it prints the input format, the resolved rate-control target, frame/byte counts, average bitrate, encode time, and compression ratio.
falcon decode <INPUT> <OUTPUT>
| Argument | Description |
|---|---|
<INPUT> |
Input .falcon file. Required. |
<OUTPUT> |
Output WAV file. Required. |
Reconstructs a 16-bit integer PCM WAV at the stream's sample rate and channel count, and reports sample rate, channels, samples per channel, decode time, and the real-time decode ratio.
falcon info <INPUT>
| Argument | Description |
|---|---|
<INPUT> |
Input .falcon file. Required. |
Prints the file header: sample rate, channel count, channel layout, total frames, bitrate, duration (frames × 20 ms), file size, and — if any bit is set — the LFE channel mask.
falcon bench-decode <INPUT> [-i <ITERATIONS>]
| Argument / option | Type / default | Description |
|---|---|---|
<INPUT> |
— | Input .falcon file. Required. |
-i, --iterations <ITERATIONS> |
u32, default 10 |
Number of decode iterations to average over. |
Loads the whole file into memory, then decodes it ITERATIONS times and reports
first-iteration time, average decode time, audio duration, real-time ratio, and
throughput in MB/s.
Three crates make up the library:
| Crate | Role | Key public items |
|---|---|---|
falcon_core |
Shared kernel: types, constants, error type | FalconError, FileHeader, ChannelLayout, Mode, FRAME_SIZE, VERSION, … |
falcon_encoder |
Encoder | encode::encode_file, Encoder, EncoderConfig, EncoderStats |
falcon_decoder |
Decoder | decode::decode_file, Decoder, DecoderConfig, FRAME_SAMPLES |
Add them to your Cargo.toml as dependencies (by path or git, pointing at this
workspace). The encoder and decoder both re-export the falcon_core items you
need, so you rarely have to depend on falcon_core directly.
| Where | Layout | Type |
|---|---|---|
encode_file input |
De-interleaved, one Vec<f32> per channel (&[Vec<f32>]) |
f32, nominally [-1.0, 1.0] |
Encoder::encode_frame input |
Interleaved, exactly channels × FRAME_SIZE (= channels × 960) samples |
f32 |
decode_file output |
De-interleaved, one Vec<f32> per channel |
f32 |
Decoder::get_interleaved_samples output |
Interleaved | f32 |
Sample rate is u32 Hz (use 48000); channel count is u8 (1–64). The CLI is
the place that converts to/from i16/24-bit WAV — the library itself is f32 end
to end.
pub enum FalconError { // implements std::error::Error (via thiserror)
InvalidMagic,
UnsupportedVersion(u8),
InvalidFrameHeader,
CrcMismatch { expected: u8, actual: u8 },
UnexpectedEof,
InvalidChannelCount(u8),
InvalidSampleRate(u32),
BitstreamError(String),
IoError(std::io::Error), // From<std::io::Error>
EncoderError(String),
DecoderError(String),
}
pub type Result<T> = std::result::Result<T, FalconError>;
pub struct FileHeader {
pub sample_rate: u32,
pub channels: u8,
pub channel_layout: ChannelLayout,
pub total_frames: u32,
pub bitrate: u16, // kbps; 0 = VBR
pub lfe_mask: u64, // one bit per channel
pub quality_index: u8, // 0 = finest .. 255 = coarsest
pub quality_frac: u8, // 0..15, sixteenths of one index step
}
pub enum ChannelLayout { Mono, Stereo, Surround51, Surround71, Custom(u8) }
// ChannelLayout::channel_count() -> usize
// ChannelLayout::from_channel_count(u8) -> ChannelLayout
pub enum Mode { // frame coding mode (2-bit header field)
Mdct, // 0: MDCT frame, long-window chain
Flagged, // 1: MDCT frame with the transient flag (four short blocks)
Ext, // 2: overflow extension (MDCT layout at a coarser rate level)
Silent, // 3: silent frame (optionally the LSB-floor payload)
}Useful constants (all pub in falcon_core):
| Constant | Value | Meaning |
|---|---|---|
MAGIC |
b"FALC" |
File magic |
VERSION |
0x10 |
Current bitstream version |
FRAME_SIZE |
960 |
Samples per channel per frame (20 ms @ 48 kHz) |
MAX_CHANNELS |
64 |
Maximum channel count |
NUM_BANDS |
27 |
Bark-scale bands |
DEFAULT_QUALITY_INDEX |
43 |
Default quality index |
High-level, one call (recommended):
pub fn encode_file<W: std::io::Write>(
writer: W,
samples: &[Vec<f32>], // one Vec per channel (de-interleaved)
sample_rate: u32,
config: EncoderConfig,
) -> Result<EncoderStats, FalconError>;EncoderStats { total_frames: u32, bytes_written: usize, average_bitrate: u16 }.
Configuration:
pub struct EncoderConfig {
pub bitrate: u16, // nominal kbps, recorded in the header (default 128)
pub stereo_coupling: bool, // M/S coupling (default true)
pub ra_interval: u32, // random-access interval in frames (default 50)
pub quality_index: u8, // 0..255; ignored when target_bytes is set
pub quality_frac: u8, // 0..15, sixteenths of a step; ignored when target_bytes is set
pub target_bytes: Option<usize>,// Some(n): pick finest quality whose file is <= n bytes
}
impl Default for EncoderConfig { /* bitrate 128, coupling on, ra_interval 50,
quality_index DEFAULT_QUALITY_INDEX, quality_frac 0,
target_bytes None */ }Lower-level, streaming (when you produce audio frame by frame):
pub struct Encoder<W: std::io::Write> { /* ... */ }
impl<W: std::io::Write> Encoder<W> {
pub fn new(writer: W, sample_rate: u32, channels: u8) -> Result<Self, FalconError>;
pub fn with_config(writer: W, sample_rate: u32, channels: u8,
config: EncoderConfig) -> Result<Self, FalconError>;
pub fn with_config_and_frames(writer: W, sample_rate: u32, channels: u8,
config: EncoderConfig, total_frames: u32)
-> Result<Self, FalconError>;
// `samples` is interleaved and must be exactly channels * FRAME_SIZE long.
pub fn encode_frame(&mut self, samples: &[f32]) -> Result<(), FalconError>;
}Most callers should prefer encode_file: it handles look-ahead transient
detection, the M/S pre-scan, rate control, and header total_frames for you.
High-level, one call (recommended):
pub fn decode_file<R: std::io::Read>(reader: R)
-> Result<(Vec<Vec<f32>>, u32, u8), FalconError>;
// ^ per-channel ^ sample ^ channels
// samples rateThe first bitstream frame is TDAC priming and is dropped internally, so the
returned per-channel length is (total_frames - 1) × 960; it may include up to
one frame of trailing zero padding (the container stores whole frames).
Lower-level, streaming:
pub struct Decoder<R: std::io::Read> { /* ... */ }
impl<R: std::io::Read> Decoder<R> {
pub fn new(reader: R) -> Result<Self, FalconError>;
pub fn with_config(reader: R, config: DecoderConfig) -> Result<Self, FalconError>;
pub fn header(&self) -> &FileHeader;
pub fn sample_rate(&self) -> u32;
pub fn channels(&self) -> usize;
pub fn current_frame(&self) -> u32;
pub fn total_frames(&self) -> u32;
pub fn decode_frame(&mut self) -> Result<usize, FalconError>; // samples/ch, 0 = EOF
pub fn get_samples(&self, channel: usize) -> &[f32]; // last decoded frame
pub fn get_interleaved_samples(&self) -> Vec<f32>; // last decoded frame
pub fn reset_state(&mut self); // for seeking
// Diagnostics (state of the last decoded MDCT frame):
pub fn band_classes(&self, channel: usize) -> &[u8; NUM_BANDS]; // coding class per band
pub fn last_class_update(&self) -> bool; // frame carried a class section
pub fn last_shape_bits(&self) -> usize; // size of its shape section
}
pub struct DecoderConfig { pub verify_crc: bool } // Default: verify_crc = true
pub const FRAME_SAMPLES: usize = 960; // == falcon_core::FRAME_SIZEDecoder::new parses the header immediately (so header()/sample_rate()/
channels() are valid before decoding). Each decode_frame() returns 960
until end of stream, then 0; the freshly decoded frame is read back with
get_samples(ch) or get_interleaved_samples().
use std::io::Cursor;
use falcon_encoder::{encode::encode_file, EncoderConfig};
use falcon_decoder::decode::decode_file;
use falcon_core::FalconError;
fn main() -> Result<(), FalconError> {
// 1 second of 48 kHz stereo, de-interleaved f32 in [-1.0, 1.0].
let sample_rate = 48_000u32;
let n = sample_rate as usize;
let left: Vec<f32> = (0..n).map(|i| 0.2 * (i as f32 * 0.05).sin()).collect();
let right: Vec<f32> = left.clone();
let channels = vec![left, right];
// Encode into an in-memory buffer (&mut Vec<u8> implements Write).
let mut buf: Vec<u8> = Vec::new();
let stats = encode_file(&mut buf, &channels, sample_rate, EncoderConfig::default())?;
println!("encoded {} frames, {} bytes", stats.total_frames, stats.bytes_written);
// Decode it back.
let (decoded, sr, ch) = decode_file(Cursor::new(buf))?;
println!("decoded {} ch @ {} Hz, {} samples/ch", ch, sr, decoded[0].len());
Ok(())
}C99 header at falcon_capi/include/falcon.h.
Build the library with cargo build --release -p falcon_capi; a static library,
a DLL, and an import library land in target/release/. See
falcon_capi/README.md for per-toolchain link lines
(MSVC, MinGW, CMake).
All PCM is interleaved 32-bit float. data/len refer to a complete
Falcon file already in memory — the decoder is not a byte-stream parser.
Return-code convention: 0 is success, negative values are errors. Getter
functions (*_sample_rate, *_channels, *_total_samples) return 0 on a
NULL handle instead of an error code.
| Macro | Value | Meaning |
|---|---|---|
FALCON_OK |
0 |
Success |
FALCON_ERR_INVALID_ARG |
-1 |
NULL pointer or out-of-domain value |
FALCON_ERR_INVALID_DATA |
-2 |
Not a valid Falcon bitstream |
FALCON_ERR_ALLOC |
-3 |
Memory allocation failed |
FALCON_ERR_PANIC |
-4 |
Internal error caught at the FFI boundary |
FALCON_ERR_UNSUPPORTED |
-5 |
Operation not supported |
FALCON_ERR_IO |
-6 |
I/O error |
FALCON_ERR_ENCODE |
-7 |
Encoding failed |
FALCON_ERR_OUT_OF_RANGE |
-8 |
Position beyond end of stream |
typedef struct FalconDecoder FalconDecoder;— opaque streaming decoder handle. Create withfalcon_decoder_open, destroy withfalcon_decoder_close.
| Function | Signature | Notes |
|---|---|---|
falcon_decoder_open |
FalconDecoder* falcon_decoder_open(const uint8_t* data, size_t len) |
Parses the header immediately; copies data internally, so the caller may free its buffer right after this returns. Returns NULL on bad args or an invalid stream. |
falcon_decoder_sample_rate |
int falcon_decoder_sample_rate(const FalconDecoder* dec) |
Hz. 0 if dec is NULL. |
falcon_decoder_channels |
int falcon_decoder_channels(const FalconDecoder* dec) |
1 = mono, 2 = stereo, …. 0 if dec is NULL. |
falcon_decoder_total_samples |
uint64_t falcon_decoder_total_samples(const FalconDecoder* dec) |
Total PCM samples per channel. May include up to one frame of trailing zero padding. |
falcon_decoder_read_f32 |
int64_t falcon_decoder_read_f32(FalconDecoder* dec, float* out_interleaved, size_t max_frames) |
Decodes up to max_frames frames of interleaved f32. out_interleaved must hold max_frames × channels floats. Returns frames written (> 0), 0 at end of stream, or a negative FALCON_ERR_*. |
falcon_decoder_seek |
int falcon_decoder_seek(FalconDecoder* dec, uint64_t sample_pos) |
Seek so the next read starts at absolute per-channel sample sample_pos (0 = start). Sample-exact but linear (decode-and-discard; backward seeks restart from the beginning) — avoid per-tick seeking of long streams. Returns FALCON_OK, FALCON_ERR_OUT_OF_RANGE if past the end, or another negative code. |
falcon_decoder_close |
void falcon_decoder_close(FalconDecoder* dec) |
Destroy the handle. NULL is a safe no-op. |
| Function | Signature | Notes |
|---|---|---|
falcon_decode_buffer |
int falcon_decode_buffer(const uint8_t* data, size_t len, float** out, uint64_t* out_frames, int* channels, int* sample_rate) |
Decodes an entire in-memory file in one call. On success *out points to (*out_frames × *channels) interleaved floats owned by the library — release with falcon_free. Returns FALCON_OK or a negative code. |
falcon_encode_buffer |
int falcon_encode_buffer(const float* pcm_interleaved, uint64_t frames, int channels, int sample_rate, int32_t target_bytes, int32_t target_kbps, uint8_t** out, size_t* out_len) |
Encodes interleaved f32 PCM to a complete Falcon file. frames > 0, channels 1–64. Rate control: target_bytes > 0 is a total-file byte budget; else target_kbps > 0 sets the bitrate; if both <= 0, defaults to 128 kbps. On success *out (*out_len bytes) is library-owned — release with falcon_free. Returns FALCON_OK or a negative code. |
falcon_free |
void falcon_free(void* ptr) |
Release a buffer returned by falcon_decode_buffer / falcon_encode_buffer. Never use free/delete on these. NULL is a safe no-op. |
| Function | Signature | Notes |
|---|---|---|
falcon_version |
const char* falcon_version(void) |
Static NUL-terminated version string (the library semver, e.g. "2.0.0"). Never NULL. |
falcon_error_message |
const char* falcon_error_message(int code) |
Static human-readable message for a FALCON_* code (unknown codes map to "unknown error code"). Never NULL. |
- Ownership. Buffers delivered through
float**/uint8_t**out-parameters are allocated by the library and must be released withfalcon_free. Strings fromfalcon_version/falcon_error_messageare static — do not free them.datapassed toopen/decode_bufferis copied, so it remains the caller's to free. - Lifetime. A
FalconDecoder*is valid fromopenuntilclose. - Thread-safety. A single
FalconDecoder*is not thread-safe; use one handle per thread or serialize access. Distinct handles are fully independent. The stateless one-shot and version/error functions are safe to call concurrently. - Panics. Every entry point is wrapped so an internal Rust panic returns
FALCON_ERR_PANICrather than unwinding into C. (The workspace release profile builds withpanic = "abort", so a panic aborts the process; all fallible paths useResult, making this a last-resort net.) - Linker note (MSVC): define
FALCON_DLLbefore includingfalcon.hwhen linking against the DLL to get__declspec(dllimport); it is optional for import-library linking.
/* cc example.c -I falcon_capi/include -L target/release -lfalcon_capi -lm */
#include <falcon.h>
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
const int sr = 48000, ch = 2;
const uint64_t frames = 48000; /* 1 second, stereo */
float* pcm = malloc(sizeof(float) * frames * ch);
for (uint64_t i = 0; i < frames; ++i) {
float s = 0.2f * sinf((float)i * 0.05f);
pcm[i * ch + 0] = s; /* interleaved */
pcm[i * ch + 1] = s;
}
uint8_t* enc = NULL; size_t enc_len = 0;
int rc = falcon_encode_buffer(pcm, frames, ch, sr,
/*target_bytes*/ 0, /*target_kbps*/ 128,
&enc, &enc_len);
if (rc != FALCON_OK) {
fprintf(stderr, "encode: %s\n", falcon_error_message(rc));
return 1;
}
printf("encoded %zu bytes\n", enc_len);
float* out = NULL; uint64_t out_frames = 0; int out_ch = 0, out_sr = 0;
rc = falcon_decode_buffer(enc, enc_len, &out, &out_frames, &out_ch, &out_sr);
if (rc != FALCON_OK) {
fprintf(stderr, "decode: %s\n", falcon_error_message(rc));
falcon_free(enc);
return 1;
}
printf("decoded %llu frames, %d ch @ %d Hz\n",
(unsigned long long)out_frames, out_ch, out_sr);
falcon_free(enc);
falcon_free(out);
free(pcm);
return 0;
}Header-only C++17 RAII wrapper at
falcon_capi/include/falcon.hpp. It
includes falcon.h and links against the same library. No exceptions are
thrown — fallible calls return falcon::Error (an expected-style pattern) and
handle validity is checked with explicit operator bool. Everything lives in
namespace falcon.
| Item | Signature | Notes |
|---|---|---|
falcon::version |
const char* version() noexcept |
Wraps falcon_version. |
falcon::errorMessage |
const char* errorMessage(Error) / (int) noexcept |
Wraps falcon_error_message. |
falcon::decodeAll |
Error decodeAll(const void* data, std::size_t len, DecodedAudio& out) noexcept |
One-shot decode; fills out and returns Error::Ok, else an error code. Handles falcon_free internally. |
falcon::encode |
Error encode(const float* pcmInterleaved, std::uint64_t frames, int channels, int sampleRate, std::int32_t targetBytes, std::int32_t targetKbps, std::vector<std::uint8_t>& out) noexcept |
One-shot encode into out. Same rate-control rule as the C API (targetBytes > 0 wins, else targetKbps > 0, else 128 kbps). |
Mirrors the FALCON_* codes: Ok, InvalidArg, InvalidData, Alloc,
Panic, Unsupported, Io, Encode, OutOfRange.
struct DecodedAudio {
std::vector<float> samples; // interleaved, frames * channels floats
std::uint64_t frames = 0; // samples per channel
int channels = 0;
int sampleRate = 0;
};| Member | Signature | Notes |
|---|---|---|
Decoder::open |
static Decoder open(const void* data, std::size_t len) noexcept |
Factory. Copies data. Check the result with operator bool. |
operator bool |
explicit operator bool() const noexcept |
True iff open succeeded and not yet closed. |
sampleRate |
int sampleRate() const noexcept |
|
channels |
int channels() const noexcept |
|
totalSamples |
std::uint64_t totalSamples() const noexcept |
Per channel; may include padding. |
read |
std::int64_t read(float* out, std::size_t maxFrames) noexcept |
Frames written, 0 at EOF, negative Error code on failure. out capacity ≥ maxFrames * channels(). |
seek |
Error seek(std::uint64_t samplePos) noexcept |
Sample-exact, O(n) linear (see the C API note). |
close |
void close() noexcept |
Also called by the destructor. |
native |
FalconDecoder* native() const noexcept |
Underlying C handle for interop; ownership stays with the object. |
Decoder is move-only (copy is deleted); the destructor releases the
handle, so there is nothing to free manually. It is not thread-safe, matching
the C handle.
// c++ -std=c++17 example.cpp -I falcon_capi/include -L target/release -lfalcon_capi
#include <falcon.hpp>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <vector>
int main() {
const int sr = 48000, ch = 2;
const std::uint64_t frames = 48000; // 1 second, stereo
std::vector<float> pcm(frames * ch);
for (std::uint64_t i = 0; i < frames; ++i) {
float s = 0.2f * std::sin(float(i) * 0.05f);
pcm[i * ch + 0] = s; // interleaved
pcm[i * ch + 1] = s;
}
std::vector<std::uint8_t> file;
falcon::Error e = falcon::encode(pcm.data(), frames, ch, sr,
/*targetBytes*/ 0, /*targetKbps*/ 128, file);
if (e != falcon::Error::Ok) {
std::fprintf(stderr, "encode: %s\n", falcon::errorMessage(e));
return 1;
}
falcon::Decoder dec = falcon::Decoder::open(file.data(), file.size());
if (!dec) { std::fprintf(stderr, "open failed (invalid stream?)\n"); return 1; }
std::vector<float> block(1024 * dec.channels());
std::int64_t n = 0;
std::uint64_t total = 0;
while ((n = dec.read(block.data(), 1024)) > 0) total += static_cast<std::uint64_t>(n);
if (n < 0) {
std::fprintf(stderr, "read: %s\n", falcon::errorMessage(static_cast<int>(n)));
return 1;
}
std::printf("decoded %llu frames, %d ch @ %d Hz\n",
static_cast<unsigned long long>(total), dec.channels(), dec.sampleRate());
return 0; // dec's destructor releases the handle
}A fuller, cross-checking example lives at
falcon_capi/examples/cpp/decode_example.cpp
(with a CMakeLists.txt alongside it).
Every Falcon file starts with a 25-byte header ("FALC" magic, a 1-byte
version, 16-bit flags carrying the quality index, a 24-bit sample rate, the
channel count, total frames, a nominal bitrate, and a 64-bit LFE mask),
followed by frames of [2-byte frame header][payload][CRC-8]. The current
bitstream version is falcon_core::VERSION = 0x10. For the exact byte
layout of the file header, frame header, and MDCT payload, see
Bitstream Format in the README.
- Version handshake. The decoder checks the magic and version on open; a
stream whose version it does not understand fails with
FalconError::UnsupportedVersion(FALCON_ERR_INVALID_DATAat the C boundary). The band layout, cell tables, prefix-code tables and the pulse-count law are part of the encoder/decoder contract, so streams are not compatible across version bumps — the version byte is the guard. - 1.x streams are not decodable by 2.x (and vice versa): 1.x wrote version
0x04. Re-encode from the source audio. - v2 stability promise. For the
v2.xseries the0x10bitstream is frozen: anyv2.xdecoder decodes anyv2.x-encoded file. The APIs documented here — the CLI subcommands/flags, the Rustencode_file/decode_fileentry points, and the C ABI infalcon.h— are stable acrossv2.x; additions will be backward-compatible. - Room to grow without breaking you. The bitstream supports random-access
frames (the encoder's
ra_interval); the CLI and C API currently emit only the first one. Periodic RA frames plus a seek index would make seeking fast (today it is sample-exact but linear) with no change to the C API —falcon_decoder_seekkeeps the same signature and semantics.
| 1.x | 2.x |
|---|---|
EncoderConfig { quality: Quality::…, .. } |
Remove the field; it had no effect. Use quality_index / target_bytes for quality vs size. |
CLI -q, --quality <preset> |
Removed (it had no effect). Use --quality-index, --target-bytes or --target-kbps. |
falcon_encoder::PsychoacousticModel, falcon_encoder::psychoacoustic |
Removed (never consulted by the encoder). |
falcon_core::coeffcode::{k_scale, k_scale_frac, K_MIN, K_MAX, SIGMA_X} |
falcon_core::rate::… (also re-exported as falcon_core::k_scale*). |
falcon_core::{BITS_PER_BAND, quantize_variable, dequantize_variable, pvq_lite, …} |
Removed (legacy scalar layout). |
Mode::Lpc / Mode::Hybrid |
Mode::Flagged / Mode::Ext (same header values 1 / 2, new meaning for 2). |
ModeDetector::detect(samples, energies), DetectedMode::{Speech, Music, …} |
ModeDetector::detect(samples), DetectedMode::{Active, Silent}. |
FalconError::RansError |
Removed (rANS is gone). |
.falcon files written by 1.x |
Not decodable; re-encode from the source audio. |
The C ABI (falcon.h) and the C++ wrapper (falcon.hpp) are unchanged
apart from the version string.
See also: the project README for benchmarks, design
philosophy, and build instructions, and
falcon_capi/README.md for detailed C/C++ link
lines per toolchain.