diff --git a/src/agents/audio_agent.rs b/src/agents/audio_agent.rs index 4e3b54a..a224f83 100644 --- a/src/agents/audio_agent.rs +++ b/src/agents/audio_agent.rs @@ -12,7 +12,12 @@ use yewtil::future::LinkFuture; const FILE_LOAD_CHUNK_SIZE: usize = 4096; -pub type AudioData = Arc; +#[derive(Debug)] +pub struct AudioData { + pub buffers: Vec>, + pub num_channels: u32, + pub sample_rate: f32, +} #[derive(Debug)] pub struct AudioAgent { @@ -53,7 +58,7 @@ impl Agent for AudioAgent { type Reach = Context; type Message = Msg; type Input = Request; - type Output = Result; + type Output = Result, String>; fn create(link: AgentLink) -> Self { // TODO: where should the AudioContext be initialized and stored? @@ -79,7 +84,18 @@ impl Agent for AudioAgent { } Msg::AudioDecoded(samples) => { let audio_buffer = web_sys::AudioBuffer::from(samples); - let audio_data = Arc::new(audio_buffer); + let num_channels = audio_buffer.number_of_channels(); + let sample_rate = audio_buffer.sample_rate(); + let buffers: Vec> = (0..num_channels) + .map(|i| audio_buffer.get_channel_data(i).unwrap()) + .collect(); + + let audio_data = AudioData { + buffers, + num_channels, + sample_rate, + }; + let audio_data = Arc::new(audio_data); for subscriber in self.subscribers.iter() { self.link.respond(*subscriber, Ok(audio_data.clone())); diff --git a/src/canvas.rs b/src/canvas.rs index 93f1983..7ac2cf2 100644 --- a/src/canvas.rs +++ b/src/canvas.rs @@ -1,5 +1,6 @@ use crate::agents::audio_agent::{AudioAgent, AudioData}; use crate::utils; +use std::sync::Arc; use wasm_bindgen::JsCast; use web_sys::HtmlCanvasElement; use yew::agent::Bridged; @@ -10,13 +11,13 @@ use yew::Bridge; pub struct Canvas { _audio_agent: Box>, canvas_node: NodeRef, - audio_data: Option, + audio_data: Option>, } #[derive(Debug)] pub enum Msg { Reset, - AudioAgentMessage(Result), + AudioAgentMessage(Result, String>), } impl Component for Canvas { @@ -55,7 +56,7 @@ impl Canvas { self.redraw_canvas(); } - fn handle_samples_loaded(&mut self, audio_data: AudioData) { + fn handle_samples_loaded(&mut self, audio_data: Arc) { self.audio_data = Some(audio_data); self.redraw_canvas(); } @@ -85,10 +86,10 @@ impl Canvas { if self.audio_data.is_some() { let audio_data = self.audio_data.as_ref().unwrap(); - let num_channels = audio_data.number_of_channels(); + let num_channels = audio_data.num_channels; for chan in 0..num_channels { - let channel_data = audio_data.get_channel_data(chan).unwrap(); + 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)| { @@ -108,8 +109,6 @@ impl Canvas { 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)); }); } } diff --git a/src/player.rs b/src/player.rs index d790a39..d35d4ce 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,6 +1,7 @@ use crate::agents::audio_agent::{self, AudioAgent, AudioData}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; -use cpal::{Device, SampleFormat, SampleRate, Stream, StreamConfig}; +use cpal::{Device, Sample, SampleFormat, SampleRate, Stream, StreamConfig}; +use std::sync::Arc; use yew::agent::Dispatcher; use yew::prelude::*; use yew::services::ConsoleService; @@ -15,12 +16,12 @@ pub struct Player { status: Status, stream: Option, audio_agent: Box>, - audio_data: Option, + audio_data: Option>, } pub enum Msg { Play, - AudioAgentMessage(Result), + AudioAgentMessage(Result, String>), } impl Component for Player { @@ -63,7 +64,7 @@ impl Component for Player { } impl Player { - fn handle_samples_loaded(&mut self, audio_data: AudioData) -> ShouldRender { + fn handle_samples_loaded(&mut self, audio_data: Arc) -> ShouldRender { ConsoleService::log("Player: samples loaded"); self.audio_data = Some(audio_data); true @@ -84,7 +85,7 @@ impl Player { let config = device .supported_output_configs() .unwrap() - .nth(1) + .nth(0) .unwrap() .with_sample_rate(SampleRate(44100)); @@ -106,14 +107,29 @@ impl Player { } } - fn run(device: &Device, config: &StreamConfig, audio_data: AudioData) -> Stream + fn run(device: &Device, config: &StreamConfig, audio_data: Arc) -> Stream where T: cpal::Sample, { let err_fn = |err| ConsoleService::warn(&format!("an error occurred on stream: {}", err)); + let num_channels = audio_data.num_channels as usize; + let mut idx: usize = 0; // current playback frame let stream = device - .build_output_stream(config, move |data: &mut [T], _| {}, err_fn) + .build_output_stream( + config, + move |output: &mut [T], _| { + for (i, frame) in output.chunks_mut(num_channels).enumerate() { + for (j, sample) in frame.iter_mut().enumerate() { + let buffer = &audio_data.buffers[j]; + let value: T = Sample::from::(&buffer[idx]); + *sample = value; + } + idx += 1; + } + }, + err_fn, + ) .unwrap(); stream.play().unwrap();