From 8cdd6b06e27f29c85b9fb360c60102909b78c75f Mon Sep 17 00:00:00 2001 From: a0kami Date: Mon, 14 Jul 2025 20:05:00 +0200 Subject: [PATCH] refactored utils into its own waveform struct, shorten main --- src/main.rs | 31 +++++++------- src/utils.rs | 80 ----------------------------------- src/waveform.rs | 110 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 96 deletions(-) delete mode 100644 src/utils.rs create mode 100644 src/waveform.rs diff --git a/src/main.rs b/src/main.rs index 6fc8d04..d1c38f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,18 @@ mod pw; -mod utils; +mod waveform; fn main() -> iced::Result { - iced::application(Spectrogram::new, Spectrogram::update, Spectrogram::view) - .subscription(Spectrogram::subscription) + iced::application(RumbleWrecker::new, RumbleWrecker::update, RumbleWrecker::view) + .subscription(RumbleWrecker::subscription) // .title("Rumble Wrecker") .run() } -struct Spectrogram { +struct RumbleWrecker { + refresh_rate: u64, stream_left: std::sync::mpsc::Receiver>, stream_right: std::sync::mpsc::Receiver>, - waveform_img: Rgba, + waveform: waveform::Waveform, } struct Rgba { @@ -25,7 +26,7 @@ enum Message { Tick, } -impl Spectrogram { +impl RumbleWrecker { fn new() -> Self { let (tx_left, rx_left) = std::sync::mpsc::channel::>(); let (tx_right, rx_right) = std::sync::mpsc::channel::>(); @@ -36,12 +37,12 @@ impl Spectrogram { let w = 1024u32; let h = 256u32; - let p = vec![255; (w*h*4) as usize]; Self { + refresh_rate: 40, stream_left: rx_left, stream_right: rx_right, - waveform_img: Rgba { width: w, height:h, pixels: p } + waveform: waveform::Waveform::new(w, h), } } @@ -58,23 +59,21 @@ impl Spectrogram { } if !left_samples.is_empty() && !right_samples.is_empty() { - utils::update_waveform(&mut self.waveform_img, (&left_samples, &right_samples), 0.5); + self.waveform.update((&left_samples, &right_samples), 0.5); } } } } fn view(&self) -> iced::Element<'_, Message> { - let img_handle = iced::widget::image::Handle::from_rgba( - self.waveform_img.width, - self.waveform_img.height, - self.waveform_img.pixels.clone() - ); + let waveform = self.waveform.view(); - iced::widget::center_x(iced::widget::image(img_handle)).into() + // refresh rate slider + + iced::widget::Column::new().push(waveform).into() } fn subscription(&self) -> iced::Subscription { - iced::time::every(std::time::Duration::from_millis(40)).map(|_| Message::Tick) + iced::time::every(std::time::Duration::from_millis(self.refresh_rate)).map(|_| Message::Tick) } } diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 0fa3912..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,80 +0,0 @@ -pub fn update_waveform( rgba: &mut crate::Rgba, samples: (&Vec, &Vec), resolution: f32 ) { - let (left_samples, right_samples) = samples; - let mut last_rows = ((rgba.height/2) as usize, (rgba.height/2) as usize); - - let span = (rgba.width as f32 * resolution) as usize; - let moved_start = rgba.width as usize - span; - - if resolution < 1.0 { - let mut previous_left_row = (rgba.height as usize, 0usize); - let mut previous_right_row = (rgba.height as usize, 0usize); - - for row in 0..rgba.height as usize { - // Get min/max left/right values for last column looking in each row - let last_pixel_idx = (((row+1)*rgba.width as usize)-1)*4; - let last_row_pixel = &rgba.pixels[last_pixel_idx..last_pixel_idx+4]; - // LEFT - if last_row_pixel[2] == 0 { - if row < previous_left_row.0 { previous_left_row.0 = row; } - if row > previous_left_row.1 { previous_left_row.1 = row; } - } - // RIGHT - if last_row_pixel[0] == 0 { - if row < previous_right_row.0 { previous_right_row.0 = row; } - if row > previous_right_row.1 { previous_right_row.1 = row; } - } - - let row_offset = row * rgba.width as usize*4; - // Translate image to the left - // |--^#####^xx| => |#####^xx^xx| - let moved_slice : Vec = rgba.pixels[row_offset+span*4..row_offset+rgba.width as usize*4].into(); - rgba.pixels[row_offset..row_offset+moved_start*4].copy_from_slice(&moved_slice); - - // |#####^xx^xx| => |#####^xx^ | - let blank = vec![255; span*4]; - rgba.pixels[row_offset+moved_start*4..row_offset+rgba.width as usize*4].copy_from_slice(&blank); - } - // Set function scoped last rows - if previous_left_row.0 != rgba.height as usize && previous_left_row.1 != 0 { - last_rows.0 = if (previous_left_row.0 as f32 - 128f32).abs() < (previous_left_row.1 as f32 - 128f32).abs() {previous_left_row.0} else {previous_left_row.1}; - } - if previous_right_row.0 != rgba.height as usize && previous_right_row.1 != 0 { - last_rows.1 = if (previous_right_row.0 as f32 - 128f32).abs() < (previous_right_row.1 as f32 - 128f32).abs() {previous_right_row.0} else {previous_right_row.1}; - } - } - else { - rgba.pixels.fill(255); - } - - let left_column_width : usize = left_samples.len() / span; - let right_column_width : usize = right_samples.len() / span; - for x in 0..span { - // LEFT CHANNEL - let column_values : &[f32] = &left_samples[x*left_column_width..(x+1)*left_column_width]; - let column_average : f32 = (column_values.iter().cloned().reduce(|acc, v| acc+v).unwrap_or(0f32) / column_values.len() as f32).clamp(-0.99, 0.99); - let row = (128f32 - (column_average * 128f32)).round() as usize; - - // Draw vertical lines from last row - let range : Vec = if row <= last_rows.0 {(row..=last_rows.0).collect()} else {(last_rows.0..=row).rev().collect()}; - for row in range { - let coord = (row * rgba.width as usize * 4) + ((moved_start + x) * 4); - rgba.pixels[coord+0] = 0; - rgba.pixels[coord+1] = 0; - } - last_rows.0 = row; - - // RIGHT CHANNEL - let column_values : &[f32] = &right_samples[x*right_column_width..(x+1)*right_column_width]; - let column_average : f32 = (column_values.iter().cloned().reduce(|acc, v| acc+v).unwrap_or(0f32) / column_values.len() as f32).clamp(-0.99, 0.99); - let row = (128f32 - (column_average * 128f32)).round() as usize; - - // Draw vertical lines from last row - let range : Vec = if row <= last_rows.1 {(row..=last_rows.1).collect()} else {(last_rows.1..=row).rev().collect()}; - for row in range { - let coord = (row * rgba.width as usize * 4) + ((moved_start + x) * 4); - rgba.pixels[coord+1] = 0; - rgba.pixels[coord+2] = 0; - } - last_rows.1 = row; - } -} diff --git a/src/waveform.rs b/src/waveform.rs new file mode 100644 index 0000000..86307ad --- /dev/null +++ b/src/waveform.rs @@ -0,0 +1,110 @@ +use crate::Rgba; + +pub struct Waveform { + rgba: Rgba, +} + +impl Waveform { + pub fn new(width: u32, height: u32) -> Self { + Self { + rgba: Rgba { + width: width, + height: height, + pixels: vec![255; (width*height*4) as usize], + } + } + } + + pub fn update(&mut self, samples: (&Vec, &Vec), resolution: f32 ) { + let (left_samples, right_samples) = samples; + let mut last_rows = ((self.rgba.height/2) as usize, (self.rgba.height/2) as usize); + + let span = (self.rgba.width as f32 * resolution) as usize; + let moved_start = self.rgba.width as usize - span; + + if resolution < 1.0 { + let mut previous_left_row = (self.rgba.height as usize, 0usize); + let mut previous_right_row = (self.rgba.height as usize, 0usize); + + for row in 0..self.rgba.height as usize { + // Get min/max left/right values for last column looking in each row + let last_pixel_idx = (((row+1)*self.rgba.width as usize)-1)*4; + let last_row_pixel = &self.rgba.pixels[last_pixel_idx..last_pixel_idx+4]; + // LEFT + if last_row_pixel[2] == 0 { + if row < previous_left_row.0 { previous_left_row.0 = row; } + if row > previous_left_row.1 { previous_left_row.1 = row; } + } + // RIGHT + if last_row_pixel[0] == 0 { + if row < previous_right_row.0 { previous_right_row.0 = row; } + if row > previous_right_row.1 { previous_right_row.1 = row; } + } + + let row_offset = row * self.rgba.width as usize*4; + // Translate image to the left + // |--^#####^xx| => |#####^xx^xx| + let moved_slice : Vec = self.rgba.pixels[row_offset+span*4..row_offset+self.rgba.width as usize*4].into(); + self.rgba.pixels[row_offset..row_offset+moved_start*4].copy_from_slice(&moved_slice); + + // |#####^xx^xx| => |#####^xx^ | + let blank = vec![255; span*4]; + self.rgba.pixels[row_offset+moved_start*4..row_offset+self.rgba.width as usize*4].copy_from_slice(&blank); + } + // Set function scoped last rows + if previous_left_row.0 != self.rgba.height as usize && previous_left_row.1 != 0 { + last_rows.0 = if (previous_left_row.0 as f32 - 128f32).abs() < (previous_left_row.1 as f32 - 128f32).abs() {previous_left_row.0} else {previous_left_row.1}; + } + if previous_right_row.0 != self.rgba.height as usize && previous_right_row.1 != 0 { + last_rows.1 = if (previous_right_row.0 as f32 - 128f32).abs() < (previous_right_row.1 as f32 - 128f32).abs() {previous_right_row.0} else {previous_right_row.1}; + } + } + else { + self.rgba.pixels.fill(255); + } + + let left_column_width : usize = left_samples.len() / span; + let right_column_width : usize = right_samples.len() / span; + for x in 0..span { + // LEFT CHANNEL + let column_values : &[f32] = &left_samples[x*left_column_width..(x+1)*left_column_width]; + let column_average : f32 = (column_values.iter().cloned().reduce(|acc, v| acc+v).unwrap_or(0f32) / column_values.len() as f32).clamp(-0.99, 0.99); + let row = (128f32 - (column_average * 128f32)).round() as usize; + + // Draw vertical lines from last row + let range : Vec = if row <= last_rows.0 {(row..=last_rows.0).collect()} else {(last_rows.0..=row).rev().collect()}; + for row in range { + let coord = (row * self.rgba.width as usize * 4) + ((moved_start + x) * 4); + self.rgba.pixels[coord+0] = 0; + self.rgba.pixels[coord+1] = 0; + } + last_rows.0 = row; + + // RIGHT CHANNEL + let column_values : &[f32] = &right_samples[x*right_column_width..(x+1)*right_column_width]; + let column_average : f32 = (column_values.iter().cloned().reduce(|acc, v| acc+v).unwrap_or(0f32) / column_values.len() as f32).clamp(-0.99, 0.99); + let row = (128f32 - (column_average * 128f32)).round() as usize; + + // Draw vertical lines from last row + let range : Vec = if row <= last_rows.1 {(row..=last_rows.1).collect()} else {(last_rows.1..=row).rev().collect()}; + for row in range { + let coord = (row * self.rgba.width as usize * 4) + ((moved_start + x) * 4); + self.rgba.pixels[coord+1] = 0; + self.rgba.pixels[coord+2] = 0; + } + last_rows.1 = row; + } + } + + + + pub fn view(&self) -> iced::Element<'_, crate::Message> { + let img_handle = iced::widget::image::Handle::from_rgba( + self.rgba.width, + self.rgba.height, + self.rgba.pixels.clone() + ); + iced::widget::center_x(iced::widget::image(img_handle)).into() + } + +} \ No newline at end of file