From a3227c52fa082d2df33f4377ed1e050dd8a52595 Mon Sep 17 00:00:00 2001 From: Paul Date: Wed, 8 May 2024 13:35:47 -0400 Subject: [PATCH] Support double precision processing (#150) * Support double precision processing If `supprotsDoublePrecision` in the processor is true, advertise FLOAT64 audio ports and adjust the processing loop. Tested by running Airwindows Consolidated as CLAP in reaper (which supports double) and Bitwig (which does not) and seeing appropriate behavior. * Respond to jatin's review request --- src/wrapper/clap-juce-wrapper.cpp | 96 ++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/src/wrapper/clap-juce-wrapper.cpp b/src/wrapper/clap-juce-wrapper.cpp index 712050b..4180749 100644 --- a/src/wrapper/clap-juce-wrapper.cpp +++ b/src/wrapper/clap-juce-wrapper.cpp @@ -920,6 +920,13 @@ class ClapJuceWrapper : public clap::helpers::Plugin< info->flags = 0; } + if (processor->supportsDoublePrecisionProcessing()) + { + info->flags |= CLAP_AUDIO_PORT_SUPPORTS_64BITS; + info->flags |= CLAP_AUDIO_PORT_PREFERS_64BITS; + info->flags |= CLAP_AUDIO_PORT_REQUIRES_COMMON_SAMPLE_SIZE; + } + if (processor->getBus(!isInput, (int)index) != nullptr) { // this bus has a corresponding bus on the other side, so it can do in-place processing @@ -1477,8 +1484,17 @@ class ClapJuceWrapper : public clap::helpers::Plugin< */ static constexpr uint32_t maxBuses = 128; std::array busses{}; + std::array doubleBusses{}; busses.fill(nullptr); + bool supportsDouble = processor->supportsDoublePrecisionProcessing(); + bool hostCalledWithDouble = false; + + if (supportsDouble) + { + doubleBusses.fill(nullptr); + } + // we can't advance `n` until we know how many samples we're processing, // so we'll increment it inside the loop. for (int n = 0; n < numSamples;) @@ -1548,7 +1564,15 @@ class ClapJuceWrapper : public clap::helpers::Plugin< { for (uint32_t ch = 0; ch < process->audio_outputs[idx].channel_count; ++ch) { - busses[outputChannels] = process->audio_outputs[idx].data32[ch] + n; + if (supportsDouble && process->audio_outputs[idx].data64 != nullptr) + { + doubleBusses[outputChannels] = process->audio_outputs[idx].data64[ch] + n; + hostCalledWithDouble = true; + } + else + { + busses[outputChannels] = process->audio_outputs[idx].data32[ch] + n; + } outputChannels++; } } @@ -1559,40 +1583,84 @@ class ClapJuceWrapper : public clap::helpers::Plugin< { for (uint32_t ch = 0; ch < process->audio_inputs[idx].channel_count; ++ch) { - auto *ic = process->audio_inputs[idx].data32[ch] + n; - if (inputChannels < outputChannels) + if (supportsDouble && process->audio_inputs[idx].data64 != nullptr) { - if (ic == busses[inputChannels]) + auto *ic = process->audio_inputs[idx].data64[ch] + n; + if (inputChannels < outputChannels) { - // The buffers overlap - no need to do anything + if (ic == doubleBusses[inputChannels]) + { + // The buffers overlap - no need to do anything + } + else + { + juce::FloatVectorOperations::copy(doubleBusses[inputChannels], ic, + numSamplesToProcess); + } } else { - juce::FloatVectorOperations::copy(busses[inputChannels], ic, - numSamplesToProcess); + doubleBusses[inputChannels] = ic; } + hostCalledWithDouble = true; } else { - busses[inputChannels] = ic; + auto *ic = process->audio_inputs[idx].data32[ch] + n; + if (inputChannels < outputChannels) + { + if (ic == busses[inputChannels]) + { + // The buffers overlap - no need to do anything + } + else + { + juce::FloatVectorOperations::copy(busses[inputChannels], ic, + numSamplesToProcess); + } + } + else + { + busses[inputChannels] = ic; + } } inputChannels++; } } auto totalChans = juce::jmax(inputChannels, outputChannels); - juce::AudioBuffer buffer(busses.data(), (int)totalChans, numSamplesToProcess); - - if (processor->isSuspended()) + if (hostCalledWithDouble) { - buffer.clear(); + juce::AudioBuffer buffer(doubleBusses.data(), (int)totalChans, + numSamplesToProcess); + + if (processor->isSuspended()) + { + buffer.clear(); + } + else + { + FIXME("Handle bypass and deactivated states") + processor->processBlock(buffer, midiBuffer); + } } else { - FIXME("Handle bypass and deactivated states") - processor->processBlock(buffer, midiBuffer); + juce::AudioBuffer buffer(busses.data(), (int)totalChans, + numSamplesToProcess); + + if (processor->isSuspended()) + { + buffer.clear(); + } + else + { + FIXME("Handle bypass and deactivated states") + processor->processBlock(buffer, midiBuffer); + } } + if (processorAsClapExtensions && processorAsClapExtensions->supportsOutboundEvents()) { processorAsClapExtensions->addOutboundEventsToQueue(process->out_events, midiBuffer,