diff --git a/addons/ofxEmscripten/libs/html5audio/include/html5audio.h b/addons/ofxEmscripten/libs/html5audio/include/html5audio.h
index 48831961100..9dd55eaa608 100644
--- a/addons/ofxEmscripten/libs/html5audio/include/html5audio.h
+++ b/addons/ofxEmscripten/libs/html5audio/include/html5audio.h
@@ -28,7 +28,7 @@ extern "C"{
extern void html5audio_sound_set_pan(int sound, double pan);
extern void html5audio_sound_free(int sound);
- extern void html5audio_stream_create(int bufferSize, int inputChannels, int outputChannels, float * inbuffer, float * outbuffer, html5audio_stream_callback callback, void * userData);
+ extern void html5audio_stream_create(int audioWorkletNode, int numInputChannels);
extern void html5audio_stream_free();
extern bool html5audio_sound_is_loaded(int sound);
}
diff --git a/addons/ofxEmscripten/libs/html5audio/lib/emscripten/library_html5audio.js b/addons/ofxEmscripten/libs/html5audio/lib/emscripten/library_html5audio.js
index 81a20818ec9..18f252a9e6c 100644
--- a/addons/ofxEmscripten/libs/html5audio/lib/emscripten/library_html5audio.js
+++ b/addons/ofxEmscripten/libs/html5audio/lib/emscripten/library_html5audio.js
@@ -24,11 +24,13 @@ var LibraryHTML5Audio = {
}
},
+ html5audio_context_create__deps: ['$emscriptenRegisterAudioObject'],
html5audio_context_create: function () {
try {
// Fix up for prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
- var context = new AudioContext({});
+ var context = new AudioContext({ sampleRate: 44100 });
+ var id = emscriptenRegisterAudioObject(context);
// Fix issue with chrome autoplay policy
document.addEventListener('mousedown', function cb(event) {
@@ -44,7 +46,7 @@ var LibraryHTML5Audio = {
fft.maxDecibels = 0;
fft.minDecibels = -100;
AUDIO.fft = fft;
- return 0;
+ return id;
} catch (e) {
console.log('Web Audio API is not supported in this browser', e);
return -1;
@@ -62,7 +64,11 @@ var LibraryHTML5Audio = {
html5audio_context_spectrum: function (bands, spectrum) {
AUDIO.fft.fftSize = bands * 2;
var spectrumArray = Module.HEAPF32.subarray(spectrum >> 2, (spectrum >> 2) + bands);
- AUDIO.fft.getFloatFrequencyData(spectrumArray);
+ var spectrumArrayCopy = new Float32Array (spectrumArray);
+ AUDIO.fft.getFloatFrequencyData(spectrumArrayCopy);
+ for (let i = 0; i < spectrumArrayCopy.length; i++) {
+ spectrumArray[i] = spectrumArrayCopy[i];
+ }
},
html5audio_context_samplerate: function () {
@@ -97,7 +103,7 @@ var LibraryHTML5Audio = {
var fileSizeInBytes = stats.size;
var tag = ext; //this covers most types
- if( ext == mp3 ){
+ if( ext == 'mp3'){
tag = 'mpeg';
}else if( ext == 'oga'){
tag = 'ogg';
@@ -201,48 +207,21 @@ var LibraryHTML5Audio = {
}
},
- html5audio_stream_create: function(bufferSize, inputChannels, outputChannels, inbuffer, outbuffer, callback, userData) {
- var stream = AUDIO.context.createScriptProcessor(bufferSize, inputChannels, outputChannels);
- var inbufferArray = Module.HEAPF32.subarray(inbuffer >> 2, (inbuffer >> 2) + bufferSize * inputChannels);
- var outbufferArray = Module.HEAPF32.subarray(outbuffer >> 2, (outbuffer >> 2) + bufferSize * outputChannels);
-
- stream.onaudioprocess = function(event) {
- var i, j, c;
- if (inputChannels > 0) {
- for (c = 0; c < inputChannels; ++c) {
- var inChannel = event.inputBuffer.getChannelData(c);
- for (i = 0, j = c; i < bufferSize; ++i, j += inputChannels) {
- inbufferArray[j] = inChannel[i];
- }
- }
- }
-
- {{{ makeDynCall('viiii', 'callback') }}}(bufferSize, inputChannels, outputChannels, userData);
-
- if (outputChannels > 0) {
- for (c = 0; c < outputChannels; ++c) {
- var outChannel = event.outputBuffer.getChannelData(c);
- for (i = 0, j = c; i < bufferSize; ++i, j += outputChannels) {
- outChannel[i] = outbufferArray[j];
- }
- }
- }
- };
-
- if (inputChannels > 0) {
- navigator.mediaDevices.getUserMedia({ audio: true })
- .then(function (audioIn) {
- var mediaElement = AUDIO.context.createMediaStreamSource(audioIn);
- mediaElement.connect(stream);
- AUDIO.mediaElement = mediaElement;
- })
- .catch(function (error) {
- console.log("Error creating audio in", error);
- });
- }
-
- stream.connect(AUDIO.fft);
- },
+ html5audio_stream_create__deps: ['$emscriptenGetAudioObject'],
+ html5audio_stream_create: function(audioWorkletNode, numInputChannels){
+ var audioWorkletNode = emscriptenGetAudioObject(audioWorkletNode);
+ if(numInputChannels > 0){
+ navigator.mediaDevices.getUserMedia({ audio: true })
+ .then(function (audioIn) {
+ var mediaElement = AUDIO.context.createMediaStreamSource(audioIn);
+ mediaElement.connect(audioWorkletNode);
+ })
+ .catch(function (error) {
+ console.log("Error creating audio in", error);
+ });
+ }
+ audioWorkletNode.connect(AUDIO.fft);
+ },
html5audio_stream_free: function () {
diff --git a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp
index 208400abbab..7aee8fcd476 100644
--- a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp
+++ b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp
@@ -6,17 +6,64 @@
*/
#include "ofxEmscriptenSoundStream.h"
-#include "html5audio.h"
#include "ofBaseApp.h"
#include "ofLog.h"
+#include "html5audio.h"
+#include "emscripten/webaudio.h"
using namespace std;
+EM_BOOL ProcessAudio(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData) {
+ ofxEmscriptenSoundStream * stream = (ofxEmscriptenSoundStream *)userData;
+ ++stream->audioProcessedCount;
+ if (stream->settings.numInputChannels > 0 && stream->settings.inCallback) {
+ stream->settings.inCallback(stream->inbuffer);
+ for (int o = 0; o < numInputs; ++o) {
+ for (int i = 0; i < 128; ++i) {
+ for (int ch = 0; ch < inputs[o].numberOfChannels; ++ch) {
+ stream->inbuffer[i * inputs[o].numberOfChannels + ch] = inputs[o].data[ch * 128 + i];
+ }
+ }
+ }
+ }
+ if (stream->settings.numOutputChannels > 0 && stream->settings.outCallback) {
+ stream->settings.outCallback(stream->outbuffer);
+ for (int o = 0; o < numOutputs; ++o) {
+ for (int i = 0; i < 128; ++i) {
+ for (int ch = 0; ch < outputs[o].numberOfChannels; ++ch) {
+ outputs[o].data[ch * 128 + i] = stream->outbuffer[i * outputs[o].numberOfChannels + ch];
+ }
+ }
+ }
+ }
+ return EM_TRUE;
+}
+
+void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void *userData) {
+ if (!success) return;
+ ofxEmscriptenSoundStream * stream = (ofxEmscriptenSoundStream *)userData;
+ int outputChannelCounts[1] = { static_cast(stream->settings.numOutputChannels) };
+ EmscriptenAudioWorkletNodeCreateOptions options = {
+ .numberOfInputs = 1,
+ .numberOfOutputs = 1,
+ .outputChannelCounts = outputChannelCounts
+ };
+ EMSCRIPTEN_AUDIO_WORKLET_NODE_T audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "audio-processor", &options, &ProcessAudio, userData);
+ html5audio_stream_create(audioWorklet, stream->settings.numInputChannels);
+}
+
+void WebAudioWorkletThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void *userData) {
+ if (!success) return;
+ WebAudioWorkletProcessorCreateOptions opts = {
+ .name = "audio-processor",
+ };
+ emscripten_create_wasm_audio_worklet_processor_async(audioContext, &opts, AudioWorkletProcessorCreated, userData);
+}
+
int ofxEmscriptenAudioContext();
ofxEmscriptenSoundStream::ofxEmscriptenSoundStream()
:context(ofxEmscriptenAudioContext())
-,tickCount(0)
{
}
@@ -33,8 +80,9 @@ std::vector ofxEmscriptenSoundStream::getDeviceList(ofSoundDevice
bool ofxEmscriptenSoundStream::setup(const ofSoundStreamSettings & settings) {
inbuffer.allocate(settings.bufferSize, settings.numInputChannels);
outbuffer.allocate(settings.bufferSize, settings.numOutputChannels);
+ audioProcessedCount = 0;
this->settings = settings;
- html5audio_stream_create(settings.bufferSize,settings.numInputChannels,settings.numOutputChannels,inbuffer.getBuffer().data(),outbuffer.getBuffer().data(),&audio_cb,this);
+ emscripten_start_wasm_audio_worklet_thread_async(context, wasmAudioWorkletStack, sizeof(wasmAudioWorkletStack), WebAudioWorkletThreadInitialized, this);
return true;
}
@@ -67,7 +115,7 @@ void ofxEmscriptenSoundStream::close() {
}
uint64_t ofxEmscriptenSoundStream::getTickCount() const{
- return tickCount;
+ return audioProcessedCount;
}
int ofxEmscriptenSoundStream::getNumInputChannels() const{
@@ -85,14 +133,3 @@ int ofxEmscriptenSoundStream::getSampleRate() const{
int ofxEmscriptenSoundStream::getBufferSize() const{
return settings.bufferSize;
}
-
-void ofxEmscriptenSoundStream::audio_cb( int bufferSize, int inputChannels, int outputChannels, void * userData){
- ofxEmscriptenSoundStream * stream = (ofxEmscriptenSoundStream*) userData;
- stream->audioCB(bufferSize,inputChannels,outputChannels);
-}
-
-void ofxEmscriptenSoundStream::audioCB(int bufferSize, int inputChannels, int outputChannels){
- if(inputChannels>0 && settings.inCallback) settings.inCallback(inbuffer);
- if(outputChannels>0 && settings.outCallback) settings.outCallback(outbuffer);
- tickCount++;
-}
diff --git a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h
index 73943c8a14d..8d0976e92a5 100644
--- a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h
+++ b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h
@@ -32,13 +32,12 @@ class ofxEmscriptenSoundStream: public ofBaseSoundStream {
int getNumOutputChannels() const;
int getSampleRate() const;
int getBufferSize() const;
-
-private:
- static void audio_cb(int bufferSize, int inputChannels, int outputChannels, void * userData);
- void audioCB(int bufferSize, int inputChannels, int outputChannels);
- int context;
- unsigned long long tickCount;
ofSoundStreamSettings settings;
ofSoundBuffer inbuffer;
ofSoundBuffer outbuffer;
+ int audioProcessedCount;
+
+private:
+ int context;
+ uint8_t wasmAudioWorkletStack[4096 * 4];
};
diff --git a/examples/sound/audioInputExample/src/ofApp.cpp b/examples/sound/audioInputExample/src/ofApp.cpp
index fef39b0d9f5..4546a7ed342 100644
--- a/examples/sound/audioInputExample/src/ofApp.cpp
+++ b/examples/sound/audioInputExample/src/ofApp.cpp
@@ -9,7 +9,7 @@ void ofApp::setup(){
soundStream.printDeviceList();
- int bufferSize = 256;
+ int bufferSize = 128;
left.assign(bufferSize, 0.0);
right.assign(bufferSize, 0.0);
@@ -44,10 +44,11 @@ void ofApp::setup(){
settings.sampleRate = 44100;
#ifdef TARGET_EMSCRIPTEN
settings.numOutputChannels = 2;
+ settings.numInputChannels = 2;
#else
settings.numOutputChannels = 0;
+ settings.numInputChannels = 1;
#endif
- settings.numInputChannels = 1;
settings.bufferSize = bufferSize;
soundStream.setup(settings);
@@ -92,7 +93,7 @@ void ofApp::draw(){
ofBeginShape();
for (unsigned int i = 0; i < left.size(); i++){
- ofVertex(i*2, 100 -left[i]*180.0f);
+ ofVertex(i*4, 100 -left[i]*180.0f);
}
ofEndShape(false);
@@ -115,7 +116,7 @@ void ofApp::draw(){
ofBeginShape();
for (unsigned int i = 0; i < right.size(); i++){
- ofVertex(i*2, 100 -right[i]*180.0f);
+ ofVertex(i*4, 100 -right[i]*180.0f);
}
ofEndShape(false);
diff --git a/examples/sound/audioOutputExample/src/ofApp.cpp b/examples/sound/audioOutputExample/src/ofApp.cpp
index ecd903b8587..d957e2263bd 100644
--- a/examples/sound/audioOutputExample/src/ofApp.cpp
+++ b/examples/sound/audioOutputExample/src/ofApp.cpp
@@ -5,7 +5,7 @@ void ofApp::setup(){
ofBackground(34, 34, 34);
- int bufferSize = 512;
+ int bufferSize = 128;
sampleRate = 44100;
phase = 0;
phaseAdder = 0.0f;
diff --git a/examples/sound/soundBufferExample/src/ofApp.cpp b/examples/sound/soundBufferExample/src/ofApp.cpp
index 6f61b138173..5df14af22a9 100644
--- a/examples/sound/soundBufferExample/src/ofApp.cpp
+++ b/examples/sound/soundBufferExample/src/ofApp.cpp
@@ -12,7 +12,7 @@ void ofApp::setup(){
ofSoundStreamSettings settings;
settings.numOutputChannels = 2;
settings.sampleRate = 44100;
- settings.bufferSize = 512;
+ settings.bufferSize = 128;
settings.numBuffers = 4;
settings.setOutListener(this);
soundStream.setup(settings);
@@ -24,7 +24,7 @@ void ofApp::update(){
// "lastBuffer" is shared between update() and audioOut(), which are called
// on two different threads. This lock makes sure we don't use lastBuffer
// from both threads simultaneously (see the corresponding lock in audioOut())
- unique_lock lock(audioMutex);
+ std::unique_lock lock(audioMutex);
// this loop is building up a polyline representing the audio contained in
// the left channel of the buffer
@@ -92,7 +92,7 @@ void ofApp::audioOut(ofSoundBuffer &outBuffer) {
pulsePhase += pulsePhaseStep;
}
- unique_lock lock(audioMutex);
+ std::unique_lock lock(audioMutex);
lastBuffer = outBuffer;
}
diff --git a/libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk b/libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk
index a5521323215..9ea24af0bef 100644
--- a/libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk
+++ b/libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk
@@ -64,8 +64,8 @@ PLATFORM_REQUIRED_ADDONS = ofxEmscripten
################################################################################
# Code Generation Option Flags (http://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html)
-PLATFORM_CFLAGS =
-PLATFORM_CXXFLAGS = -Wall -std=c++17 -Wno-warn-absolute-paths
+PLATFORM_CFLAGS = -s USE_PTHREADS=1
+PLATFORM_CXXFLAGS = -Wall -std=c++17 -Wno-warn-absolute-paths -s USE_PTHREADS=1
################################################################################
# PLATFORM LDFLAGS
@@ -93,7 +93,7 @@ ifdef USE_CCACHE
endif
endif
-PLATFORM_LDFLAGS = -Wl --gc-sections --preload-file bin/data@data --emrun --bind --profiling-funcs -s USE_FREETYPE=1 -s ALLOW_MEMORY_GROWTH=1 -s MAX_WEBGL_VERSION=2 -s WEBGL2_BACKWARDS_COMPATIBILITY_EMULATION=1 -s FULL_ES2 -sFULL_ES3=1
+PLATFORM_LDFLAGS = -Wl --gc-sections --preload-file bin/data@data --emrun --bind --profiling-funcs -s USE_FREETYPE=1 -s ALLOW_MEMORY_GROWTH=1 -s MAX_WEBGL_VERSION=2 -s WEBGL2_BACKWARDS_COMPATIBILITY_EMULATION=1 -s FULL_ES2 -sFULL_ES3=1 -s USE_PTHREADS=1 -s AUDIO_WORKLET=1 -s WASM_WORKERS=1 -sENVIRONMENT="web,worker" -s WEBAUDIO_DEBUG=1
PLATFORM_LDFLAGS += --js-library $(OF_ADDONS_PATH)/ofxEmscripten/libs/html5video/lib/emscripten/library_html5video.js
PLATFORM_LDFLAGS += --js-library $(OF_ADDONS_PATH)/ofxEmscripten/libs/html5audio/lib/emscripten/library_html5audio.js