examples(webaudio): Tidy up the webaudio example

This commit is contained in:
Nick Fitzgerald
2018-09-10 17:50:34 -07:00
parent e730ee9a62
commit 42ea38187f
5 changed files with 79 additions and 80 deletions

View File

@ -1,7 +1,7 @@
[package] [package]
name = "webaudio" name = "webaudio"
version = "0.1.0" version = "0.1.0"
authors = ["Andrew Chin <achin@eminence32.net>"] authors = ["The wasm-bindgen Developers"]
[lib] [lib]
crate-type = ["cdylib"] crate-type = ["cdylib"]

View File

@ -3,16 +3,9 @@
<meta content="text/html;charset=utf-8" http-equiv="Content-Type"/> <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
</head> </head>
<body> <body>
<script>
// Global variable, so a user can play with it via the JS console
var fm;
function play() {
console.log("Rust module not loaded yet!");
}
</script>
<script src='./index.js'></script> <script src='./index.js'></script>
<input type="button" value="Click me first to turn on audio" onclick="javascript: play();" /> <input id="play" type="button" value="Click me first to turn on audio"/>
(headphone users, please make sure your volume is not too loud!) (headphone users, please make sure your volume is not too loud!)
<div> <div>

View File

@ -1,40 +1,35 @@
const rust = import('./webaudio'); import('./webaudio').then(rust_module => {
let fm = null;
const play_button = document.getElementById("play");
// Most browsers don't let WebAudio autoplay without some interaction from the user. So once the module is loaded, play_button.addEventListener("click", event => {
// it's passed to this function which will set up the UI elements for the user to interact with if (fm === null) {
function setup(rust_module) {
play = function() {
console.log("About to create some music!");
fm = new rust_module.FmOsc(); fm = new rust_module.FmOsc();
fm.set_note(50); fm.set_note(50);
fm.set_fm_frequency(0); fm.set_fm_frequency(0);
fm.set_fm_amount(0); fm.set_fm_amount(0);
fm.set_gain(0.8); fm.set_gain(0.8);
}
});
};
// create some UI elements
const primary_slider = document.getElementById("primary_input"); const primary_slider = document.getElementById("primary_input");
primary_slider.oninput = (e) => { primary_slider.addEventListener("input", event => {
fm.set_note(e.target.value); if (fm) {
}; fm.set_note(event.target.value);
}
});
const fm_freq = document.getElementById("fm_freq"); const fm_freq = document.getElementById("fm_freq");
fm_freq.oninput = (e) => { fm_freq.addEventListener("input", event => {
fm.set_fm_frequency(e.target.value); if (fm) {
}; fm.set_fm_frequency(event.target.value);
}
});
const fm_amount = document.getElementById("fm_amount"); const fm_amount = document.getElementById("fm_amount");
fm_amount.oninput = (e) => { fm_amount.addEventListener("input", event => {
fm.set_fm_amount(e.target.value); if (fm) {
}; fm.set_fm_amount(event.target.value);
console.log("Ready! Press the play button!");
} }
});
rust.then(m => {
setup(m);
}); });

View File

@ -4,6 +4,7 @@
}, },
"devDependencies": { "devDependencies": {
"webpack": "^4.16.5", "webpack": "^4.16.5",
"webpack-serve": "^2.0.2" "webpack-cli": "^2.0.10",
"webpack-dev-server": "^3.1.0"
} }
} }

View File

