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 {
32 Self::new(InputData::Planar(data), sample_rate)
33 }
34
35 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 pub fn compression_level(mut self, level: u32) -> Self {
54 self.compression_level = level;
55 self
56 }
57
58 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
463pub 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}