commit 13dc87e37d1d97d4d3070c187758f1b2a1a1e014
parent a081d44151c1f3c124f84fedda8c246e4427ba03
Author: jatinchowdhury18 <[email protected]>
Date: Mon, 14 Sep 2020 12:10:22 -0700
Manage mix groups through shared resource pointer, instead of OSC (#88)
* Manage mix groups through shared resource pointer, instead of OSC
* Update Mac builds
* Update Windows builds
Co-authored-by: jatinchowdhury18 <[email protected]>
Co-authored-by: Travis CI <[email protected]>
Diffstat:
6 files changed, 129 insertions(+), 155 deletions(-)
diff --git a/Plugin/CHOWTapeModel.jucer b/Plugin/CHOWTapeModel.jucer
@@ -128,7 +128,6 @@
<MODULEPATH id="juce_dsp" path="Juce/modules"/>
<MODULEPATH id="foleys_gui_magic" path="."/>
<MODULEPATH id="juce_opengl" path="Juce/modules"/>
- <MODULEPATH id="juce_osc" path="Juce/modules"/>
</MODULEPATHS>
</XCODE_MAC>
<VS2017 targetFolder="Builds/VisualStudio2017">
@@ -153,7 +152,6 @@
<MODULEPATH id="juce_dsp" path="Juce/modules"/>
<MODULEPATH id="foleys_gui_magic" path="."/>
<MODULEPATH id="juce_opengl" path="Juce/modules"/>
- <MODULEPATH id="juce_osc" path="Juce/modules"/>
</MODULEPATHS>
</VS2017>
<LINUX_MAKE targetFolder="Builds/LinuxMakefile">
@@ -177,7 +175,6 @@
<MODULEPATH id="juce_dsp" path="Juce/modules"/>
<MODULEPATH id="foleys_gui_magic" path="."/>
<MODULEPATH id="juce_opengl" path="Juce/modules"/>
- <MODULEPATH id="juce_osc" path="Juce/modules"/>
</MODULEPATHS>
</LINUX_MAKE>
</EXPORTFORMATS>
@@ -198,7 +195,6 @@
<MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
<MODULE id="juce_opengl" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
- <MODULE id="juce_osc" showAllCode="1" useLocalCopy="0" useGlobalPath="0"/>
</MODULES>
<LIVE_SETTINGS>
<WINDOWS/>
diff --git a/Plugin/Source/GUI/Assets/gui.xml b/Plugin/Source/GUI/Assets/gui.xml
@@ -144,7 +144,7 @@
<ComboBox lookAndFeel="ComboBoxLNF" padding="0" border="0" background-color="00000000"
name="Mix Group" caption="Mix Group" caption-size="0" flex-grow="0.9"
combo-text="FFEAA92C" caption-color="FFFFFFFF" max-height="100" margin="0"
- parameter="mix_group" combo-background="00000000" tooltip="Adds this plugin to a mix group. If you add this plugin to a group, the parameter settings will be copied to all other plugins in the group, and their parameters will remain in sync."/>
+ parameter="mix_group" combo-background="00000000" tooltip="Adds this plugin to a mix group. When the plugin is added to a group, the group parameters will be copied to this plugin, and their parameters will remain in sync."/>
<presets margin="5" padding="0" background-color="00000000" border-color="595C6B"
radius="" border="" lookAndFeel="ComboBoxLNF" tooltip="Selects a preset for the plugin."
flex-grow="1.75" max-height="100"/>
diff --git a/Plugin/Source/MixGroups/MixGroupsController.cpp b/Plugin/Source/MixGroups/MixGroupsController.cpp
@@ -6,20 +6,22 @@ MixGroupsController::MixGroupsController (AudioProcessorValueTreeState& vts,
AudioProcessor* proc) :
vts (vts)
{
- // connect sender
- sender.connect ("127.0.0.1", portNum);
-
// load parameters
auto params = proc->getParameters();
loadParameterList (params);
// set up receiver
- paramReceiver->loadReceiverListeners (paramList);
- paramReceiver->addChangeListener (this);
+ sharedData->loadParameterList (paramList);
+ sharedData->addListener (this);
mixGroupParam = vts.getRawParameterValue (mixGroupParamID);
}
+MixGroupsController::~MixGroupsController()
+{
+ sharedData->removeListener (this);
+}
+
void MixGroupsController::createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params)
{
params.push_back (std::make_unique<AudioParameterChoice> (mixGroupParamID, "Mix Group", StringArray ({"N/A", "1", "2", "3", "4"}), 0));
@@ -45,127 +47,65 @@ void MixGroupsController::loadParameterList (Array<AudioProcessorParameter*>& pa
void MixGroupsController::parameterChanged (const String& parameterID, float newValue)
{
- int mixGroup = (int) mixGroupParam->load();
- if (mixGroup == 0) // no mix group, don't bother sending
+ if (parameterID == lastParameterChanged) // I just changed this param, don't want to get stuck in an endless loop...
+ {
+ lastParameterChanged = String();
return;
+ }
+
+ int mixGroup = (int) mixGroupParam->load();
if (parameterID == mixGroupParamID) // mix group was changed
{
- OSCMessage message (allParamsAddress);
- message.addArgument (uuid.toString());
- message.addArgument (mixGroup);
+ sharedData->pluginGroupChanged (uuid.toString(), mixGroup);
+
+ if (mixGroup == 0)
+ return;
- for (const auto& paramID : paramList)
+ int numPluginsInGroup = sharedData->getNumPluginsInGroup (mixGroup);
+ if (numPluginsInGroup == 1) // I'm the only plugin in this group
{
- auto param = vts.getParameter (paramID);
- auto value = param->convertFrom0to1 (param->getValue());
-
- message.addArgument (paramID);
- message.addArgument (value);
+ sharedData->copyPluginState (mixGroup, vts);
+ }
+ else if (numPluginsInGroup > 1) // there are already plugins in this group
+ {
+ // copy shared state to me
+ for (const auto& paramID : paramList)
+ {
+ auto param = vts.getParameter (paramID);
+ auto value = sharedData->getParameter (paramID, mixGroup);
+
+ lastParameterChanged = paramID;
+ param->setValueNotifyingHost (param->convertTo0to1 (value));
+ }
}
-
- sender.send (message);
return;
}
- if (! paramList.contains (parameterID)) // parameter is not in list
+ if (mixGroup == 0) // no mix group, don't bother sending
return;
- sendParameter (parameterID, mixGroup, newValue);
-}
-
-void MixGroupsController::sendParameter (const String& paramID, int mixGroup, float value)
-{
- String address = oscAddressPrefix + paramID;
- String pluginID = uuid.toString();
+ if (! paramList.contains (parameterID)) // parameter is not in list
+ return;
- sender.send (address, pluginID, mixGroup, value);
+ sharedData->setParameter (parameterID, mixGroup, newValue, uuid.toString());
}
-void MixGroupsController::changeListenerCallback (ChangeBroadcaster* source)
+void MixGroupsController::mixGroupParamChanged (const String& paramID, int mixGroup, float value, String otherUuid)
{
- if (source != paramReceiver)
+ if (uuid == otherUuid) // this message came from me!
return;
- // get message from receiver, we already know this will be a valid message!
- auto& message = paramReceiver->getOSCMessage();
-
- // get address
- auto address = message.getAddressPattern().toString();
-
- // special case: all parameters
- if (address == allParamsAddress)
- {
- parseAllParams (message);
- return;
- }
-
- // get paramID from address
- auto paramID = address.fromFirstOccurrenceOf (oscAddressPrefix, false, false);
+ // load parameter
auto param = vts.getParameter (paramID);
-
if (param == nullptr) // invalid parameter
return;
- String pluginID = message[0].getString();
- if (uuid == pluginID) // this message came from me!
- return;
-
- int mixGroup = message[1].getInt32();
if (mixGroup != (int) mixGroupParam->load()) // received message does not apply to this mix group
return;
-
- auto value = message[2].getFloat32();
+
+ // set parameter value
+ lastParameterChanged = paramID;
param->setValueNotifyingHost (param->convertTo0to1 (value));
}
-
-void MixGroupsController::parseAllParams (const OSCMessage& message)
-{
- if (message.size() % 2 != 0) // must have even number of arguments!
- return;
-
- String pluginID = message[0].getString();
- if (uuid == pluginID) // this message came from me!
- return;
-
- int mixGroup = message[1].getInt32();
- if (mixGroup != (int) mixGroupParam->load()) // received message does not apply to this mix group
- return;
-
- for (int argIdx = 2; argIdx < message.size(); argIdx += 2)
- {
- auto temp1 = message[argIdx];
- auto temp2 = message[argIdx+1];
- if (! (message[argIdx].isString() && message[argIdx + 1].isFloat32())) // incorrect format...
- continue;
-
- String paramID = message[argIdx].getString();
- auto param = vts.getParameter (paramID);
-
- if (param == nullptr) // invalid parameter
- continue;
-
- auto value = message[argIdx + 1].getFloat32();
- param->setValueNotifyingHost (param->convertTo0to1 (value));
- }
-}
-
-bool MixGroupsController::isValidOSCMessage (const OSCMessage& message)
-{
- // special case: all parameters
- if (message.getAddressPattern() == allParamsAddress && message.size() > 2)
- return true;
-
- // other valid messages will be of the form: string, int, float
- if (message.size() != 3)
- return false;
-
- bool isValid = true;
-
- isValid &= message[0].isString();
- isValid &= message[1].isInt32();
- isValid &= message[2].isFloat32();
-
- return isValid;
-}
diff --git a/Plugin/Source/MixGroups/MixGroupsController.h b/Plugin/Source/MixGroups/MixGroupsController.h
@@ -5,28 +5,22 @@
namespace MixGroupsConstants
{
- constexpr int portNum = 1818;
- const String oscAddressPrefix = "/chowdsp/tape/";
- const String allParamsAddress = oscAddressPrefix + "all";
- const String mixGroupParamID = "mix_group";
+ constexpr int numMixGroups = 4;
+ const String mixGroupParamID = "mix_group";
}
/** Class to control syncing parameters between multiple mix groups */
class MixGroupsController : private AudioProcessorValueTreeState::Listener,
- private ChangeListener
+ private MixGroupsSharedData::Listener
{
public:
MixGroupsController (AudioProcessorValueTreeState& vts, AudioProcessor* proc);
+ ~MixGroupsController();
static void createParameterLayout (std::vector<std::unique_ptr<RangedAudioParameter>>& params);
void parameterChanged (const String& parameterID, float newValue) override;
- void changeListenerCallback (ChangeBroadcaster* source) override;
- void sendParameter (const String& paramID, int mixGroup, float value);
-
- void parseAllParams (const OSCMessage& message);
-
- static bool isValidOSCMessage (const OSCMessage& message);
+ void mixGroupParamChanged (const String& paramID, int mixGroup, float value, String otherUuid) override;
private:
void loadParameterList (Array<AudioProcessorParameter*>& params);
@@ -34,10 +28,11 @@ private:
AudioProcessorValueTreeState& vts;
std::atomic<float>* mixGroupParam = nullptr;
Array<String> paramList;
- OSCSender sender;
Uuid uuid;
- SharedResourcePointer<MixGroupsParamReceiver> paramReceiver;
+ String lastParameterChanged = "";
+
+ SharedResourcePointer<MixGroupsSharedData> sharedData;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixGroupsController)
};
diff --git a/Plugin/Source/MixGroups/MixGroupsParamReceiver.cpp b/Plugin/Source/MixGroups/MixGroupsParamReceiver.cpp
@@ -1,38 +1,64 @@
#include "MixGroupsParamReceiver.h"
#include "MixGroupsController.h"
-MixGroupsParamReceiver::MixGroupsParamReceiver()
+MixGroupsSharedData::MixGroupsSharedData()
{
- // connect datagram socket
- socket.bindToPort (MixGroupsConstants::portNum);
- socket.setEnablePortReuse (true);
- connectToSocket (socket);
+ for (int i = 0; i < MixGroupsConstants::numMixGroups; ++i)
+ pluginsInGroup.add (std::make_unique<StringArray>());
}
-void MixGroupsParamReceiver::loadReceiverListeners (Array<String>& paramList)
+void MixGroupsSharedData::loadParameterList (Array<String>& paramList)
{
- if (listenersInitialized) // listeners are already set up!
+ if (! paramMaps.empty()) // already loaded
return;
- for (auto& paramID : paramList)
- addListener (this, MixGroupsConstants::oscAddressPrefix + paramID);
+ for (int i = 0; i < MixGroupsConstants::numMixGroups; ++i)
+ {
+ auto paramMap = std::make_unique<ParamMap>();
- addListener (this, MixGroupsConstants::oscAddressPrefix + "all");
+ for (const auto& paramID : paramList)
+ paramMap->set (paramID, 0.0f);
- listenersInitialized = true;
+ paramMaps.push_back(std::move (paramMap));
+ }
}
-void MixGroupsParamReceiver::oscMessageReceived (const OSCMessage& message)
+void MixGroupsSharedData::pluginGroupChanged (const String& pluginID, int mixGroup)
{
- // clear existing OSC message
- oscMessage.clear();
+ // remove plugin from any group it's currently in
+ for (auto& group : pluginsInGroup)
+ group->removeString (pluginID);
- if (message.isEmpty()) // no message!
+ if (mixGroup == 0)
return;
- if (! MixGroupsController::isValidOSCMessage (message))
- return;
+ // add plugin to new group
+ pluginsInGroup[mixGroup - 1]->addIfNotAlreadyThere (pluginID);
+}
+
+int MixGroupsSharedData::getNumPluginsInGroup (int mixGroup) const
+{
+ return pluginsInGroup[mixGroup - 1]->size();
+}
+
+void MixGroupsSharedData::copyPluginState (int mixGroup, AudioProcessorValueTreeState& vts)
+{
+ auto paramMap = paramMaps[mixGroup - 1].get();
+ auto mapIter = paramMap->begin();
+ while (mapIter.next())
+ {
+ const auto param = vts.getRawParameterValue (mapIter.getKey());
+ paramMap->set (mapIter.getKey(), param->load());
+ }
+}
+
+void MixGroupsSharedData::setParameter (const String& paramID, int mixGroup, float value, String uuid)
+{
+ paramMaps[mixGroup - 1]->set (paramID, value);
+ listeners.call (&Listener::mixGroupParamChanged, paramID, mixGroup, value, uuid);
+}
- oscMessage = message;
- sendChangeMessage();
+float MixGroupsSharedData::getParameter (const String& paramID, int mixGroup) const
+{
+ return paramMaps[mixGroup - 1]->operator[] (paramID);
}
diff --git a/Plugin/Source/MixGroups/MixGroupsParamReceiver.h b/Plugin/Source/MixGroups/MixGroupsParamReceiver.h
@@ -3,27 +3,44 @@
#include <JuceHeader.h>
-/**
- * Class to receive parameter info from other instances of the plugin.
- * Create instances of this class with a SharedResourcePointer, otherwise
- * the messages won't sync correctly between instances of the plugin.
- */
-class MixGroupsParamReceiver : public ChangeBroadcaster,
- private OSCReceiver,
- private OSCReceiver::ListenerWithOSCAddress<OSCReceiver::MessageLoopCallback>
+class MixGroupsSharedData
{
public:
- MixGroupsParamReceiver();
+ MixGroupsSharedData();
- void oscMessageReceived (const OSCMessage& message) override;
- void loadReceiverListeners (Array<String>& paramList);
- const OSCMessage& getOSCMessage() { return oscMessage; }
+ /** Create parameter maps from list of parameters */
+ void loadParameterList (Array<String>& paramList);
+
+ /** A plugin has changed mix group. */
+ void pluginGroupChanged (const String& pluginID, int mixGroup);
+
+ /** Get the number of plugins already in this mix group */
+ int getNumPluginsInGroup (int mixGroup) const;
+
+ /** Copy the plugin state into the parameter map for a given mix group */
+ void copyPluginState (int mixGroup, AudioProcessorValueTreeState& vts);
+
+ void setParameter (const String& paramID, int mixGroup, float value, String uuid);
+ float getParameter (const String& paramID, int mixGroup) const;
+
+ struct Listener
+ {
+ ~Listener() {}
+ virtual void mixGroupParamChanged (const String& /*paramID*/, int /*mixGroup*/, float /*value*/, String /*uuid*/) {}
+ };
+
+ void addListener (Listener* l) { listeners.add (l); }
+ void removeListener (Listener* l) { listeners.remove (l); }
private:
- DatagramSocket socket;
- OSCMessage oscMessage { "/noaddress" };
- bool listenersInitialized = false;
+ using ParamMap = HashMap<String, float>;
+ std::vector<std::unique_ptr<ParamMap>> paramMaps;
+
+ OwnedArray<StringArray> pluginsInGroup; // list of plugin IDs of all the plugins in each mix group
+
+ ListenerList<Listener> listeners;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MixGroupsSharedData)
};
#endif // MIXGROUPSPARAMRECEIVER
-