From 8e712e3c0293ee312872314f502be5a6c1635681 Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Mon, 6 Nov 2023 22:20:54 -0500 Subject: [PATCH 1/5] Started calculating the number of output files needed --- Cargo.toml | 2 +- src/main.rs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 20c2b89..3bb1760 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,6 @@ edition = "2021" [dependencies] keepawake = "0.4.3" rustfft = "6.0.1" -wave_stream = "0.3.0" +wave_stream = "0.5.0" # Uncomment to test pre-release changes # wave_stream = { git = "https://github.com/GWBasic/wave_stream.git", branch = "28-support-51-and-other-channel-layouts" } diff --git a/src/main.rs b/src/main.rs index 02a671b..9e81b5e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,6 +62,17 @@ fn main() { sample_rate: source_wav.sample_rate(), }; + // Wave files have a max size of 4GB. (Due to RIFF using 32 bits to track its size.) It's very easy to exceed this length + // when upmixing a file over (approximately) 58 minutes in length. 6 channels @ 32 bits / sample (float) adds up quickly + + let max_samples = header.max_samples(); + let mut num_target_files = source_wav.len_samples() / max_samples; + if source_wav.len_samples() % max_samples > 0 { + num_target_files += 1; + } + + // Need to update the path if there are multiple targets + let open_target_wav_result = write_wav_to_file_path(&options.target_wav_path, header); let target_wav = match open_target_wav_result { From fb40e11553cd8c0f7ce25c2d58b6360a6058aa77 Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Sat, 11 Nov 2023 15:23:10 -0500 Subject: [PATCH 2/5] First pass at breaking the output up into multiple files --- src/main.rs | 81 ++++++++++++++++++++++++++++++++-------- src/panner_and_writer.rs | 28 +++++++++----- src/upmixer.rs | 14 +++++-- 3 files changed, 95 insertions(+), 28 deletions(-) diff --git a/src/main.rs b/src/main.rs index 9e81b5e..10cacf7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,9 @@ +use std::ffi::OsStr; +use std::path::Path; + use wave_stream::open_wav::OpenWav; use wave_stream::wave_header::{Channels, SampleFormat, WavHeader}; +use wave_stream::wave_writer::OpenWavWriter; use wave_stream::{read_wav_from_file_path, write_wav_to_file_path}; mod logger; @@ -65,27 +69,72 @@ fn main() { // Wave files have a max size of 4GB. (Due to RIFF using 32 bits to track its size.) It's very easy to exceed this length // when upmixing a file over (approximately) 58 minutes in length. 6 channels @ 32 bits / sample (float) adds up quickly - let max_samples = header.max_samples(); - let mut num_target_files = source_wav.len_samples() / max_samples; - if source_wav.len_samples() % max_samples > 0 { + let max_samples_in_file = header.max_samples(); + let mut num_target_files = source_wav.len_samples() / max_samples_in_file; + if source_wav.len_samples() % max_samples_in_file > 0 { num_target_files += 1; } - // Need to update the path if there are multiple targets + let mut target_open_wav_writers: Vec = Vec::with_capacity(num_target_files); + + if num_target_files > 1 { + // Need to update the path if there are multiple targets + let file_stem = match options.target_wav_path.file_stem() { + Some(file_stem) => file_stem, + None => { + println!( + "Not a valid filename: {}", + options.target_wav_path.display() + ); + return; + } + }; + let extension = options + .target_wav_path + .extension() + .unwrap_or(OsStr::new("wav")); + let folder = options.target_wav_path.parent().unwrap_or(&Path::new("/")); + + for file_ctr in 1..(num_target_files + 1) { + let target_wav_filename_string = format!( + "{} - {} of {}.{}", + file_stem.to_string_lossy(), + file_ctr, + num_target_files, + extension.to_string_lossy() + ); - let open_target_wav_result = write_wav_to_file_path(&options.target_wav_path, header); + let target_wav_path = folder.join(target_wav_filename_string); - let target_wav = match open_target_wav_result { - Err(error) => { - println!( - "Can not open {}: {:?}", - &options.target_wav_path.display(), - error - ); - return; + let open_target_wav_result = write_wav_to_file_path(&target_wav_path, header); + + let target_wav = match open_target_wav_result { + Err(error) => { + println!("Can not open {}: {:?}", &target_wav_path.display(), error); + return; + } + Ok(target_wav) => target_wav, + }; + + target_open_wav_writers.push(target_wav) } - Ok(target_wav) => target_wav, - }; + } else { + let open_target_wav_result = write_wav_to_file_path(&options.target_wav_path, header); + + let target_wav = match open_target_wav_result { + Err(error) => { + println!( + "Can not open {}: {:?}", + &options.target_wav_path.display(), + error + ); + return; + } + Ok(target_wav) => target_wav, + }; + + target_open_wav_writers.push(target_wav) + } let length_seconds = (source_wav.len_samples() as f64) / (source_wav.sample_rate() as f64); println!( @@ -113,7 +162,7 @@ fn main() { None }; - match upmix(options, source_wav, target_wav) { + match upmix(options, source_wav, target_open_wav_writers) { Err(error) => { println!("Error upmixing: {:?}", error); } diff --git a/src/panner_and_writer.rs b/src/panner_and_writer.rs index 3a16d7d..340b1e9 100644 --- a/src/panner_and_writer.rs +++ b/src/panner_and_writer.rs @@ -28,11 +28,13 @@ pub struct PannerAndWriter { fft_inverse: Arc>, lfe_levels: Option>, + + max_samples_in_file: usize, } // Wraps types used during writing so they can be within a mutex struct WriterState { - pub target_wav_writer: RandomAccessWavWriter, + pub target_random_access_wav_writers: Vec>, pub total_samples_written: usize, } @@ -41,8 +43,9 @@ impl PannerAndWriter { options: &Options, window_size: usize, sample_rate: usize, - target_wav_writer: RandomAccessWavWriter, + target_random_access_wav_writers: Vec>, fft_inverse: Arc>, + max_samples_in_file: usize, ) -> PannerAndWriter { let lfe_levels = if options.channels.low_frequency { let mut lfe_levels = vec![0.0f32; window_size]; @@ -84,11 +87,12 @@ impl PannerAndWriter { PannerAndWriter { transformed_window_and_averaged_pans_queue: Mutex::new(VecDeque::new()), writer_state: Mutex::new(WriterState { - target_wav_writer, + target_random_access_wav_writers, total_samples_written: 0, }), fft_inverse, lfe_levels, + max_samples_in_file, } } @@ -411,9 +415,11 @@ impl PannerAndWriter { None => {} } - writer_state - .target_wav_writer - .write_samples(sample_ctr, samples_by_channel)?; + let out_file_index = sample_ctr / self.max_samples_in_file; + let sample_ctr_in_file = sample_ctr - (self.max_samples_in_file * out_file_index); + + writer_state.target_random_access_wav_writers[out_file_index] + .write_samples(sample_ctr_in_file, samples_by_channel)?; writer_state.total_samples_written += 1; @@ -427,8 +433,12 @@ impl Drop for PannerAndWriter { self.writer_state .lock() .expect("Cannot aquire lock because a thread panicked") - .target_wav_writer - .flush() - .expect("Can not flush writer"); + .target_random_access_wav_writers + .iter_mut() + .for_each(|target_random_access_wav_writer| { + target_random_access_wav_writer + .flush() + .expect("Can not flush writer") + }); } } diff --git a/src/upmixer.rs b/src/upmixer.rs index 9d1c1bb..3a8b58e 100644 --- a/src/upmixer.rs +++ b/src/upmixer.rs @@ -47,7 +47,7 @@ unsafe impl Sync for Upmixer {} pub fn upmix( options: Options, source_wav_reader: OpenWavReader, - target_wav_writer: OpenWavWriter, + target_open_wav_writers: Vec, ) -> Result<()> { let max_low_frequency = (source_wav_reader.sample_rate() / 8) as f32; if options.low_frequency >= max_low_frequency { @@ -83,7 +83,14 @@ pub fn upmix( } let source_wav_reader = source_wav_reader.get_stream_f32_reader()?; - let target_wav_writer = target_wav_writer.get_random_access_f32_writer()?; + let mut target_random_access_wav_writers = Vec::with_capacity(target_open_wav_writers.len()); + for target_open_wav_writer in target_open_wav_writers { + target_random_access_wav_writers + .push(target_open_wav_writer.get_random_access_f32_writer()?); + } + + let max_samples_in_file = + (source_wav_reader.info().len_samples() / target_random_access_wav_writers.len()) + 1; // rustfft states that the scale is 1/len() // See "noramlization": https://docs.rs/rustfft/latest/rustfft/#normalization @@ -103,8 +110,9 @@ pub fn upmix( &options, window_size, sample_rate, - target_wav_writer, + target_random_access_wav_writers, fft_inverse, + max_samples_in_file, ); let mut stdout = stdout(); From 7b67ef6c3fa825f97d577e6316904eaa9a712e5e Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Sat, 11 Nov 2023 16:09:36 -0500 Subject: [PATCH 3/5] Now listing all targets --- src/main.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main.rs b/src/main.rs index 10cacf7..6074ac7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,6 @@ use std::path::Path; use wave_stream::open_wav::OpenWav; use wave_stream::wave_header::{Channels, SampleFormat, WavHeader}; -use wave_stream::wave_writer::OpenWavWriter; use wave_stream::{read_wav_from_file_path, write_wav_to_file_path}; mod logger; @@ -75,7 +74,8 @@ fn main() { num_target_files += 1; } - let mut target_open_wav_writers: Vec = Vec::with_capacity(num_target_files); + let mut target_paths = Vec::with_capacity(num_target_files); + let mut target_open_wav_writers = Vec::with_capacity(num_target_files); if num_target_files > 1 { // Need to update the path if there are multiple targets @@ -116,7 +116,8 @@ fn main() { Ok(target_wav) => target_wav, }; - target_open_wav_writers.push(target_wav) + target_open_wav_writers.push(target_wav); + target_paths.push(target_wav_path); } } else { let open_target_wav_result = write_wav_to_file_path(&options.target_wav_path, header); @@ -133,7 +134,8 @@ fn main() { Ok(target_wav) => target_wav, }; - target_open_wav_writers.push(target_wav) + target_open_wav_writers.push(target_wav); + target_paths.push(options.target_wav_path.to_path_buf()); } let length_seconds = (source_wav.len_samples() as f64) / (source_wav.sample_rate() as f64); @@ -142,7 +144,15 @@ fn main() { &options.source_wav_path.display(), length_seconds ); - println!("\tTarget: {}", &options.target_wav_path.display()); + + if target_paths.len() == 1 { + println!("\tTarget: {}", target_paths[0].display()); + } else { + println!("\tTargets:"); + for target_path in target_paths { + println!("\t\t{}", target_path.display()); + } + } let mut _keepawake = if options.keep_awake { let reason = format!( From 947d8e2fa0028523adc273cbe4fc6b95bc54badf Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Sat, 11 Nov 2023 22:00:32 -0500 Subject: [PATCH 4/5] Fixed issue where keepawake wasn't set up correctly --- src/main.rs | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6074ac7..8eb5f2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -160,14 +160,23 @@ fn main() { &options.source_wav_path.display(), &options.target_wav_path.display() ); - Some( - keepawake::Builder::new() - .display(false) - .idle(true) - .app_name("soft_matrix") - .reason(reason) - .app_reverse_domain("io.github.gwbasic.soft_matrix"), - ) + + let awake_handle = match keepawake::Builder::new() + .display(false) + .idle(true) + .sleep(true) + .app_name("soft_matrix") + .reason(reason) + .app_reverse_domain("io.github.gwbasic.soft_matrix") + .create() { + Ok(awake_handle) => awake_handle, + Err(error) => { + println!("Cannot keep the computer awake: {}", error); + return; + } + }; + + Some(awake_handle) } else { None }; From 481b70ba4c66c1c3cf97fbe829942a2a0e2d41bf Mon Sep 17 00:00:00 2001 From: Andrew Rondeau Date: Sat, 11 Nov 2023 22:03:23 -0500 Subject: [PATCH 5/5] Formatting --- src/main.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8eb5f2b..95b61bb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -168,13 +168,14 @@ fn main() { .app_name("soft_matrix") .reason(reason) .app_reverse_domain("io.github.gwbasic.soft_matrix") - .create() { - Ok(awake_handle) => awake_handle, - Err(error) => { - println!("Cannot keep the computer awake: {}", error); - return; - } - }; + .create() + { + Ok(awake_handle) => awake_handle, + Err(error) => { + println!("Cannot keep the computer awake: {}", error); + return; + } + }; Some(awake_handle) } else {