Introduce Arc-based sharing of audio data
This commit is contained in:
parent
1821eeba14
commit
62fda0fe6c
|
@ -1,6 +1,6 @@
|
||||||
use js_sys::Uint8Array;
|
use js_sys::Uint8Array;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
use wasm_bindgen::JsValue;
|
use wasm_bindgen::JsValue;
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
use web_sys::console;
|
use web_sys::console;
|
||||||
|
@ -12,7 +12,7 @@ use yewtil::future::LinkFuture;
|
||||||
|
|
||||||
const FILE_LOAD_CHUNK_SIZE: usize = 4096;
|
const FILE_LOAD_CHUNK_SIZE: usize = 4096;
|
||||||
|
|
||||||
pub type AudioData = Rc<web_sys::AudioBuffer>;
|
pub type AudioData = Arc<web_sys::AudioBuffer>;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AudioAgent {
|
pub struct AudioAgent {
|
||||||
|
@ -79,7 +79,7 @@ impl Agent for AudioAgent {
|
||||||
}
|
}
|
||||||
Msg::AudioDecoded(samples) => {
|
Msg::AudioDecoded(samples) => {
|
||||||
let audio_buffer = web_sys::AudioBuffer::from(samples);
|
let audio_buffer = web_sys::AudioBuffer::from(samples);
|
||||||
let audio_data = Rc::new(audio_buffer);
|
let audio_data = Arc::new(audio_buffer);
|
||||||
|
|
||||||
for subscriber in self.subscribers.iter() {
|
for subscriber in self.subscribers.iter() {
|
||||||
self.link.respond(*subscriber, Ok(audio_data.clone()));
|
self.link.respond(*subscriber, Ok(audio_data.clone()));
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::agents::audio_agent::{self, AudioAgent};
|
use crate::agents::audio_agent::{self, AudioAgent, AudioData};
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use cpal::{Device, SampleFormat, SampleRate, Stream, StreamConfig};
|
use cpal::{Device, SampleFormat, SampleRate, Stream, StreamConfig};
|
||||||
use yew::agent::Dispatcher;
|
use yew::agent::Dispatcher;
|
||||||
|
@ -14,11 +14,13 @@ pub struct Player {
|
||||||
link: ComponentLink<Self>,
|
link: ComponentLink<Self>,
|
||||||
status: Status,
|
status: Status,
|
||||||
stream: Option<Stream>,
|
stream: Option<Stream>,
|
||||||
audio_agent: Dispatcher<AudioAgent>,
|
audio_agent: Box<dyn Bridge<AudioAgent>>,
|
||||||
|
audio_data: Option<AudioData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Msg {
|
pub enum Msg {
|
||||||
Play,
|
Play,
|
||||||
|
AudioAgentMessage(Result<AudioData, String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Player {
|
impl Component for Player {
|
||||||
|
@ -26,11 +28,14 @@ impl Component for Player {
|
||||||
type Properties = ();
|
type Properties = ();
|
||||||
|
|
||||||
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
|
||||||
|
let cb = link.callback(Msg::AudioAgentMessage);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
link,
|
link,
|
||||||
status: Status::Stopped,
|
status: Status::Stopped,
|
||||||
stream: None,
|
stream: None,
|
||||||
audio_agent: AudioAgent::dispatcher(),
|
audio_agent: AudioAgent::bridge(cb),
|
||||||
|
audio_data: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +44,8 @@ impl Component for Player {
|
||||||
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
fn update(&mut self, msg: Self::Message) -> ShouldRender {
|
||||||
match msg {
|
match msg {
|
||||||
Msg::Play => self.handle_play_button_clicked(),
|
Msg::Play => self.handle_play_button_clicked(),
|
||||||
|
Msg::AudioAgentMessage(Ok(audio_data)) => self.handle_samples_loaded(audio_data),
|
||||||
|
Msg::AudioAgentMessage(Err(err)) => self.handle_samples_loaded_error(&err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +63,20 @@ impl Component for Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
|
fn handle_samples_loaded(&mut self, audio_data: AudioData) -> ShouldRender {
|
||||||
|
ConsoleService::log("Player: samples loaded");
|
||||||
|
self.audio_data = Some(audio_data);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_samples_loaded_error(&mut self, err: &str) -> ShouldRender {
|
||||||
|
ConsoleService::log(&format!("Player: error loading samples: {:?}", err));
|
||||||
|
self.audio_data = None;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn play(&mut self) {
|
fn play(&mut self) {
|
||||||
|
if let Some(audio_data) = &self.audio_data {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let device = host.default_output_device().unwrap();
|
let device = host.default_output_device().unwrap();
|
||||||
|
|
||||||
|
@ -71,15 +91,22 @@ impl Player {
|
||||||
ConsoleService::log(&format!("Using output config: {:?}", config));
|
ConsoleService::log(&format!("Using output config: {:?}", config));
|
||||||
|
|
||||||
let stream = match config.sample_format() {
|
let stream = match config.sample_format() {
|
||||||
SampleFormat::F32 => Player::run::<f32>(&device, &config.into()),
|
SampleFormat::F32 => {
|
||||||
SampleFormat::I16 => Player::run::<i16>(&device, &config.into()),
|
Player::run::<f32>(&device, &config.into(), audio_data.clone())
|
||||||
SampleFormat::U16 => Player::run::<u16>(&device, &config.into()),
|
}
|
||||||
|
SampleFormat::I16 => {
|
||||||
|
Player::run::<i16>(&device, &config.into(), audio_data.clone())
|
||||||
|
}
|
||||||
|
SampleFormat::U16 => {
|
||||||
|
Player::run::<u16>(&device, &config.into(), audio_data.clone())
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.stream = Some(stream);
|
self.stream = Some(stream);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn run<T>(device: &Device, config: &StreamConfig) -> Stream
|
fn run<T>(device: &Device, config: &StreamConfig, audio_data: AudioData) -> Stream
|
||||||
where
|
where
|
||||||
T: cpal::Sample,
|
T: cpal::Sample,
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue