diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/flac/decoder.rs | 139 | ||||
| -rw-r--r-- | src/flac/mod.rs (renamed from src/flac.rs) | 84 |
2 files changed, 196 insertions, 27 deletions
diff --git a/src/flac/decoder.rs b/src/flac/decoder.rs new file mode 100644 index 0000000..99ac36e --- /dev/null +++ b/src/flac/decoder.rs @@ -0,0 +1,139 @@ +use std::{ + error::Error, + ffi::{CString, c_void}, + fmt::Display, + path::Path, +}; + +use libflac_sys::{ + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED, + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, FLAC__STREAM_DECODER_INIT_STATUS_OK, + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, FLAC__StreamDecoder, + FLAC__StreamDecoderInitStatus, FLAC__bool, FLAC__stream_decoder_finish, + FLAC__stream_decoder_get_bits_per_sample, FLAC__stream_decoder_get_channels, + FLAC__stream_decoder_get_sample_rate, FLAC__stream_decoder_init_file, FLAC__stream_decoder_new, + FLAC__stream_decoder_process_single, FLAC__stream_decoder_set_md5_checking, +}; + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +#[repr(u32)] +pub enum FlacDecoderInitError { + AlreadyInitialized = FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED, + + ErrorOpeningFile = FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE, + + InvalidCallbacks = FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS, + + MemoryAllocationError = FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR, + + UnsupportedContainer = FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER, +} + +impl Error for FlacDecoderInitError {} + +impl Display for FlacDecoderInitError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self:?}") + } +} + +impl From<FlacDecoderInitError> for FLAC__StreamDecoderInitStatus { + fn from(val: FlacDecoderInitError) -> Self { + val as FLAC__StreamDecoderInitStatus + } +} + +impl TryFrom<FLAC__StreamDecoderInitStatus> for FlacDecoderInitError { + type Error = (); + + #[allow(non_upper_case_globals)] + fn try_from(raw: FLAC__StreamDecoderInitStatus) -> Result<FlacDecoderInitError, ()> { + Ok(match raw { + FLAC__STREAM_DECODER_INIT_STATUS_ALREADY_INITIALIZED => { + FlacDecoderInitError::AlreadyInitialized + } + FLAC__STREAM_DECODER_INIT_STATUS_ERROR_OPENING_FILE => { + FlacDecoderInitError::ErrorOpeningFile + } + FLAC__STREAM_DECODER_INIT_STATUS_INVALID_CALLBACKS => { + FlacDecoderInitError::InvalidCallbacks + } + FLAC__STREAM_DECODER_INIT_STATUS_MEMORY_ALLOCATION_ERROR => { + FlacDecoderInitError::MemoryAllocationError + } + FLAC__STREAM_DECODER_INIT_STATUS_UNSUPPORTED_CONTAINER => { + FlacDecoderInitError::UnsupportedContainer + } + _ => return Err(()), + }) + } +} + +fn convert_path(path: &Path) -> CString { + CString::new(path.to_str().expect("non-UTF-8 filename")).expect("filename has internal NULs") +} + +pub(crate) struct FlacDecoder(pub *mut FLAC__StreamDecoder); + +impl FlacDecoder { + pub(crate) fn new() -> Self { + FlacDecoder(unsafe { FLAC__stream_decoder_new() }) + } + + pub(crate) fn init_decode_from_file<P: AsRef<Path>>( + &self, + file: &P, + buf: &mut Vec<i32>, + ) -> Result<(), FlacDecoderInitError> { + unsafe { + FLAC__stream_decoder_set_md5_checking(self.0, true as FLAC__bool); + } + + let filename = convert_path(file.as_ref()); + unsafe { + let result: FLAC__StreamDecoderInitStatus = FLAC__stream_decoder_init_file( + self.0, + filename.as_ptr(), + None, + None, + None, + buf.as_mut_ptr() as *mut c_void, + ); + if result != FLAC__STREAM_DECODER_INIT_STATUS_OK { + return Err(FlacDecoderInitError::try_from(result).unwrap()); + } + } + + Ok(()) + } + + pub(crate) fn decode_frame(&self) -> Result<(), ()> { + if unsafe { FLAC__stream_decoder_process_single(self.0) } != 0 { + Ok(()) + } else { + Err(()) + } + } + + pub(crate) fn get_channels(&self) -> u32 { + unsafe { FLAC__stream_decoder_get_channels(self.0) } + } + + pub(crate) fn get_bps(&self) -> u32 { + unsafe { FLAC__stream_decoder_get_bits_per_sample(self.0) } + } + + pub(crate) fn get_samplerate(&self) -> u32 { + unsafe { FLAC__stream_decoder_get_sample_rate(self.0) } + } +} + +impl Drop for FlacDecoder { + fn drop(&mut self) { + if !(self.0.is_null()) { + unsafe { FLAC__stream_decoder_finish(self.0) }; + } + } +} diff --git a/src/flac.rs b/src/flac/mod.rs index 05d915f..7a79e44 100644 --- a/src/flac.rs +++ b/src/flac/mod.rs @@ -1,31 +1,25 @@ +mod decoder; + use anyhow::{Result, anyhow}; use flac_bound::{FlacEncoder, WriteWrapper}; -use md5::{Digest, Md5}; -use metaflac::{Block, Tag}; -use std::{ - fs::File, - path::{Path, PathBuf}, -}; -use symphonia::core::{ - audio::{Audio, GenericAudioBufferRef}, - codecs::audio::AudioDecoder, - formats::{FormatOptions, FormatReader, TrackType, probe::Hint}, - io::MediaSourceStream, - meta::MetadataOptions, -}; +/* use md5::Digest; */ +use metaflac::Tag; +use std::{fs::File, path::Path}; + +use crate::flac::decoder::FlacDecoder; pub const CURRENT_VENDOR: &str = "reference libFLAC 1.5.0 20250211"; -type BoxedFormatReader = Box<dyn FormatReader>; -type BoxedAudioDecoder = Box<dyn AudioDecoder + 'static>; +/* type BoxedFormatReader = Box<dyn FormatReader>; +type BoxedAudioDecoder = Box<dyn AudioDecoder + 'static>; */ -struct StreamConfig { +/* pub struct StreamConfig { channels: u32, bits_per_sample: Bps, sample_rate: u32, } -enum Bps { +pub enum Bps { _16, _24, _32, @@ -48,9 +42,9 @@ impl Bps { Bps::_32 => 32, } } -} +} */ -struct FileEncoder { +/* struct FileEncoder { filename: PathBuf, streamdata: StreamConfig, format: BoxedFormatReader, @@ -91,8 +85,16 @@ impl FileEncoder { if let GenericAudioBufferRef::S32(buf) = self.decoder.decode(&packet)? { for sample in buf.iter_interleaved() { - let real_sample = sample >> (32 - offset); - hasher.update(real_sample.to_le_bytes()); + let mut real_sample = sample; + if offset != 32 { + real_sample = sample >> (32 - offset) + } + match offset { + 16 => hasher.update(i16::try_from(real_sample)?.to_le_bytes()), + 24 => hasher + .update(i24::i24::try_from_i32(real_sample).unwrap().to_le_bytes()), + _ => hasher.update(real_sample.to_le_bytes()), + } buffer.push(real_sample); } encoder @@ -172,10 +174,10 @@ fn init_decoder( }; Ok((format, decoder, config)) -} +} */ pub fn encode_file(filename: impl AsRef<Path>) -> Result<()> { - let mut filencoder = FileEncoder::new(filename)?; + /* let mut filencoder = FileEncoder::new(filename)?; let temp_name = filencoder.temp_name(); if temp_name.exists() { @@ -197,7 +199,32 @@ pub fn encode_file(filename: impl AsRef<Path>) -> Result<()> { let hash = filencoder.encode(enc)?; filencoder.write_tags(hash)?; std::fs::rename(filencoder.temp_name(), filencoder.filename)?; - Ok(()) + Ok(()) */ + + let decoder = FlacDecoder::new(); + let mut buffer: Vec<i32> = Vec::new(); + decoder.init_decode_from_file(&filename, &mut buffer)?; + + let temp_name = filename.as_ref().with_extension("tmp"); + let mut outf = File::create(temp_name)?; + let mut outw = WriteWrapper(&mut outf); + let mut enc = FlacEncoder::new() + .unwrap() + .channels(decoder.get_channels()) + .bits_per_sample(decoder.get_bps()) + .sample_rate(decoder.get_samplerate()) + .compression_level(8) + .verify(false) + .init_write(&mut outw) + .unwrap(); + + while decoder.decode_frame().is_ok() { + enc.process_interleaved(&buffer, buffer.iter().len() as u32 / decoder.get_channels()) + .unwrap(); + buffer.clear(); + } + + todo!() } pub fn get_vendor(file: impl AsRef<Path>) -> Result<String> { @@ -231,7 +258,8 @@ mod tests { .unwrap() .md5 .clone(); - std::fs::remove_file(tempname).unwrap(); + std::fs::remove_file(name).unwrap(); + std::fs::rename(tempname, name).unwrap(); assert_eq!(target_md5, encoded_md5); } @@ -253,7 +281,8 @@ mod tests { .unwrap() .md5 .clone(); - std::fs::remove_file(tempname).unwrap(); + std::fs::remove_file(name).unwrap(); + std::fs::rename(tempname, name).unwrap(); assert_eq!(target_md5, encoded_md5); } @@ -275,7 +304,8 @@ mod tests { .unwrap() .md5 .clone(); - std::fs::remove_file(tempname).unwrap(); + std::fs::remove_file(name).unwrap(); + std::fs::rename(tempname, name).unwrap(); assert_eq!(target_md5, encoded_md5); } } |
