use js_sys::Uint8Array; use wasm_bindgen::JsValue; use wasm_bindgen_futures::JsFuture; use web_sys::{AudioContext, File}; use yew::services::reader::{FileChunk, ReaderTask}; use yew::services::{ConsoleService, ReaderService}; use yew::worker::*; use yewtil::future::LinkFuture; const FILE_LOAD_CHUNK_SIZE: usize = 4096; pub struct AudioAgent { link: AgentLink, reader_service: ReaderService, reader_task: Option, audio_context: AudioContext, bytes_buffer: Vec, } pub enum Request { LoadSamplesFromFile(File), } pub enum Msg { FileProgress(Option), AudioDecoded(JsValue), AudioDecodingFailed(JsValue), } impl From> for Msg { fn from(item: Result) -> Self { match item { Ok(samples) => Msg::AudioDecoded(samples), Err(e) => Msg::AudioDecodingFailed(e), } } } impl Agent for AudioAgent { type Reach = Context; type Message = Msg; type Input = Request; type Output = (); fn create(link: AgentLink) -> Self { // TODO: where should the AudioContext be initialized and stored? Self { link, reader_service: ReaderService::new(), reader_task: None, audio_context: AudioContext::new().unwrap(), bytes_buffer: vec![], } } fn update(&mut self, msg: Self::Message) { match msg { Msg::FileProgress(Some(FileChunk::DataChunk { data, progress })) => { self.handle_file_progress(data, progress) } Msg::FileProgress(Some(FileChunk::Finished)) => { if let Err(e) = self.handle_file_loaded() { ConsoleService::error(&e); } } Msg::AudioDecoded(samples) => { let audio_buffer = web_sys::AudioBuffer::from(samples); let data = audio_buffer.get_channel_data(0).unwrap(); // TODO: error handling let vec: Vec = data.to_vec(); ConsoleService::log(&format!("Success decoding: {:?}", vec)); } Msg::AudioDecodingFailed(err) => { ConsoleService::error(&format!("Error decoding: {:?}", err)); } _ => (), }; } fn handle_input(&mut self, input: Self::Input, _: HandlerId) { match input { Request::LoadSamplesFromFile(file) => self.load_samples_from_file(file), }; } } impl AudioAgent { fn load_samples_from_file(&mut self, file: File) -> Result<(), String> { self.bytes_buffer = Vec::with_capacity(file.size() as usize); let cb = self.link.callback(Msg::FileProgress); let task = self .reader_service .read_file_by_chunks(file, cb, FILE_LOAD_CHUNK_SIZE) .map_err(|e| e.to_string())?; self.reader_task = Some(task); Ok(()) } fn handle_file_progress(&mut self, mut data: Vec, _progress: f32) { self.bytes_buffer.append(&mut data); } fn handle_file_loaded(&self) -> Result<(), String> { let audio_data = Uint8Array::from(&self.bytes_buffer[..]); match self.audio_context.decode_audio_data(&audio_data.buffer()) { Ok(promise) => { self.link.send_future(JsFuture::from(promise)); Ok(()) } Err(e) => Err(format!("Error from decode_audio_data: {:?}", e)), } } }