use crate::agents::audio_agent::{AudioAgent, AudioData}; use crate::utils; use wasm_bindgen::JsCast; use web_sys::HtmlCanvasElement; use yew::agent::Bridged; use yew::prelude::*; use yew::services::ConsoleService; use yew::Bridge; pub struct Canvas { _audio_agent: Box>, canvas_node: NodeRef, audio_data: Option, } #[derive(Debug)] pub enum Msg { Reset, AudioAgentMessage(Result), } impl Component for Canvas { type Message = Msg; type Properties = (); fn create(_: Self::Properties, link: ComponentLink) -> Self { Self { _audio_agent: AudioAgent::bridge(link.callback(Msg::AudioAgentMessage)), canvas_node: NodeRef::default(), audio_data: None, } } fn update(&mut self, msg: Self::Message) -> ShouldRender { match msg { Msg::AudioAgentMessage(Ok(audio_data)) => self.handle_samples_loaded(audio_data), Msg::AudioAgentMessage(Err(err)) => self.handle_samples_loaded_error(&err), Msg::Reset => self.reset(), }; false } fn change(&mut self, _: Self::Properties) -> ShouldRender { false } fn view(&self) -> Html { html! { } } } impl Canvas { fn reset(&mut self) { self.audio_data = None; self.redraw_canvas(); } fn handle_samples_loaded(&mut self, audio_data: AudioData) { self.audio_data = Some(audio_data); self.redraw_canvas(); } fn handle_samples_loaded_error(&mut self, err: &str) { ConsoleService::error(&format!("Error loading samples: {}", err)); self.audio_data = None; self.redraw_canvas(); } fn redraw_canvas(&mut self) { let canvas_element = self.canvas_node.cast::().unwrap(); let width = canvas_element.width() as f64; let height = canvas_element.height() as f64; let context = canvas_element .get_context("2d") .unwrap() .unwrap() .dyn_into::() .unwrap(); context.set_fill_style(&"#000000".into()); context.set_stroke_style(&"#00ff00".into()); context.fill_rect(0.0, 0.0, width, height); if self.audio_data.is_some() { let audio_data = self.audio_data.as_ref().unwrap(); let num_channels = audio_data.number_of_channels(); for chan in 0..num_channels { let channel_data = audio_data.get_channel_data(chan).unwrap(); let chunks = utils::chunks_fixed(&channel_data, canvas_element.width() as usize); chunks.enumerate().for_each(|(i, chunk)| { let max = chunk .iter() .map(|v| (v * 32767.0) as i32) .map(|v| v.abs()) .max() .unwrap(); let pc = (max as f64 / 32767.0) * 100.0; let max_height = (height / num_channels as f64).floor(); let len = (max_height / 100.0 * pc).floor(); let mid = max_height * (chan + 1) as f64 - (max_height / 2.0); context.begin_path(); context.move_to(i as f64, mid - (len / 2.0)); context.line_to(i as f64, mid + (len / 2.0)); context.stroke(); // ConsoleService::log(&format!("index {}: max {}, pc {}, len {}", i, max, pc, len)); }); } } } }