commit d68b861aea8b696370f350729ebd8764195ad8d4 Author: Ivan Bushchik Date: Thu Jan 4 19:51:56 2024 +0300 0.1.0: Initial commit Signed-off-by: Ivan Bushchik diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..72e0e91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +.idea +*.DS_Store +*.wav \ No newline at end of file diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..32e806d --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,9 @@ +edition = "2021" +hard_tabs = true +merge_derives = true +reorder_imports = true +reorder_modules = true +use_field_init_shorthand = true +use_small_heuristics = "Off" +wrap_comments = true +comment_width = 80 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..0921c6f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "dasp_frame" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a3937f5fe2135702897535c8d4a5553f8b116f76c1529088797f2eee7c5cd6" +dependencies = [ + "dasp_sample", +] + +[[package]] +name = "dasp_sample" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" + +[[package]] +name = "ebur128" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12aebdd6b6b47b5880c049efb0e77f8762178a0745ef778878908f5981c05f52" +dependencies = [ + "bitflags", + "dasp_frame", + "dasp_sample", + "smallvec", +] + +[[package]] +name = "exp" +version = "0.1.0" +dependencies = [ + "ebur128", + "wav", +] + +[[package]] +name = "riff" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" + +[[package]] +name = "smallvec" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" + +[[package]] +name = "wav" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65e199c799848b4f997072aa4d673c034f80f40191f97fe2f0a23f410be1609" +dependencies = [ + "riff", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7a2501b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "war-rs" +version = "0.1.0" +edition = "2021" +repository = "https://github.com/ivabus/WAR.rs" +authors = [ "Ivan Bushchik " ] +license = "MIT" +description = "Automatic encoder of \"1-bit\" WAVE files with LUFS calculation" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ebur128 = "0.1.8" +wav = "1.0.0" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e5e9014 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright (c) 2023 Ivan Bushchik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS +OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF +OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd7a161 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# WAR.rs + +> Winner of THE LOUDNESS WAR + +Automatic encoder of "1-bit" WAVE files with LUFS calculation. + +## Usage + +```shell +cargo run +``` + +## License + +WAR.rs is licensed under the terms of the [MIT license](./LICENSE). diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..c15a82e --- /dev/null +++ b/src/main.rs @@ -0,0 +1,70 @@ +use std::env::args; +use std::fs::File; +use std::path::Path; +use std::time::Instant; +use wav::BitDepth; + +fn perform_voluming_i + Copy>(data: Vec) -> Vec { + data.iter() + .map(|i| { + if Into::::into(*i) > 0.0f64 { + 0x7fff + } else { + -0x8000 + } + }) + .collect() +} + +// u8 PCM should be treated other way (if we are going without unsafing (we could transform it into i8)), than a i{16,32} and f32 +fn perform_voluming_u8(data: Vec) -> Vec { + data.iter() + .map(|&i| { + if i > 128 { + 0x7fff + } else { + -0x8000 + } + }) + .collect() +} + +fn main() { + let args: Vec = args().collect(); + if args.len() != 3 { + eprintln!("WAR.rs: winner of THE LOUDNESS WAR"); + eprintln!("Usage: {} ", args[0]); + std::process::exit(1); + } + let (input, output) = (args[1].clone(), args[2].clone()); + let start = Instant::now(); + let mut inp_file = File::open(Path::new(&input)).unwrap(); + let (header, data) = wav::read(&mut inp_file).unwrap(); + let new_header = wav::Header::new( + wav::header::WAV_FORMAT_PCM, + header.channel_count, + header.sampling_rate, + 16, + ); + + let volumed = match data { + BitDepth::Eight(data) => perform_voluming_u8(data), + BitDepth::Sixteen(data) => perform_voluming_i(data), + BitDepth::TwentyFour(data) => perform_voluming_i(data), + BitDepth::ThirtyTwoFloat(data) => perform_voluming_i(data), + BitDepth::Empty => { + eprintln!("No audio data found in input file."); + std::process::exit(1); + } + }; + + let mut out_file = File::create(Path::new(&output)).unwrap(); + wav::write(new_header, &BitDepth::Sixteen(volumed.clone()), &mut out_file).unwrap(); + println!("Saved to {} in {} seconds", output, (Instant::now() - start).as_secs_f64()); + + let mut ebur = + ebur128::EbuR128::new(header.channel_count as u32, header.sampling_rate, ebur128::Mode::I) + .unwrap(); + ebur.add_frames_i16(volumed.as_slice()).unwrap(); + println!("New loudness:\t{} LUFS", ebur.loudness_global().unwrap()); +}