diff --git a/distrho/DistrhoPlugin.hpp b/distrho/DistrhoPlugin.hpp index b9f9cdc6..4c2f4d0b 100644 --- a/distrho/DistrhoPlugin.hpp +++ b/distrho/DistrhoPlugin.hpp @@ -185,6 +185,29 @@ START_NAMESPACE_DISTRHO #endif +/* ------------------------------------------------------------------------------------------------------------ + * Audio Port Hints */ + +/** + @defgroup AudioPortHints Audio Port Hints + + Various audio port hints. + @see AudioPort::hints + @{ + */ + +/** + Audio port can be used as control voltage (LV2 only). + */ +static const uint32_t kAudioPortIsCV = 0x1; + +/** + Audio port should be used as sidechan (LV2 only). + */ +static const uint32_t kAudioPortIsSidechain = 0x2; + +/** @} */ + /* ------------------------------------------------------------------------------------------------------------ * Parameter Hints */ @@ -243,6 +266,40 @@ static const uint32_t kParameterIsCV = 0x20; @{ */ +/** + Audio Port. + */ +struct AudioPort { + /** + Hints describing this audio port. + @see AudioPortHints + */ + uint32_t hints; + + /** + The name of this audio port. + An audio port name can contain any character, but hosts might have a hard time with non-ascii ones. + The name doesn't have to be unique within a plugin instance, but it's recommended. + */ + d_string name; + + /** + The symbol of this audio port. + An audio port symbol is a short restricted name used as a machine and human readable identifier. + The first character must be one of _, a-z or A-Z and subsequent characters can be from _, a-z, A-Z and 0-9. + @note: Audio port and parameter symbols MUST be unique within a plugin instance. + */ + d_string symbol; + + /** + Default constructor for a regular audio port. + */ + AudioPort() noexcept + : hints(0x0), + name(), + symbol() {} +}; + /** Parameter ranges. This is used to set the default, minimum and maximum values of a parameter. @@ -670,6 +727,12 @@ protected: /* -------------------------------------------------------------------------------------------------------- * Init */ + /** + Initialize the audio port @a index. + This function will be called once, shortly after the plugin is created. + */ + virtual void d_initAudioPort(bool input, uint32_t index, AudioPort& port); + /** Initialize the parameter @a index. This function will be called once, shortly after the plugin is created. diff --git a/distrho/src/DistrhoPlugin.cpp b/distrho/src/DistrhoPlugin.cpp index d640422c..b411a519 100644 --- a/distrho/src/DistrhoPlugin.cpp +++ b/distrho/src/DistrhoPlugin.cpp @@ -28,6 +28,7 @@ double d_lastSampleRate = 0.0; * Static fallback data, see DistrhoPluginInternal.hpp */ const d_string PluginExporter::sFallbackString; +const AudioPort PluginExporter::sFallbackAudioPort; const ParameterRanges PluginExporter::sFallbackRanges; /* ------------------------------------------------------------------------------------------------------------ @@ -36,6 +37,10 @@ const ParameterRanges PluginExporter::sFallbackRanges; Plugin::Plugin(const uint32_t parameterCount, const uint32_t programCount, const uint32_t stateCount) : pData(new PrivateData()) { +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + pData->audioPorts = new AudioPort[DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS]; +#endif + if (parameterCount > 0) { pData->parameterCount = parameterCount; @@ -104,6 +109,27 @@ bool Plugin::d_writeMidiEvent(const MidiEvent& /*midiEvent*/) noexcept } #endif +/* ------------------------------------------------------------------------------------------------------------ + * Init */ + +void Plugin::d_initAudioPort(bool input, uint32_t index, AudioPort& port) +{ + if (port.hints & kAudioPortIsCV) + { + port.name = input ? "CV Input " : "CV Output "; + port.name += d_string(index+1); + port.symbol = input ? "cv_in_" : "cv_out_"; + port.symbol += d_string(index+1); + } + else + { + port.name = input ? "Audio Input " : "Audio Output "; + port.name += d_string(index+1); + port.symbol = input ? "audio_in_" : "audio_out_"; + port.symbol += d_string(index+1); + } +} + /* ------------------------------------------------------------------------------------------------------------ * Callbacks (optional) */ diff --git a/distrho/src/DistrhoPluginInternal.hpp b/distrho/src/DistrhoPluginInternal.hpp index d41be3d9..50781354 100644 --- a/distrho/src/DistrhoPluginInternal.hpp +++ b/distrho/src/DistrhoPluginInternal.hpp @@ -38,6 +38,10 @@ extern double d_lastSampleRate; struct Plugin::PrivateData { bool isProcessing; +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + AudioPort* audioPorts; +#endif + uint32_t parameterCount; Parameter* parameters; @@ -65,6 +69,9 @@ struct Plugin::PrivateData { PrivateData() noexcept : isProcessing(false), +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + audioPorts(nullptr), +#endif parameterCount(0), parameters(nullptr), #if DISTRHO_PLUGIN_WANT_PROGRAMS @@ -88,6 +95,14 @@ struct Plugin::PrivateData { ~PrivateData() noexcept { +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + if (audioPorts != nullptr) + { + delete[] audioPorts; + audioPorts = nullptr; + } +#endif + if (parameters != nullptr) { delete[] parameters; @@ -132,6 +147,16 @@ public: DISTRHO_SAFE_ASSERT_RETURN(fPlugin != nullptr,); DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr,); +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + { + uint32_t j=0; + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++j) + fPlugin->d_initAudioPort(true, i, fData->audioPorts[j]); + for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++j) + fPlugin->d_initAudioPort(false, i, fData->audioPorts[j]); + } +#endif + for (uint32_t i=0, count=fData->parameterCount; i < count; ++i) fPlugin->d_initParameter(i, fData->parameters[i]); @@ -211,6 +236,21 @@ public: } #endif +#if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 + const AudioPort& getAudioPort(const bool input, const uint32_t index) const noexcept + { + DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, sFallbackAudioPort); + + if (input) { + DISTRHO_SAFE_ASSERT_RETURN(index < DISTRHO_PLUGIN_NUM_INPUTS, sFallbackAudioPort); + } else { + DISTRHO_SAFE_ASSERT_RETURN(index < DISTRHO_PLUGIN_NUM_OUTPUTS, sFallbackAudioPort); + } + + return fData->audioPorts[index + (input ? 0 : DISTRHO_PLUGIN_NUM_INPUTS)]; + } +#endif + uint32_t getParameterCount() const noexcept { DISTRHO_SAFE_ASSERT_RETURN(fData != nullptr, 0); @@ -458,6 +498,7 @@ private: // Static fallback data, see DistrhoPlugin.cpp static const d_string sFallbackString; + static const AudioPort sFallbackAudioPort; static const ParameterRanges sFallbackRanges; DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(PluginExporter) diff --git a/distrho/src/DistrhoPluginLV2export.cpp b/distrho/src/DistrhoPluginLV2export.cpp index 157839d4..b59f30b8 100644 --- a/distrho/src/DistrhoPluginLV2export.cpp +++ b/distrho/src/DistrhoPluginLV2export.cpp @@ -109,6 +109,7 @@ void lv2_generate_ttl(const char* const basename) # else manifestString += " ui:binary <" + pluginDLL + "." DISTRHO_DLL_EXTENSION "> ;\n"; #endif + manifestString += "\n"; manifestString += " lv2:extensionData ui:idleInterface ,\n"; # if DISTRHO_PLUGIN_WANT_PROGRAMS manifestString += " ui:showInterface ,\n"; @@ -116,9 +117,11 @@ void lv2_generate_ttl(const char* const basename) # else manifestString += " ui:showInterface ;\n"; # endif + manifestString += "\n"; manifestString += " lv2:optionalFeature ui:noUserResize ,\n"; manifestString += " ui:resize ,\n"; manifestString += " ui:touch ;\n"; + manifestString += "\n"; # if DISTRHO_PLUGIN_WANT_DIRECT_ACCESS manifestString += " lv2:requiredFeature <" LV2_DATA_ACCESS_URI "> ,\n"; manifestString += " <" LV2_INSTANCE_ACCESS_URI "> ,\n"; @@ -205,18 +208,27 @@ void lv2_generate_ttl(const char* const basename) #if DISTRHO_PLUGIN_NUM_INPUTS > 0 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_INPUTS; ++i, ++portIndex) { + const AudioPort& port(plugin.getAudioPort(true, i)); + if (i == 0) pluginString += " lv2:port [\n"; else pluginString += " [\n"; - pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; + if (port.hints & kAudioPortIsCV) + pluginString += " a lv2:InputPort, lv2:CVPort ;\n"; + else + pluginString += " a lv2:InputPort, lv2:AudioPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; - pluginString += " lv2:symbol \"lv2_audio_in_" + d_string(i+1) + "\" ;\n"; - pluginString += " lv2:name \"Audio Input " + d_string(i+1) + "\" ;\n"; + pluginString += " lv2:symbol \"" + port.symbol + "\" ;\n"; + pluginString += " lv2:name \"" + port.name + "\" ;\n"; + + if (port.hints & kAudioPortIsSidechain) + pluginString += " lv2:portProperty lv2:isSideChain;\n"; if (i+1 == DISTRHO_PLUGIN_NUM_INPUTS) - pluginString += " ] ;\n\n"; + pluginString += " ] ;\n"; else pluginString += " ] ,\n"; } @@ -226,18 +238,27 @@ void lv2_generate_ttl(const char* const basename) #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0 for (uint32_t i=0; i < DISTRHO_PLUGIN_NUM_OUTPUTS; ++i, ++portIndex) { + const AudioPort& port(plugin.getAudioPort(false, i)); + if (i == 0) pluginString += " lv2:port [\n"; else pluginString += " [\n"; - pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; + if (port.hints & kAudioPortIsCV) + pluginString += " a lv2:OutputPort, lv2:CVPort ;\n"; + else + pluginString += " a lv2:OutputPort, lv2:AudioPort ;\n"; + pluginString += " lv2:index " + d_string(portIndex) + " ;\n"; - pluginString += " lv2:symbol \"lv2_audio_out_" + d_string(i+1) + "\" ;\n"; - pluginString += " lv2:name \"Audio Output " + d_string(i+1) + "\" ;\n"; + pluginString += " lv2:symbol \"" + port.symbol + "\" ;\n"; + pluginString += " lv2:name \"" + port.name + "\" ;\n"; + + if (port.hints & kAudioPortIsSidechain) + pluginString += " lv2:portProperty lv2:isSideChain;\n"; if (i+1 == DISTRHO_PLUGIN_NUM_OUTPUTS) - pluginString += " ] ;\n\n"; + pluginString += " ] ;\n"; else pluginString += " ] ,\n"; }