diff options
| author | jakka <jakkadoujin@gmail.com> | 2025-06-10 21:06:34 +0300 |
|---|---|---|
| committer | jakka <jakkadoujin@gmail.com> | 2025-06-10 21:06:34 +0300 |
| commit | 7aa03666d53522b91b72d77c669a84ee22cf2d98 (patch) | |
| tree | ebad86698ca0ca380bf98807989cc43a49e8f849 | |
| parent | 9450972d8414065b005827198340adcd8a3e864a (diff) | |
moved per-bitrate logic before the encoding loop
| -rw-r--r-- | src/flac.rs | 245 |
1 files changed, 169 insertions, 76 deletions
diff --git a/src/flac.rs b/src/flac.rs index 1c647d6..9b1b7f3 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -1,12 +1,13 @@ use anyhow::{Result, anyhow}; use flac_bound::{FlacEncoder, WriteWrapper}; use i24::i24; -use md5::{Digest, Md5}; +use md5::{Digest, Md5, Md5Core, digest::core_api::CoreWrapper}; use metaflac::{Block, Tag}; use std::fs::File; use symphonia::core::{ audio::{Audio, GenericAudioBufferRef}, - formats::{FormatOptions, TrackType, probe::Hint}, + codecs::audio::AudioDecoder, + formats::{FormatOptions, FormatReader, TrackType, probe::Hint}, io::MediaSourceStream, meta::MetadataOptions, }; @@ -42,6 +43,164 @@ impl Bps { } } +fn encode_cycle_16( + mut format: Box<dyn FormatReader>, + mut decoder: Box<dyn AudioDecoder + 'static>, + mut encoder: FlacEncoder, + hasher: &mut CoreWrapper<Md5Core>, +) -> Result<()> { + let mut buffer: Vec<i32> = Vec::new(); + let track_id = format.default_track(TrackType::Audio).unwrap().id; + + loop { + let packet = match format.next_packet() { + Ok(Some(packet)) => packet, + Ok(None) => break, + Err(error) => return Err(error.into()), + }; + + if packet.track_id() != track_id { + continue; + } + + match decoder.decode(&packet) { + Ok(audio_buf) => match audio_buf { + GenericAudioBufferRef::S32(buf) => { + let _ = buf + .iter_interleaved() + .map(|sample| { + let real_sample = sample >> 16; + hasher.update(i16::try_from(real_sample).unwrap().to_le_bytes()); + buffer.push(real_sample); + }) + .collect::<Vec<_>>(); + + encoder + .process_interleaved( + buffer.as_slice(), + u32::try_from(buf.samples_planar()).unwrap(), + ) + .unwrap(); + buffer.clear(); + } + _ => return Err(anyhow!("unsupported codec")), + }, + Err(err) => return Err(err.into()), + } + } + + if let Err(enc) = encoder.finish() { + return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); + } + + Ok(()) +} + +fn encode_cycle_24( + mut format: Box<dyn FormatReader>, + mut decoder: Box<dyn AudioDecoder + 'static>, + mut encoder: FlacEncoder, + hasher: &mut CoreWrapper<Md5Core>, +) -> Result<()> { + let mut buffer: Vec<i32> = Vec::new(); + let track_id = format.default_track(TrackType::Audio).unwrap().id; + + loop { + let packet = match format.next_packet() { + Ok(Some(packet)) => packet, + Ok(None) => break, + Err(error) => return Err(error.into()), + }; + + if packet.track_id() != track_id { + continue; + } + + match decoder.decode(&packet) { + Ok(audio_buf) => match audio_buf { + GenericAudioBufferRef::S32(buf) => { + let _ = buf + .iter_interleaved() + .map(|sample| { + let real_sample = sample >> 8; + hasher.update(i24::try_from(real_sample).unwrap().to_le_bytes()); + buffer.push(real_sample); + }) + .collect::<Vec<_>>(); + + encoder + .process_interleaved( + buffer.as_slice(), + u32::try_from(buf.samples_planar()).unwrap(), + ) + .unwrap(); + buffer.clear(); + } + _ => return Err(anyhow!("unsupported codec")), + }, + Err(err) => return Err(err.into()), + } + } + + if let Err(enc) = encoder.finish() { + return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); + } + + Ok(()) +} + +fn encode_cycle_32( + mut format: Box<dyn FormatReader>, + mut decoder: Box<dyn AudioDecoder + 'static>, + mut encoder: FlacEncoder, + hasher: &mut CoreWrapper<Md5Core>, +) -> Result<()> { + let mut buffer: Vec<i32> = Vec::new(); + let track_id = format.default_track(TrackType::Audio).unwrap().id; + + loop { + let packet = match format.next_packet() { + Ok(Some(packet)) => packet, + Ok(None) => break, + Err(error) => return Err(error.into()), + }; + + if packet.track_id() != track_id { + continue; + } + + match decoder.decode(&packet) { + Ok(audio_buf) => match audio_buf { + GenericAudioBufferRef::S32(buf) => { + let _ = buf + .iter_interleaved() + .map(|sample| { + hasher.update(sample.to_le_bytes()); + buffer.push(sample); + }) + .collect::<Vec<_>>(); + + encoder + .process_interleaved( + buffer.as_slice(), + u32::try_from(buf.samples_planar()).unwrap(), + ) + .unwrap(); + buffer.clear(); + } + _ => return Err(anyhow!("unsupported codec")), + }, + Err(err) => return Err(err.into()), + } + } + + if let Err(enc) = encoder.finish() { + return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); + } + + Ok(()) +} + pub fn encode_file(file: &std::path::Path) -> Result<()> { let src = std::fs::File::open(file)?; let mss = MediaSourceStream::new(Box::new(src), Default::default()); @@ -51,21 +210,19 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> { let format_opts: FormatOptions = Default::default(); let metadata_opts: MetadataOptions = Default::default(); - let mut format = symphonia::default::get_probe() + let format = symphonia::default::get_probe() .probe(&hint, mss, format_opts, metadata_opts) .unwrap(); let track = format.default_track(TrackType::Audio).unwrap(); - let mut decoder = symphonia::default::get_codecs() + let decoder = symphonia::default::get_codecs() .make_audio_decoder( track.codec_params.as_ref().unwrap().audio().unwrap(), &Default::default(), ) .unwrap(); - let track_id = track.id; - let params = track.codec_params.as_ref().unwrap().audio().unwrap(); let config = StreamConfig { @@ -78,7 +235,7 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> { let mut outf = File::create(&tempname)?; let mut outw = WriteWrapper(&mut outf); - let mut enc = FlacEncoder::new() + let enc = FlacEncoder::new() .unwrap() .channels(config.channels) .bits_per_sample(config.bits_per_sample.value()) @@ -90,75 +247,11 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> { let mut hasher = Md5::new(); - let mut sample_buf: Vec<i32> = Vec::new(); - - loop { - let packet = match format.next_packet() { - Ok(Some(packet)) => packet, - Ok(None) => break, - Err(error) => return Err(error.into()), - }; - - if packet.track_id() != track_id { - continue; - } - - match decoder.decode(&packet) { - Ok(audio_buf) => match audio_buf { - GenericAudioBufferRef::S32(buf) => { - match config.bits_per_sample { - Bps::_16 => { - let _ = buf - .iter_interleaved() - .map(|sample| { - let real_sample = - sample >> (32 - config.bits_per_sample.value()); - hasher - .update(i16::try_from(real_sample).unwrap().to_le_bytes()); - sample_buf.push(real_sample); - }) - .collect::<Vec<_>>(); - } - Bps::_24 => { - let _ = buf - .iter_interleaved() - .map(|sample| { - let real_sample = - sample >> (32 - config.bits_per_sample.value()); - hasher.update( - i24::try_from_i32(real_sample).unwrap().to_le_bytes(), - ); - sample_buf.push(real_sample); - }) - .collect::<Vec<_>>(); - } - Bps::_32 => { - let _ = buf - .iter_interleaved() - .map(|sample| { - hasher.update(sample.to_le_bytes()); - sample_buf.push(sample); - }) - .collect::<Vec<_>>(); - } - } - - enc.process_interleaved( - sample_buf.as_slice(), - u32::try_from(buf.samples_planar()).unwrap(), - ) - .unwrap(); - sample_buf.clear(); - } - _ => return Err(anyhow!("unsupported codec")), - }, - Err(err) => return Err(err.into()), - } - } - - if let Err(enc) = enc.finish() { - return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); - } + match config.bits_per_sample { + Bps::_16 => encode_cycle_16(format, decoder, enc, &mut hasher), + Bps::_24 => encode_cycle_24(format, decoder, enc, &mut hasher), + Bps::_32 => encode_cycle_32(format, decoder, enc, &mut hasher), + }?; let tags = Tag::read_from_path(file)?; let mut output = Tag::read_from_path(&tempname)?; |
