Signed-off-by: falkTX <falktx@falktx.com>pull/321/head
| @@ -35,6 +35,11 @@ START_NAMESPACE_DISTRHO | |||||
| */ | */ | ||||
| bool supportsAudioInput(); | bool supportsAudioInput(); | ||||
| /** | |||||
| Check if the current standalone supports dynamic buffer size changes. | |||||
| */ | |||||
| bool supportsBufferSizeChanges(); | |||||
| /** | /** | ||||
| Check if the current standalone supports MIDI. | Check if the current standalone supports MIDI. | ||||
| */ | */ | ||||
| @@ -50,12 +55,22 @@ bool isAudioInputEnabled(); | |||||
| */ | */ | ||||
| bool isMIDIEnabled(); | bool isMIDIEnabled(); | ||||
| /** | |||||
| Get the current buffer size. | |||||
| */ | |||||
| uint32_t getBufferSize(); | |||||
| /** | /** | ||||
| Request permissions to use audio input. | Request permissions to use audio input. | ||||
| Only valid to call if audio input is supported but not currently enabled. | Only valid to call if audio input is supported but not currently enabled. | ||||
| */ | */ | ||||
| bool requestAudioInput(); | bool requestAudioInput(); | ||||
| /** | |||||
| Request change to a new buffer size. | |||||
| */ | |||||
| bool requestBufferSizeChange(uint32_t newBufferSize); | |||||
| /** | /** | ||||
| Request permissions to use MIDI. | Request permissions to use MIDI. | ||||
| Only valid to call if MIDI is supported but not currently enabled. | Only valid to call if MIDI is supported but not currently enabled. | ||||
| @@ -1185,7 +1185,13 @@ bool jackbridge_set_buffer_size_callback(jack_client_t* client, JackBufferSizeCa | |||||
| #elif defined(JACKBRIDGE_DIRECT) | #elif defined(JACKBRIDGE_DIRECT) | ||||
| return (jack_set_buffer_size_callback(client, bufsize_callback, arg) == 0); | return (jack_set_buffer_size_callback(client, bufsize_callback, arg) == 0); | ||||
| #else | #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__ | # ifdef __WINE__ | ||||
| WineBridge::getInstance().set_bufsize(bufsize_callback); | 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) | #elif defined(JACKBRIDGE_DIRECT) | ||||
| return jack_set_buffer_size(client, nframes); | return jack_set_buffer_size(client, nframes); | ||||
| #else | #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 | #endif | ||||
| return false; | return false; | ||||
| } | } | ||||
| @@ -2257,6 +2264,15 @@ bool supportsAudioInput() | |||||
| return true; | return true; | ||||
| } | } | ||||
| bool supportsBufferSizeChanges() | |||||
| { | |||||
| #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) | |||||
| if (usingNativeBridge) | |||||
| return nativeBridge->supportsBufferSizeChanges(); | |||||
| #endif | |||||
| return false; | |||||
| } | |||||
| bool supportsMIDI() | bool supportsMIDI() | ||||
| { | { | ||||
| #if defined(JACKBRIDGE_DUMMY) | #if defined(JACKBRIDGE_DUMMY) | ||||
| @@ -2290,6 +2306,15 @@ bool isMIDIEnabled() | |||||
| return true; | return true; | ||||
| } | } | ||||
| uint32_t getBufferSize() | |||||
| { | |||||
| #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) | |||||
| if (usingNativeBridge) | |||||
| return nativeBridge->getBufferSize(); | |||||
| #endif | |||||
| return 0; | |||||
| } | |||||
| bool requestAudioInput() | bool requestAudioInput() | ||||
| { | { | ||||
| #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) | #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) | ||||
| @@ -2299,6 +2324,15 @@ bool requestAudioInput() | |||||
| return false; | 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() | bool requestMIDI() | ||||
| { | { | ||||
| #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) | #if !(defined(JACKBRIDGE_DUMMY) || defined(JACKBRIDGE_DIRECT)) | ||||
| @@ -36,7 +36,9 @@ struct NativeBridge { | |||||
| // JACK callbacks | // JACK callbacks | ||||
| JackProcessCallback jackProcessCallback = nullptr; | JackProcessCallback jackProcessCallback = nullptr; | ||||
| JackBufferSizeCallback bufferSizeCallback = nullptr; | |||||
| void* jackProcessArg = nullptr; | void* jackProcessArg = nullptr; | ||||
| void* jackBufferSizeArg = nullptr; | |||||
| // Runtime buffers | // Runtime buffers | ||||
| enum PortMask { | enum PortMask { | ||||
| @@ -85,11 +87,18 @@ struct NativeBridge { | |||||
| #endif | #endif | ||||
| } | } | ||||
| virtual bool supportsBufferSizeChanges() const { return false; } | |||||
| virtual bool supportsMIDI() const { return false; } | virtual bool supportsMIDI() const { return false; } | ||||
| virtual bool isMIDIEnabled() const { return false; } | virtual bool isMIDIEnabled() const { return false; } | ||||
| virtual bool requestAudioInput() { return false; } | virtual bool requestAudioInput() { return false; } | ||||
| virtual bool requestBufferSizeChange(uint32_t) { return false; } | |||||
| virtual bool requestMIDI() { return false; } | virtual bool requestMIDI() { return false; } | ||||
| uint32_t getBufferSize() const noexcept | |||||
| { | |||||
| return bufferSize; | |||||
| } | |||||
| uint32_t getEventCount() | uint32_t getEventCount() | ||||
| { | { | ||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT | ||||
| @@ -114,7 +114,7 @@ struct WebBridge : NativeBridge { | |||||
| return false; | return false; | ||||
| } | } | ||||
| bufferSize = 512; | |||||
| bufferSize = 2048; | |||||
| sampleRate = EM_ASM_INT_V({ | sampleRate = EM_ASM_INT_V({ | ||||
| var WAB = Module['WebAudioBridge']; | var WAB = Module['WebAudioBridge']; | ||||
| return WAB.audioContext.sampleRate; | return WAB.audioContext.sampleRate; | ||||
| @@ -212,15 +212,24 @@ struct WebBridge : NativeBridge { | |||||
| // we need to use this weird awkward way for objects, otherwise build fails | // we need to use this weird awkward way for objects, otherwise build fails | ||||
| constraints['audio'] = true; | constraints['audio'] = true; | ||||
| constraints['video'] = false; | 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 | // old property for chrome | ||||
| constraints['mandatory']['googAutoGainControl'] = false; | |||||
| constraints['googAutoGainControl'] = false; | |||||
| var success = function(stream) { | var success = function(stream) { | ||||
| WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream); | WAB.captureStreamNode = WAB.audioContext['createMediaStreamSource'](stream); | ||||
| @@ -239,6 +248,88 @@ struct WebBridge : NativeBridge { | |||||
| return true; | 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 | bool supportsMIDI() const override | ||||
| { | { | ||||
| #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | #if DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | ||||