|
- /*
- * DISTRHO Cardinal Plugin
- * Copyright (C) 2021-2022 Filipe Coelho <falktx@falktx.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 3 of
- * the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * For a full copy of the GNU General Public License see the LICENSE file.
- */
-
- #include <juce_audio_processors/juce_audio_processors.h>
-
- #include <AvailabilityMacros.h>
- #if MAC_OS_X_VERSION_MAX_ALLOWED > 101200
- #error unwanted macOS version, too new
- #endif
-
- #define createPlugin createStaticPlugin
- #include "src/DistrhoPluginInternal.hpp"
- #include "src/DistrhoUIInternal.hpp"
-
- START_NAMESPACE_DISTRHO
-
- // --------------------------------------------------------------------------------------------------------------------
-
- class ParameterFromDPF : public juce::AudioProcessorParameter
- {
- PluginExporter& plugin;
- const ParameterEnumerationValues& enumValues;
- const ParameterRanges& ranges;
- const uint32_t hints;
- const uint index;
- bool* const updatedPtr;
- mutable juce::StringArray dpfValueStrings;
-
- public:
- ParameterFromDPF(PluginExporter& plugin_, const uint index_, bool* const updatedPtr_)
- : plugin(plugin_),
- enumValues(plugin_.getParameterEnumValues(index_)),
- ranges(plugin_.getParameterRanges(index_)),
- hints(plugin_.getParameterHints(index_)),
- index(index_),
- updatedPtr(updatedPtr_) {}
-
- void setValueNotifyingHostFromDPF(const float newValue)
- {
- setValueNotifyingHost(ranges.getNormalizedValue(newValue));
- *updatedPtr = false;
- }
-
- protected:
- float getValue() const override
- {
- return ranges.getNormalizedValue(plugin.getParameterValue(index));
- }
-
- void setValue(const float newValue) override
- {
- *updatedPtr = true;
- plugin.setParameterValue(index, ranges.getUnnormalizedValue(newValue));
- }
-
- float getDefaultValue() const override
- {
- return ranges.getNormalizedValue(plugin.getParameterDefault(index));
- }
-
- juce::String getName(const int maximumStringLength) const override
- {
- if (maximumStringLength <= 0)
- return juce::String(plugin.getParameterName(index).buffer());
-
- return juce::String(plugin.getParameterName(index).buffer(), static_cast<size_t>(maximumStringLength));
- }
-
- juce::String getLabel() const override
- {
- return plugin.getParameterUnit(index).buffer();
- }
-
- int getNumSteps() const override
- {
- if (hints & kParameterIsBoolean)
- return 2;
-
- if (enumValues.restrictedMode)
- return enumValues.count;
-
- if (hints & kParameterIsInteger)
- return ranges.max - ranges.min;
-
- return juce::AudioProcessorParameter::getNumSteps();
- }
-
- bool isDiscrete() const override
- {
- if (hints & (kParameterIsBoolean|kParameterIsInteger))
- return true;
-
- if (enumValues.restrictedMode)
- return true;
-
- return false;
- }
-
- bool isBoolean() const override
- {
- return (hints & kParameterIsBoolean) != 0x0;
- }
-
- juce::String getText(const float normalizedValue, const int maximumStringLength) const override
- {
- float value = ranges.getUnnormalizedValue(normalizedValue);
-
- if (hints & kParameterIsBoolean)
- {
- const float midRange = ranges.min + (ranges.max - ranges.min) * 0.5f;
- value = value > midRange ? ranges.max : ranges.min;
- }
- else if (hints & kParameterIsInteger)
- {
- value = std::round(value);
- }
-
- if (enumValues.restrictedMode)
- {
- for (uint32_t i=0; i < enumValues.count; ++i)
- {
- if (d_isEqual(enumValues.values[i].value, value))
- {
- if (maximumStringLength <= 0)
- return juce::String(enumValues.values[i].label);
-
- return juce::String(enumValues.values[i].label, static_cast<size_t>(maximumStringLength));
- }
- }
- }
-
- juce::String text;
- if (hints & kParameterIsInteger)
- text = juce::String(static_cast<int>(value));
- else
- text = juce::String(value);
-
- if (maximumStringLength <= 0)
- return text;
-
- return juce::String(text.toRawUTF8(), static_cast<size_t>(maximumStringLength));
- }
-
- float getValueForText(const juce::String& text) const override
- {
- if (enumValues.restrictedMode)
- {
- for (uint32_t i=0; i < enumValues.count; ++i)
- {
- if (text == enumValues.values[i].label.buffer())
- return ranges.getNormalizedValue(enumValues.values[i].value);
- }
- }
-
- float value;
- if (hints & kParameterIsInteger)
- value = std::atoi(text.toRawUTF8());
- else
- value = std::atof(text.toRawUTF8());
-
- return ranges.getFixedAndNormalizedValue(value);
- }
-
- bool isAutomatable() const override
- {
- return (hints & kParameterIsAutomatable) != 0x0;
- }
-
- juce::String getCurrentValueAsText() const override
- {
- const float value = plugin.getParameterValue(index);
-
- if (enumValues.restrictedMode)
- {
- for (uint32_t i=0; i < enumValues.count; ++i)
- {
- if (d_isEqual(enumValues.values[i].value, value))
- return juce::String(enumValues.values[i].label);
- }
- }
-
- if (hints & kParameterIsInteger)
- return juce::String(static_cast<int>(value));
-
- return juce::String(value);
- }
-
- juce::StringArray getAllValueStrings() const override
- {
- if (dpfValueStrings.size() != 0)
- return dpfValueStrings;
-
- if (enumValues.restrictedMode)
- {
- for (uint32_t i=0; i < enumValues.count; ++i)
- dpfValueStrings.add(enumValues.values[i].label.buffer());
-
- return dpfValueStrings;
- }
-
- if (hints & kParameterIsBoolean)
- {
- if (hints & kParameterIsInteger)
- {
- dpfValueStrings.add(juce::String(static_cast<int>(ranges.min)));
- dpfValueStrings.add(juce::String(static_cast<int>(ranges.max)));
- }
- else
- {
- dpfValueStrings.add(juce::String(ranges.min));
- dpfValueStrings.add(juce::String(ranges.max));
- }
- }
- else if (hints & kParameterIsInteger)
- {
- const int imin = static_cast<int>(ranges.min);
- const int imax = static_cast<int>(ranges.max);
-
- for (int i=imin; i<=imax; ++i)
- dpfValueStrings.add(juce::String(i));
- }
-
- return dpfValueStrings;
- }
- };
-
- // --------------------------------------------------------------------------------------------------------------------
-
- // unused in cardinal
- static constexpr const requestParameterValueChangeFunc nullRequestParameterValueChangeFunc = nullptr;
-
- // only needed for headless builds, which this wrapper never builds for
- static constexpr const updateStateValueFunc nullUpdateStateValueFunc = nullptr;
-
- // DSP/processor implementation
- class CardinalWrapperProcessor : public juce::AudioProcessor
- {
- friend class CardinalWrapperEditor;
-
- PluginExporter plugin;
- MidiEvent midiEvents[kMaxMidiEvents];
- TimePosition timePosition;
- const uint32_t parameterCount;
-
- juce::AudioProcessorParameter* bypassParameter;
- juce::MidiBuffer* currentMidiMessages;
- bool* updatedParameters;
-
- public:
- CardinalWrapperProcessor()
- : plugin(this, writeMidiFunc, nullRequestParameterValueChangeFunc, nullUpdateStateValueFunc),
- parameterCount(plugin.getParameterCount()),
- bypassParameter(nullptr),
- currentMidiMessages(nullptr),
- updatedParameters(nullptr)
- {
- if (const double sampleRate = getSampleRate())
- if (sampleRate > 0.0)
- plugin.setSampleRate(sampleRate);
-
- if (const int samplesPerBlock = getBlockSize())
- if (samplesPerBlock > 0)
- plugin.setBufferSize(static_cast<uint32_t>(samplesPerBlock));
-
- if (parameterCount != 0)
- {
- updatedParameters = new bool[parameterCount];
- std::memset(updatedParameters, 0, sizeof(bool)*parameterCount);
-
- for (uint i=0; i<parameterCount; ++i)
- {
- ParameterFromDPF* const param = new ParameterFromDPF(plugin, i, updatedParameters + i);
- addParameter(param);
-
- if (plugin.getParameterDesignation(i) == kParameterDesignationBypass)
- bypassParameter = param;
- }
- }
- }
-
- ~CardinalWrapperProcessor() override
- {
- delete[] updatedParameters;
- }
-
- protected:
- const juce::String getName() const override
- {
- return plugin.getName();
- }
-
- juce::StringArray getAlternateDisplayNames() const override
- {
- return juce::StringArray(plugin.getLabel());
- }
-
- void prepareToPlay(const double sampleRate, const int samplesPerBlock) override
- {
- DISTRHO_SAFE_ASSERT_RETURN(samplesPerBlock > 0,);
-
- plugin.deactivateIfNeeded();
- plugin.setSampleRate(sampleRate);
- plugin.setBufferSize(static_cast<uint32_t>(samplesPerBlock));
- plugin.activate();
- }
-
- void releaseResources() override
- {
- plugin.deactivateIfNeeded();
- }
-
- void processBlock(juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages) override
- {
- const int numSamples = buffer.getNumSamples();
- DISTRHO_SAFE_ASSERT_INT_RETURN(numSamples > 0, numSamples, midiMessages.clear());
-
- uint32_t midiEventCount = 0;
-
- for (const juce::MidiMessageMetadata midiMessage : midiMessages)
- {
- DISTRHO_SAFE_ASSERT_CONTINUE(midiMessage.numBytes > 0);
- DISTRHO_SAFE_ASSERT_CONTINUE(midiMessage.samplePosition >= 0);
-
- if (midiMessage.numBytes > static_cast<int>(MidiEvent::kDataSize))
- continue;
-
- MidiEvent& midiEvent(midiEvents[midiEventCount++]);
-
- midiEvent.frame = static_cast<uint32_t>(midiMessage.samplePosition);
- midiEvent.size = (static_cast<uint8_t>(midiMessage.numBytes));
- std::memcpy(midiEvent.data, midiMessage.data, midiEvent.size);
-
- if (midiEventCount == kMaxMidiEvents)
- break;
- }
-
- midiMessages.clear();
-
- const juce::ScopedValueSetter<juce::MidiBuffer*> cvs(currentMidiMessages, &midiMessages, nullptr);
-
- juce::AudioPlayHead* const playhead = getPlayHead();
- juce::AudioPlayHead::CurrentPositionInfo posInfo;
-
- if (playhead != nullptr && playhead->getCurrentPosition(posInfo))
- {
- timePosition.playing = posInfo.isPlaying;
- timePosition.bbt.valid = true;
-
- // ticksPerBeat is not possible with JUCE
- timePosition.bbt.ticksPerBeat = 1920.0;
-
- if (posInfo.timeInSamples >= 0)
- timePosition.frame = static_cast<uint64_t>(posInfo.timeInSamples);
- else
- timePosition.frame = 0;
-
- timePosition.bbt.beatsPerMinute = posInfo.bpm;
-
- const double ppqPos = std::abs(posInfo.ppqPosition);
- const int ppqPerBar = posInfo.timeSigNumerator * 4 / posInfo.timeSigDenominator;
- const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * posInfo.timeSigNumerator;
- const double rest = std::fmod(barBeats, 1.0);
-
- timePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
- timePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
- timePosition.bbt.tick = rest * timePosition.bbt.ticksPerBeat;
- timePosition.bbt.beatsPerBar = posInfo.timeSigNumerator;
- timePosition.bbt.beatType = posInfo.timeSigDenominator;
-
- if (posInfo.ppqPosition < 0.0)
- {
- --timePosition.bbt.bar;
- timePosition.bbt.beat = posInfo.timeSigNumerator - timePosition.bbt.beat + 1;
- timePosition.bbt.tick = timePosition.bbt.ticksPerBeat - timePosition.bbt.tick - 1;
- }
-
- timePosition.bbt.barStartTick = timePosition.bbt.ticksPerBeat*
- timePosition.bbt.beatsPerBar*
- (timePosition.bbt.bar-1);
- }
- else
- {
- timePosition.frame = 0;
- timePosition.playing = false;
- timePosition.bbt.valid = false;
- }
-
- plugin.setTimePosition(timePosition);
-
- DISTRHO_SAFE_ASSERT_RETURN(buffer.getNumChannels() >= 2,);
-
- const float* audioBufferIn[18] = {};
- float* audioBufferOut[18] = {};
-
- for (int i=buffer.getNumChannels(); --i >= 0;)
- {
- audioBufferIn[i] = buffer.getReadPointer(i);
- audioBufferOut[i] = buffer.getWritePointer(i);
- }
-
- plugin.run(audioBufferIn, audioBufferOut, static_cast<uint32_t>(numSamples), midiEvents, midiEventCount);
- }
-
- // fix compiler warning
- void processBlock(juce::AudioBuffer<double>&, juce::MidiBuffer&) override {}
-
- double getTailLengthSeconds() const override
- {
- return 0.0;
- }
-
- bool acceptsMidi() const override
- {
- return true;
- }
-
- bool producesMidi() const override
- {
- return true;
- }
-
- juce::AudioProcessorParameter* getBypassParameter() const override
- {
- return bypassParameter;
- }
-
- juce::AudioProcessorEditor* createEditor() override;
-
- bool hasEditor() const override
- {
- return true;
- }
-
- int getNumPrograms() override
- {
- return 1;
- }
-
- int getCurrentProgram() override
- {
- return 0;
- }
-
- void setCurrentProgram(int) override
- {
- }
-
- const juce::String getProgramName(int) override
- {
- return "Default";
- }
-
- void changeProgramName(int, const juce::String&) override
- {
- }
-
- void getStateInformation(juce::MemoryBlock& destData) override
- {
- juce::XmlElement xmlState("CardinalState");
-
- for (uint32_t i=0; i<parameterCount; ++i)
- xmlState.setAttribute(plugin.getParameterSymbol(i).buffer(), plugin.getParameterValue(i));
-
- for (uint32_t i=0, stateCount=plugin.getStateCount(); i<stateCount; ++i)
- {
- const String& key(plugin.getStateKey(i));
- xmlState.setAttribute(key.buffer(), plugin.getStateValue(key).buffer());
- }
-
- copyXmlToBinary(xmlState, destData);
- }
-
- void setStateInformation(const void* const data, const int sizeInBytes) override
- {
- std::unique_ptr<juce::XmlElement> xmlState(getXmlFromBinary(data, sizeInBytes));
- DISTRHO_SAFE_ASSERT_RETURN(xmlState.get() != nullptr,);
-
- const juce::Array<juce::AudioProcessorParameter*>& parameters(getParameters());
-
- for (uint32_t i=0; i<parameterCount; ++i)
- {
- const double value = xmlState->getDoubleAttribute(plugin.getParameterSymbol(i).buffer(),
- plugin.getParameterDefault(i));
- const float normalizedValue = plugin.getParameterRanges(i).getFixedAndNormalizedValue(value);
- parameters.getUnchecked(static_cast<int>(i))->setValueNotifyingHost(normalizedValue);
- }
-
- for (uint32_t i=0, stateCount=plugin.getStateCount(); i<stateCount; ++i)
- {
- const String& key(plugin.getStateKey(i));
- const juce::String value = xmlState->getStringAttribute(key.buffer(),
- plugin.getStateDefaultValue(i).buffer());
- plugin.setState(key, value.toRawUTF8());
- }
- }
-
- private:
- static bool writeMidiFunc(void* const ptr, const MidiEvent& midiEvent)
- {
- CardinalWrapperProcessor* const processor = static_cast<CardinalWrapperProcessor*>(ptr);
- DISTRHO_SAFE_ASSERT_RETURN(processor != nullptr, false);
-
- juce::MidiBuffer* const currentMidiMessages = processor->currentMidiMessages;
- DISTRHO_SAFE_ASSERT_RETURN(currentMidiMessages != nullptr, false);
-
- const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
- return currentMidiMessages->addEvent(data,
- static_cast<int>(midiEvent.size),
- static_cast<int>(midiEvent.frame));
- }
- };
-
- // --------------------------------------------------------------------------------------------------------------------
-
- // unused in cardinal
- static constexpr const sendNoteFunc nullSendNoteFunc = nullptr;
-
- // unwanted, juce file dialogs are ugly
- static constexpr const fileRequestFunc nullFileRequestFunc = nullptr;
-
- // UI/editor implementation
- class CardinalWrapperEditor : public juce::AudioProcessorEditor,
- private juce::Timer
- {
- CardinalWrapperProcessor& cardinalProcessor;
-
- UIExporter* ui;
- void* const dspPtr;
-
- public:
- CardinalWrapperEditor(CardinalWrapperProcessor& cardinalProc)
- : juce::AudioProcessorEditor(cardinalProc),
- cardinalProcessor(cardinalProc),
- ui(nullptr),
- dspPtr(cardinalProc.plugin.getInstancePointer())
- {
- setOpaque(true);
- setResizable(true, false);
- // setResizeLimits(648, 538, -1, -1);
- setSize(1228, 666);
-
- startTimer(1000.0 / 60.0);
- }
-
- ~CardinalWrapperEditor() override
- {
- stopTimer();
- delete ui;
- }
-
- protected:
- void timerCallback() override
- {
- if (ui == nullptr)
- return;
-
- for (uint32_t i=0; i<cardinalProcessor.parameterCount; ++i)
- {
- if (cardinalProcessor.updatedParameters[i])
- {
- cardinalProcessor.updatedParameters[i] = false;
- ui->parameterChanged(i, cardinalProcessor.plugin.getParameterValue(i));
- }
- }
-
- repaint();
- }
-
- void paint(juce::Graphics&) override
- {
- if (ui == nullptr)
- {
- juce::ComponentPeer* const peer = getPeer();
- DISTRHO_SAFE_ASSERT_RETURN(peer != nullptr,);
-
- void* const nativeHandle = peer->getNativeHandle();
- DISTRHO_SAFE_ASSERT_RETURN(nativeHandle != nullptr,);
-
- ui = new UIExporter(this,
- (uintptr_t)nativeHandle,
- cardinalProcessor.getSampleRate(),
- editParamFunc,
- setParamFunc,
- setStateFunc,
- nullSendNoteFunc,
- setSizeFunc,
- nullFileRequestFunc,
- nullptr, // bundlePath
- dspPtr,
- 0.0 // scaleFactor
- );
-
- if (cardinalProcessor.wrapperType == juce::AudioProcessor::wrapperType_Standalone)
- {
- const double scaleFactor = ui->getScaleFactor();
- ui->setWindowOffset(4 * scaleFactor, 30 * scaleFactor);
- }
- }
-
- ui->plugin_idle();
- }
-
- private:
- static void editParamFunc(void* const ptr, const uint32_t index, const bool started)
- {
- CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
- DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
-
- CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
-
- if (started)
- cardinalProcessor.getParameters().getUnchecked(static_cast<int>(index))->beginChangeGesture();
- else
- cardinalProcessor.getParameters().getUnchecked(static_cast<int>(index))->endChangeGesture();
- }
-
- static void setParamFunc(void* const ptr, const uint32_t index, const float value)
- {
- CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
- DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
-
- CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
- const juce::Array<juce::AudioProcessorParameter*>& parameters(cardinalProcessor.getParameters());
- juce::AudioProcessorParameter* const parameter = parameters.getUnchecked(static_cast<int>(index));
- static_cast<ParameterFromDPF*>(parameter)->setValueNotifyingHostFromDPF(value);
- }
-
- static void setStateFunc(void* const ptr, const char* const key, const char* const value)
- {
- CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
- DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
-
- CardinalWrapperProcessor& cardinalProcessor(editor->cardinalProcessor);
- cardinalProcessor.plugin.setState(key, value);
- }
-
- static void setSizeFunc(void* const ptr, uint width, uint height)
- {
- CardinalWrapperEditor* const editor = static_cast<CardinalWrapperEditor*>(ptr);
- DISTRHO_SAFE_ASSERT_RETURN(editor != nullptr,);
-
- #ifdef DISTRHO_OS_MAC
- UIExporter* const ui = editor->ui;
- DISTRHO_SAFE_ASSERT_RETURN(ui != nullptr,);
-
- const double scaleFactor = ui->getScaleFactor();
- width /= scaleFactor;
- height /= scaleFactor;
- #endif
-
- editor->setSize(static_cast<int>(width), static_cast<int>(height));
- }
- };
-
- juce::AudioProcessorEditor* CardinalWrapperProcessor::createEditor()
- {
- return new CardinalWrapperEditor(*this);
- }
-
- // --------------------------------------------------------------------------------------------------------------------
-
- END_NAMESPACE_DISTRHO
-
- // --------------------------------------------------------------------------------------------------------------------
-
- juce::AudioProcessor* createPluginFilter()
- {
- // set valid but dummy values
- d_nextBufferSize = 512;
- d_nextSampleRate = 48000.0;
- return new DISTRHO_NAMESPACE::CardinalWrapperProcessor;
- }
-
- // --------------------------------------------------------------------------------------------------------------------
|