A lightweight C++ library to make it easy to work with realtime, low latency WASM audio worklets under Emscripten.
- Live demo: https://armchair-software.github.io/emscripten-sound-demo/
- Demo source code: https://github.com/Armchair-Software/emscripten-sound-demo
Minimal example, logging when playback starts and creating a simple white noise generator:
emscripten_audio audio{{
.callbacks{
.playback_started{[&]{ // called when the user clicks on the window (which is needed to enable audio playback in modern browsers)
std::cout << "Hello world, playback has started!" << std::endl;
}},
.processing{[&](std::span<AudioSampleFrame const> /*inputs*/,
std::span<AudioSampleFrame> outputs,
std::span<AudioParamFrame const > /*params*/){
for(auto const &output : outputs) {
for(int i{0}; i != output.samplesPerChannel; ++i) {
for(int channel{0}; channel != output.numberOfChannels; ++channel) {
// set your samples here however you want them - this example just generates white noise:
output.data[channel * output.samplesPerChannel + i] = emscripten_random() * 0.2 - 0.1;
}
}
}
}},
},
}};
emscripten_audio
's constructor accepts a construction_options
struct, which can be used to configure all initial behaviour:
struct construction_options {
unsigned int inputs{0}; // number of inputs
std::vector<unsigned int> output_channels{2}; // number of outputs, and number of channels for each output
latencies latency_hint{latencies::interactive}; // hint for requested latency mode
std::string worklet_name{"emscripten-audio-worklet"};
callback_types callbacks{}; // action and data processing callbacks
};
All members are optional, with sensible defaults (as defined above). Callbacks can be set initially, or left unset and set later if desired, or simply not set at all. To produce any sound, you'll eventually want to set the output
callback at least.
How latency hints are handled is up to the implementation. The available hints are:
enum class latencies {
balanced,
interactive,
playback,
};
Here interactive
will usually be the lowest available latency, and balanced
will typically aim to use the least device power, but exact results may vary for your device.
Number of inputs, outputs and their channels, latency hint and worklet name are fixed after initial construction. The values of inputs and outputs and their channels can be accessed as const members audio.inputs
and audio.output_channels
.
Callbacks can also be updated at any time after program start, by modifying audio.callbacks.processing
(and audio.callbacks.playback_started
, although you're not likely to need to). If no processing callback is created, but non-zero output channels exist, the output will be filled with zeros to avoid inadvertently generating white noise.
Getters are provided for further information:
EMSCRIPTEN_WEBAUDIO_T get_context() const;
states get_state() const;
unsigned int get_sample_rate() const;
The playback states match up with Emscripten's AUDIO_CONTEXT_STATE_*
macros, as follows:
enum class states {
suspended,
running,
closed,
interrupted,
};