From c13cd6819159da7d2f5ad4b8cdea93aa2b2a23ee Mon Sep 17 00:00:00 2001
From: pac-dev
Date: Mon, 12 Oct 2015 18:18:43 +0200
Subject: [PATCH 1/5] Patch JUCE to choose number of I/Os at runtime
Patch JUCE to allow optionally choosing the number of I/O audio channels
at plugin load time.
---
.../VST/juce_VST_Wrapper.cpp | 12 +++----
.../processors/juce_AudioProcessor.cpp | 28 ++++++++++++++++
.../processors/juce_AudioProcessor.h | 32 +++++++++++++++++++
3 files changed, 66 insertions(+), 6 deletions(-)
diff --git a/Frameworks/JuceModules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp b/Frameworks/JuceModules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
index bb561b7c..28587c95 100644
--- a/Frameworks/JuceModules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
+++ b/Frameworks/JuceModules/juce_audio_plugin_client/VST/juce_VST_Wrapper.cpp
@@ -251,8 +251,8 @@ class JuceVSTWrapper : public AudioEffectX,
chunkMemoryTime (0),
speakerIn (kSpeakerArrEmpty),
speakerOut (kSpeakerArrEmpty),
- numInChans (JucePlugin_MaxNumInputChannels),
- numOutChans (JucePlugin_MaxNumOutputChannels),
+ numInChans (af->getMaxInputs()),
+ numOutChans (af->getMaxOutputs()),
isProcessing (false),
isBypassed (false),
hasShutdown (false),
@@ -433,7 +433,7 @@ class JuceVSTWrapper : public AudioEffectX,
bool getInputProperties (VstInt32 index, VstPinProperties* properties) override
{
- if (filter == nullptr || index >= JucePlugin_MaxNumInputChannels)
+ if (filter == nullptr || index >= filter->getMaxInputs())
return false;
setPinProperties (*properties, filter->getInputChannelName ((int) index),
@@ -443,7 +443,7 @@ class JuceVSTWrapper : public AudioEffectX,
bool getOutputProperties (VstInt32 index, VstPinProperties* properties) override
{
- if (filter == nullptr || index >= JucePlugin_MaxNumOutputChannels)
+ if (filter == nullptr || index >= filter->getMaxOutputs())
return false;
setPinProperties (*properties, filter->getOutputChannelName ((int) index),
@@ -917,7 +917,7 @@ class JuceVSTWrapper : public AudioEffectX,
return 0;
}
};
-
+ /*
bool setSpeakerArrangement (VstSpeakerArrangement* pluginInput,
VstSpeakerArrangement* pluginOutput) override
{
@@ -955,7 +955,7 @@ class JuceVSTWrapper : public AudioEffectX,
filter->setSpeakerArrangement (String::empty, String::empty);
return false;
}
-
+ */
static const char* getSpeakerArrangementString (VstSpeakerArrangementType type) noexcept
{
switch (type)
diff --git a/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.cpp b/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.cpp
index 83e2f622..e7de6a9c 100644
--- a/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.cpp
+++ b/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.cpp
@@ -40,6 +40,8 @@ AudioProcessor::AudioProcessor()
suspended (false),
nonRealtime (false)
{
+ maxInputs = JucePlugin_MaxNumInputChannels;
+ maxOutputs = JucePlugin_MaxNumOutputChannels;
}
AudioProcessor::~AudioProcessor()
@@ -397,6 +399,32 @@ XmlElement* AudioProcessor::getXmlFromBinary (const void* data, const int sizeIn
return nullptr;
}
+
+void AudioProcessor::initializeMaxChannels(int numIn, int numOut)
+{
+ // todo assert false if this is called after getters?
+ maxInputs = numIn;
+ maxOutputs = numOut;
+}
+
+int AudioProcessor::getMaxInputs()
+{
+ return maxInputs;
+}
+
+int AudioProcessor::getMaxOutputs()
+{
+ return maxOutputs;
+}
+
+std::vector> AudioProcessor::getPreferredChannelConfigs()
+{
+ // only half-implemented and not really settable (todo?)
+ std::vector> ret;
+ ret.push_back(std::pair(maxInputs, maxOutputs));
+ return ret;
+}
+
//==============================================================================
void AudioProcessorListener::audioProcessorParameterChangeGestureBegin (AudioProcessor*, int) {}
void AudioProcessorListener::audioProcessorParameterChangeGestureEnd (AudioProcessor*, int) {}
diff --git a/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.h b/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.h
index 662eec74..e68caa27 100644
--- a/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.h
+++ b/Frameworks/JuceModules/juce_audio_processors/processors/juce_AudioProcessor.h
@@ -712,6 +712,35 @@ class JUCE_API AudioProcessor
*/
static XmlElement* getXmlFromBinary (const void* data, int sizeInBytes);
+ /** Protoplug extension: initialize max number of channels.
+
+ This overrides a number of preprocessor constants, and allows for the number of channels to
+ be defined at runtime instead of compile time. This is meant to be called in the
+ constructor of the class inheriting AudioProcessor.
+ */
+ void initializeMaxChannels(int numIn, int numOut);
+
+ /** Protoplug extension: get maximum number of input channels.
+
+ This replaces the JucePlugin_MaxNumInputChannels constant, allowing it to be defined at
+ runtime.
+ */
+ int getMaxInputs();
+
+ /** Protoplug extension: get maximum number of output channels.
+
+ This replaces the JucePlugin_MaxNumOutputChannels constant, allowing it to be defined at
+ runtime.
+ */
+ int getMaxOutputs();
+
+ /** Protoplug extension: get preferred channel configurations.
+
+ This replaces the JucePlugin_PreferredChannelConfigurations constant, allowing
+ it to be defined at runtime.
+ */
+ std::vector> getPreferredChannelConfigs();
+
/** @internal */
static void JUCE_CALLTYPE setTypeOfNextNewPlugin (WrapperType);
@@ -734,6 +763,9 @@ class JUCE_API AudioProcessor
OwnedArray managedParameters;
AudioProcessorParameter* getParamChecked (int) const noexcept;
+ // protoplug extension
+ int maxInputs, maxOutputs;
+
#if JUCE_DEBUG && ! JUCE_DISABLE_AUDIOPROCESSOR_BEGIN_END_GESTURE_CHECKING
BigInteger changingParams;
#endif
From 80a53a6892e2627cbe3f835b5df62967fa6373ae Mon Sep 17 00:00:00 2001
From: pac-dev
Date: Mon, 12 Oct 2015 18:26:30 +0200
Subject: [PATCH 2/5] Determine number of channels from plugin name
The number of channels can now be optionally chosen by changing the
plugin name.
---
Source/LuaLink.cpp | 41 ++++++++++++++++++++++++++++++++++++
Source/LuaLink.h | 1 +
Source/PluginProcessor.cpp | 29 ++++++++++++++++++++++++-
Source/PluginProcessor.h | 6 ++++--
Source/guiclasses/AboutBox.h | 1 +
5 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/Source/LuaLink.cpp b/Source/LuaLink.cpp
index 62f2cd6a..4be8e83e 100644
--- a/Source/LuaLink.cpp
+++ b/Source/LuaLink.cpp
@@ -41,6 +41,34 @@ static int LuaSetParam (protolua::lua_State *L) {
return 0;
}
+static int LuaGetMaxInputChannels (protolua::lua_State *L) {
+ LuaLink *luli = globalStates[L];
+ if (!luli) return 0;
+ luli->ls->pushnumber(luli->pfx->getMaxInputs());
+ return 1;
+}
+
+static int LuaGetMaxOutputChannels (protolua::lua_State *L) {
+ LuaLink *luli = globalStates[L];
+ if (!luli) return 0;
+ luli->ls->pushnumber(luli->pfx->getMaxOutputs());
+ return 1;
+}
+
+static int LuaGetNumConnectedInputChannels (protolua::lua_State *L) {
+ LuaLink *luli = globalStates[L];
+ if (!luli) return 0;
+ luli->ls->pushnumber(luli->pfx->getNumInputChannels());
+ return 1;
+}
+
+static int LuaGetNumConnectedOutputChannels (protolua::lua_State *L) {
+ LuaLink *luli = globalStates[L];
+ if (!luli) return 0;
+ luli->ls->pushnumber(luli->pfx->getNumOutputChannels());
+ return 1;
+}
+
LuaLink::LuaLink(LuaProtoplugJuceAudioProcessor *_pfx)
{
ls = 0;
@@ -115,6 +143,14 @@ void LuaLink::compile() {
ls->setglobal("print");
ls->pushcclosure(LuaSetParam, 0);
ls->setglobal("plugin_setParameter");
+ ls->pushcclosure(LuaGetMaxInputChannels, 0);
+ ls->setglobal("plugin_getMaxInputChannels");
+ ls->pushcclosure(LuaGetMaxOutputChannels, 0);
+ ls->setglobal("plugin_getMaxOutputChannels");
+ ls->pushcclosure(LuaGetNumConnectedInputChannels, 0);
+ ls->setglobal("plugin_getNumConnectedInputChannels");
+ ls->pushcclosure(LuaGetNumConnectedOutputChannels, 0);
+ ls->setglobal("plugin_getNumConnectedOutputChannels");
// set globals
ls->pushlightuserdata((void *)&pfx->params);
@@ -389,6 +425,11 @@ double LuaLink::getTailLengthSeconds()
return ret;
}
+void LuaLink::numChannelsChanged()
+{
+ callVoidOverride("plugin_numChannelsChanged", 0);
+}
+
void LuaLink::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages, AudioPlayHead* ph)
{
bool res = callVoidOverride("plugin_processBlock" , LUA_TNUMBER, (double)buffer.getNumSamples(),
diff --git a/Source/LuaLink.h b/Source/LuaLink.h
index 33dd4803..903bf160 100644
--- a/Source/LuaLink.h
+++ b/Source/LuaLink.h
@@ -25,6 +25,7 @@ class LuaLink
bool parameterText2Double (int index, String text, double &d);
void paramChanged(int index);
double getTailLengthSeconds();
+ void numChannelsChanged();
String save();
void load(String loadData);
void preClose();
diff --git a/Source/PluginProcessor.cpp b/Source/PluginProcessor.cpp
index 6a1c65d6..f2b42681 100644
--- a/Source/PluginProcessor.cpp
+++ b/Source/PluginProcessor.cpp
@@ -12,7 +12,6 @@
#include "PluginEditor.h"
#include "ProtoplugDir.h"
-
//==============================================================================
LuaProtoplugJuceAudioProcessor::LuaProtoplugJuceAudioProcessor()
{
@@ -26,6 +25,8 @@ LuaProtoplugJuceAudioProcessor::LuaProtoplugJuceAudioProcessor()
for (int i=0; i nchans = getNumChannelsFromFilename(File::getSpecialLocation(File::currentExecutableFile));
+ initializeMaxChannels(nchans.first, nchans.second);
luli = new LuaLink(this);
}
@@ -161,6 +162,32 @@ void LuaProtoplugJuceAudioProcessor::setStateInformation (const void* data, int
}
}
+void LuaProtoplugJuceAudioProcessor::numChannelsChanged ()
+{
+ luli->numChannelsChanged();
+}
+
+std::pair LuaProtoplugJuceAudioProcessor::getNumChannelsFromFilename(File file)
+{
+ // example: "Lua Protoplug Fx 12in 2out"
+ // example: "Lua Protoplug Gen 4out"
+ String filename = file.getFileName();
+ int in, out;
+#ifdef _PROTOGEN
+ in = 0;
+ out = filename.substring(18).upToFirstOccurrenceOf("out", false, false).getIntValue();
+ if (out==0) out = 2;
+#else
+ in = filename.upToFirstOccurrenceOf("in", false, false).getLastCharacters(2).getIntValue();
+ out = filename.upToFirstOccurrenceOf("out", false, false).getLastCharacters(2).getIntValue();
+ //in = filename.substring(17).upToFirstOccurrenceOf("in", false, false).getIntValue();
+ //out = filename.fromFirstOccurrenceOf("out" .upToFirstOccurrenceOf("in", false, false).getIntValue();
+ if (in==0) in = 2;
+ if (out==0) out = 2;
+#endif
+ return std::pair(in,out);
+}
+
//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
diff --git a/Source/PluginProcessor.h b/Source/PluginProcessor.h
index 6112ba25..4067f6af 100644
--- a/Source/PluginProcessor.h
+++ b/Source/PluginProcessor.h
@@ -24,10 +24,11 @@ class LuaProtoplugJuceAudioProcessor : public AudioProcessor
double getTailLengthSeconds() const;
void getStateInformation (MemoryBlock& destData);
void setStateInformation (const void* data, int sizeInBytes);
+ void numChannelsChanged();
// some inlined overrides
- const String getInputChannelName (int channelIndex) const { return String (channelIndex + 1); }
- const String getOutputChannelName (int channelIndex) const { return String (channelIndex + 1); }
+ const String getInputChannelName (int channelIndex) const { return "protoplug in " + String (channelIndex + 1); }
+ const String getOutputChannelName (int channelIndex) const { return "protoplug out " + String (channelIndex + 1); }
float getParameterDefaultValue (int /*parameterIndex*/) { return 0.5; }
bool isInputChannelStereoPair (int /*index*/) const { return true; }
bool isOutputChannelStereoPair (int /*index*/) const { return true; }
@@ -50,6 +51,7 @@ class LuaProtoplugJuceAudioProcessor : public AudioProcessor
ProtoWindow *getProtoEditor();
void setProtoEditor(ProtoWindow * _ed);
bool parameterText2Double (int index, String text, double &d);
+ static std::pair getNumChannelsFromFilename(File file);
int lastUIWidth, lastUIHeight, lastUISplit, lastUIPanel;
int lastPopoutX, lastPopoutY;
diff --git a/Source/guiclasses/AboutBox.h b/Source/guiclasses/AboutBox.h
index 828eeed2..5bbfa542 100644
--- a/Source/guiclasses/AboutBox.h
+++ b/Source/guiclasses/AboutBox.h
@@ -44,6 +44,7 @@ class AboutBox
plugType = "error";
String m;
m << "Protoplug " << JucePlugin_VersionString << newLine
+ << "Channels in: " << processor->getMaxInputs() << ", out: " << processor->getMaxOutputs() << newLine
<< "Author: Pierre Cusa" << newLine
<< "Homepage: http://osar.fr/protoplug" << newLine
<< "Build date: " << __DATE__ << newLine
From 21a1c72dd7f31c47022c8a00865ae5d8701952c3 Mon Sep 17 00:00:00 2001
From: pac-dev
Date: Mon, 12 Oct 2015 18:35:01 +0200
Subject: [PATCH 3/5] Replace `stereofx` with generic `multiio`
Works the same way but on any number of channels. Also removed the
requirement to call a useless and confusing `init` function.
---
ProtoplugFiles/effects/classic-filter.lua | 6 +-
ProtoplugFiles/effects/delay line.lua | 10 +-
ProtoplugFiles/effects/distortion - power.lua | 5 +-
ProtoplugFiles/effects/pitch distort.lua | 6 +-
.../effects/pitch shift - bernsee algo.lua | 6 +-
ProtoplugFiles/effects/spectral filter.lua | 6 +-
ProtoplugFiles/include/core/multiio.lua | 116 ++++++++++++++++++
ProtoplugFiles/include/core/plugin.lua | 5 +-
ProtoplugFiles/include/core/stereofx.lua | 69 -----------
ProtoplugFiles/include/protoplug.lua | 4 +-
10 files changed, 135 insertions(+), 98 deletions(-)
create mode 100644 ProtoplugFiles/include/core/multiio.lua
delete mode 100644 ProtoplugFiles/include/core/stereofx.lua
diff --git a/ProtoplugFiles/effects/classic-filter.lua b/ProtoplugFiles/effects/classic-filter.lua
index 4e3edee5..1a0fb6f2 100644
--- a/ProtoplugFiles/effects/classic-filter.lua
+++ b/ProtoplugFiles/effects/classic-filter.lua
@@ -10,9 +10,7 @@ require "include/protoplug"
local cbFilter = require "include/dsp/cookbook filters"
local filters = {}
-stereoFx.init()
-
-function stereoFx.Channel:init()
+function multiIO.Channel:init()
-- create per-channel fields (filters)
self.filter = cbFilter
{
@@ -25,7 +23,7 @@ function stereoFx.Channel:init()
table.insert(filters, self.filter)
end
-function stereoFx.Channel:processBlock(s, smax)
+function multiIO.Channel:processBlock(s, smax)
for i = 0,smax do
s[i] = self.filter.process(s[i])
end
diff --git a/ProtoplugFiles/effects/delay line.lua b/ProtoplugFiles/effects/delay line.lua
index 68685cad..e8b0829e 100644
--- a/ProtoplugFiles/effects/delay line.lua
+++ b/ProtoplugFiles/effects/delay line.lua
@@ -8,16 +8,14 @@ require "include/protoplug"
local length, feedback
-stereoFx.init()
-
-- everything is per stereo channel
-function stereoFx.Channel:init()
+function multiIO.Channel:init()
self.buf = ffi.new("double[512]")
self.it = 0 -- iterator
self.dc1, self.dc2 = 0,0
end
-function stereoFx.Channel:goBack()
+function multiIO.Channel:goBack()
local nit = self.it-length
if nit<0 then nit = nit+512 end
local o = self.buf[nit]
@@ -28,14 +26,14 @@ function stereoFx.Channel:goBack()
return o
end
-function stereoFx.Channel:dcRemove(s)
+function multiIO.Channel:dcRemove(s)
self.dc1 = self.dc1 + (s - self.dc2) * 0.000002
self.dc2 = self.dc2 + self.dc1
self.dc1 = self.dc1 * 0.96
return s-self.dc2
end
-function stereoFx.Channel:processBlock(samples, smax)
+function multiIO.Channel:processBlock(samples, smax)
for i = 0,smax do
samples[i] = samples[i]+self:dcRemove(samples[i]+self:goBack())
self.buf[self.it] = samples[i]
diff --git a/ProtoplugFiles/effects/distortion - power.lua b/ProtoplugFiles/effects/distortion - power.lua
index 51b37595..7436fb50 100644
--- a/ProtoplugFiles/effects/distortion - power.lua
+++ b/ProtoplugFiles/effects/distortion - power.lua
@@ -13,14 +13,13 @@ local function dist (x)
return math.pow (x,power)
end
-stereoFx.init ()
-function stereoFx.Channel:init ()
+function multiIO.Channel:init ()
-- create per-channel fields (filters)
self.low = cbFilter {type = "lp"; f = 100; gain = 0; Q = 0.3}
self.high = cbFilter {type = "hp"; f = 50; gain = 0; Q = 0.3}
end
-function stereoFx.Channel:processBlock (samples, smax)
+function multiIO.Channel:processBlock (samples, smax)
for i = 0, smax do
local s = dist (self.high.process (samples[i]))
samples[i] = s + self.low.process (samples[i])*2
diff --git a/ProtoplugFiles/effects/pitch distort.lua b/ProtoplugFiles/effects/pitch distort.lua
index 6069b3d5..c202b537 100644
--- a/ProtoplugFiles/effects/pitch distort.lua
+++ b/ProtoplugFiles/effects/pitch distort.lua
@@ -9,8 +9,6 @@ author: osar.fr
require "include/protoplug"
require "include/Pickle"
-stereoFx.init()
-
fftlib = script.ffiLoad("libfftw3.so.3", "libfftw3-3")
@@ -57,7 +55,7 @@ local function ApplyWindow (samples)
end
-- channel buffers
-function stereoFx.Channel:init()
+function multiIO.Channel:init()
self.inbuf = ffi.new("double[?]", lineMax)
self.outbuf = ffi.new("double[?]", lineMax)
self.bufi = 0
@@ -114,7 +112,7 @@ function wrap (i)
return (i>lineMax-1) and i-lineMax or i
end
-function stereoFx.Channel:processBlock(s, smax)
+function multiIO.Channel:processBlock(s, smax)
for i = 0,smax do
self.inbuf[self.bufi] = s[i]
s[i] = self.outbuf[self.bufi]
diff --git a/ProtoplugFiles/effects/pitch shift - bernsee algo.lua b/ProtoplugFiles/effects/pitch shift - bernsee algo.lua
index 5ae245c4..c3508c2f 100644
--- a/ProtoplugFiles/effects/pitch shift - bernsee algo.lua
+++ b/ProtoplugFiles/effects/pitch shift - bernsee algo.lua
@@ -6,8 +6,6 @@ author: osar.fr
require "include/protoplug"
-stereoFx.init()
-
fftlib = script.ffiLoad("libfftw3.so.3", "libfftw3-3")
-- params
@@ -54,7 +52,7 @@ local r2c = fftlib.fftw_plan_dft_r2c_1d(fftSize, dbuf, spectrum, 64)
local c2r = fftlib.fftw_plan_dft_c2r_1d(fftSize, spectrum, dbuf, 64)
-- per-channel buffers
-function stereoFx.Channel:init()
+function multiIO.Channel:init()
self.inbuf = ffi.new("double[?]", lineMax)
self.outbuf = ffi.new("double[?]", lineMax)
self.bufi = 0
@@ -119,7 +117,7 @@ function wrap (i)
return (i>lineMax-1) and i-lineMax or i
end
-function stereoFx.Channel:processBlock(s, smax)
+function multiIO.Channel:processBlock(s, smax)
for i = 0,smax do
self.inbuf[self.bufi] = s[i]
s[i] = self.outbuf[self.bufi]
diff --git a/ProtoplugFiles/effects/spectral filter.lua b/ProtoplugFiles/effects/spectral filter.lua
index c9ff8069..a596a012 100644
--- a/ProtoplugFiles/effects/spectral filter.lua
+++ b/ProtoplugFiles/effects/spectral filter.lua
@@ -7,8 +7,6 @@ author: osar.fr
require "include/protoplug"
require "include/Pickle"
-stereoFx.init()
-
-- settings (change these here)
local fftSize = 512
local xPixels = 400
@@ -59,7 +57,7 @@ local r2c = fftlib.fftw_plan_dft_r2c_1d(fftSize, samples, spectrum, 64)
local c2r = fftlib.fftw_plan_dft_c2r_1d(fftSize, spectrum, samples, 64)
-- per-channel buffers
-function stereoFx.Channel:init()
+function multiIO.Channel:init()
self.inbuf = ffi.new("double[?]", fftSize)
self.outbuf = ffi.new("double[?]", fftSize)
self.bufi = 0
@@ -69,7 +67,7 @@ local function wrap (i)
return (i>fftSize-1) and i-fftSize or i
end
-function stereoFx.Channel:processBlock(s, smax)
+function multiIO.Channel:processBlock(s, smax)
for i = 0,smax do
self.inbuf[self.bufi] = s[i]
s[i] = self.outbuf[self.bufi]
diff --git a/ProtoplugFiles/include/core/multiio.lua b/ProtoplugFiles/include/core/multiio.lua
new file mode 100644
index 00000000..dd0617b2
--- /dev/null
+++ b/ProtoplugFiles/include/core/multiio.lua
@@ -0,0 +1,116 @@
+--- This module allows handling of multiple audio channels (stereo or more).
+-- For more info on multichannel handling, see this article:
+-- http://osar.fr/articles/10/Handling_Stereo_and_Multichannel_in_Protoplug
+--
+-- Most effects and non-mono instruments should use `multiIO.Channel` for easy
+-- management of multiple channels. Define @{Channel:processBlock}
+-- instead of @{plugin.processBlock}, and processing will be applied to every
+-- channel.
+--
+-- The `multiIO` global is available to every protoplug script after including the
+-- main protoplug header :
+-- require "include/protoplug"
+-- @module multiIO
+
+local script = require "include/core/script"
+local multiIO = {}
+
+
+--- Callback functions.
+-- Functions that your script can call.
+--
+-- @section callbacks
+
+--- Get the number of input channels.
+-- Returns the total number of input channels (connected or not), as set by the plugin's
+-- filename (eg. will return 4 if using `Lua Protoplug Fx 4in 8out.dll`).
+-- @treturn number
+-- @function multiIO.getNumInputs
+multiIO.getNumInputs = plugin_getMaxInputChannels
+
+--- Get the number of output channels.
+-- Returns the total number of output channels (connected or not), as set by the plugin's
+-- filename (eg. will return 8 if using `Lua Protoplug Fx 4in 8out.dll`).
+-- @treturn number
+-- @function multiIO.getNumOutputs
+multiIO.getNumOutputs = plugin_getMaxOutputChannels
+
+--- Get the number of currently connected input channels.
+-- Returns number of input channels currently connected to another node in the host.
+-- @treturn number
+-- @function multiIO.getNumConnectedInputs
+multiIO.getNumConnectedInputs = plugin_getNumConnectedInputChannels
+
+--- Get the number of output channels.
+-- Returns number of output channels currently connected to another node in the host.
+-- @treturn number
+-- @function multiIO.getNumConnectedOutputs
+multiIO.getNumConnectedOutputs = plugin_getNumConnectedOutputChannels
+
+
+--- Override functions.
+-- Define these functions and the host will call them.
+--
+-- @section overrides
+
+--- Receive MIDI before multichannel audio.
+-- If you defined @{Channel:processBlock}, and you also want to
+-- process MIDI, define this function and it will get called on every block
+-- before any audio processing happens.
+--
+-- @tparam midi.Buffer midiBuf the MIDI data for this block, serving as input and output
+-- @function multiIO.processMIDI
+
+
+--- Channel.
+-- This class represents an Input/Output audio channel.
+--
+-- @type multiIO.Channel
+
+
+--- Override to process a channel's audio block.
+-- Define the audio processing of an I/O channel in this function. If this is
+-- defined, it will be called instead of @{plugin.processBlock}.
+-- @param samples a C float* serving as input and output
+-- @param smax the maximum sample index (nSamples - 1)
+-- @function Channel:processBlock
+
+--- Override to handle initialisation.
+-- Override this method to perform initialisation tasks on each channel,
+-- for example to create any per-channel fields.
+-- @function Channel:init
+
+local Channel = { }
+function Channel:new (o)
+ setmetatable(o, self)
+ self.__index = self
+ return o
+end
+
+multiIO.channels = {}
+local nChannels = math.max(multiIO.getNumInputs(), multiIO.getNumOutputs())
+for i = 1, nChannels do
+ multiIO.channels[i] = Channel:new{ index = i }
+end
+
+script.addHandler("init", function ()
+ if Channel.processBlock==nil then return 0 end
+ function plugin.processBlock (samples, smax, midiBuf)
+ if multiIO.processMIDI then
+ multiIO.processMIDI(midiBuf)
+ end
+ for _, c in pairs(multiIO.channels) do
+ c:processBlock(samples[c.index-1], smax)
+ end
+ end
+ if Channel.init~=nil then
+ for _, c in pairs(multiIO.channels) do
+ c:init()
+ end
+ end
+end)
+
+
+multiIO.Channel = Channel
+
+return multiIO
\ No newline at end of file
diff --git a/ProtoplugFiles/include/core/plugin.lua b/ProtoplugFiles/include/core/plugin.lua
index 9b3a8a4c..8c5b98ef 100644
--- a/ProtoplugFiles/include/core/plugin.lua
+++ b/ProtoplugFiles/include/core/plugin.lua
@@ -47,7 +47,7 @@ script.addHandler("init", function ()
--- Process Audio Block.
-- Override this function to input and output audio and MIDI data.
--
- -- This override is handled automatically if @{stereoFx} or @{polyGen} are used.
+ -- This override is handled automatically if @{multiIO} or @{polyGen} are used.
-- Use this function to handle the raw data instead.
-- @param samples a C float** pointing to two channels of samples, serving as input and output
-- @param smax the maximum sample index (nSamples - 1)
@@ -61,7 +61,8 @@ script.addHandler("init", function ()
-- end
-- @function plugin.processBlock
local dbged = false
- if type(plugin.processBlock) == "function" then
+ if type(plugin.processBlock) == "function"
+ or (multiIO and type(multiIO.Channel.processBlock) == "function") then
local prepared = false
function plugin_processBlock(nSamples, samples, midiBuf, playHead, _sampleRate)
if not dbged then
diff --git a/ProtoplugFiles/include/core/stereofx.lua b/ProtoplugFiles/include/core/stereofx.lua
deleted file mode 100644
index e6966ae3..00000000
--- a/ProtoplugFiles/include/core/stereofx.lua
+++ /dev/null
@@ -1,69 +0,0 @@
---- Use this module to create a stereo effect.
--- Example at @{classic-filter.lua}.
---
--- This module acts as a layer that conceals the @{plugin.processBlock} function,
--- manages stereo channels, and exposes the `stereoFx.Channel` prototype for you
--- define per-channel audio processing. Initialize it by calling `stereoFx.init`.
---
--- The `stereoFx` global is available to every protoplug script after including the
--- main protoplug header :
--- require "include/protoplug"
--- @module stereoFx
-
-
-
---- Set up channels.
--- This function must be called by any script that wishes to use this module.
--- @function stereoFx.init
-
---- Channel.
--- This class represents a channel (ie. left or right).
---
--- @type stereoFx.Channel
-
-
---- Override to process a channel's audio block.
--- Define the audio processing of a single channel in this function.
--- @param samples a C float* serving as input and output
--- @param smax the maximum sample index (nSamples - 1)
--- @function Channel:processBlock
-
---- Override to handle initialisation.
--- Override this method to perform initialisation tasks on each channel,
--- for example to create any per-channel fields.
--- @function Channel:init
-
-local stereoFx = {}
-
-local script = require "include/core/script"
-
-local Channel = { }
-function Channel:new (o)
- setmetatable(o, self)
- self.__index = self
- return o
-end
-
-local LChannel = Channel:new{ }
-local RChannel = Channel:new{ }
-
-function stereoFx.init()
- function plugin.processBlock (samples, smax)
- if Channel.processBlock==nil then return 0 end
- LChannel:processBlock(samples[0], smax)
- RChannel:processBlock(samples[1], smax)
- end
-
- script.addHandler("init", function ()
- if Channel.init~=nil then
- LChannel:init()
- RChannel:init()
- end
- end)
-end
-
-stereoFx.Channel = Channel
-stereoFx.LChannel = LChannel
-stereoFx.RChannel = RChannel
-
-return stereoFx
\ No newline at end of file
diff --git a/ProtoplugFiles/include/protoplug.lua b/ProtoplugFiles/include/protoplug.lua
index 95bec45a..ae1915ba 100644
--- a/ProtoplugFiles/include/protoplug.lua
+++ b/ProtoplugFiles/include/protoplug.lua
@@ -13,7 +13,7 @@ juce = require "include/protojuce"
midi = require "include/core/midi"
script = require "include/core/script"
plugin = require "include/core/plugin"
-gui = require "include/core/gui"
+multiIO = require "include/core/multiio"
polyGen = require "include/core/polygen"
-stereoFx = require "include/core/stereofx"
+gui = require "include/core/gui"
From 7c8bc93077fd569d2466a897b37b99e65e435d42 Mon Sep 17 00:00:00 2001
From: pac-dev
Date: Mon, 12 Oct 2015 18:37:27 +0200
Subject: [PATCH 4/5] Rebuild documentation
---
.../doc/classes/juce.AffineTransform.html | 6 +-
.../doc/classes/juce.AudioFormatReader.html | 6 +-
ProtoplugFiles/doc/classes/juce.Colour.html | 6 +-
.../doc/classes/juce.ColourGradient.html | 6 +-
.../doc/classes/juce.Component.html | 6 +-
ProtoplugFiles/doc/classes/juce.FillType.html | 6 +-
ProtoplugFiles/doc/classes/juce.Font.html | 6 +-
ProtoplugFiles/doc/classes/juce.Graphics.html | 6 +-
ProtoplugFiles/doc/classes/juce.Image.html | 6 +-
.../doc/classes/juce.Justification.html | 6 +-
.../classes/juce.LagrangeInterpolator.html | 6 +-
ProtoplugFiles/doc/classes/juce.Line.html | 6 +-
ProtoplugFiles/doc/classes/juce.Path.html | 6 +-
ProtoplugFiles/doc/classes/juce.Point.html | 6 +-
.../doc/classes/juce.RectanglePlacement.html | 6 +-
.../doc/classes/juce.Rectangle_float.html | 6 +-
.../doc/classes/juce.Rectangle_int.html | 6 +-
.../doc/examples/classic-filter.lua.html | 12 +-
.../doc/examples/midi-chordify.lua.html | 6 +-
.../doc/examples/sine-organ.lua.html | 6 +-
.../doc/examples/sinemouse-demo.lua.html | 6 +-
.../doc/examples/soundfile-test.lua.html | 6 +-
ProtoplugFiles/doc/index.html | 22 +-
ProtoplugFiles/doc/modules/gui.html | 6 +-
ProtoplugFiles/doc/modules/midi.html | 6 +-
ProtoplugFiles/doc/modules/multiIO.html | 344 ++++++++++++++++++
ProtoplugFiles/doc/modules/plugin.html | 8 +-
ProtoplugFiles/doc/modules/polyGen.html | 6 +-
ProtoplugFiles/doc/modules/script.html | 6 +-
ProtoplugFiles/doc/src/config.ld | 8 +-
30 files changed, 443 insertions(+), 101 deletions(-)
create mode 100644 ProtoplugFiles/doc/modules/multiIO.html
diff --git a/ProtoplugFiles/doc/classes/juce.AffineTransform.html b/ProtoplugFiles/doc/classes/juce.AffineTransform.html
index 0fdcd916..b8f77a99 100644
--- a/ProtoplugFiles/doc/classes/juce.AffineTransform.html
+++ b/ProtoplugFiles/doc/classes/juce.AffineTransform.html
@@ -67,12 +67,12 @@
This module allows handling of multiple audio channels (stereo or more).
+
+
+
For more info on multichannel handling, see this article:
+ http://osar.fr/articles/10/HandlingStereoandMultichannelin_Protoplug
+
+
Most effects and non-mono instruments should use multiIO.Channel for easy
+ management of multiple channels. Define Channel:processBlock
+ instead of plugin.processBlock, and processing will be applied to every
+ channel.
+
+
The multiIO global is available to every protoplug script after including the
+ main protoplug header :
+ Get the number of input channels.
+ Returns the total number of input channels (connected or not), as set by the plugin's
+ filename (eg. will return 4 if using Lua Protoplug Fx 4in 8out.dll).
+
+
+
+
+
Returns:
+
+
+ number
+
+
+
+
+
+
+
+
+
+
+
+ multiIO.getNumOutputs ()
+
+
+ Get the number of output channels.
+ Returns the total number of output channels (connected or not), as set by the plugin's
+ filename (eg. will return 8 if using Lua Protoplug Fx 4in 8out.dll).
+
+
+
+
+
Returns:
+
+
+ number
+
+
+
+
+
+
+
+
+
+
+
+ multiIO.getNumConnectedInputs ()
+
+
+ Get the number of currently connected input channels.
+ Returns number of input channels currently connected to another node in the host.
+
+
+
+
+
Returns:
+
+
+ number
+
+
+
+
+
+
+
+
+
+
+
+ multiIO.getNumConnectedOutputs ()
+
+
+ Get the number of output channels.
+ Returns number of output channels currently connected to another node in the host.
+
+
+
+
+
Returns:
+
+
+ number
+
+
+
+
+
+
+
+
+
+
+
Override functions
+
+ Define these functions and the host will call them.
+
+
+
+
+ multiIO.processMIDI (midiBuf)
+
+
+ Receive MIDI before multichannel audio.
+ If you defined Channel:processBlock, and you also want to
+ process MIDI, define this function and it will get called on every block
+ before any audio processing happens.
+
+
+
+
+
Parameters:
+
+
midiBuf
+ midi.Buffer
+ the MIDI data for this block, serving as input and output
+
+
+
+
+
+
+
+
+
+
Class multiIO.Channel
+
+ This class represents an Input/Output audio channel.
+
+ Override to process a channel's audio block.
+ Define the audio processing of an I/O channel in this function. If this is
+ defined, it will be called instead of plugin.processBlock.
+
+
+
+
Parameters:
+
+
samples
+ a C float* serving as input and output
+
+
smax
+ the maximum sample index (nSamples - 1)
+
+
+
+
+
+
+
+
+
+
+ multiIO.Channel:init ()
+
+
+ Override to handle initialisation.
+ Override this method to perform initialisation tasks on each channel,
+ for example to create any per-channel fields.
+
+
+
+
+
+
+
+
+
Process Audio Block.
Override this function to input and output audio and MIDI data.
-
This override is handled automatically if stereoFx or polyGen are used.
+
This override is handled automatically if multiIO or polyGen are used.
Use this function to handle the raw data instead.
diff --git a/ProtoplugFiles/doc/modules/polyGen.html b/ProtoplugFiles/doc/modules/polyGen.html
index 01cd33e0..a90de622 100644
--- a/ProtoplugFiles/doc/modules/polyGen.html
+++ b/ProtoplugFiles/doc/modules/polyGen.html
@@ -45,12 +45,12 @@
diff --git a/ProtoplugFiles/doc/src/config.ld b/ProtoplugFiles/doc/src/config.ld
index 68314b01..87f9188b 100644
--- a/ProtoplugFiles/doc/src/config.ld
+++ b/ProtoplugFiles/doc/src/config.ld
@@ -21,7 +21,7 @@ local function softenPrefix(s)
end
end
-local forcePrefix = {"plugin", "script", "gui", "midi", "polyGen", "stereoFx"}
+local forcePrefix = {"plugin", "script", "gui", "midi", "polyGen", "multiIO"}
local function formatName(item, default_display_name)
for _, v in ipairs(forcePrefix) do
if item.module.name == v then
@@ -56,12 +56,12 @@ new_type("simplefield","Fields")
new_type("predefined","Predefined values")
file = {
- '../../include/core/plugin.lua';
'../../include/core/script.lua';
+ '../../include/core/plugin.lua';
+ '../../include/core/multiio.lua';
'../../include/core/midi.lua';
- '../../include/core/gui.lua';
'../../include/core/polygen.lua';
- '../../include/core/stereofx.lua';
+ '../../include/core/gui.lua';
'../../include/protojuce';
}
examples = {
From b031b6d9b6b7cef510b8b50a85b49dd70cbc1c69 Mon Sep 17 00:00:00 2001
From: pac-dev
Date: Mon, 12 Oct 2015 18:39:29 +0200
Subject: [PATCH 5/5] Add channel-specific scripts with example
Add optional ability to make scripts restricted to a specific number of
I/O channels and a 2-in 4-out example.
---
ProtoplugFiles/effects/2in 4out/KrossOut.lua | 21 ++++++++++++++++++++
ProtoplugFiles/effects/2in 4out/README.txt | 3 +++
Source/guiclasses/ProtoWindow.cpp | 6 ++++++
3 files changed, 30 insertions(+)
create mode 100644 ProtoplugFiles/effects/2in 4out/KrossOut.lua
create mode 100644 ProtoplugFiles/effects/2in 4out/README.txt
diff --git a/ProtoplugFiles/effects/2in 4out/KrossOut.lua b/ProtoplugFiles/effects/2in 4out/KrossOut.lua
new file mode 100644
index 00000000..6977d780
--- /dev/null
+++ b/ProtoplugFiles/effects/2in 4out/KrossOut.lua
@@ -0,0 +1,21 @@
+require "include/protoplug"
+
+local pos
+
+function plugin.processBlock (samples, smax)
+ for i = 0, smax do
+ samples[0][i] = samples[0][i] * (1-pos)
+ samples[1][i] = samples[1][i] * (1-pos)
+ samples[2][i] = samples[0][i] * pos
+ samples[3][i] = samples[1][i] * pos
+ end
+end
+
+params = plugin.manageParams {
+ {
+ name = "Position";
+ min = 0;
+ max = 1;
+ changed = function (val) pos = val end;
+ };
+}
diff --git a/ProtoplugFiles/effects/2in 4out/README.txt b/ProtoplugFiles/effects/2in 4out/README.txt
new file mode 100644
index 00000000..6965f936
--- /dev/null
+++ b/ProtoplugFiles/effects/2in 4out/README.txt
@@ -0,0 +1,3 @@
+Scripts placed in folders named "Xin Yout" (where X and Y are numbers of channels) will only be visible to protoplug versions with the corresponding number of channels.
+
+To make a version of protoplug with any number of channels, simply copy the plugin (`.dll`, `.so`, or `.component`, depending on your platform) and specify the number of channels in the filename. For example, in Windows, copy `Lua Protoplug Fx.dll` and rename the copy to `Lua Protoplug Fx 8in 8out.dll` for a version with 8 input and output channels.
diff --git a/Source/guiclasses/ProtoWindow.cpp b/Source/guiclasses/ProtoWindow.cpp
index b2ab64f8..53e5e4fa 100644
--- a/Source/guiclasses/ProtoWindow.cpp
+++ b/Source/guiclasses/ProtoWindow.cpp
@@ -238,6 +238,12 @@ StringArray ProtoWindow::getMenuBarNames()
void ProtoWindow::addFolderToMenu(File folder, PopupMenu &menu, String filter, int &mapCounter)
{
+ if (folder.getFileName().matchesWildcard("*in *out", false))
+ {
+ std::pair chans = processor->getNumChannelsFromFilename(folder);
+ if (chans.first != processor->getMaxInputs() || chans.second != processor->getMaxOutputs())
+ return;
+ }
Array res;
int max = folder.findChildFiles(res, File::findDirectories, 0);
for (int i=0; i