flac_encoder/
lib.rs

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