-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Allow plugins to request custom extensions (#136)
* Basic check that we can get the Reaper extension * Muting a track works! * Make extensionGet available in the plugin constructor * Cleanup extension/capabilities interface * Setting up dedicated example plugin for host-specific extensions * Cleaning up function forward-declaration * Workarounds in case user loads the plugin on REAPER's mater track
- Loading branch information
1 parent
76a9c20
commit 5f369ad
Showing
9 changed files
with
291 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
if(CLAP_WRAP_PROJUCER_PLUGIN) | ||
return() | ||
endif() | ||
|
||
if(NOT DEFINED REAPER_SDK_PATH) | ||
message(STATUS "REAPER SDK path not supplied, skipping configuration for host-specific extensions plugin.") | ||
return() | ||
endif() | ||
|
||
if (NOT EXISTS "${REAPER_SDK_PATH}/sdk/reaper_plugin.h") | ||
message(WARNING "REAPER SDK: reaper_plugin.h not found! (Looking at: ${REAPER_SDK_PATH}/sdk/reaper_plugin.h)") | ||
endif() | ||
|
||
message(STATUS "Configuring host-specific extensions plugin with REAPER SDK: ${REAPER_SDK_PATH}") | ||
|
||
juce_add_plugin(HostSpecificExtensionsPlugin | ||
COMPANY_NAME "${COMPANY_NAME}" | ||
PLUGIN_MANUFACTURER_CODE "${COMPANY_CODE}" | ||
PLUGIN_CODE Hsep | ||
FORMATS ${JUCE_FORMATS} | ||
PRODUCT_NAME "Host-Specific Extensions Tester" | ||
) | ||
|
||
clap_juce_extensions_plugin( | ||
TARGET HostSpecificExtensionsPlugin | ||
CLAP_ID "org.free-audio.HostSpecificExtensionsPlugin" | ||
CLAP_FEATURES audio-effect utility | ||
CLAP_PROCESS_EVENTS_RESOLUTION_SAMPLES 64 | ||
) | ||
|
||
target_sources(HostSpecificExtensionsPlugin PRIVATE | ||
HostSpecificExtensionsPlugin.cpp | ||
PluginEditor.cpp | ||
) | ||
|
||
target_compile_definitions(HostSpecificExtensionsPlugin PUBLIC | ||
JUCE_DISPLAY_SPLASH_SCREEN=1 | ||
JUCE_REPORT_APP_USAGE=0 | ||
JUCE_WEB_BROWSER=0 | ||
JUCE_USE_CURL=0 | ||
JUCE_JACK=1 | ||
JUCE_ALSA=1 | ||
JUCE_MODAL_LOOPS_PERMITTED=1 # required for Linux FileChooser with JUCE 6.0.7 | ||
JUCE_VST3_CAN_REPLACE_VST2=0 | ||
) | ||
|
||
target_include_directories(HostSpecificExtensionsPlugin | ||
PRIVATE | ||
"${REAPER_SDK_PATH}/sdk" | ||
) | ||
|
||
target_link_libraries(HostSpecificExtensionsPlugin | ||
PRIVATE | ||
juce::juce_audio_utils | ||
juce::juce_audio_plugin_client | ||
clap_juce_extensions | ||
PUBLIC | ||
juce::juce_recommended_config_flags | ||
juce::juce_recommended_lto_flags | ||
juce::juce_recommended_warning_flags | ||
) |
71 changes: 71 additions & 0 deletions
71
examples/HostSpecificExtensionsPlugin/HostSpecificExtensionsPlugin.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
#include "HostSpecificExtensionsPlugin.h" | ||
#include "PluginEditor.h" | ||
|
||
HostSpecificExtensionsPlugin::HostSpecificExtensionsPlugin() | ||
: juce::AudioProcessor(BusesProperties() | ||
.withInput("Input", juce::AudioChannelSet::stereo(), true) | ||
.withOutput("Output", juce::AudioChannelSet::stereo(), true)) | ||
{ | ||
// load extensions here! | ||
reaperPluginExtension = | ||
static_cast<const reaper_plugin_info_t *>(getExtension("cockos.reaper_extension")); | ||
jassert(reaperPluginExtension != nullptr || !juce::PluginHostType{}.isReaper()); | ||
|
||
if (reaperPluginExtension != nullptr) | ||
{ | ||
// we want to check that we can load/use the extensions in the plugin constructor. | ||
// for REAPER our silly test is to try muting track 0. | ||
using GetMasterTrackFunc = MediaTrack *(*)(ReaProject *); | ||
auto getMasterTrackFunc = | ||
reinterpret_cast<GetMasterTrackFunc>(reaperPluginExtension->GetFunc("GetMasterTrack")); | ||
auto *masterTrack = getMasterTrackFunc(nullptr); | ||
|
||
using SetMuteFunc = int (*)(MediaTrack *track, int mute, int igngroupflags); | ||
auto setMuteFunc = | ||
reinterpret_cast<SetMuteFunc>(reaperPluginExtension->GetFunc("SetTrackUIMute")); | ||
auto result = (*setMuteFunc)(masterTrack, 1, 0); | ||
jassert(result == 1); | ||
} | ||
} | ||
|
||
bool HostSpecificExtensionsPlugin::isBusesLayoutSupported( | ||
const juce::AudioProcessor::BusesLayout &layouts) const | ||
{ | ||
// only supports mono and stereo | ||
if (layouts.getMainOutputChannelSet() != juce::AudioChannelSet::mono() && | ||
layouts.getMainOutputChannelSet() != juce::AudioChannelSet::stereo()) | ||
return false; | ||
|
||
// input and output layout must be the same | ||
if (layouts.getMainOutputChannelSet() != layouts.getMainInputChannelSet()) | ||
return false; | ||
|
||
return true; | ||
} | ||
|
||
void HostSpecificExtensionsPlugin::prepareToPlay(double, int) {} | ||
|
||
void HostSpecificExtensionsPlugin::processBlock(juce::AudioBuffer<float> &, juce::MidiBuffer &) {} | ||
|
||
juce::AudioProcessorEditor *HostSpecificExtensionsPlugin::createEditor() | ||
{ | ||
return new PluginEditor(*this); | ||
} | ||
|
||
juce::String HostSpecificExtensionsPlugin::getPluginTypeString() const | ||
{ | ||
if (wrapperType == juce::AudioProcessor::wrapperType_Undefined && is_clap) | ||
return "CLAP"; | ||
|
||
return juce::AudioProcessor::getWrapperTypeDescription(wrapperType); | ||
} | ||
|
||
void HostSpecificExtensionsPlugin::getStateInformation(juce::MemoryBlock &) {} | ||
|
||
void HostSpecificExtensionsPlugin::setStateInformation(const void *, int) {} | ||
|
||
// This creates new instances of the plugin | ||
juce::AudioProcessor *JUCE_CALLTYPE createPluginFilter() | ||
{ | ||
return new HostSpecificExtensionsPlugin(); | ||
} |
55 changes: 55 additions & 0 deletions
55
examples/HostSpecificExtensionsPlugin/HostSpecificExtensionsPlugin.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
#pragma once | ||
|
||
#include <juce_audio_utils/juce_audio_utils.h> | ||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wunused-parameter", "-Wextra-semi", "-Wnon-virtual-dtor") | ||
#include <clap-juce-extensions/clap-juce-extensions.h> | ||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||
|
||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE("-Wc++98-compat-extra-semi", | ||
"-Wgnu-anonymous-struct", | ||
"-Wzero-as-null-pointer-constant", | ||
"-Wextra-semi", | ||
"-Wunused-parameter") | ||
#include <reaper_plugin.h> | ||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE | ||
|
||
class ModulatableFloatParameter; | ||
class HostSpecificExtensionsPlugin : public juce::AudioProcessor, | ||
public clap_juce_extensions::clap_juce_audio_processor_capabilities, | ||
protected clap_juce_extensions::clap_properties | ||
{ | ||
public: | ||
HostSpecificExtensionsPlugin(); | ||
|
||
const juce::String getName() const override { return JucePlugin_Name; } | ||
bool acceptsMidi() const override { return false; } | ||
bool producesMidi() const override { return false; } | ||
bool isMidiEffect() const override { return false; } | ||
|
||
double getTailLengthSeconds() const override { return 0.0; } | ||
|
||
int getNumPrograms() override { return 1; } | ||
int getCurrentProgram() override { return 0; } | ||
void setCurrentProgram(int) override {} | ||
const juce::String getProgramName(int) override { return juce::String(); } | ||
void changeProgramName(int, const juce::String &) override {} | ||
|
||
bool isBusesLayoutSupported(const juce::AudioProcessor::BusesLayout &layouts) const override; | ||
void prepareToPlay(double sampleRate, int samplesPerBlock) override; | ||
void releaseResources() override {} | ||
void processBlock(juce::AudioBuffer<float> &, juce::MidiBuffer &) override; | ||
void processBlock(juce::AudioBuffer<double> &, juce::MidiBuffer &) override {} | ||
|
||
bool hasEditor() const override { return true; } | ||
juce::AudioProcessorEditor *createEditor() override; | ||
|
||
void getStateInformation(juce::MemoryBlock &data) override; | ||
void setStateInformation(const void *data, int sizeInBytes) override; | ||
|
||
juce::String getPluginTypeString() const; | ||
|
||
const reaper_plugin_info_t* reaperPluginExtension = nullptr; | ||
|
||
private: | ||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(HostSpecificExtensionsPlugin) | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
#include "PluginEditor.h" | ||
|
||
PluginEditor::PluginEditor(HostSpecificExtensionsPlugin &plug) | ||
: juce::AudioProcessorEditor(plug), plugin(plug) | ||
{ | ||
addAndMakeVisible(changeTrackColour); | ||
changeTrackColour.setEnabled(plugin.reaperPluginExtension != nullptr); | ||
changeTrackColour.onClick = [reaperExt = plugin.reaperPluginExtension] { | ||
using GetTrackFunc = MediaTrack *(*)(ReaProject *, int); | ||
auto getTrackFunc = reinterpret_cast<GetTrackFunc>(reaperExt->GetFunc("GetTrack")); | ||
auto *track0 = getTrackFunc(nullptr, 0); | ||
if (track0 == nullptr) | ||
return; | ||
|
||
using ColorToNativeFunc = int (*)(int r, int g, int b); | ||
auto colorToNativeFunc = | ||
reinterpret_cast<ColorToNativeFunc>(reaperExt->GetFunc("ColorToNative")); | ||
|
||
using SetTrackColorFunc = void (*)(MediaTrack *track, int color); | ||
auto setTrackColorFunc = | ||
reinterpret_cast<SetTrackColorFunc>(reaperExt->GetFunc("SetTrackColor")); | ||
|
||
auto &rand = juce::Random::getSystemRandom(); | ||
const auto red = rand.nextInt(256); | ||
const auto green = rand.nextInt(256); | ||
const auto blue = rand.nextInt(256); | ||
setTrackColorFunc(track0, colorToNativeFunc(red, green, blue)); | ||
}; | ||
|
||
setSize(300, 300); | ||
} | ||
|
||
void PluginEditor::resized() | ||
{ | ||
changeTrackColour.setBounds(juce::Rectangle{100, 35}.withCentre(getLocalBounds().getCentre())); | ||
} | ||
|
||
void PluginEditor::paint(juce::Graphics &g) | ||
{ | ||
g.fillAll(juce::Colours::grey); | ||
|
||
auto bounds = getLocalBounds(); | ||
|
||
g.setColour(juce::Colours::black); | ||
g.setFont(25.0f); | ||
const auto titleText = "Host-Specific Extensions Plugin " + plugin.getPluginTypeString(); | ||
g.drawFittedText(titleText, bounds.removeFromTop(30), juce::Justification::centred, 1); | ||
|
||
g.setFont(18.0f); | ||
const auto reaperExtText = | ||
"REAPER plugin extension: " + | ||
juce::String(plugin.reaperPluginExtension != nullptr ? "FOUND" : "NOT FOUND"); | ||
g.drawFittedText(reaperExtText, bounds.removeFromTop(25), juce::Justification::centred, 1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#pragma once | ||
|
||
#include "HostSpecificExtensionsPlugin.h" | ||
|
||
class PluginEditor : public juce::AudioProcessorEditor | ||
{ | ||
public: | ||
explicit PluginEditor(HostSpecificExtensionsPlugin &plugin); | ||
~PluginEditor() override = default; | ||
|
||
void resized() override; | ||
void paint(juce::Graphics &g) override; | ||
|
||
private: | ||
HostSpecificExtensionsPlugin &plugin; | ||
|
||
juce::TextButton changeTrackColour { "Change Track Colour" }; | ||
|
||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginEditor) | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters