From 3b55294f1fc09a957ca63c3a9a718b1082b9a894 Mon Sep 17 00:00:00 2001 From: jakka Date: Wed, 4 Jun 2025 20:01:07 +0300 Subject: implemented flac reencoding. started working on md5sum implementation --- .gitignore | 4 +- Cargo.lock | 206 +++++++++++++++++++++++++++++++++++++++--------------------- Cargo.toml | 6 +- src/flac.rs | 151 +++++++++++++++----------------------------- src/main.rs | 4 +- 5 files changed, 195 insertions(+), 176 deletions(-) diff --git a/.gitignore b/.gitignore index ec31677..50319b0 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,6 @@ .vscode/launch.json target *.flac -*.wav \ No newline at end of file +*.wav +*.tmp + diff --git a/Cargo.lock b/Cargo.lock index 791e294..bec0084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,12 +23,6 @@ version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" -[[package]] -name = "arrayvec" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" - [[package]] name = "backtrace" version = "0.3.75" @@ -45,16 +39,29 @@ dependencies = [ ] [[package]] -name = "bitflags" -version = "1.3.2" +name = "bincode" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "serde", + "unty", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] [[package]] -name = "bytemuck" -version = "1.23.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" @@ -71,6 +78,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "claxon" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bfbf56724aa9eca8afa4fcfadeb479e722935bb2a0900c2d37e0cc477af0688" + [[package]] name = "cmake" version = "0.1.54" @@ -81,12 +94,23 @@ dependencies = [ ] [[package]] -name = "encoding_rs" -version = "0.8.35" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "cfg-if", + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", ] [[package]] @@ -98,6 +122,16 @@ dependencies = [ "libflac-sys", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gimli" version = "0.31.1" @@ -105,10 +139,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] -name = "lazy_static" -version = "1.5.0" +name = "hex" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "libc" @@ -127,10 +161,14 @@ dependencies = [ ] [[package]] -name = "log" -version = "0.4.27" +name = "md-5" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" +dependencies = [ + "cfg-if", + "digest", +] [[package]] name = "memchr" @@ -138,6 +176,16 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "metaflac" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf25a3451319c52a4a56d956475fbbb763bfb8420e2187d802485cb0fd8d965" +dependencies = [ + "byteorder", + "hex", +] + [[package]] name = "miniz_oxide" version = "0.8.8" @@ -162,14 +210,34 @@ 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", - "symphonia", - "symphonia-metadata", + "md-5", + "metaflac", "tokio", ] @@ -180,68 +248,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "symphonia" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "815c942ae7ee74737bb00f965fa5b5a2ac2ce7b6c01c0cc169bbeaf7abd5f5a9" -dependencies = [ - "lazy_static", - "symphonia-bundle-flac", - "symphonia-core", - "symphonia-metadata", -] - -[[package]] -name = "symphonia-bundle-flac" -version = "0.5.4" +name = "serde" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e34f34298a7308d4397a6c7fbf5b84c5d491231ce3dd379707ba673ab3bd97" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", + "serde_derive", ] [[package]] -name = "symphonia-core" -version = "0.5.4" +name = "serde_derive" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "798306779e3dc7d5231bd5691f5a813496dc79d3f56bf82e25789f2094e022c3" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ - "arrayvec", - "bitflags", - "bytemuck", - "lazy_static", - "log", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "symphonia-metadata" -version = "0.5.4" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc622b9841a10089c5b18e99eb904f4341615d5aa55bbf4eedde1be721a4023c" -dependencies = [ - "encoding_rs", - "lazy_static", - "log", - "symphonia-core", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "symphonia-utils-xiph" -version = "0.5.4" +name = "syn" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "484472580fa49991afda5f6550ece662237b00c6f562c7d9638d1b086ed010fe" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ - "symphonia-core", - "symphonia-metadata", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -254,6 +294,30 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "typenum" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 1017e32..72bc5fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +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 } -symphonia = { version = "0.5.4", default-features = false, features = ["flac"] } -symphonia-metadata = "0.5.4" +md-5 = "0.10.6" +metaflac = "0.2.8" tokio = "1.45.1" diff --git a/src/flac.rs b/src/flac.rs index 8b03664..1881756 100644 --- a/src/flac.rs +++ b/src/flac.rs @@ -1,130 +1,79 @@ use anyhow::{Result, anyhow}; use flac_bound::{FlacEncoder, WriteWrapper}; +use md5::{Digest, Md5}; +use metaflac::Tag; use std::fs::File; -use symphonia::core::{ - audio::SampleBuffer, - codecs::{DecoderOptions, CODEC_TYPE_NULL}, - errors::Error::DecodeError, - formats::{FormatOptions, Track}, - io::MediaSourceStream, - meta::MetadataOptions, - probe::{Hint, ProbeResult}, -}; #[derive(Debug)] struct StreamConfig { channels: u32, - bits_per_sample: Bps, + bits_per_sample: u32, sample_rate: u32, total_samples_estimate: u64, } -#[derive(Debug)] -enum Bps { - _16, - _24, - _32 -} - -fn get_probe(file: &std::path::Path) -> Result { - let src = std::fs::File::open(file)?; - let mss = MediaSourceStream::new(Box::new(src), Default::default()); - let mut hint = Hint::new(); - hint.with_extension("flac"); - let meta_opts: MetadataOptions = Default::default(); - let fmt_opts: FormatOptions = Default::default(); - Ok(symphonia::default::get_probe().format(&hint, mss, &fmt_opts, &meta_opts)?) -} - -fn read_streaminfo(track: &Track) -> Result { - let params = &track.codec_params; - - Ok(StreamConfig { - channels: params.channels.unwrap().count() as u32, - bits_per_sample: match params.bits_per_sample.unwrap() { - 16 => Bps::_16, - 24 => Bps::_24, - 32 => Bps::_32, - _ => return Err(anyhow!("invalid Bps")) - }, - sample_rate: params.sample_rate.unwrap(), - total_samples_estimate: params.n_frames.unwrap(), - }) -} - pub fn encode_file(file: &std::path::Path) -> Result<()> { - let probed = get_probe(file)?; - let mut format = probed.format; - let dec_opts: DecoderOptions = Default::default(); - let track = format - .tracks() - .iter() - .find(|t| t.codec_params.codec != CODEC_TYPE_NULL) - .unwrap(); - - let config = read_streaminfo(track)?; - - let mut outf = File::create(format!("{}.{}", file.display(), "tmp"))?; + let mut reader = claxon::FlacReader::open(file)?; + let config = StreamConfig { + channels: reader.streaminfo().channels, + bits_per_sample: 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(match config.bits_per_sample { - Bps::_16 => 16, - Bps::_24 => 24, - Bps::_32 => 32 - }) + .bits_per_sample(config.bits_per_sample) .sample_rate(config.sample_rate) .total_samples_estimate(config.total_samples_estimate) .compression_level(8) - .verify(true) + .verify(false) .init_write(&mut outw) .unwrap(); - let mut decoder = symphonia::default::get_codecs() - .make(&track.codec_params, &dec_opts) - .unwrap(); - let track_id = track.id; - - let mut sample_buf = None; + 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, 2048).unwrap(); + let _ = samples + .iter() + .map(|sample| { + for byte in sample.to_le_bytes() { + bytes.push(byte) + } + }) + .collect::>(); + hasher.update(&bytes); + bytes.clear(); + } - loop { - let packet = format.next_packet().unwrap(); + match enc.finish() { + Ok(_) => {} + Err(enc) => return Err(anyhow!("Encoding failed:\t{:?}", enc.state())), + } - if packet.track_id() != track_id { - continue; - } + /* let source_tags = Tag::read_from_path(file)?; + let mut target_tags = Tag::read_from_path(tempname)?; - match decoder.decode(&packet) { - Ok(audio_buf) => { - if sample_buf.is_none() { - let spec = *audio_buf.spec(); - let duration = audio_buf.capacity() as u64; - sample_buf = Some(SampleBuffer::::new(duration, spec)); + for block in source_tags.blocks() { + todo!() + } */ - /* match config.bits_per_sample { - Bps::_16 => sample_buf = Some(SampleBuffer::::new(duration, spec)), - Bps::_24 => sample_buf = Some(SampleBuffer::::new(duration, spec)), - Bps::_32 => sample_buf = Some(SampleBuffer::::new(duration, spec)), - } */ - } + let mut tags = Tag::read_from_path(tempname)?; + let mut streaminfo = tags.get_streaminfo().unwrap().clone(); + streaminfo.md5 = hasher.finalize()[..].to_vec(); + tags.set_streaminfo(streaminfo); + tags.save()?; - if let Some(buf) = &mut sample_buf { - buf.copy_interleaved_ref(audio_buf); - let mut samples = Vec::new(); - _ = buf.samples().iter().map(|sample| samples.push(*sample as i32)).collect::>(); - enc.process_interleaved(samples.as_slice(), samples.iter().len() as u32 / config.channels) - .unwrap(); - } - } - Err(DecodeError(_)) => (), - Err(_) => break, - } - } - - match enc.finish() { - Ok(_) => Ok(()), - Err(enc) => Err(anyhow!("Encoding failed:\t{:?}", enc.state())), - } + Ok(()) } diff --git a/src/main.rs b/src/main.rs index 6c4cb43..ba95650 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ mod flac; fn main() { - flac::encode_file(std::path::Path::new("./1.flac")).unwrap(); + if let Err(error) = flac::encode_file(std::path::Path::new("./1.flac")) { + println!("{}", error) + }; } -- cgit v1.3.1