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 @@

Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.AudioFormatReader.html b/ProtoplugFiles/doc/classes/juce.AudioFormatReader.html index 11d4451b..9bd65ae0 100644 --- a/ProtoplugFiles/doc/classes/juce.AudioFormatReader.html +++ b/ProtoplugFiles/doc/classes/juce.AudioFormatReader.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Colour.html b/ProtoplugFiles/doc/classes/juce.Colour.html index 0887fd6e..55cbbdde 100644 --- a/ProtoplugFiles/doc/classes/juce.Colour.html +++ b/ProtoplugFiles/doc/classes/juce.Colour.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.ColourGradient.html b/ProtoplugFiles/doc/classes/juce.ColourGradient.html index 5e32a494..b25dac51 100644 --- a/ProtoplugFiles/doc/classes/juce.ColourGradient.html +++ b/ProtoplugFiles/doc/classes/juce.ColourGradient.html @@ -65,12 +65,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Component.html b/ProtoplugFiles/doc/classes/juce.Component.html index 67667226..62173295 100644 --- a/ProtoplugFiles/doc/classes/juce.Component.html +++ b/ProtoplugFiles/doc/classes/juce.Component.html @@ -64,12 +64,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.FillType.html b/ProtoplugFiles/doc/classes/juce.FillType.html index 8026f5f3..cb1863b8 100644 --- a/ProtoplugFiles/doc/classes/juce.FillType.html +++ b/ProtoplugFiles/doc/classes/juce.FillType.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Font.html b/ProtoplugFiles/doc/classes/juce.Font.html index 50ae9742..373d3365 100644 --- a/ProtoplugFiles/doc/classes/juce.Font.html +++ b/ProtoplugFiles/doc/classes/juce.Font.html @@ -65,12 +65,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Graphics.html b/ProtoplugFiles/doc/classes/juce.Graphics.html index 7a8a1379..0b25121a 100644 --- a/ProtoplugFiles/doc/classes/juce.Graphics.html +++ b/ProtoplugFiles/doc/classes/juce.Graphics.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Image.html b/ProtoplugFiles/doc/classes/juce.Image.html index e97ef5bf..37623470 100644 --- a/ProtoplugFiles/doc/classes/juce.Image.html +++ b/ProtoplugFiles/doc/classes/juce.Image.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Justification.html b/ProtoplugFiles/doc/classes/juce.Justification.html index 29d4790b..810e5c1c 100644 --- a/ProtoplugFiles/doc/classes/juce.Justification.html +++ b/ProtoplugFiles/doc/classes/juce.Justification.html @@ -64,12 +64,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.LagrangeInterpolator.html b/ProtoplugFiles/doc/classes/juce.LagrangeInterpolator.html index ff4e40f4..80ef9a31 100644 --- a/ProtoplugFiles/doc/classes/juce.LagrangeInterpolator.html +++ b/ProtoplugFiles/doc/classes/juce.LagrangeInterpolator.html @@ -65,12 +65,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Line.html b/ProtoplugFiles/doc/classes/juce.Line.html index 16a43bfb..c80101fc 100644 --- a/ProtoplugFiles/doc/classes/juce.Line.html +++ b/ProtoplugFiles/doc/classes/juce.Line.html @@ -65,12 +65,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Path.html b/ProtoplugFiles/doc/classes/juce.Path.html index 93b41c42..05d6d3b5 100644 --- a/ProtoplugFiles/doc/classes/juce.Path.html +++ b/ProtoplugFiles/doc/classes/juce.Path.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Point.html b/ProtoplugFiles/doc/classes/juce.Point.html index 05900184..22d64000 100644 --- a/ProtoplugFiles/doc/classes/juce.Point.html +++ b/ProtoplugFiles/doc/classes/juce.Point.html @@ -65,12 +65,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.RectanglePlacement.html b/ProtoplugFiles/doc/classes/juce.RectanglePlacement.html index 73519fe7..13856069 100644 --- a/ProtoplugFiles/doc/classes/juce.RectanglePlacement.html +++ b/ProtoplugFiles/doc/classes/juce.RectanglePlacement.html @@ -64,12 +64,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Rectangle_float.html b/ProtoplugFiles/doc/classes/juce.Rectangle_float.html index be905fa8..bbc9c88e 100644 --- a/ProtoplugFiles/doc/classes/juce.Rectangle_float.html +++ b/ProtoplugFiles/doc/classes/juce.Rectangle_float.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/classes/juce.Rectangle_int.html b/ProtoplugFiles/doc/classes/juce.Rectangle_int.html index 0c7892ff..ba673418 100644 --- a/ProtoplugFiles/doc/classes/juce.Rectangle_int.html +++ b/ProtoplugFiles/doc/classes/juce.Rectangle_int.html @@ -66,12 +66,12 @@

    Classes

