mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Added support saving/restoring plugin bus layouts in the audio host demo
This commit is contained in:
parent
101a59ad2a
commit
83a4f74b1f
3 changed files with 140 additions and 0 deletions
|
|
@ -288,6 +288,67 @@ void FilterGraph::setLastDocumentOpened (const File& file)
|
|||
}
|
||||
|
||||
//==============================================================================
|
||||
static void readBusLayoutFromXml (AudioProcessor::BusesLayout& busesLayout, AudioProcessor* plugin, const XmlElement& xml, const bool isInput)
|
||||
{
|
||||
Array<AudioChannelSet>& targetBuses = (isInput ? busesLayout.inputBuses : busesLayout.outputBuses);
|
||||
int maxNumBuses = 0;
|
||||
|
||||
if (auto* buses = xml.getChildByName (isInput ? "INPUTS" : "OUTPUTS"))
|
||||
{
|
||||
forEachXmlChildElementWithTagName (*buses, e, "BUS")
|
||||
{
|
||||
const int busIdx = e->getIntAttribute ("index");
|
||||
maxNumBuses = jmax (maxNumBuses, busIdx + 1);
|
||||
|
||||
// the number of buses on busesLayout may not be in sync with the plugin after adding buses
|
||||
// because adding an input bus could also add an output bus
|
||||
for (int actualIdx = plugin->getBusCount (isInput) - 1; actualIdx < busIdx; ++actualIdx)
|
||||
if (! plugin->addBus (isInput)) return;
|
||||
|
||||
for (int actualIdx = targetBuses.size() - 1; actualIdx < busIdx; ++actualIdx)
|
||||
targetBuses.add (plugin->getChannelLayoutOfBus (isInput, busIdx));
|
||||
|
||||
const String& layout = e->getStringAttribute("layout");
|
||||
|
||||
if (layout.isNotEmpty())
|
||||
targetBuses.getReference (busIdx) = AudioChannelSet::fromAbbreviatedString (layout);
|
||||
}
|
||||
}
|
||||
|
||||
// if the plugin has more buses than specified in the xml, then try to remove them!
|
||||
while (maxNumBuses < targetBuses.size())
|
||||
{
|
||||
if (! plugin->removeBus (isInput))
|
||||
return;
|
||||
|
||||
targetBuses.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static XmlElement* createBusLayoutXml (const AudioProcessor::BusesLayout& layout, const bool isInput)
|
||||
{
|
||||
const Array<AudioChannelSet>& buses = (isInput ? layout.inputBuses : layout.outputBuses);
|
||||
|
||||
XmlElement* xml = new XmlElement (isInput ? "INPUTS" : "OUTPUTS");
|
||||
|
||||
const int n = buses.size();
|
||||
for (int busIdx = 0; busIdx < n; ++busIdx)
|
||||
{
|
||||
XmlElement* bus = new XmlElement ("BUS");
|
||||
bus->setAttribute ("index", busIdx);
|
||||
|
||||
const AudioChannelSet& set = buses.getReference (busIdx);
|
||||
const String layoutName = set.isDisabled() ? "disabled" : set.getSpeakerArrangementAsString();
|
||||
|
||||
bus->setAttribute ("layout", layoutName);
|
||||
|
||||
xml->addChildElement (bus);
|
||||
}
|
||||
|
||||
return xml;
|
||||
}
|
||||
|
||||
static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcept
|
||||
{
|
||||
AudioPluginInstance* plugin = dynamic_cast<AudioPluginInstance*> (node->getProcessor());
|
||||
|
|
@ -327,6 +388,14 @@ static XmlElement* createNodeXml (AudioProcessorGraph::Node* const node) noexcep
|
|||
state->addTextElement (m.toBase64Encoding());
|
||||
e->addChildElement (state);
|
||||
|
||||
XmlElement* layouts = new XmlElement ("LAYOUT");
|
||||
const AudioProcessor::BusesLayout layout = plugin->getBusesLayout();
|
||||
|
||||
for (bool isInput : { true, false })
|
||||
layouts->addChildElement (createBusLayoutXml (layout, isInput));
|
||||
|
||||
e->addChildElement (layouts);
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
|
@ -347,6 +416,16 @@ void FilterGraph::createNodeFromXml (const XmlElement& xml)
|
|||
if (instance == nullptr)
|
||||
return;
|
||||
|
||||
if (const XmlElement* const layoutEntity = xml.getChildByName ("LAYOUT"))
|
||||
{
|
||||
AudioProcessor::BusesLayout layout = instance->getBusesLayout();
|
||||
|
||||
for (bool isInput : { true, false })
|
||||
readBusLayoutFromXml (layout, instance, *layoutEntity, isInput);
|
||||
|
||||
instance->setBusesLayout (layout);
|
||||
}
|
||||
|
||||
AudioProcessorGraph::Node::Ptr node (graph.addNode (instance, (uint32) xml.getIntAttribute ("uid")));
|
||||
|
||||
if (const XmlElement* const state = xml.getChildByName ("STATE"))
|
||||
|
|
|
|||
|
|
@ -114,6 +114,43 @@ String AudioChannelSet::getAbbreviatedChannelTypeName (AudioChannelSet::ChannelT
|
|||
return "";
|
||||
}
|
||||
|
||||
AudioChannelSet::ChannelType AudioChannelSet::getChannelTypeFromAbbreviation (const String& abbr)
|
||||
{
|
||||
if (abbr.length() > 0 && (abbr[0] >= '0' && abbr[0] <= '9'))
|
||||
return static_cast<AudioChannelSet::ChannelType> (static_cast<int> (discreteChannel0)
|
||||
+ abbr.getIntValue() + 1);
|
||||
|
||||
if (abbr == "L") return left;
|
||||
else if (abbr == "R") return right;
|
||||
else if (abbr == "C") return centre;
|
||||
else if (abbr == "Lfe") return LFE;
|
||||
else if (abbr == "Ls") return leftSurround;
|
||||
else if (abbr == "Rs") return rightSurround;
|
||||
else if (abbr == "Lc") return leftCentre;
|
||||
else if (abbr == "Rc") return rightCentre;
|
||||
else if (abbr == "Cs") return centreSurround;
|
||||
else if (abbr == "Lrs") return leftSurroundRear;
|
||||
else if (abbr == "Rrs") return rightSurroundRear;
|
||||
else if (abbr == "Tm") return topMiddle;
|
||||
else if (abbr == "Tfl") return topFrontLeft;
|
||||
else if (abbr == "Tfc") return topFrontCentre;
|
||||
else if (abbr == "Tfr") return topFrontRight;
|
||||
else if (abbr == "Trl") return topRearLeft;
|
||||
else if (abbr == "Trc") return topRearCentre;
|
||||
else if (abbr == "Trr") return topRearRight;
|
||||
else if (abbr == "Wl") return wideLeft;
|
||||
else if (abbr == "Wr") return wideRight;
|
||||
else if (abbr == "Lfe2") return LFE2;
|
||||
else if (abbr == "Lss") return leftSurroundSide;
|
||||
else if (abbr == "Rss") return rightSurroundSide;
|
||||
else if (abbr == "W") return ambisonicW;
|
||||
else if (abbr == "X") return ambisonicX;
|
||||
else if (abbr == "Y") return ambisonicY;
|
||||
else if (abbr == "Z") return ambisonicZ;
|
||||
|
||||
return unknown;
|
||||
}
|
||||
|
||||
String AudioChannelSet::getSpeakerArrangementAsString() const
|
||||
{
|
||||
StringArray speakerTypes;
|
||||
|
|
@ -130,6 +167,22 @@ String AudioChannelSet::getSpeakerArrangementAsString() const
|
|||
return speakerTypes.joinIntoString (" ");
|
||||
}
|
||||
|
||||
AudioChannelSet AudioChannelSet::fromAbbreviatedString (const String& str)
|
||||
{
|
||||
StringArray abbr = StringArray::fromTokens(str, true);
|
||||
AudioChannelSet set;
|
||||
|
||||
for (int i = 0; i < abbr.size(); ++i)
|
||||
{
|
||||
AudioChannelSet::ChannelType type = getChannelTypeFromAbbreviation (abbr[i]);
|
||||
|
||||
if (type != unknown)
|
||||
set.addChannel (type);
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
String AudioChannelSet::getDescription() const
|
||||
{
|
||||
if (isDiscreteLayout()) return String ("Discrete #") + String (size());
|
||||
|
|
|
|||
|
|
@ -308,6 +308,9 @@ public:
|
|||
/** Returns the abbreviated name of a channel type. For example, this method may return "Ls". */
|
||||
static String getAbbreviatedChannelTypeName (ChannelType);
|
||||
|
||||
/** Returns the channel type from an abbreviated name. */
|
||||
static ChannelType getChannelTypeFromAbbreviation (const String& abbreviation);
|
||||
|
||||
//==============================================================================
|
||||
enum
|
||||
{
|
||||
|
|
@ -342,6 +345,11 @@ public:
|
|||
the returned string will be empty.*/
|
||||
String getSpeakerArrangementAsString() const;
|
||||
|
||||
/** Returns an AudioChannelSet from a string returned by getSpeakerArrangementAsString
|
||||
|
||||
@see getSpeakerArrangementAsString */
|
||||
static AudioChannelSet fromAbbreviatedString (const String& set);
|
||||
|
||||
/** Returns the description of the current layout. For example, this method may return
|
||||
"Quadraphonic". Note that the returned string may not be unique. */
|
||||
String getDescription() const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue