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