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 pub fn from_planar(data: &'data [Vec<Sample>], sample_rate: u32) -> Self {
33 Self::new(InputData::Planar(data), sample_rate)
34 }
35
36 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 pub fn compression_level(mut self, level: u32) -> Self {
56 self.compression_level = level;
57 self
58 }
59
60 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
465pub 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}