use std::task::Waker;
use wasm_bindgen::{closure::Closure, JsCast};
use web_sys::{
AudioContext, AudioDestinationNode, AudioProcessingEvent,
MediaStreamAudioSourceNode, ScriptProcessorNode,
};
use crate::consts::BUFFER_SIZE;
struct State {
context: Option<AudioContext>,
speaker: Option<AudioDestinationNode>,
microphone: Vec<MediaStreamAudioSourceNode>,
i_buffer: [f32; BUFFER_SIZE as usize],
l_buffer: [f32; BUFFER_SIZE as usize],
r_buffer: [f32; BUFFER_SIZE as usize],
proc: Option<ScriptProcessorNode>,
speaker_waker: Option<Waker>,
mics_waker: Option<Waker>,
played: bool,
recorded: bool,
sample_rate: Option<f64>,
}
impl State {
fn lazy_init(&mut self) {
if state().context.is_none() {
let audio_context =
AudioContext::new().expect("Couldn't initialize AudioContext");
state().sample_rate = Some(audio_context.sample_rate().into());
state().context = Some(audio_context);
}
if self.proc.is_none() {
let proc = self
.context
.as_ref()
.unwrap()
.create_script_processor_with_buffer_size(BUFFER_SIZE.into())
.unwrap();
#[allow(trivial_casts)] let js_function: Closure<dyn Fn(AudioProcessingEvent)> =
Closure::wrap(Box::new(move |event| {
if let Some(waker) = state().mics_waker.take() {
let inbuf = event
.input_buffer()
.expect("Failed to get input buffer");
inbuf
.copy_from_channel(&mut state().i_buffer, 0)
.unwrap();
state().recorded = true;
waker.wake();
}
if let Some(waker) = state().speaker_waker.take() {
state().played = true;
waker.wake();
let out = event
.output_buffer()
.expect("Failed to get output buffer");
out.copy_to_channel(&mut state().l_buffer, 0).unwrap();
out.copy_to_channel(&mut state().r_buffer, 1).unwrap();
}
}));
proc.set_onaudioprocess(Some(js_function.as_ref().unchecked_ref()));
js_function.forget();
self.proc = Some(proc);
}
}
}
static mut STATE: State = State {
context: None,
speaker: None,
microphone: Vec::new(),
i_buffer: [0.0; BUFFER_SIZE as usize],
l_buffer: [0.0; BUFFER_SIZE as usize],
r_buffer: [0.0; BUFFER_SIZE as usize],
proc: None,
speaker_waker: None,
mics_waker: None,
played: false,
recorded: false,
sample_rate: None,
};
#[allow(unsafe_code)]
#[inline(always)]
fn state() -> &'static mut State {
unsafe { &mut STATE }
}
mod device_list;
mod microphone;
mod speakers;
use device_list::SoundDevice;
pub(crate) use device_list::device_list;
pub(super) use microphone::{Microphone, MicrophoneStream};
pub(super) use speakers::{Speakers, SpeakersSink};