Modules

Examples

    diff --git a/ProtoplugFiles/doc/examples/classic-filter.lua.html b/ProtoplugFiles/doc/examples/classic-filter.lua.html index fd7e3773..19779c7c 100644 --- a/ProtoplugFiles/doc/examples/classic-filter.lua.html +++ b/ProtoplugFiles/doc/examples/classic-filter.lua.html @@ -48,12 +48,12 @@

    Examples

Modules

Classes

    @@ -93,9 +93,7 @@

    Classes

    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 { @@ -108,7 +106,7 @@

    Classes

    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/doc/examples/midi-chordify.lua.html b/ProtoplugFiles/doc/examples/midi-chordify.lua.html index bf93b2d7..f6f91ad6 100644 --- a/ProtoplugFiles/doc/examples/midi-chordify.lua.html +++ b/ProtoplugFiles/doc/examples/midi-chordify.lua.html @@ -48,12 +48,12 @@

    Examples

Modules

Classes

    diff --git a/ProtoplugFiles/doc/examples/sine-organ.lua.html b/ProtoplugFiles/doc/examples/sine-organ.lua.html index 38863692..408e4b95 100644 --- a/ProtoplugFiles/doc/examples/sine-organ.lua.html +++ b/ProtoplugFiles/doc/examples/sine-organ.lua.html @@ -48,12 +48,12 @@

    Examples

Modules

Classes

    diff --git a/ProtoplugFiles/doc/examples/sinemouse-demo.lua.html b/ProtoplugFiles/doc/examples/sinemouse-demo.lua.html index b58391c1..23be91f6 100644 --- a/ProtoplugFiles/doc/examples/sinemouse-demo.lua.html +++ b/ProtoplugFiles/doc/examples/sinemouse-demo.lua.html @@ -48,12 +48,12 @@

    Examples

Modules

Classes

    diff --git a/ProtoplugFiles/doc/examples/soundfile-test.lua.html b/ProtoplugFiles/doc/examples/soundfile-test.lua.html index d176f9a7..69af7917 100644 --- a/ProtoplugFiles/doc/examples/soundfile-test.lua.html +++ b/ProtoplugFiles/doc/examples/soundfile-test.lua.html @@ -48,12 +48,12 @@

    Examples

Modules

Classes

+ + +

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. +

+
+
+ + multiIO.Channel:processBlock (samples, smax) +
+
+ 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. + + + + + + + + +
+
+ + + + +
+generated by LDoc 1.4.2 +
+ + + diff --git a/ProtoplugFiles/doc/modules/plugin.html b/ProtoplugFiles/doc/modules/plugin.html index 86e99498..1e579cde 100644 --- a/ProtoplugFiles/doc/modules/plugin.html +++ b/ProtoplugFiles/doc/modules/plugin.html @@ -46,12 +46,12 @@

Contents

Modules

Classes

    @@ -417,7 +417,7 @@

    Override functions

    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 @@

Contents

Modules

Classes

    diff --git a/ProtoplugFiles/doc/modules/script.html b/ProtoplugFiles/doc/modules/script.html index d522581d..29332601 100644 --- a/ProtoplugFiles/doc/modules/script.html +++ b/ProtoplugFiles/doc/modules/script.html @@ -45,12 +45,12 @@

    Contents

    Modules

    Classes

      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