DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

commit 29e9fe9a9f449a92a5426066d8ac00a698050ee7
parent e2e2b5507f683a6b62adbd984b62cdde0ed05c3d
Author: falkTX <falktx@falktx.com>
Date:   Mon, 11 Jul 2022 17:55:13 +0100

Allow runtime buffer size changes in wasm/bridge modules

Signed-off-by: falkTX <falktx@falktx.com>

Diffstat:
Mdistrho/DistrhoStandaloneUtils.hpp | 15+++++++++++++++
Mdistrho/src/jackbridge/JackBridge.cpp | 42++++++++++++++++++++++++++++++++++++++----
Mdistrho/src/jackbridge/NativeBridge.hpp | 9+++++++++
Mdistrho/src/jackbridge/WebBridge.hpp | 109++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
4 files changed, 162 insertions(+), 13 deletions(-)

diff --git a/distrho/DistrhoStandaloneUtils.hpp b/distrho/DistrhoStandaloneUtils.hpp @@ -36,6 +36,11 @@ START_NAMESPACE_DISTRHO bool supportsAudioInput(); /** + Check if the current standalone supports dynamic buffer size changes. +*/ +bool supportsBufferSizeChanges(); + +/** Check if the current standalone supports MIDI. */ bool supportsMIDI(); @@ -51,12 +56,22 @@ bool isAudioInputEnabled(); bool isMIDIEnabled(); /** + Get the current buffer size. +*/ +uint32_t getBufferSize(); + +/** Request permissions to use audio input. Only valid to call if audio input is supported but not currently enabled. */ bool requestAudioInput(); /** + Request change to a new buffer size. +*/ +bool requestBufferSizeChange(uint32_t newBufferSize); + +/** Request permissions to use MIDI. Only valid to call if MIDI is supported but not currently enabled. */ diff --git a/distrho/src/jackbridge/JackBridge.cpp b/distrho/src/jackbridge/JackBridge.cpp @@ -1185,7 +1185,13 @@ bool jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCa #elif defined(JACKBRIDGE_DIRECT) return (jack_set_buffer_size_callback(client, bufsize_callback, arg) == 0); #else - if (usingRealJACK && getBridgeInstance().set_buffer_size_callback_ptr != nullptr) + if (usingNativeBridge) + { + nativeBridge->bufferSizeCallback = bufsize_callback; + nativeBridge->jackBufferSizeArg = arg; + return true; + } + if (getBridgeInstance().set_buffer_size_callback_ptr != nullptr) { # ifdef __WINE__ WineBridge::getInstance().set_bufsize(bufsize_callback); @@ -1371,9 +1377,10 @@ bool jackbridge_set_buffer_size(jack_client_t* client, jack_nframes_t nframes) #elif defined(JACKBRIDGE_DIRECT) return jack_set_buffer_size(client, nframes); #else - if (usingRealJACK) - if (getBridgeInstance().set_buffer_size_ptr != nullptr) - return getBridgeInstance().set_buffer_size_ptr(client, nframes); + if (usingNativeBridge) + return nativeBridge->requestBufferSizeChange(nframes); + if (getBridgeInstance().set_buffer_size_ptr != nullptr) + return getBridgeInstance().set_buffer_size_ptr(client, nframes); #endif return false; } @@ -2257,6 +2264,15 @@ bool supportsAudioInput() return true; } +bool supportsBufferSizeChanges() +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->supportsBufferSizeChanges(); +#endif + return false; +} + bool supportsMIDI() { #if defined(JACKBRIDGE_DUMMY) @@ -2290,6 +2306,15 @@ bool isMIDIEnabled() return true; } +uint32_t getBufferSize() +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->getBufferSize(); +#endif + return 0; +} + bool requestAudioInput() { #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) @@ -2299,6 +2324,15 @@ bool requestAudioInput() return false; } +bool requestBufferSizeChange(const uint32_t newBufferSize) +{ +#if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) + if (usingNativeBridge) + return nativeBridge->requestBufferSizeChange(newBufferSize); +#endif + return false; +} + bool requestMIDI() { #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) diff --git a/distrho/src/jackbridge/NativeBridge.hpp b/distrho/src/jackbridge/NativeBridge.hpp @@ -36,7 +36,9 @@ struct NativeBridge { // JACK callbacks JackProcessCallback jackProcessCallback = nullptr; + JackBufferSizeCallback bufferSizeCallback = nullptr; void* jackProcessArg = nullptr; + void* jackBufferSizeArg = nullptr; // Runtime buffers enum PortMask { @@ -85,11 +87,18 @@ struct NativeBridge { #endif } + virtual bool supportsBufferSizeChanges() const { return false; } virtual bool supportsMIDI() const { return false; } virtual bool isMIDIEnabled() const { return false; } virtual bool requestAudioInput() { return false; } + virtual bool requestBufferSizeChange(uint32_t) { return false; } virtual bool requestMIDI() { return false; } + uint32_t getBufferSize() const noexcept + { + return bufferSize; + } + uint32_t getEventCount() { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT diff --git a/distrho/src/jackbridge/WebBridge.hpp b/distrho/src/jackbridge/WebBridge.hpp @@ -114,7 +114,7 @@ struct WebBridge : NativeBridge { return false; } - bufferSize = 512; + bufferSize = 2048; sampleRate = EM_ASM_INT_V({ var WAB = Module['WebAudioBridge']; return WAB.audioContext.sampleRate; @@ -212,15 +212,24 @@ struct WebBridge : NativeBridge { // we need to use this weird awkward way for objects, otherwise build fails constraints['audio'] = true; constraints['video'] = false; - constraints['latency'] = 0; - constraints['sampleSize'] = 24; - constraints['mandatory'] = {}; - constraints['mandatory']['autoGainControl'] = false; - constraints['mandatory']['echoCancellation'] = false; - constraints['mandatory']['noiseSuppression'] = false; - constraints['mandatory']['channelCount'] = numInputs; + constraints['autoGainControl'] = {}; + constraints['autoGainControl']['exact'] = false; + constraints['echoCancellation'] = {}; + constraints['echoCancellation']['exact'] = false; + constraints['noiseSuppression'] = {}; + constraints['noiseSuppression']['exact'] = false; + constraints['channelCount'] = {}; + constraints['channelCount']['min'] = 0; + constraints['channelCount']['ideal'] = numInputs; + constraints['latency'] = {}; + constraints['latency']['min'] = 0; + constraints['latency']['ideal'] = 0; + constraints['sampleSize'] = {}; + constraints['sampleSize']['min'] = 8; + constraints['sampleSize']['max'] = 32; + constraints['sampleSize']['ideal'] = 16; // old property for chrome - constraints['mandatory']['googAutoGainControl'] = false; + constraints['googAutoGainControl'] = false; var success = function(stream) { WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream); @@ -239,6 +248,88 @@ struct WebBridge : NativeBridge { return true; } + bool supportsBufferSizeChanges() const override + { + return true; + } + + bool requestBufferSizeChange(const uint32_t newBufferSize) override + { + // try to create new processor first + bool success = EM_ASM_INT({ + var numInputs = $0; + var numOutputs = $1; + var newBufferSize = $2; + var WAB = Module['WebAudioBridge']; + + try { + WAB.newProcessor = WAB.audioContext['createScriptProcessor'](newBufferSize, numInputs, numOutputs); + } catch (e) { + return 0; + } + + // got new processor, disconnect old one + WAB.processor['disconnect'](WAB.audioContext['destination']); + + if (WAB.captureStreamNode) + WAB.captureStreamNode.disconnect(WAB.processor); + + return 1; + }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, newBufferSize) != 0; + + if (!success) + return false; + + bufferSize = newBufferSize; + freeBuffers(); + allocBuffers(); + + if (bufferSizeCallback != nullptr) + bufferSizeCallback(newBufferSize, jackBufferSizeArg); + + EM_ASM({ + var numInputs = $0; + var numOutputs = $1; + var bufferSize = $2; + var WAB = Module['WebAudioBridge']; + + // store the new processor + delete WAB.processor; + WAB.processor = WAB.newProcessor; + delete WAB.newProcessor; + + // setup new processor the same way as old one + WAB.processor['onaudioprocess'] = function (e) { + // var timestamp = performance.now(); + for (var i = 0; i < numInputs; ++i) { + var buffer = e['inputBuffer']['getChannelData'](i); + for (var j = 0; j < bufferSize; ++j) { + // setValue($3 + ((bufferSize * i) + j) * 4, buffer[j], 'float'); + HEAPF32[$3 + (((bufferSize * i) + j) << 2) >> 2] = buffer[j]; + } + } + dynCall('vi', $4, [$5]); + for (var i = 0; i < numOutputs; ++i) { + var buffer = e['outputBuffer']['getChannelData'](i); + var offset = bufferSize * (numInputs + i); + for (var j = 0; j < bufferSize; ++j) { + buffer[j] = HEAPF32[$3 + ((offset + j) << 2) >> 2]; + } + } + }; + + // connect to output + WAB.processor['connect'](WAB.audioContext['destination']); + + // and input, if available + if (WAB.captureStreamNode) + WAB.captureStreamNode.connect(WAB.processor); + + }, DISTRHO_PLUGIN_NUM_INPUTS, DISTRHO_PLUGIN_NUM_OUTPUTS, bufferSize, audioBufferStorage, WebAudioCallback, this); + + return true; + } + bool supportsMIDI() const override { #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT