155 lines
4.7 KiB
Rust
155 lines
4.7 KiB
Rust
use crate::agents::audio_agent::{AudioAgent, AudioData};
|
|
use crate::utils;
|
|
use std::sync::Arc;
|
|
use wasm_bindgen::prelude::*;
|
|
use wasm_bindgen::JsCast;
|
|
use web_sys::HtmlCanvasElement;
|
|
use yew::prelude::*;
|
|
use yew::services::ConsoleService;
|
|
use yew::Bridge;
|
|
|
|
pub struct Canvas {
|
|
props: Props,
|
|
_audio_agent: Box<dyn Bridge<AudioAgent>>,
|
|
canvas_node: NodeRef,
|
|
audio_data: Option<Arc<AudioData>>,
|
|
cb: Option<Closure<dyn FnMut()>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub enum Msg {
|
|
Reset,
|
|
AudioAgentMessage(Result<Arc<AudioData>, String>),
|
|
}
|
|
|
|
#[derive(Properties, Clone, PartialEq, Debug)]
|
|
pub struct Props {
|
|
#[prop_or(1)]
|
|
pub zoom_factor: i32,
|
|
}
|
|
|
|
impl Component for Canvas {
|
|
type Message = Msg;
|
|
type Properties = Props;
|
|
|
|
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
|
|
Self {
|
|
props,
|
|
_audio_agent: AudioAgent::bridge(link.callback(Msg::AudioAgentMessage)),
|
|
canvas_node: NodeRef::default(),
|
|
audio_data: None,
|
|
cb: None,
|
|
}
|
|
}
|
|
|
|
fn rendered(&mut self, first_render: bool) {
|
|
if first_render {
|
|
let cb = Closure::wrap(Box::new(|| {
|
|
ConsoleService::log(&format!("In setInterval callback"));
|
|
}) as Box<dyn FnMut()>);
|
|
|
|
// TODO: request_animation_frame() ?
|
|
let window = web_sys::window().unwrap();
|
|
window
|
|
.set_interval_with_callback_and_timeout_and_arguments_0(
|
|
cb.as_ref().unchecked_ref(),
|
|
1000,
|
|
)
|
|
.unwrap();
|
|
|
|
self.cb = Some(cb);
|
|
}
|
|
}
|
|
|
|
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, props: Self::Properties) -> ShouldRender {
|
|
if self.props.zoom_factor != props.zoom_factor {
|
|
ConsoleService::log(&format!("Switch to Zoom factor: {:?}", props));
|
|
self.props.zoom_factor = props.zoom_factor;
|
|
self.redraw_canvas();
|
|
}
|
|
|
|
if self.props != props {
|
|
self.props = props;
|
|
}
|
|
false
|
|
}
|
|
|
|
fn view(&self) -> Html {
|
|
html! { <canvas ref=self.canvas_node.clone() width="800" height="300" style="border: 1px solid grey"></canvas> }
|
|
}
|
|
}
|
|
|
|
impl Canvas {
|
|
fn reset(&mut self) {
|
|
self.audio_data = None;
|
|
self.redraw_canvas();
|
|
}
|
|
|
|
fn handle_samples_loaded(&mut self, audio_data: Arc<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::<HtmlCanvasElement>().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::<web_sys::CanvasRenderingContext2d>()
|
|
.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.num_channels;
|
|
|
|
for chan in 0..num_channels {
|
|
let channel_data = &audio_data.buffers[chan as usize];
|
|
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();
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|