Skip to main content

flac_io/
lib.rs

1#![forbid(unsafe_code)]
2#![warn(unreachable_pub)]
3//! A pure-Rust FLAC decoder and encoder with no unsafe code and no C
4//! dependency.
5//!
6//! FLAC (Free Lossless Audio Codec) stores audio so that decoding returns the
7//! exact original samples, bit for bit. This crate reads a FLAC byte stream
8//! into its raw integer samples and writes raw samples back into a valid FLAC
9//! stream, without any lossy intermediate.
10//!
11//! It exists for steganography, watermarking, forensic analysis, and any audio
12//! work that needs the decoded sample plane with a guarantee that a decode
13//! followed by an encode preserves the data exactly.
14//!
15//! # Quick start
16//!
17//! ```no_run
18//! use flac_io::{decode, encode};
19//!
20//! let bytes = std::fs::read("song.flac").unwrap();
21//! let audio = decode(&bytes).unwrap();
22//! println!("{} Hz, {} ch, {} bit", audio.sample_rate, audio.channels, audio.bits_per_sample);
23//!
24//! let out = encode(&audio).unwrap();
25//! std::fs::write("song_reencoded.flac", out).unwrap();
26//! ```
27//!
28//! # Sample layout
29//!
30//! [`FlacAudio::samples`] holds one inner vector per channel, each the same
31//! length (the number of samples per channel). Samples are signed integers in
32//! the stream's native bit depth, sign-extended into `i32`. For stereo, index
33//! 0 is the left channel and index 1 is the right.
34
35mod bitstream;
36mod bitwriter;
37mod crc;
38mod decoder;
39mod encoder;
40mod error;
41mod frame;
42mod md5;
43mod metadata;
44mod ogg;
45mod sample_bytes;
46
47pub use error::FlacError;
48pub use metadata::StreamInfo;
49
50/// Decoded FLAC audio: the stream parameters plus the samples, one vector per
51/// channel.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct FlacAudio {
54    /// Sample rate in hertz.
55    pub sample_rate: u32,
56    /// Number of channels (1 to 8).
57    pub channels: u8,
58    /// Bits per sample (4 to 32).
59    pub bits_per_sample: u8,
60    /// Decoded samples, `samples[channel][index]`. Every channel vector has
61    /// the same length.
62    pub samples: Vec<Vec<i32>>,
63}
64
65impl FlacAudio {
66    /// Number of samples per channel.
67    pub fn samples_per_channel(&self) -> usize {
68        self.samples.first().map_or(0, |c| c.len())
69    }
70}
71
72/// Read just the stream metadata (sample rate, channels, bit depth, total
73/// samples, MD5) without decoding any audio.
74///
75/// The container is detected automatically: both native FLAC and Ogg-wrapped
76/// FLAC are accepted. Only the metadata is read, so this stays cheap even for a
77/// large file. Use it when you need the format of a stream but not its samples.
78///
79/// # Errors
80///
81/// Returns [`FlacError`] if the input is not FLAC, is truncated, its STREAMINFO
82/// block is corrupt, or it declares a channel count or bit depth this crate
83/// cannot decode (so `info` and [`decode`] agree on which streams are valid).
84pub fn info(bytes: &[u8]) -> Result<StreamInfo, FlacError> {
85    let native;
86    let header = if ogg::is_ogg(bytes) {
87        native = ogg::to_native_flac(bytes, true)?;
88        metadata::read_header(&native)?
89    } else {
90        metadata::read_header(bytes)?
91    };
92    decoder::validate_stream_info(&header.stream_info)?;
93    Ok(header.stream_info)
94}
95
96/// Decode a FLAC byte stream into its samples and parameters.
97///
98/// The container is detected automatically: both native FLAC (the `.flac` form,
99/// starting with `fLaC`) and Ogg-wrapped FLAC (the `.oga` form, starting with
100/// `OggS`) are accepted.
101///
102/// Decoding is bounded: a crafted stream cannot make this function allocate
103/// without limit. The total number of decoded samples (summed across channels)
104/// is capped near one billion, which holds the output buffer under about four
105/// gibibytes; a stream that needs more returns [`FlacError::LimitExceeded`]
106/// rather than risking the process.
107///
108/// # Errors
109///
110/// Returns [`FlacError`] if the input is not FLAC, is truncated, is corrupt, a
111/// stored CRC does not match, it uses a feature this crate does not implement,
112/// or it exceeds the decode size cap.
113pub fn decode(bytes: &[u8]) -> Result<FlacAudio, FlacError> {
114    decoder::decode(bytes)
115}
116
117/// Encode samples into a valid native FLAC byte stream (the `.flac` form).
118///
119/// # Errors
120///
121/// Returns [`FlacError::InvalidInput`] if the channel vectors disagree in
122/// length, the bit depth or channel count is out of range, or there are no
123/// samples.
124pub fn encode(audio: &FlacAudio) -> Result<Vec<u8>, FlacError> {
125    encoder::encode(audio)
126}
127
128/// Encode samples into a FLAC stream wrapped in the Ogg container (the `.oga`
129/// form).
130///
131/// The audio is encoded identically to [`encode`]; only the container differs.
132/// Reading is symmetric: [`decode`] auto-detects either container, so there is
133/// no separate Ogg decode function.
134///
135/// # Errors
136///
137/// Returns [`FlacError::InvalidInput`] under the same conditions as [`encode`].
138pub fn encode_ogg(audio: &FlacAudio) -> Result<Vec<u8>, FlacError> {
139    encoder::encode_ogg(audio)
140}