From d3bb66c4d1700c743c819c8e204c5f767bd33dd3 Mon Sep 17 00:00:00 2001 From: danny Date: Sun, 24 Nov 2024 10:31:53 +0900 Subject: [PATCH] loop working --- Cargo.lock | 17 +- ast/Cargo.toml | 2 +- core/src/manager/render_manager.rs | 252 +++++++++++++++------- core/src/portaudio/mod.rs | 4 +- core/src/portaudio/real_time.rs | 127 ++++++----- instrument/src/oscillator.rs | 5 + instrument/src/renderable/render_voice.rs | 7 +- instrument/src/voice.rs | 4 + shared/src/settings.rs | 2 +- 9 files changed, 273 insertions(+), 147 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0e8476cb..9c4232e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1000,13 +1000,18 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "meval" version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9" +source = "git+https://github.com/xasopheno/meval-rs#6bf579fd402928745cf4f24e5c975bece3285179" dependencies = [ "fnv", "nom", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1036,9 +1041,13 @@ checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nom" -version = "1.2.4" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] [[package]] name = "notify" diff --git a/ast/Cargo.toml b/ast/Cargo.toml index c24bd568..b29dd492 100644 --- a/ast/Cargo.toml +++ b/ast/Cargo.toml @@ -22,7 +22,7 @@ colored = "2.0.0" num-traits = "0.2.14" num-integer = "0.1.44" polynomials = "0.2.4" -meval = "0.2.0" +meval = { git = "https://github.com/xasopheno/meval-rs"} csv = "1.1.6" hamcrest2 = "0.3.0" peekread = "0.1.1" diff --git a/core/src/manager/render_manager.rs b/core/src/manager/render_manager.rs index d3f3f601..610cce98 100644 --- a/core/src/manager/render_manager.rs +++ b/core/src/manager/render_manager.rs @@ -42,6 +42,8 @@ pub struct RenderManager { kill_channel: KillChannel, once: bool, paused: bool, + total_samples_per_loop: usize, + samples_processed: usize, } pub fn render_op_to_normalized_op4d(render_op: &RenderOp, normalizer: &Normalizer) -> Option { @@ -97,6 +99,8 @@ impl RenderManager { kill_channel, once, paused: false, + total_samples_per_loop: 0, + samples_processed: 0, } } @@ -121,6 +125,8 @@ impl RenderManager { kill_channel: None, once: false, paused: false, + total_samples_per_loop: 0, + samples_processed: 0, } } @@ -167,94 +173,192 @@ impl RenderManager { ) -> Option<(StereoWaveform, Vec)> { if self.paused { return None; - }; + } + + let mut remaining_buffer_size = buffer_size; + let mut total_rendered_per_batch: Vec> = Vec::new(); + let mut total_ops: Vec = Vec::new(); - let next = self.exists_next_render(); let vtx = self.visualization.channel.clone(); let normalizer = self.visualization.normalizer; - let current = self.current_render(); - - match current { - Some(render_voices) => { - let (ops, rendered): (Vec<_>, Vec<_>) = render_voices - .iter_mut() - .filter_map(|voice| { - voice - .get_batch(Settings::global().buffer_size, None) - .map(|mut batch| { - ( - if vtx.is_some() { - batch.iter().filter(|op| op.index == 0).cloned().collect() - } else { - vec![] - }, - batch.render(&mut voice.oscillator, Some(&offset)), - ) - }) - }) - .unzip(); - - if let Some(tx) = vtx { - let mut opmap: OpMap = OpMap::with_capacity(ops.len()); - - ops.iter().flatten().for_each(|v| { - let name = v.names.last().map_or("nameless", |n| n); - - let op = render_op_to_normalized_op4d(v, &normalizer); - if let Some(mut o) = op { - o.y = o.y * offset.freq; - o.z = o.z * offset.gain; - opmap.insert(name, o); - }; - }); - if tx.send(VisEvent::Ops(opmap)).is_err() { - info!("WereSoCool server closed"); - std::process::exit(0); - } - } - - if !rendered.is_empty() { - let mut sw: StereoWaveform = sum_all_waveforms(rendered); - if next { - sw.fade_out(); - - *current = None; - self.inc_render(); + while remaining_buffer_size > 0 { + // Compute next_exists before mutable borrow + let next_exists = self.exists_next_render(); + + // Start mutable borrow scope + let (samples_processed, render_finished) = { + let current_render_option = self.current_render(); + + match current_render_option { + Some(render_voices) => { + let mut any_data_rendered = false; + let mut ops_per_voice: Vec> = Vec::new(); + let mut rendered_per_voice: Vec = Vec::new(); + + let mut min_samples_processed = remaining_buffer_size; + + for voice in render_voices.iter_mut() { + match voice.get_batch( + remaining_buffer_size, + None, + !next_exists && Settings::global().loop_play, + ) { + Some(mut batch) => { + any_data_rendered = true; + let samples = batch.iter().map(|op| op.samples).sum::(); + min_samples_processed = min_samples_processed.min(samples); + + if let Some(_vtx) = &vtx { + let voice_ops: Vec<_> = batch + .iter() + .filter(|op| op.index == 0) + .cloned() + .collect(); + ops_per_voice.push(voice_ops); + } + let voice_rendered = + batch.render(&mut voice.oscillator, Some(&offset)); + // Collect the rendered waveform per voice + rendered_per_voice.push(voice_rendered); + } + None => { + // Voice has finished + // Do not set min_samples_processed to zero here + } + } + } + + if any_data_rendered && min_samples_processed > 0 { + // Store the per-voice rendered waveforms for this batch + total_rendered_per_batch.push(rendered_per_voice); + total_ops.extend(ops_per_voice.into_iter().flatten()); + + (min_samples_processed, false) + } else if any_data_rendered { + // Some data rendered, but min_samples_processed is zero + (0, false) + } else { + // All voices have finished + (0, true) + } + } + None => { + // No current render + (0, true) } + } + }; // End of mutable borrow - sw.pad(buffer_size); + if samples_processed > 0 { + remaining_buffer_size = remaining_buffer_size.saturating_sub(samples_processed); + } - let ramp = self.ramp_to_current_volume(buffer_size); - Some((sw, ramp)) + if render_finished { + if self.exists_next_render() { + self.inc_render(); + continue; // Continue processing with next render } else { - *self.current_render() = None; - if self.once { - self.kill().expect("Not able to kill"); - None - } else { - None + self.kill().expect("Unable to kill"); } + break; // No more renders, exit loop } } - None => { - if next { - self.inc_render(); - self.read(buffer_size, offset) - } else { - None + + if samples_processed == 0 { + // No samples processed, break to avoid infinite loop + break; + } + } + + // Now, we have total_rendered_per_batch: Vec> + // Each inner Vec corresponds to per-voice waveforms for a batch + // Now, we need to sum the per-voice waveforms for each batch and append them to build the final combined waveform + + if !total_rendered_per_batch.is_empty() { + let mut combined_sw = StereoWaveform::new_empty(); + + for rendered_per_voice in total_rendered_per_batch { + let batch_sw = sum_all_waveforms(rendered_per_voice); + combined_sw.append(batch_sw); + } + + combined_sw.pad(buffer_size); + + // Visualization + if let Some(tx) = vtx { + let mut opmap: OpMap = OpMap::with_capacity(total_ops.len()); + + total_ops.iter().for_each(|v| { + let name = v.names.last().map_or("nameless", |n| n); + + let op = render_op_to_normalized_op4d(v, &normalizer); + if let Some(mut o) = op { + o.y = o.y * offset.freq; + o.z = o.z * offset.gain; + opmap.insert(name, o); + }; + }); + + if tx.send(VisEvent::Ops(opmap)).is_err() { + info!("Visualization channel closed"); + std::process::exit(0); } } + + let ramp = self.ramp_to_current_volume(buffer_size); + Some((combined_sw, ramp)) + } else { + None } } pub fn inc_render(&mut self) { - self.render_idx = (self.render_idx + 1) % 2; + info!("Incrementing render"); + // Update the render index + + // Since self.renders has length 2, we can split it at index 1 + let (first, second) = self.renders.split_at_mut(1); + + let (current_render_option, next_render_option) = if self.render_idx == 0 { + (&first[0], &mut second[0]) + } else { + (&second[0], &mut first[0]) + }; + + if let (Some(current_voices), Some(next_voices)) = + (current_render_option.as_ref(), next_render_option.as_mut()) + { + // Ensure that both renders have the same number of voices + let min_length = std::cmp::min(current_voices.len(), next_voices.len()); + for i in 0..min_length { + let current_oscillator = ¤t_voices[i].oscillator; + let next_oscillator = &mut next_voices[i].oscillator; + + // Copy the oscillator state + next_oscillator.copy_state_from(current_oscillator); + } + } + + // Reset samples processed for the new render + self.samples_processed = 0; + + // Update total_samples_per_loop for the new render + if let Some(next_render) = next_render_option.as_ref() { + if !next_render.is_empty() { + self.total_samples_per_loop = next_render[0].ops.iter().map(|op| op.samples).sum(); + } + } + + // Send visualization reset event if necessary if let Some(vtx) = self.visualization.channel.clone() { vtx.send(VisEvent::Reset) - .expect("couldn't send VisEvent::Reset"); - }; + .expect("Couldn't send VisEvent::Reset"); + } + + *self.current_render() = None; + self.render_idx = (self.render_idx + 1) % 2; } pub fn current_render(&mut self) -> &mut Option> { @@ -265,12 +369,16 @@ impl RenderManager { &mut self.renders[(self.render_idx + 1) % 2] } - pub fn exists_current_render(&mut self) -> bool { - self.current_render().is_some() + pub fn current_render_ref(&self) -> &Option> { + &self.renders[self.render_idx] + } + + pub fn exists_current_render(&self) -> bool { + self.renders[(self.render_idx) % 2].is_some() } - pub fn exists_next_render(&mut self) -> bool { - self.next_render().is_some() + pub fn exists_next_render(&self) -> bool { + self.renders[(self.render_idx + 1) % 2].is_some() } pub fn push_render(&mut self, render: Vec, once: bool) { diff --git a/core/src/portaudio/mod.rs b/core/src/portaudio/mod.rs index b43b2e1a..0f34722e 100644 --- a/core/src/portaudio/mod.rs +++ b/core/src/portaudio/mod.rs @@ -1,11 +1,11 @@ pub mod duplex; -pub mod real_time; +// pub mod real_time; pub mod real_time_buffer_manager; pub mod real_time_render_manager; pub mod real_time_render_manager_mic; pub use self::duplex::duplex_setup; -pub use self::real_time::real_time; +// pub use self::real_time::real_time; pub use self::real_time_buffer_manager::real_time_buffer_manager; pub use self::real_time_render_manager::real_time_render_manager; pub use self::real_time_render_manager_mic::real_time_render_manager_mic; diff --git a/core/src/portaudio/real_time.rs b/core/src/portaudio/real_time.rs index 40b9e8d7..15d506f0 100644 --- a/core/src/portaudio/real_time.rs +++ b/core/src/portaudio/real_time.rs @@ -1,64 +1,63 @@ -use crate::{generation::parsed_to_render::sum_all_waveforms, write::write_output_buffer}; -use rayon::prelude::*; -use weresocool_error::Error; -use weresocool_instrument::{renderable::Renderable, RenderVoice}; -use weresocool_portaudio as pa; -use weresocool_shared::Settings; - -pub fn real_time( - mut voices: Vec, -) -> Result>, Error> { - let pa = pa::PortAudio::new()?; - let output_stream_settings = get_output_settings(&pa)?; - - let output_stream = pa.open_non_blocking_stream(output_stream_settings, move |args| { - #[cfg(feature = "app")] - let iter = voices.par_iter_mut(); - #[cfg(feature = "wasm")] - let iter = voices.iter_mut(); - - let result: Vec<(_, _)> = iter - .filter_map(|voice| { - let ops = voice.get_batch(Settings::global().buffer_size, None); - match ops { - Some(mut batch) => { - Some((batch.clone(), batch.render(&mut voice.oscillator, None))) - } - None => None, - } - }) - .collect(); - let (_ops, result): (Vec<_>, Vec<_>) = result.into_iter().map(|(a, b)| (a, b)).unzip(); - - if !result.is_empty() { - let stereo_waveform = sum_all_waveforms(result); - write_output_buffer(args.buffer, stereo_waveform); - pa::Continue - } else { - pa::Complete - } - })?; - - Ok(output_stream) -} - -pub fn get_output_settings(pa: &pa::PortAudio) -> Result, Error> { - let def_output = pa.default_output_device()?; - let output_info = pa.device_info(def_output)?; - // println!("Default output device info: {:#?}", &output_info); - let latency = output_info.default_low_output_latency; - let output_params = pa::StreamParameters::new( - def_output, - Settings::global().channels, - Settings::global().interleaved, - latency, - ); - - let output_settings = pa::OutputStreamSettings::new( - output_params, - Settings::global().sample_rate, - Settings::global().buffer_size as u32, - ); - - Ok(output_settings) -} +// use rayon::prelude::*; +// use weresocool_error::Error; +// use weresocool_instrument::{renderable::Renderable, RenderVoice}; +// use weresocool_portaudio as pa; +// use weresocool_shared::Settings; + +// pub fn real_time( +// mut voices: Vec, +// ) -> Result>, Error> { +// let pa = pa::PortAudio::new()?; +// let output_stream_settings = get_output_settings(&pa)?; + +// let output_stream = pa.open_non_blocking_stream(output_stream_settings, move |args| { +// #[cfg(feature = "app")] +// let iter = voices.par_iter_mut(); +// #[cfg(feature = "wasm")] +// let iter = voices.iter_mut(); + +// let result: Vec<(_, _)> = iter +// .filter_map(|voice| { +// let ops = voice.get_batch(Settings::global().buffer_size, None); +// match ops { +// Some(mut batch) => { +// Some((batch.clone(), batch.render(&mut voice.oscillator, None))) +// } +// None => None, +// } +// }) +// .collect(); +// let (_ops, result): (Vec<_>, Vec<_>) = result.into_iter().map(|(a, b)| (a, b)).unzip(); + +// if !result.is_empty() { +// let stereo_waveform = sum_all_waveforms(result); +// write_output_buffer(args.buffer, stereo_waveform); +// pa::Continue +// } else { +// pa::Complete +// } +// })?; + +// Ok(output_stream) +// } + +// pub fn get_output_settings(pa: &pa::PortAudio) -> Result, Error> { +// let def_output = pa.default_output_device()?; +// let output_info = pa.device_info(def_output)?; +// // println!("Default output device info: {:#?}", &output_info); +// let latency = output_info.default_low_output_latency; +// let output_params = pa::StreamParameters::new( +// def_output, +// Settings::global().channels, +// Settings::global().interleaved, +// latency, +// ); + +// let output_settings = pa::OutputStreamSettings::new( +// output_params, +// Settings::global().sample_rate, +// Settings::global().buffer_size as u32, +// ); + +// Ok(output_settings) +// } diff --git a/instrument/src/oscillator.rs b/instrument/src/oscillator.rs index f5cccd99..3c2c635b 100644 --- a/instrument/src/oscillator.rs +++ b/instrument/src/oscillator.rs @@ -40,6 +40,11 @@ impl Oscillator { } } + pub fn copy_state_from(&mut self, other: &Oscillator) { + self.voices.0.copy_state_from(&other.voices.0); + self.voices.1.copy_state_from(&other.voices.1); + } + pub fn update(&mut self, op: &RenderOp, offset: &Offset) { let (ref mut l_voice, ref mut r_voice) = self.voices; l_voice.update(op, offset); diff --git a/instrument/src/renderable/render_voice.rs b/instrument/src/renderable/render_voice.rs index 061ec600..c3ade106 100644 --- a/instrument/src/renderable/render_voice.rs +++ b/instrument/src/renderable/render_voice.rs @@ -27,10 +27,11 @@ impl RenderVoice { &mut self, samples_left_in_batch: usize, result: Option>, + loop_play: bool, ) -> Option> { let mut result = result.unwrap_or_default(); - if Settings::global().loop_play && self.op_index >= self.ops.len() { + if loop_play && self.op_index >= self.ops.len() { self.op_index = 0; } @@ -69,7 +70,7 @@ impl RenderVoice { self.sample_index = 0; - return self.get_batch(samples_left_in_batch - n_samples, Some(result)); + return self.get_batch(samples_left_in_batch - n_samples, Some(result), loop_play); } Some(result) @@ -80,7 +81,7 @@ impl RenderVoice { n_samples: usize, offset: Option<&Offset>, ) -> Option { - let batch = self.get_batch(n_samples, None); + let batch = self.get_batch(n_samples, None, false); batch.map(|mut b| b.render(&mut self.oscillator, offset)) } diff --git a/instrument/src/voice.rs b/instrument/src/voice.rs index e70c80e5..2ec96835 100644 --- a/instrument/src/voice.rs +++ b/instrument/src/voice.rs @@ -99,6 +99,10 @@ impl Voice { } } + pub fn copy_state_from(&mut self, other: &Voice) { + *self = other.clone(); + } + /// Renders a single RenderOp given an Offset /// This is where all of the rendering logic for a single render_op happens pub fn generate_waveform(&mut self, op: &RenderOp, offset: &Offset) -> Vec { diff --git a/shared/src/settings.rs b/shared/src/settings.rs index 3deef14c..8753891c 100644 --- a/shared/src/settings.rs +++ b/shared/src/settings.rs @@ -49,7 +49,7 @@ pub struct Settings { pub const fn default_settings() -> Settings { Settings { loop_play: true, - pad_end: true, + pad_end: false, mic: true, sample_rate: 44_100.0, yin_buffer_size: 1024,