From a7615b9471dfe6fe94a3ace6e4f94e9c78844b51 Mon Sep 17 00:00:00 2001 From: jakka Date: Wed, 4 Jun 2025 21:30:40 +0300 Subject: added 16 and 24 bit md5sum calculation support --- Cargo.lock | 104 +++++++++++++++------------------------------- Cargo.toml | 2 +- src/flac.rs | 134 +++++++++++++++++++++++++++++++++++++++++++++++------------- src/main.rs | 64 +++++++++++++++++++++++++++-- 4 files changed, 200 insertions(+), 104 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bec0084..786a917 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,12 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + [[package]] name = "backtrace" version = "0.3.75" @@ -38,16 +44,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "bincode" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" -dependencies = [ - "serde", - "unty", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -57,6 +53,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bytemuck" +version = "1.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" + [[package]] name = "byteorder" version = "1.5.0" @@ -144,6 +146,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "i24" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe4e9f59fce450959a3bfe4a762ebba4afe2327ab3795ba11c489233c5d29733" +dependencies = [ + "bytemuck", + "num-traits", +] + [[package]] name = "libc" version = "0.2.172" @@ -195,6 +207,15 @@ dependencies = [ "adler2", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.7" @@ -210,32 +231,14 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - [[package]] name = "reencoder" version = "0.1.0" dependencies = [ "anyhow", - "bincode", "claxon", "flac-bound", + "i24", "md-5", "metaflac", "tokio", @@ -247,43 +250,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "serde" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.219" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "syn" -version = "2.0.101" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "tokio" version = "1.45.1" @@ -300,18 +272,6 @@ version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" - -[[package]] -name = "unty" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" - [[package]] name = "version_check" version = "0.9.5" diff --git a/Cargo.toml b/Cargo.toml index 72bc5fc..380c178 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,9 @@ edition = "2024" [dependencies] anyhow = "1.0.98" -bincode = { version = "2.0.1", default-features = false, features = ["std"] } claxon = "0.4.3" flac-bound = { version = "0.5.0", features = ["libflac-noogg"], default-features = false } +i24 = "2.1.0" md-5 = "0.10.6" metaflac = "0.2.8" tokio = "1.45.1" diff --git a/src/flac.rs b/src/flac.rs index 92c4b77..8653881 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -1,32 +1,126 @@ use anyhow::{Result, anyhow}; +use claxon::FlacReader; use flac_bound::{FlacEncoder, WriteWrapper}; +use i24::i24; use md5::{Digest, Md5}; use metaflac::Tag; use std::fs::File; -#[derive(Debug)] struct StreamConfig { channels: u32, - bits_per_sample: u32, + bits_per_sample: Bps, sample_rate: u32, total_samples_estimate: u64, } +enum Bps { + _16, + _24, + _32, +} + +impl Bps { + fn new(num: u32) -> Result { + match num { + 16 => Ok(Bps::_16), + 24 => Ok(Bps::_24), + 32 => Ok(Bps::_32), + _ => Err(anyhow!("Invalid BPS")), + } + } + + fn value(&self) -> u32 { + match self { + Bps::_16 => 16, + Bps::_24 => 24, + Bps::_32 => 32, + } + } +} + +fn process_samples_i16( + hasher: &mut impl Digest, + mut reader: FlacReader, + enc: &mut FlacEncoder, + config: &StreamConfig, +) -> Result<()> { + for samples in reader + .samples() + .map(|sample| sample.unwrap()) + .collect::>() + .chunks(4096) + { + enc.process_interleaved(samples, 4096 / config.channels) + .unwrap(); + let _ = samples + .iter() + .map(|sample| hasher.update((i16::try_from(*sample)).unwrap().to_le_bytes())) + .collect::>(); + } + Ok(()) +} + +fn process_samples_i24( + hasher: &mut impl Digest, + mut reader: FlacReader, + enc: &mut FlacEncoder, + config: &StreamConfig, +) -> Result<()> { + for samples in reader + .samples() + .map(|sample| sample.unwrap()) + .collect::>() + .chunks(4096) + { + enc.process_interleaved(samples, 4096 / config.channels) + .unwrap(); + let _ = samples + .iter() + .map(|sample| hasher.update((i24::try_from(*sample)).unwrap().to_le_bytes())) + .collect::>(); + } + Ok(()) +} + +fn process_samples_i32( + hasher: &mut impl Digest, + mut reader: FlacReader, + enc: &mut FlacEncoder, + config: &StreamConfig, +) -> Result<()> { + for samples in reader + .samples() + .map(|sample| sample.unwrap()) + .collect::>() + .chunks(4096) + { + enc.process_interleaved(samples, 4096 / config.channels) + .unwrap(); + let _ = samples + .iter() + .map(|sample| hasher.update(sample.to_le_bytes())) + .collect::>(); + } + Ok(()) +} + pub fn encode_file(file: &std::path::Path) -> Result<()> { - let mut reader = claxon::FlacReader::open(file)?; + let reader = claxon::FlacReader::open(file)?; let config = StreamConfig { channels: reader.streaminfo().channels, - bits_per_sample: reader.streaminfo().bits_per_sample, + bits_per_sample: Bps::new(reader.streaminfo().bits_per_sample)?, sample_rate: reader.streaminfo().sample_rate, total_samples_estimate: reader.streaminfo().samples.unwrap(), }; + let tempname = format!("{}.{}", file.display(), "tmp"); + let mut outf = File::create(&tempname)?; let mut outw = WriteWrapper(&mut outf); let mut enc = FlacEncoder::new() .unwrap() .channels(config.channels) - .bits_per_sample(config.bits_per_sample) + .bits_per_sample(config.bits_per_sample.value()) .sample_rate(config.sample_rate) .total_samples_estimate(config.total_samples_estimate) .compression_level(8) @@ -35,30 +129,15 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> { .unwrap(); let mut hasher = Md5::new(); - let mut bytes = Vec::new(); - for samples in reader - .samples() - .map(|sample| sample.unwrap()) - .collect::>() - .chunks(4096) - { - enc.process_interleaved(samples, 4096 / config.channels).unwrap(); - let _ = samples - .iter() - .map(|sample| { - for byte in sample.to_le_bytes() { - bytes.push(byte) - } - }) - .collect::>(); - hasher.update(&bytes); - bytes.clear(); - } + match config.bits_per_sample { + Bps::_16 => process_samples_i16(&mut hasher, reader, &mut enc, &config)?, + Bps::_24 => process_samples_i24(&mut hasher, reader, &mut enc, &config)?, + Bps::_32 => process_samples_i32(&mut hasher, reader, &mut enc, &config)?, + }; - match enc.finish() { - Ok(_) => {} - Err(enc) => return Err(anyhow!("Encoding failed:\t{:?}", enc.state())), + if let Err(enc) = enc.finish() { + return Err(anyhow!("Encoding failed:\t{:?}", enc.state())); } /* let source_tags = Tag::read_from_path(file)?; @@ -68,7 +147,6 @@ pub fn encode_file(file: &std::path::Path) -> Result<()> { for block in source_tags.blocks() { todo!() } */ - let mut tags = Tag::read_from_path(tempname)?; let mut streaminfo = tags.get_streaminfo().unwrap().clone(); streaminfo.md5 = hasher.finalize()[..].to_vec(); diff --git a/src/main.rs b/src/main.rs index ba95650..89343f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,65 @@ mod flac; fn main() { - if let Err(error) = flac::encode_file(std::path::Path::new("./1.flac")) { - println!("{}", error) - }; + todo!() +} + +#[cfg(test)] +mod tests { + use super::*; + use metaflac::Tag; + #[test] + fn bit16() { + flac::encode_file(std::path::Path::new("16bit.flac")).unwrap(); + let target_md5 = Tag::read_from_path("16bit.flac.tmp") + .unwrap() + .get_streaminfo() + .unwrap() + .md5 + .clone(); + let source_md5 = Tag::read_from_path("16bit.flac") + .unwrap() + .get_streaminfo() + .unwrap() + .md5 + .clone(); + std::fs::remove_file(std::path::Path::new("16bit.flac.tmp")).unwrap(); + assert_eq!(target_md5, source_md5); + } + #[test] + fn bit24() { + flac::encode_file(std::path::Path::new("24bit.flac")).unwrap(); + let target_md5 = Tag::read_from_path("24bit.flac.tmp") + .unwrap() + .get_streaminfo() + .unwrap() + .md5 + .clone(); + let source_md5 = Tag::read_from_path("24bit.flac") + .unwrap() + .get_streaminfo() + .unwrap() + .md5 + .clone(); + std::fs::remove_file(std::path::Path::new("24bit.flac.tmp")).unwrap(); + assert_eq!(target_md5, source_md5); + } + #[test] + fn bit32() { + flac::encode_file(std::path::Path::new("32bit.flac")).unwrap(); + let target_md5 = Tag::read_from_path("32bit.flac.tmp") + .unwrap() + .get_streaminfo() + .unwrap() + .md5 + .clone(); + let source_md5 = Tag::read_from_path("32bit.flac") + .unwrap() + .get_streaminfo() + .unwrap() + .md5 + .clone(); + std::fs::remove_file(std::path::Path::new("32bit.flac.tmp")).unwrap(); + assert_eq!(target_md5, source_md5); + } } -- cgit v1.3.1