@ -2,7 +2,9 @@ extern crate wasm_bindgen;
extern crate web_sys; extern crate web_sys;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use web_sys::{AudioContext, BaseAudioContext, AudioNode, AudioScheduledSourceNode, OscillatorType}; use web_sys::{
AudioContext, AudioNode, AudioScheduledSourceNode, BaseAudioContext, OscillatorType,
};
/// Converts a midi note to frequency /// Converts a midi note to frequency
/// ///
@ -32,16 +34,12 @@ pub struct FmOsc {
fm_freq_ratio: f32, fm_freq_ratio: f32,
fm_gain_ratio: f32, fm_gain_ratio: f32,
} }
#[wasm_bindgen] #[wasm_bindgen]
impl FmOsc { impl FmOsc {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new() -> FmOsc { pub fn new() -> FmOsc {
// TODO, how to throw from a constructor?
let ctx = web_sys::AudioContext::new().unwrap(); let ctx = web_sys::AudioContext::new().unwrap();
let primary; let primary;
let fm_osc; let fm_osc;
@ -51,14 +49,14 @@ impl FmOsc {
{ {
let base: &BaseAudioContext = ctx.as_ref(); let base: &BaseAudioContext = ctx.as_ref();
// create our web audio objects // Create our web audio objects.
primary = base.create_oscillator().unwrap(); primary = base.create_oscillator().unwrap();
fm_osc = base.create_oscillator().unwrap(); fm_osc = base.create_oscillator().unwrap();
gain = base.create_gain().unwrap(); gain = base.create_gain().unwrap();
fm_gain = base.create_gain().unwrap(); fm_gain = base.create_gain().unwrap();
} }
// some initial settings: // Some initial settings:
primary.set_type(OscillatorType::Sine); primary.set_type(OscillatorType::Sine);
primary.frequency().set_value(440.0); // A4 note primary.frequency().set_value(440.0); // A4 note
gain.gain().set_value(0.0); // starts muted gain.gain().set_value(0.0); // starts muted
@ -66,7 +64,6 @@ impl FmOsc {
fm_osc.set_type(OscillatorType::Sine); fm_osc.set_type(OscillatorType::Sine);
fm_osc.frequency().set_value(0.0); fm_osc.frequency().set_value(0.0);
// Create base class references: // Create base class references:
{ {
let primary_node: &AudioNode = primary.as_ref(); let primary_node: &AudioNode = primary.as_ref();
@ -77,26 +74,36 @@ impl FmOsc {
let destination = base.destination(); let destination = base.destination();
let destination_node: &AudioNode = destination.as_ref(); let destination_node: &AudioNode = destination.as_ref();
// Connect the nodes up!
// connect them up: // The primary oscillator is routed through the gain node, so that
// it can control the overall output volume.
// The primary oscillator is routed through the gain node, so that it can control the overall output volume
primary_node.connect_with_audio_node(gain.as_ref()).unwrap(); primary_node.connect_with_audio_node(gain.as_ref()).unwrap();
// Then connect the gain node to the AudioContext destination (aka your speakers)
// Then connect the gain node to the AudioContext destination (aka
// your speakers).
gain_node.connect_with_audio_node(destination_node).unwrap(); gain_node.connect_with_audio_node(destination_node).unwrap();
// the FM oscillator is connected to its own gain node, so it can control the amount of modulation // The FM oscillator is connected to its own gain node, so it can
fm_osc_node.connect_with_audio_node(fm_gain.as_ref()).unwrap(); // control the amount of modulation.
fm_osc_node
.connect_with_audio_node(fm_gain.as_ref())
.unwrap();
// Connect the FM oscillator to the frequency parameter of the main oscillator, so that the // Connect the FM oscillator to the frequency parameter of the main
// FM node can modulate its frequency // oscillator, so that the FM node can modulate its frequency.
fm_gain_node.connect_with_audio_param(&primary.frequency()).unwrap(); fm_gain_node
.connect_with_audio_param(&primary.frequency())
.unwrap();
} }
// Start the oscillators!
// start the oscillators! AsRef::<AudioScheduledSourceNode>::as_ref(&primary)
AsRef::<AudioScheduledSourceNode>::as_ref(&primary).start().unwrap(); .start()
AsRef::<AudioScheduledSourceNode>::as_ref(&fm_osc).start().unwrap(); .unwrap();
AsRef::<AudioScheduledSourceNode>::as_ref(&fm_osc)
.start()
.unwrap();
FmOsc { FmOsc {
ctx, ctx,
@ -107,14 +114,17 @@ impl FmOsc {
fm_freq_ratio: 0.0, fm_freq_ratio: 0.0,
fm_gain_ratio: 0.0, fm_gain_ratio: 0.0,
} }
} }
/// Sets the gain for this oscillator, between 0.0 and 1.0 /// Sets the gain for this oscillator, between 0.0 and 1.0.
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_gain(&self, mut gain: f32) { pub fn set_gain(&self, mut gain: f32) {
if gain > 1.0 { gain = 1.0; } if gain > 1.0 {
if gain < 0.0 { gain = 0.0; } gain = 1.0;
}
if gain < 0.0 {
gain = 0.0;
}
self.gain.gain().set_value(gain); self.gain.gain().set_value(gain);
} }
@ -122,11 +132,10 @@ impl FmOsc {
pub fn set_primary_frequency(&self, freq: f32) { pub fn set_primary_frequency(&self, freq: f32) {
self.primary.frequency().set_value(freq); self.primary.frequency().set_value(freq);
// The frequency of the FM oscillator depends on the frequency of the primary oscillator, so // The frequency of the FM oscillator depends on the frequency of the
// we update the frequency of both in this method // primary oscillator, so we update the frequency of both in this method.
self.fm_osc.frequency().set_value(self.fm_freq_ratio * freq); self.fm_osc.frequency().set_value(self.fm_freq_ratio * freq);
self.fm_gain.gain().set_value(self.fm_gain_ratio * freq); self.fm_gain.gain().set_value(self.fm_gain_ratio * freq);
} }
#[wasm_bindgen] #[wasm_bindgen]
@ -135,21 +144,22 @@ impl FmOsc {
self.set_primary_frequency(freq); self.set_primary_frequency(freq);
} }
/// This should be between 0 and 1, though higher values are accepted /// This should be between 0 and 1, though higher values are accepted.
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_fm_amount(&mut self, amt: f32) { pub fn set_fm_amount(&mut self, amt: f32) {
self.fm_gain_ratio = amt; self.fm_gain_ratio = amt;
self.fm_gain.gain().set_value(self.fm_gain_ratio * self.primary.frequency().value()); self.fm_gain
.gain()
.set_value(self.fm_gain_ratio * self.primary.frequency().value());
} }
/// This should be between 0 and 1, though higher values are accepted /// This should be between 0 and 1, though higher values are accepted.
#[wasm_bindgen] #[wasm_bindgen]
pub fn set_fm_frequency(&mut self, amt: f32) { pub fn set_fm_frequency(&mut self, amt: f32) {
self.fm_freq_ratio = amt; self.fm_freq_ratio = amt;
self.fm_osc.frequency().set_value(self.fm_freq_ratio * self.primary.frequency().value()); self.fm_osc
.frequency()
.set_value(self.fm_freq_ratio * self.primary.frequency().value());
} }
} }