From 6b6a6ae25ee96661512fe72f52cc25b0e636393d Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 2 Aug 2016 15:10:14 +0300 Subject: [PATCH 1/5] Separate config module. --- Cargo.lock | 10 ++-- Cargo.toml | 2 +- src/config.rs | 100 ++++++++++++++++++++++++++++++++++ src/main.rs | 148 ++++++++++++-------------------------------------- 4 files changed, 140 insertions(+), 120 deletions(-) create mode 100644 src/config.rs diff --git a/Cargo.lock b/Cargo.lock index cb97305..bcc5025 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,8 +1,8 @@ [root] name = "zrtstr" -version = "0.1.0" +version = "0.1.2" dependencies = [ - "clap 2.9.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "hound 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)", "pbr 0.2.1 (git+https://github.com/a8m/pb.git)", @@ -10,15 +10,15 @@ dependencies = [ [[package]] name = "bitflags" -version = "0.5.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "clap" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/Cargo.toml b/Cargo.toml index ff94a66..6305dfb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "zrtstr" -version = "0.1.0" +version = "0.1.2" authors = ["Kirill I. "] exclude = [ diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..28f72ef --- /dev/null +++ b/src/config.rs @@ -0,0 +1,100 @@ +extern crate clap; + +use clap::{Arg, App}; +use std::fs; +use std::process; + +static LICENSE: &'static str = "zrtstr licensed under GNU General Public License version 2 or later; +Full License available at . +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law."; + +pub struct Conf { + pub dither: u32, + pub dry_run: bool, + pub no_overwrites: bool, +} + +pub fn get() -> (Option, Conf) { + let matches = App::new("zrtstr") + .author(crate_authors!()) + .version(crate_version!()) + .about("Check stereo WAV-file for identical channels, detecting \ + faux-stereo files generated by some audio-editing software and \ + DAWs.\nOutputs a true-mono WAV file on detection of \ + faux-stereo. Takes left channel of the input file, writes in the \ + same location with -MONO suffix in file name.") + .arg(Arg::with_name("INPUT") + .help("Path to the input file to process. If no input given, \ + process all WAV files in current directory.") + .index(1) + .validator(validate_path)) + .arg(Arg::with_name("dither") + .short("d") + .long("dither") + .value_name("THRESHOLD") + .required(false) + .help("Set threshold for sample level difference to process \ + dithered audio.{n}Positive number (amplitude delta).") + .next_line_help(true) + .takes_value(true) + .default_value("10") + .validator(validate_u32)) + .arg(Arg::with_name("license") + .short("l") + .long("license") + .help("Display the license information.")) + .arg(Arg::with_name("dry_run") + .short("n") + .long("nowrite") + .help("Disable saving converted mono files. Analyze pass only.") + .required(false)) + .arg(Arg::with_name("no_overwrites") + .short("p") + .long("protect") + .help("Disable overwriting when saving converted mono files.") + .required(false)) + .get_matches(); + + if matches.is_present("license") { + println!("{}", LICENSE); + println!("\nclap (Command Line Argument Parser) License:"); + println!(include_str!("../LICENSE-CLAP")); + process::exit(0); + } + + let conf = Conf { + dither: if matches.is_present("dither") { + value_t!(matches, "dither", u32).unwrap_or(10) + } else { + 0 + }, + dry_run: matches.is_present("dry_run"), + no_overwrites: matches.is_present("no_overwrites"), + }; + let input_fname = if matches.is_present("INPUT") { + Some(matches.value_of("INPUT").unwrap().to_string()) + } else { + None + }; + (input_fname, conf) +} + +pub fn validate_u32(value: String) -> Result<(), String> { + value.parse::() + .map(|_| ()) + .map_err(|_| "Supplied parameter is not a valid number!".to_owned()) +} + +pub fn validate_path(path: String) -> Result<(), String> { + if fs::metadata(&path).is_ok() { + if path.to_lowercase().ends_with(".wav") { + Ok(()) + } else { + // clap automatically adds "error: " to the beginning of the message. + Err(String::from("Input file must have .wav extension!")) + } + } else { + Err(String::from("Input file doesn't exist")) + } +} diff --git a/src/main.rs b/src/main.rs index 31ef01d..e5aaddb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,141 +9,61 @@ extern crate pbr; #[macro_use] extern crate clap; +mod config; + use std::path::Path; use std::fs::{remove_file, read_dir}; use std::env; use hound::{WavReader, WavSpec, WavWriter, SampleFormat}; use itertools::Itertools; use pbr::ProgressBar; -use clap::{Arg, App}; - -struct Conf { - dither: u32, - dry_run: bool, - no_overwrites: bool, -} -static LICENSE: &'static str = "zrtstr licensed under GNU General Public License version 2 or later; -Full License available at . -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law."; +use config::{Conf, validate_path}; fn main() { - let matches = App::new("zrtstr") - .author(crate_authors!()) - .version(crate_version!()) - .about("Check stereo WAV-file for identical channels, detecting \ - faux-stereo files generated by some audio-editing software and \ - DAWs.\nOutputs a true-mono WAV file on detection of \ - faux-stereo. Takes left channel of the input file, writes in the \ - same location with -MONO suffix in file name.") - .arg(Arg::with_name("INPUT") - .help("Path to the input file to process. If no input given, \ - process all WAV files in current directory.") - .index(1) - .validator(validate_path)) - .arg(Arg::with_name("dither") - .short("d") - .long("dither") - .value_name("THRESHOLD") - .required(false) - .help("Set threshold for sample level difference to process \ - dithered audio.{n}Positive number (amplitude delta).") - .next_line_help(true) - .takes_value(true) - .default_value("10") - .validator(validate_u32)) - .arg(Arg::with_name("license") - .short("l") - .long("license") - .help("Display the license information.")) - .arg(Arg::with_name("dry_run") - .short("n") - .long("nowrite") - .help("Disable saving converted mono files. Analyze pass only.") - .required(false)) - .arg(Arg::with_name("no_overwrites") - .short("p") - .long("protect") - .help("Disable overwriting when saving converted mono files.") - .required(false)) - .get_matches(); + let (input_fname, conf) = config::get(); - if matches.is_present("license") { - println!("{}", LICENSE); - println!("\nclap (Command Line Argument Parser) License:"); - println!(include_str!("../LICENSE-CLAP")); - std::process::exit(0); - } - - let config = Conf { - dither: if matches.is_present("dither") { - value_t!(matches, "dither", u32).unwrap_or(10) - } else { - 0 - }, - dry_run: matches.is_present("dry_run"), - no_overwrites: matches.is_present("no_overwrites"), - }; - if config.dither > 100 { + if conf.dither > 100 { println!(" ! Dither threshold probably set too high! False positives possible.") } - - if matches.is_present("INPUT") { - // If a file name was given, process - let input_fname = matches.value_of("INPUT").unwrap(); - match process_file(input_fname, &config) { - Ok(_) => {} - Err(err) => println!("{}", err.to_string()), - } - } else { - // If no file name present, process all WAVs in current dir - println!("No input file given. Processing current directory."); - let current_dir = env::current_dir().unwrap(); - for dir_entry in read_dir(current_dir).expect("Can't read current directory") { - match dir_entry.map(|entry| { - // scan each directory entry, if accessible - let path_str = entry.path().to_str().unwrap_or("").to_string(); //get path String - validate_path(path_str.clone()).map(|_| { - // if it has wav extension - match process_file(&path_str, &config) {// process path - Ok(_) => Ok(()), - Err(err) => Err(err.to_string()), - } - }) - }) { + match input_fname { + Some(fname) => { + match process_file(&fname, &conf) { Ok(_) => {} Err(err) => println!("{}", err.to_string()), } + }, + None => { + // If no file name present, process all WAVs in current dir + println!("No input file given. Processing current directory."); + let current_dir = env::current_dir().unwrap(); + for dir_entry in read_dir(current_dir).expect("Can't read current directory") { + match dir_entry.map(|entry| { + // scan each directory entry, if accessible + let path_str = entry.path().to_str().unwrap_or("").to_string(); //get path String + validate_path(path_str.clone()).map(|_| { + // if it has wav extension + match process_file(&path_str, &conf) {// process path + Ok(_) => Ok(()), + Err(err) => Err(err.to_string()), + } + }) + }) { + Ok(_) => {} + Err(err) => println!("{}", err.to_string()), + } + } } } } -fn validate_u32(value: String) -> Result<(), String> { - value.parse::() - .map(|_| ()) - .map_err(|_| "Supplied parameter is not a valid number!".to_owned()) -} - -fn validate_path(path: String) -> Result<(), String> { - if std::fs::metadata(&path).is_ok() { - if path.to_lowercase().ends_with(".wav") { - Ok(()) - } else { - // clap automatically adds "error: " to the beginning of the message. - Err(String::from("Input file must have .wav extension!")) - } - } else { - Err(String::from("Input file doesn't exist")) - } -} fn read_file(fname: &str) -> Result>, String> { WavReader::open(fname).map_err(|err| err.to_string()) } -fn process_file(fname: &str, config: &Conf) -> Result<(), String> { +fn process_file(fname: &str, conf: &Conf) -> Result<(), String> { println!("--- Analyzing: {}", fname); let reader = try!(read_file(fname)); let spec = reader.spec(); @@ -155,13 +75,13 @@ fn process_file(fname: &str, config: &Conf) -> Result<(), String> { return Err(String::from("IEEE Float files are not supported! Exiting")); } - if zero_test(reader, config.dither) { + if zero_test(reader, conf.dither) { println!("\nFile is not double mono, channels are different!"); Ok(()) } else { println!("\nChannels are identical! Faux stereo detected"); - if !config.dry_run { - try!(copy_to_mono(fname, &spec, config.no_overwrites)); + if !conf.dry_run { + try!(copy_to_mono(fname, &spec, conf.no_overwrites)); } Ok(()) } @@ -209,7 +129,7 @@ fn zero_test(mut reader: WavReader, dither_threshold: u32) .any(|diff| comparator(diff)) //Actual comparison via closure } -// Copy left channel of the input file to mono wav +/// Copy left channel of the input file to mono wav fn copy_to_mono(input_fname: &str, spec: &WavSpec, no_overwrites: bool) -> Result<(), String> { println!(" * Converting to true-mono..."); From fe562955763f1701ce130a71d0d5848ae08e016a Mon Sep 17 00:00:00 2001 From: Kirill Date: Tue, 2 Aug 2016 17:07:06 +0300 Subject: [PATCH 2/5] Abstracting sample copying logic to macros. --- src/main.rs | 90 +++++++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/src/main.rs b/src/main.rs index e5aaddb..96c42f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -58,7 +58,6 @@ fn main() { } } - fn read_file(fname: &str) -> Result>, String> { WavReader::open(fname).map_err(|err| err.to_string()) } @@ -109,26 +108,28 @@ fn zero_test(mut reader: WavReader, dither_threshold: u32) // Read pairs of samples, update progress bar each progress_chunk iteration. reader.samples::() - .zip(progress_iter) - .batching(|mut it| { - match it.next() { - None => None, - Some(x) => { - match it.next() { - None => None, - Some(y) => { - if y.1 >= progress_chunk { - pb.add(progress_chunk); - }; - Some(x.0.unwrap() - y.0.unwrap()) - } - } - } - } - }) - .any(|diff| comparator(diff)) //Actual comparison via closure + .zip(progress_iter) + .batching(|mut it| { + match it.next() { + None => None, + Some(x) => { + match it.next() { + None => None, + Some(y) => { + if y.1 >= progress_chunk { + pb.add(progress_chunk); + }; + Some(x.0.unwrap() - y.0.unwrap()) + } + } + } + } + }) + .any(|diff| comparator(diff)) //Actual comparison via closure } + + /// Copy left channel of the input file to mono wav fn copy_to_mono(input_fname: &str, spec: &WavSpec, no_overwrites: bool) -> Result<(), String> { println!(" * Converting to true-mono..."); @@ -155,50 +156,37 @@ fn copy_to_mono(input_fname: &str, spec: &WavSpec, no_overwrites: bool) -> Resul let mut writer = try!(WavWriter::create(&output_path, new_spec).map_err(|err| err.to_string())); let mut error_occurred = false; - match spec.bits_per_sample { - 8 => { - for sample in reader.samples::().step(2) { - if writer.write_sample(sample.unwrap()).is_err() { - error_occurred = true; - println!("Failed to write sample"); - break; - } - } - } - 16 => { - for sample in reader.samples::().step(2) { - if writer.write_sample(sample.unwrap()).is_err() { - error_occurred = true; + + // Macros for abstracting sample-copying logic, streaming from reader to writer + macro_rules! stream_samples { + ($num:ty, $reader:ident, $writer:ident, $error:ident) => { + for sample in $reader.samples::<$num>().step(2) { + if $writer.write_sample(sample.unwrap()).is_err() { + $error = true; println!("Failed to write sample"); break; } } } + } + + match spec.bits_per_sample { + 8 => stream_samples!(i8, reader, writer, error_occurred), + 16 => stream_samples!(i16, reader, writer, error_occurred), 24 | 32 => { match spec.sample_format { - SampleFormat::Float => { - for sample in reader.samples::().step(2) { - if writer.write_sample(sample.unwrap()).is_err() { - error_occurred = true; - println!("Failed to write sample"); - break; - } - }}, - SampleFormat::Int => { - for sample in reader.samples::().step(2) { - if writer.write_sample(sample.unwrap()).is_err() { - error_occurred = true; - println!("Failed to write sample"); - break; - } - }} + SampleFormat::Float => + stream_samples!(f32, reader, writer, error_occurred), + SampleFormat::Int => + stream_samples!(i32, reader, writer, error_occurred), } - } + }, _ => { error_occurred = true; println!("Can't write a file! Unsupported sample rate requested!"); } - }; + } + if writer.finalize().is_err() { error_occurred = true; println!("Failed to finalize wav file"); From 8ef5dedc64f4b558d6cd52e18a476c018bbb8129 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 11 Aug 2016 13:49:35 +0300 Subject: [PATCH 3/5] feat: Support reading IEEE Float files fix: Showing errors when processing multiple files --- .gitignore | 1 + Cargo.lock | 12 +++++ src/main.rs | 150 +++++++++++++++++++++++++++++++++------------------- 3 files changed, 110 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index 61817bc..53e0247 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ target Thumbs.db +reaper diff --git a/Cargo.lock b/Cargo.lock index bcc5025..b906d7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,3 +82,15 @@ name = "winapi-build" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[metadata] +"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" +"checksum clap 2.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6adb6a046b8155874daf331e6cb6f4a3edf3ea3cbc625809eb4077a384124761" +"checksum hound 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e971fe26207d3ccdc66806fd9154508b28101fccb53fe152695e3ebcb53bd0f" +"checksum itertools 0.4.16 (registry+https://github.com/rust-lang/crates.io-index)" = "ac6e56e7cfd710efcf4c4f614bd101794845d9fe5f406b87ac5108b9153d033f" +"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum libc 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "39dfaaa0f4da0f1a06876c5d94329d739ad0150868069cc235f1ddf80a0480e7" +"checksum pbr 0.2.1 (git+https://github.com/a8m/pb.git)" = "" +"checksum time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "3c7ec6d62a20df54e07ab3b78b9a3932972f4b7981de295563686849eb3989af" +"checksum vec_map 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cac5efe5cb0fa14ec2f84f83c701c562ee63f6dcc680861b21d65c682adfb05f" +"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" +"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/src/main.rs b/src/main.rs index 96c42f1..7d9d127 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ // TODO: -// * IEEE Float support +// * IEEE Float writing support // * Tests -// * Refactor copy_to_mono nestiness. extern crate hound; extern crate itertools; @@ -33,25 +32,30 @@ fn main() { Ok(_) => {} Err(err) => println!("{}", err.to_string()), } - }, + } None => { // If no file name present, process all WAVs in current dir println!("No input file given. Processing current directory."); let current_dir = env::current_dir().unwrap(); for dir_entry in read_dir(current_dir).expect("Can't read current directory") { - match dir_entry.map(|entry| { + match dir_entry { // scan each directory entry, if accessible - let path_str = entry.path().to_str().unwrap_or("").to_string(); //get path String - validate_path(path_str.clone()).map(|_| { - // if it has wav extension - match process_file(&path_str, &conf) {// process path - Ok(_) => Ok(()), - Err(err) => Err(err.to_string()), + Ok(entry) => { + // get path String + let path_str = entry.path().to_str().unwrap_or("").to_string(); + let res = if validate_path(path_str.clone()).is_ok() { + // if file has wav extension + process_file(&path_str, &conf) + } else { + // ignore other files + Ok(()) + }; + match res { + Ok(_) => {}, + Err(err) => println!("{}", err.to_string()), } - }) - }) { - Ok(_) => {} - Err(err) => println!("{}", err.to_string()), + } + Err(_) => {} } } } @@ -70,32 +74,23 @@ fn process_file(fname: &str, conf: &Conf) -> Result<(), String> { if spec.channels != 2 { return Err(String::from("File is not stereo! Exiting")); } - if spec.sample_format == SampleFormat::Float { - return Err(String::from("IEEE Float files are not supported! Exiting")); - } - if zero_test(reader, conf.dither) { + if zero_test(reader, conf.dither, spec.sample_format) { println!("\nFile is not double mono, channels are different!"); - Ok(()) } else { println!("\nChannels are identical! Faux stereo detected"); if !conf.dry_run { try!(copy_to_mono(fname, &spec, conf.no_overwrites)); } - Ok(()) } + Ok(()) } /// Check if data in each pair of samples is identical, or lies within given difference -fn zero_test(mut reader: WavReader, dither_threshold: u32) -> bool { - - // Define a closure which compares the difference of two samples. - // If dither_threshold is given, compare to it, else it must be 0 - let comparator: Box bool> = if dither_threshold == 0 { - Box::new(|x: i32| x != 0) - } else { - Box::new(|x: i32| x.abs() as u32 > dither_threshold) - }; +fn zero_test(mut reader: WavReader, + dither_threshold: u32, + format: SampleFormat) + -> bool { let dur_samples = reader.duration(); let progress_chunk = dur_samples as u64 / 100; @@ -106,34 +101,84 @@ fn zero_test(mut reader: WavReader, dither_threshold: u32) // Initialize progress bar let mut pb = ProgressBar::new(dur_samples as u64); - // Read pairs of samples, update progress bar each progress_chunk iteration. - reader.samples::() - .zip(progress_iter) - .batching(|mut it| { - match it.next() { - None => None, - Some(x) => { + match format { + // TODO: (?) Write a macro to unify logic for both formats + + // Process INT samples + SampleFormat::Int => { + // Define a closure which compares the difference of two samples. + // If dither_threshold is given, compare to it, else it must be 0 + let comparator: Box bool> = if dither_threshold == 0 { + Box::new(|x: i32| x != 0) + } else { + Box::new(|x: i32| x.abs() as u32 > dither_threshold) + }; + + reader.samples::() + .zip(progress_iter) + .batching(|mut it| { match it.next() { None => None, - Some(y) => { - if y.1 >= progress_chunk { - pb.add(progress_chunk); - }; - Some(x.0.unwrap() - y.0.unwrap()) + Some(x) => { + match it.next() { + None => None, + Some(y) => { + if y.1 >= progress_chunk { + pb.add(progress_chunk); + }; + Some(x.0.unwrap() - y.0.unwrap()) + } + } } } - } - } - }) - .any(|diff| comparator(diff)) //Actual comparison via closure + }) + .any(|diff| comparator(diff)) //Actual comparison via closure + } + // Process FLOAT samples + SampleFormat::Float => { + // Define a closure which compares the difference of two samples. + // If dither_threshold is given, compare to it, else it must be 0 + let comparator: Box bool> = if dither_threshold == 0 { + Box::new(|x: f32| x != 0f32) + } else { + // Average 16-bit dither sample is ~ 0.000117 + // However, fluctuations are quite high, short tests showed + // x10 multiplier (=default one) for the Threshold to be reasonable. + // !! Needs more research! + Box::new(|x: f32| x.abs() > dither_threshold as f32 * 0.000117f32) + }; + reader.samples::() + .zip(progress_iter) + .batching(|mut it| { + match it.next() { + None => None, + Some(x) => { + match it.next() { + None => None, + Some(y) => { + if y.1 >= progress_chunk { + pb.add(progress_chunk); + }; + Some(x.0.unwrap() - y.0.unwrap()) + } + } + } + } + }) + .any(|diff| comparator(diff)) //Actual comparison via closure + } + } } - - /// Copy left channel of the input file to mono wav fn copy_to_mono(input_fname: &str, spec: &WavSpec, no_overwrites: bool) -> Result<(), String> { println!(" * Converting to true-mono..."); + // TODO: Remove as Hound supports writing float data + if spec.sample_format == SampleFormat::Float { + return Err(" ! Writing IEEE_FLOAT files not implemented yet!".to_string()); + } + let new_spec = WavSpec { channels: 1, sample_rate: spec.sample_rate, @@ -175,12 +220,11 @@ fn copy_to_mono(input_fname: &str, spec: &WavSpec, no_overwrites: bool) -> Resul 16 => stream_samples!(i16, reader, writer, error_occurred), 24 | 32 => { match spec.sample_format { - SampleFormat::Float => - stream_samples!(f32, reader, writer, error_occurred), - SampleFormat::Int => - stream_samples!(i32, reader, writer, error_occurred), + SampleFormat::Float => unreachable!(), + // stream_samples!(f32, reader, writer, error_occurred), + SampleFormat::Int => stream_samples!(i32, reader, writer, error_occurred), } - }, + } _ => { error_occurred = true; println!("Can't write a file! Unsupported sample rate requested!"); @@ -200,7 +244,7 @@ fn copy_to_mono(input_fname: &str, spec: &WavSpec, no_overwrites: bool) -> Resul Err(format!("Failed writing \"{}\"", output_path.to_str().unwrap())) } else { println!("\"{}\" successfully written!", - output_path.to_str().unwrap()); + output_path.to_str().unwrap()); Ok(()) } } From 686725903bea298d0c0e89f3f02ecfc4257781a5 Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 11 Aug 2016 13:57:25 +0300 Subject: [PATCH 4/5] docs: README updates --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5583354..11bed47 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,8 @@ and the executable will be in `target/release/zrtstr`. ## TODO Here are the current problems and planned features: -- [ ] **Float support**: Add support for IEEE Float files +- [ ] **Float support**: Add writing support for IEEE Float files (upstream-dependent) - [ ] **Add automatic tests** -- [ ] **Refactor excessive copy-pasted nested blocks in code** ## Contributing ## This is an enthusiast project, so any help and/or critique will be much appreciated. @@ -45,4 +44,4 @@ Feel free to file a bug report or feature request via [Issues](https://github.co ## License ## **zrtstr** licensed under GNU General Public License version 2 or later; -See `LICENSE` file for full details. +See `LICENSE` file for full details. \ No newline at end of file From 89bb6b85a6e103a4a052243c1bc2a7c8f3446cfc Mon Sep 17 00:00:00 2001 From: Kirill Date: Thu, 11 Aug 2016 15:13:42 +0300 Subject: [PATCH 5/5] refactor: Per Clippy warnings fix: Small progress bar fixes --- src/main.rs | 61 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/src/main.rs b/src/main.rs index 7d9d127..6316a5d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,24 +38,20 @@ fn main() { println!("No input file given. Processing current directory."); let current_dir = env::current_dir().unwrap(); for dir_entry in read_dir(current_dir).expect("Can't read current directory") { - match dir_entry { + if let Ok(entry) = dir_entry { // scan each directory entry, if accessible - Ok(entry) => { - // get path String - let path_str = entry.path().to_str().unwrap_or("").to_string(); - let res = if validate_path(path_str.clone()).is_ok() { - // if file has wav extension - process_file(&path_str, &conf) - } else { - // ignore other files - Ok(()) - }; - match res { - Ok(_) => {}, - Err(err) => println!("{}", err.to_string()), - } - } - Err(_) => {} + // get path String + let path_str = entry.path().to_str().unwrap_or("").to_string(); + let res = if validate_path(path_str.clone()).is_ok() { + // if file has wav extension + process_file(&path_str, &conf) + } else { + // ignore other files + Ok(()) + }; + if let Err(err) = res { + println!("{}", err.to_string()); + }; } } } @@ -93,17 +89,26 @@ fn zero_test(mut reader: WavReader, -> bool { let dur_samples = reader.duration(); - let progress_chunk = dur_samples as u64 / 100; - let progress_iter = (1..progress_chunk + 1).cycle(); + let progress_chunk = dur_samples / 100; // println!("Duration in samples={}, Sample rate={}",reader.duration(),spec.sample_rate); // Initialize progress bar let mut pb = ProgressBar::new(dur_samples as u64); + // Closure for updating progress bar + let mut update_pb = |sample_num: usize| { + if sample_num as u32 % progress_chunk == 0 { + pb.add(progress_chunk as u64); + } else if sample_num as u32 == dur_samples - 1 { + // - 1 since comparing the left channel + pb.finish(); + } + }; + match format { // TODO: (?) Write a macro to unify logic for both formats - + // // Process INT samples SampleFormat::Int => { // Define a closure which compares the difference of two samples. @@ -115,7 +120,7 @@ fn zero_test(mut reader: WavReader, }; reader.samples::() - .zip(progress_iter) + .enumerate() .batching(|mut it| { match it.next() { None => None, @@ -123,10 +128,8 @@ fn zero_test(mut reader: WavReader, match it.next() { None => None, Some(y) => { - if y.1 >= progress_chunk { - pb.add(progress_chunk); - }; - Some(x.0.unwrap() - y.0.unwrap()) + update_pb(x.0); + Some(x.1.unwrap() - y.1.unwrap()) } } } @@ -148,7 +151,7 @@ fn zero_test(mut reader: WavReader, Box::new(|x: f32| x.abs() > dither_threshold as f32 * 0.000117f32) }; reader.samples::() - .zip(progress_iter) + .enumerate() .batching(|mut it| { match it.next() { None => None, @@ -156,10 +159,8 @@ fn zero_test(mut reader: WavReader, match it.next() { None => None, Some(y) => { - if y.1 >= progress_chunk { - pb.add(progress_chunk); - }; - Some(x.0.unwrap() - y.0.unwrap()) + update_pb(x.0); + Some(x.1.unwrap() - y.1.unwrap()) } } }