diff --git a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp index 208400abbab..30b6b520587 100644 --- a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp +++ b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.cpp @@ -10,6 +10,55 @@ #include "ofBaseApp.h" #include "ofLog.h" +ofEvent audioWorkletEvent; + +// This event will fire on the audio worklet thread. +void MessageReceivedInAudioWorkletThread_1(int inputChannels, int outputChannels, int inbuffer) { + EM_ASM({globalThis.inputChannels = $0; globalThis.outputChannels = $1; globalThis.inbuffer = $2;}, inputChannels, outputChannels, inbuffer); +} + +void MessageReceivedInAudioWorkletThread_2(int outbuffer, int stream_callback, int userData) { + EM_ASM({globalThis.outbuffer = $0; globalThis.stream_callback = $1; globalThis.userData = $2;}, outbuffer, stream_callback, userData); +} + +EM_BOOL ProcessAudio(int numInputs, const AudioSampleFrame *inputs, int numOutputs, AudioSampleFrame *outputs, int numParams, const AudioParamFrame *params, void *userData) { + return EM_TRUE; +} + +// This callback will fire after the Audio Worklet Processor has finished being added to the Worklet global scope. +void AudioWorkletProcessorCreated(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void *userData) { + if (!success) return; + // Specify the input and output node configurations for the Wasm Audio Worklet. A simple setup with single mono output channel here, and no inputs. + int outputChannelCounts[1] = { 2 }; + EmscriptenAudioWorkletNodeCreateOptions options = { + .numberOfInputs = 1, + .numberOfOutputs = 1, + .outputChannelCounts = outputChannelCounts + }; + // Instantiate the noise-generator Audio Worklet Processor. + EMSCRIPTEN_AUDIO_WORKLET_NODE_T audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "tone-generator", &options, &ProcessAudio, 0); + html5audio_stream_create(audioWorklet, 0); +} + +// This callback will fire when the Wasm Module has been shared to the AudioWorklet global scope, and is now ready to begin adding Audio Worklet Processors. +void WebAudioWorkletThreadInitialized(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void *userData) { + if (!success) return; + audioWorkletEvent.notify(); + WebAudioWorkletProcessorCreateOptions opts = { + .name = "tone-generator", + }; + emscripten_create_wasm_audio_worklet_processor_async(audioContext, &opts, AudioWorkletProcessorCreated, 0); +} + +void ofxEmscriptenSoundStream::audioWorklet() { + emscripten_audio_worklet_post_function_viii(context, MessageReceivedInAudioWorkletThread_1, /*inputChannels=*/settings.numInputChannels, /*outputChannels=*/settings.numOutputChannels, /*inbuffer=*/EM_ASM_INT(return $0, inbuffer.getBuffer().data())); + emscripten_audio_worklet_post_function_viii(context, MessageReceivedInAudioWorkletThread_2, /*outbuffer=*/EM_ASM_INT(return $0, outbuffer.getBuffer().data()), /*stream_callback=*/EM_ASM_INT(return $0, &audio_cb), /*userData=*/EM_ASM_INT(return $0, this)); +} + +// Define a global stack space for the AudioWorkletGlobalScope. Note that all AudioWorkletProcessors and/or AudioWorkletNodes on the given Audio Context all share the same AudioWorkerGlobalScope, +// i.e. they all run on the same one audio thread (multiple nodes/processors do not each get their own thread). Hence one stack is enough. +uint8_t wasmAudioWorkletStack[4096]; + using namespace std; int ofxEmscriptenAudioContext(); @@ -31,10 +80,11 @@ std::vector ofxEmscriptenSoundStream::getDeviceList(ofSoundDevice } bool ofxEmscriptenSoundStream::setup(const ofSoundStreamSettings & settings) { + ofAddListener(audioWorkletEvent, this, &ofxEmscriptenSoundStream::audioWorklet); inbuffer.allocate(settings.bufferSize, settings.numInputChannels); outbuffer.allocate(settings.bufferSize, settings.numOutputChannels); 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, 0); return true; } diff --git a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h index 73943c8a14d..fa8d672fd1f 100644 --- a/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h +++ b/addons/ofxEmscripten/src/ofxEmscriptenSoundStream.h @@ -9,6 +9,8 @@ #include "ofSoundBaseTypes.h" #include "ofSoundBuffer.h" +#include "ofEvents.h" +#include "emscripten/webaudio.h" class ofxEmscriptenSoundStream: public ofBaseSoundStream { public: @@ -26,6 +28,7 @@ class ofxEmscriptenSoundStream: public ofBaseSoundStream { void start(); void stop(); void close(); + void audioWorklet(); uint64_t getTickCount() const; int getNumInputChannels() const; diff --git a/libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk b/libs/openFrameworksCompiled/project/emscripten/config.emscripten.default.mk index 325de8098e7..16dbf013e06 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 +PLATFORM_LDFLAGS = -Wl --gc-sections --preload-file bin/data@data --emrun --bind --profiling-funcs -s USE_FREETYPE=1 -s ALLOW_MEMORY_GROWTH=0 -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 @@ -105,7 +105,7 @@ endif PLATFORM_OPTIMIZATION_LDFLAGS_RELEASE = -O3 -s TOTAL_MEMORY=$(PLATFORM_EMSCRIPTEN_TOTAL_MEMORY) --memory-init-file 1 -PLATFORM_OPTIMIZATION_LDFLAGS_DEBUG = -g3 -s TOTAL_MEMORY=134217728 --memory-init-file 1 -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2 +PLATFORM_OPTIMIZATION_LDFLAGS_DEBUG = -g3 -s TOTAL_MEMORY=134217728 --memory-init-file 1 -s DEMANGLE_SUPPORT=1 -s ASSERTIONS=2 ################################################################################ # PLATFORM OPTIMIZATION CFLAGS