use std::{cell::Cell, rc::Rc}; use cpal::{ traits::{DeviceTrait, HostTrait, StreamTrait}, Stream, }; use wasm_bindgen::prelude::*; use web_sys::console; // This is like the `main` function, except for JavaScript. #[wasm_bindgen(start)] pub fn main_js() -> Result<(), JsValue> { // This provides better error messages in debug mode. // It's disabled in release mode, so it doesn't bloat up the file size. #[cfg(debug_assertions)] console_error_panic_hook::set_once(); let document = gloo::utils::document(); let play_button = document.get_element_by_id("play").unwrap(); let stop_button = document.get_element_by_id("stop").unwrap(); // stream needs to be referenced from the "play" and "stop" closures let stream = Rc::new(Cell::new(None)); // set up play button { let stream = stream.clone(); let closure = Closure::::new(move |_event: web_sys::MouseEvent| { stream.set(Some(beep())); }); play_button .add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())?; closure.forget(); } // set up stop button { let closure = Closure::::new(move |_event: web_sys::MouseEvent| { // stop the stream by dropping it stream.take(); }); stop_button .add_event_listener_with_callback("mousedown", closure.as_ref().unchecked_ref())?; closure.forget(); } Ok(()) } fn beep() -> Stream { let host = cpal::default_host(); let device = host .default_output_device() .expect("failed to find a default output device"); let config = device.default_output_config().unwrap(); match config.sample_format() { cpal::SampleFormat::F32 => run::(&device, config.into()), cpal::SampleFormat::I16 => run::(&device, config.into()), cpal::SampleFormat::U16 => run::(&device, config.into()), _ => panic!("unsupported sample format"), } } fn run(device: &cpal::Device, config: cpal::StreamConfig) -> Stream where T: cpal::Sample + cpal::SizedSample + cpal::FromSample, { let sample_rate = config.sample_rate as f32; let channels = config.channels as usize; // Produce a sinusoid of maximum amplitude. let mut sample_clock = 0f32; let mut next_value = move || { sample_clock = (sample_clock + 1.0) % sample_rate; (sample_clock * 440.0 * 2.0 * 3.141592 / sample_rate).sin() }; let err_fn = |err| console::error_1(&format!("an error occurred on stream: {}", err).into()); let stream = device .build_output_stream( config, move |data: &mut [T], _| write_data(data, channels, &mut next_value), err_fn, None, ) .unwrap(); stream.play().unwrap(); stream } fn write_data(output: &mut [T], channels: usize, next_sample: &mut dyn FnMut() -> f32) where T: cpal::Sample + cpal::FromSample, { for frame in output.chunks_mut(channels) { let sample = next_sample(); let value = T::from_sample::(sample); for sample in frame.iter_mut() { *sample = value; } } }