flac_encoder/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::{
4    ffi::{c_char, CString},
5    mem::zeroed,
6    os::raw::c_void,
7    path::Path,
8    ptr::null_mut,
9    slice::from_raw_parts,
10    str::FromStr,
11};
12
13use libflac_sys::*;
14
15pub struct FlacBuilder<'data, Sample>
16where
17    Sample: IntoSample,
18{
19    data: InputData<'data, Sample>,
20    bps: BpsLevel,
21    sample_rate: u32,
22    compression_level: u32,
23    padding: u32,
24    vorbis_comments: Vec<(CString, CString)>,
25    metadata_blocks: Vec<*mut FLAC__StreamMetadata>,
26}
27
28impl<'data, Sample: IntoSample> FlacBuilder<'data, Sample> {
29    /// New with planar audio data. The input data must be a list of channels where each channel is
30    /// a list of frames/samples. Samples can be either `f32` or `f64` in range [-1.0, 1.0].
31    pub fn from_planar(data: &'data [Vec<Sample>], sample_rate: u32) -> Self {
32        Self::new(InputData::Planar(data), sample_rate)
33    }
34
35    /// New with interleaved (e.g. LRLRLRLRLRLR) audio data. Samples can be either `f32` or `f64` in range [-1.0, 1.0].
36    pub fn from_interleaved(data: &'data [Sample], channels: usize, sample_rate: u32) -> Self {
37        Self::new(InputData::Interleaved { data, channels }, sample_rate)
38    }
39
40    fn new(data: InputData<'data, Sample>, sample_rate: u32) -> Self {
41        FlacBuilder {
42            data,
43            sample_rate,
44            bps: BpsLevel::Bps16,
45            compression_level: 5,
46            padding: 500,
47            vorbis_comments: vec![],
48            metadata_blocks: vec![],
49        }
50    }
51
52    /// See https://xiph.org/flac/api/group__flac__stream__encoder.html#gaacc01aab02849119f929b8516420fcd3
53    pub fn compression_level(mut self, level: u32) -> Self {
54        self.compression_level = level;
55        self
56    }
57
58    /// Set bits per sample.
59    pub fn bps(mut self, bps: BpsLevel) -> Self {
60        self.bps = bps;
61        self
62    }
63
64    pub fn padding(mut self, padding: u32) -> Self {
65        self.padding = padding;
66        self
67    }
68
69    pub fn artist(self, artist: &str) -> Self {
70        self.vorbis_comment("ARTIST", artist)
71    }
72
73    pub fn album(self, album: &str) -> Self {
74        self.vorbis_comment("ALBUM", album)
75    }
76
77    pub fn title(self, title: &str) -> Self {
78        self.vorbis_comment("TITLE", title)
79    }
80
81    pub fn year(self, year: u32) -> Self {
82        self.vorbis_comment("YEAR", &year.to_string())
83    }
84
85    pub fn track_number(self, number: i32) -> Self {
86        self.vorbis_comment("TRACKNUMBER", &number.to_string())
87    }
88
89    pub fn vorbis_comment(mut self, key: &str, value: &str) -> Self {
90        self.vorbis_comments.push((
91            CString::from_str(key).unwrap_or_default(),
92            CString::from_str(value).unwrap_or_default(),
93        ));
94        self
95    }
96
97    unsafe fn prepare(&mut self) -> Result<*mut FLAC__StreamEncoder, EncoderError> {
98        if !self.data.channel_sizes_match() {
99            return Err(EncoderError::MismatchedSampleCountPerChannels);
100        }
101
102        if self.data.total_samples() == 0 {
103            return Err(EncoderError::NoData);
104        }
105
106        let encoder = FLAC__stream_encoder_new();
107
108        if encoder.is_null() {
109            return Err(EncoderError::InitializationError);
110        }
111
112        if 0 == FLAC__stream_encoder_set_verify(encoder, 1) {
113            return Err(EncoderError::VerificationError);
114        }
115
116        if 0 == FLAC__stream_encoder_set_compression_level(encoder, self.compression_level) {
117            return Err(EncoderError::InvalidCompressionLevel);
118        }
119
120        let channels = self.data.channel_count();
121
122        if 0 == FLAC__stream_encoder_set_channels(encoder, channels as u32) {
123            return Err(EncoderError::InvalidChannelCount);
124        }
125
126        if 0 == FLAC__stream_encoder_set_bits_per_sample(encoder, self.bps.to_u32()) {
127            return Err(EncoderError::InvalidSampleType);
128        }
129
130        if 0 == FLAC__stream_encoder_set_sample_rate(encoder, self.sample_rate) {
131            return Err(EncoderError::InvalidSampleRate);
132        }
133
134        if 0 == FLAC__stream_encoder_set_total_samples_estimate(
135            encoder,
136            self.data.total_samples() as u64,
137        ) {
138            return Err(EncoderError::TooManyOrTooFewSamples);
139        }
140
141        if self.vorbis_comments.is_empty() {
142            if 0 == FLAC__stream_encoder_set_metadata(encoder, null_mut(), 0) {
143                return Err(EncoderError::FailedToSetMetadata);
144            }
145        }
146
147        if !self.vorbis_comments.is_empty() {
148            let metadata_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT);
149
150            if metadata_block.is_null() {
151                return Err(EncoderError::InitializationError);
152            }
153
154            for (key, value) in &self.vorbis_comments {
155                let mut entry: FLAC__StreamMetadata_VorbisComment_Entry = zeroed();
156
157                if 0 == FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(
158                    &mut entry,
159                    key.as_bytes().as_ptr() as *const c_char,
160                    value.as_bytes().as_ptr() as *const c_char,
161                ) {
162                    return Err(EncoderError::InvalidVorbisComment(
163                        key.to_string_lossy().to_string(),
164                    ));
165                }
166
167                if 0 == FLAC__metadata_object_vorbiscomment_append_comment(metadata_block, entry, 0)
168                {
169                    return Err(EncoderError::FailedToSetMetadata);
170                }
171            }
172
173            self.metadata_blocks.push(metadata_block);
174        }
175
176        let padding_block = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING);
177        if !padding_block.is_null() {
178            (*padding_block).length = self.padding;
179            self.metadata_blocks.push(padding_block);
180        }
181
182        if 0 == FLAC__stream_encoder_set_metadata(
183            encoder,
184            self.metadata_blocks.as_mut_ptr(),
185            self.metadata_blocks.len() as u32,
186        ) {
187            return Err(EncoderError::FailedToSetMetadata);
188        }
189
190        Ok(encoder)
191    }
192
193    pub fn write_file(mut self, path: impl AsRef<Path>) -> Result<(), EncoderError> {
194        unsafe {
195            let encoder = self.prepare()?;
196
197            let Ok(path) = CString::from_str(&path.as_ref().to_string_lossy()) else {
198                return Err(EncoderError::NullCharInPath);
199            };
200
201            FLAC__stream_encoder_init_file(
202                encoder,
203                path.as_bytes().as_ptr() as *const _,
204                None,
205                null_mut(),
206            );
207
208            self.feed_entire_input(encoder)?;
209
210            finish(encoder)?;
211
212            Ok(())
213        }
214    }
215
216    pub fn build(mut self) -> Result<Vec<u8>, EncoderError> {
217        unsafe {
218            let encoder = self.prepare()?;
219
220            let mut callback_data = WriteCallbackData {
221                data: Vec::with_capacity(self.data.total_samples()),
222                cursor: 0,
223            };
224
225            FLAC__stream_encoder_init_stream(
226                encoder,
227                Some(write_callback),
228                Some(seek_callback),
229                Some(tell_callback),
230                None,
231                &mut callback_data as *mut _ as *mut c_void,
232            );
233
234            self.feed_entire_input(encoder)?;
235
236            finish(encoder)?;
237
238            Ok(callback_data.data)
239        }
240    }
241
242    fn feed_entire_input(&mut self, encoder: *mut FLAC__StreamEncoder) -> Result<(), EncoderError> {
243        let mut input_cursor = 0;
244
245        while input_cursor < self.data.samples_per_channel() {
246            self.consume_input_chunk(encoder, &mut input_cursor, 1024)?;
247        }
248
249        Ok(())
250    }
251
252    fn consume_input_chunk(
253        &mut self,
254        encoder: *mut FLAC__StreamEncoder,
255        input_cursor: &mut usize,
256        chunk_size: usize,
257    ) -> Result<(), EncoderError> {
258        let channels = self.data.channel_count();
259
260        let mut input_data: Vec<FLAC__int32> = Vec::with_capacity(chunk_size * channels);
261
262        for block_sample_i in 0..chunk_size {
263            for channel_i in 0..self.data.channel_count() {
264                input_data.push(
265                    match &self.data {
266                        InputData::Interleaved { data, channels } => data
267                            .get((*input_cursor + block_sample_i) * channels + channel_i)
268                            .copied()
269                            .unwrap_or(Sample::default()),
270                        InputData::Planar(data) => data
271                            .get(channel_i)
272                            .and_then(|c| c.get(*input_cursor + block_sample_i))
273                            .copied()
274                            .unwrap_or(Sample::default()),
275                    }
276                    .to_bps_level(self.bps),
277                );
278            }
279        }
280
281        let remaining = self.data.total_samples() - *input_cursor;
282        let actual_size = chunk_size.min(remaining);
283
284        let block_ptr = input_data.as_ptr() as *const FLAC__int32;
285
286        unsafe {
287            if 0 == FLAC__stream_encoder_process_interleaved(encoder, block_ptr, actual_size as u32)
288            {
289                return Err(EncoderError::EncodingError);
290            }
291        }
292
293        *input_cursor += chunk_size;
294
295        Ok(())
296    }
297
298    unsafe fn cleanup(&mut self) {
299        for block in self.metadata_blocks.iter() {
300            FLAC__metadata_object_delete(*block);
301        }
302        self.metadata_blocks.clear();
303    }
304}
305
306unsafe fn finish(encoder: *mut FLAC__StreamEncoder) -> Result<(), EncoderError> {
307    if 0 == FLAC__stream_encoder_finish(encoder) {
308        return Err(EncoderError::EncodingError);
309    }
310
311    Ok(())
312}
313
314impl<'data, Sample: IntoSample> Drop for FlacBuilder<'data, Sample> {
315    fn drop(&mut self) {
316        unsafe {
317            self.cleanup();
318        }
319    }
320}
321
322#[derive(Debug, Clone, Copy)]
323pub enum BpsLevel {
324    Bps16,
325    Bps20,
326    Bps24,
327}
328
329impl BpsLevel {
330    pub fn to_u32(&self) -> u32 {
331        match self {
332            BpsLevel::Bps16 => 16,
333            BpsLevel::Bps20 => 20,
334            BpsLevel::Bps24 => 24,
335        }
336    }
337}
338
339pub struct WriteCallbackData {
340    pub data: Vec<u8>,
341    pub cursor: usize,
342}
343
344pub enum InputData<'a, Sample>
345where
346    Sample: IntoSample,
347{
348    Interleaved { data: &'a [Sample], channels: usize },
349    Planar(&'a [Vec<Sample>]),
350}
351
352impl<'a, Sample: IntoSample> InputData<'a, Sample> {
353    fn channel_count(&self) -> usize {
354        match self {
355            InputData::Interleaved { channels, .. } => *channels,
356            InputData::Planar(data) => data.len(),
357        }
358    }
359
360    fn samples_per_channel(&self) -> usize {
361        match self {
362            InputData::Interleaved { data, channels } => data.len() / channels,
363            InputData::Planar(data) => {
364                if data.is_empty() {
365                    return 0;
366                }
367                data[0].len()
368            }
369        }
370    }
371
372    fn total_samples(&self) -> usize {
373        match self {
374            InputData::Interleaved { data, .. } => data.len(),
375            InputData::Planar(data) => data.iter().map(|channel| channel.len()).sum(),
376        }
377    }
378
379    fn channel_sizes_match(&self) -> bool {
380        match self {
381            InputData::Interleaved { data, channels } => data.len() % *channels == 0,
382            InputData::Planar(data) => {
383                if data.is_empty() {
384                    return true;
385                }
386                let size = data[0].len();
387                data.iter().all(|channel| channel.len() == size)
388            }
389        }
390    }
391}
392
393#[no_mangle]
394unsafe extern "C" fn write_callback(
395    _encoder: *const FLAC__StreamEncoder,
396    buffer: *const FLAC__byte,
397    bytes: usize,
398    _samples: u32,
399    _current_frame: u32,
400    client_data: *mut std::ffi::c_void,
401) -> u32 {
402    let data = unsafe { &mut *(client_data as *mut WriteCallbackData) };
403
404    if data.cursor + bytes > data.data.len() {
405        let needed = (data.cursor + bytes) - data.data.len();
406        data.data.extend(vec![0u8; needed]);
407    }
408
409    let new_data = from_raw_parts(buffer, bytes);
410
411    for i in 0..bytes {
412        data.data[data.cursor] = new_data[i];
413        data.cursor += 1;
414    }
415
416    0
417}
418
419#[no_mangle]
420unsafe extern "C" fn seek_callback(
421    _encoder: *const FLAC__StreamEncoder,
422    absolute_byte_offset: u64,
423    client_data: *mut std::ffi::c_void,
424) -> u32 {
425    let data = unsafe { &mut *(client_data as *mut WriteCallbackData) };
426
427    data.cursor = absolute_byte_offset as usize;
428
429    FLAC__STREAM_ENCODER_SEEK_STATUS_OK
430}
431
432#[no_mangle]
433unsafe extern "C" fn tell_callback(
434    _encoder: *const FLAC__StreamEncoder,
435    absolute_byte_offset: *mut u64,
436    client_data: *mut std::ffi::c_void,
437) -> u32 {
438    let data = unsafe { &mut *(client_data as *mut WriteCallbackData) };
439
440    *absolute_byte_offset = data.cursor as u64;
441
442    FLAC__STREAM_ENCODER_SEEK_STATUS_OK
443}
444
445#[derive(Debug)]
446pub enum EncoderError {
447    NoData,
448    InitializationError,
449    VerificationError,
450    InvalidCompressionLevel,
451    InvalidChannelCount,
452    InvalidSampleType,
453    TooManyOrTooFewSamples,
454    MismatchedSampleCountPerChannels,
455    FailedToInitializeEncoder,
456    InvalidVorbisComment(String),
457    FailedToSetMetadata,
458    EncodingError,
459    InvalidSampleRate,
460    NullCharInPath,
461}
462
463/// `f32` and `f64` in `[-1.0, 1.0]`.
464pub trait IntoSample: Copy + Default {
465    fn to_i16(&self) -> i16;
466    fn to_i20(&self) -> i32;
467    fn to_i24(&self) -> i32;
468
469    fn to_bps_level(&self, bps: BpsLevel) -> FLAC__int32 {
470        match bps {
471            BpsLevel::Bps16 => self.to_i16() as FLAC__int32,
472            BpsLevel::Bps20 => self.to_i20(),
473            BpsLevel::Bps24 => self.to_i24(),
474        }
475    }
476}
477
478impl IntoSample for f32 {
479    fn to_i16(&self) -> i16 {
480        let max = (1 << 15) - 1;
481        (self.clamp(-1.0, 1.0) * max as f32) as i16
482    }
483
484    fn to_i20(&self) -> i32 {
485        let max = (1 << 19) - 1;
486        ((self.clamp(-1.0, 1.0) * max as f32) as i32).clamp(-max, max)
487    }
488
489    fn to_i24(&self) -> i32 {
490        let max = (1 << 23) - 1;
491        ((self.clamp(-1.0, 1.0) * max as f32) as i32).clamp(-max, max)
492    }
493}
494
495impl IntoSample for f64 {
496    fn to_i16(&self) -> i16 {
497        let max = (1 << 15) - 1;
498        (self.clamp(-1.0, 1.0) * max as f64) as i16
499    }
500
501    fn to_i20(&self) -> i32 {
502        let max = (1 << 19) - 1;
503        ((self.clamp(-1.0, 1.0) * max as f64) as i32).clamp(-max, max)
504    }
505
506    fn to_i24(&self) -> i32 {
507        let max = (1 << 23) - 1;
508        ((self.clamp(-1.0, 1.0) * max as f64) as i32).clamp(-max, max)
509    }
510}