Need to translate old patch to new patch
There's a few parameters that I need to retrieve from old patches to apply those parameters correctly to the synth. I think I just need to get the lowpass/highpass filter settings from the old patch and apply it to the lowpass/highpass filters.
How do I do it?
hmmm...so you want to write some temporary throw-away code to adapt the old patches? i guess i would check in setStateFromXml for the tag-name of the xml, and if it has the name of the old module, pass it to some temporary function (retrieveOldParameters or something) and there extract the xml-attributes and set the parameters accordingly. ...and then just load the old patch with the new module.
disclaimer: i never did this
not throw away
Edit: Also, it should not depend on module name, it needs to retrieve the filter setting... the tag for the filter setting will be specific of course, but the module name will be anything.
ok, then just keep it. i would still try to do it that way
it should not depend on module name, it needs to retrieve the filter setting
you could just check the passed xml for an attribute of the name, under which the parameter was stored and set your new parameter, something like
highpassParam->setValue(xml->getDoubleAttriubute("HighpassFreq", 20.0) );
assuming that your new highpass parameter has a different parameter name than the attribute-name under which the setting is stored in the xml. if the new parameter name matches the attribute-name stored in the xml, it should actually just work without doing anything special
generally, in state recall, i just loop through all parameters of the module and check the xml for an attribute with the same name as the parameter name. if i find such an attribute, i retrieve the value and set the parameter
and then i also retrieve child xml-elements with names matching the module-names of the child modules and invoke their state-recall with the child-xml ....which works recursively...but that's probably not relevant here
solved.
wait no, this is not solved!
What about modulation connections?
modulations are stored gloabally in their own xml element with source and target where the target is given as a sort of path (in the format module.submodule.subsubmodule.parameter) like this:
<Modulations>
<Connection Source="Slot2-BreakpointModulator" Target="Slot1-EllipseOscillator.LowHigh"
Depth="0.86138392857142864756" DepthMin="-1" DepthMax="1" Mode="Absolute"/>
<Connection Source="Slot2-BreakpointModulator" Target="Slot1-EllipseOscillator.Amplitude"
Depth="1" DepthMin="-1" DepthMax="1" Mode="Absolute"/>
<Connection Source="Slot3-BreakpointModulator" Target="Slot1-EllipseOscillator.Phase"
Depth="180" DepthMin="-180" DepthMax="180" Mode="Absolute"/>
</Modulations>
so, sources and targets are identified by their name (or path, in case of targets). the example is apparently from ToolChain where i additionally have stored the slot to make the identifiers unique even in case of having mutliple sources/targets of the same kind in the slots
so, if the names of the parameters and the module.submodule.etc... structure with all the module names matches, it should work - otherwise, you need to update these identifiers in the "Modulations" xml-element
What about this problem? The difference is the .Filter. My new plugin looks for .Filter, my old plugin has that omitted.
<Modulations>
<Connection Source="Amp Env" Target="MushroomGenerator.Lowpass" Depth="19"
DepthMin="-20" DepthMax="20" Mode="Exponential"/>
<RangeLimits>
<MushroomGenerator.Lowpass Min="0" Max="10000"/>
</RangeLimits>
</Modulations>
VS
<Modulations>
<Connection Source="Amp Env" Target="MushroomGenerator.Filter.Lowpass" Depth="19"
DepthMin="-20" DepthMax="20" Mode="Exponential"/>
<RangeLimits>
<MushroomGenerator.Filter.Lowpass Min="0" Max="10000"/>
</RangeLimits>
</Modulations>
hmmm...in this case, i would probably override AudioModule::convertXmlStateIfNecessary, retrieve the "Modulations" (child) xml there, loop through all the "Connection" (child) xmls, retrieve the "Target" attribute, and if it is "MushroomGenerator.Lowpass", use setAttribute("Target", "MushroomGenerator.Filter.Lowpass"), so you would overwrite the old attribute setting
or, if it's more convenient (because it also appears in the "Range" settings and who knows where else), you could convert the whole xml to a string via
https://docs.juce.com/master/classXmlElement.html#a4a55313aba630bc87deb927375f06cff
and do a text-replacement in the string via this:
https://docs.juce.com/master/classString.html#abbd337cd9c4e82ee357ae2e8bf48b16c
and then convert back to xml. .....yes, i guess that's the most convenient way to do it. should be just 3 lines of code (convert, replace, convert back)
or maybe a few more, if you need the same thing also for highpass, bandpass, etc.
yeah.. changing the preset format or (even worse) the module structure while retaining backward compatibility to old presets/state-xmls can be a pain in the ass. something we should strive to avoid. but i know - sometimes it's just inevitable. for these cases, we often must resort to very inelegant procedures
How do I write a new patch format number so I can check to see if it's the old version so I can bypass this function when it's the new patch version?
<MushroomGenerator PatchFormat="1" Lowpass="24000" Phase="0.125" Glide_Amount="1e-05"
HP_Feedback="30" LP_Feedback="10000" Feedback_Amount="1" Stem_Twist="0.1"
Cluster_Spread="0.5" Vel_Scale="0.5">
String xmlStr = xmlState.createDocument("");
xmlStr = xmlStr.replace("MushroomGenerator.Lowpass", "MushroomGenerator.Filter.Lowpass");
xmlStr = xmlStr.replace("MushroomGenerator.Highpass", "MushroomGenerator.Filter.Highpass");
const juce::XmlElement newXml = XmlElement(xmlStr);
is that right? it's not loading the modulation connections, the string seems to look right though
edit: if i manually edit the xml file before loading, it DOES work.... ugh... edit: the code above causes modulation not to load edit: ok it seems like loading the patch completely fails with the above code

How do I write a new patch format number
class AudioModule has a member patchFormatIndex. you can set this in the constructor of your subclass. when you then request a state-xml, the baseclass implementation will write patch format number into the xml
edit: if i manually edit the xml file before loading, it DOES work.... ugh... edit: the code above causes modulation not to load edit: ok it seems like loading the patch completely fails with the above code
hmm...your code looks actually good to me (although i was meaning to override convertXml... - but it should work in setState... too). can you add some debug code to write out the newXml into a temporary file and look at it? edit: you can use my convenience function saveXmlToFile
OMG, I used saveXmlToFile, it revealed the problem, notice the double xml header and the <<
<?xml version="1.0" encoding="UTF-8"?>
<<?xml version="1.0" encoding="UTF-8"?>
<MushroomGenerator PatchFormat="1" Reset_Mode="Never" Glide_Mode="Always" Octave="-1"
Frequency_Multiply="1" Glide_Amount="0.0170584" HP_Feedback="30"
LP_Feedback="10000" Feedback_Source="TriPhasor" Release="1.58082"
Linear_Time="1000" Grow="0.496198" Density="3" CapStem_Ratio="0">
<ADSR1 PatchFormat="1" Speed="1238.13" Vel_Influence="0.53125" Attack="0.711629"
Decay="1.72326" Release="1.47209" Linear_Time="5969.34" Rel_Shape="S-CRV"
Gain="0.955882"/>
<ADSR2 PatchFormat="1"/>
<ADSR3 PatchFormat="1"/>
<ADSR4 PatchFormat="1" Linear_Time="20000"/>
<ADSR5 PatchFormat="1"/>
<ADSR6 PatchFormat="1"/>
<LFO1 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO2 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO3 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO4 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO5 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO6 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO7 PatchFormat="1" Gain="1" Frequency="1"/>
<LFO8 PatchFormat="1" Gain="1" Frequency="1"/>
<MIDI1 PatchFormat="1"/>
<MIDI2 PatchFormat="1"/>
<MIDI3 PatchFormat="1"/>
<MIDI4 PatchFormat="1"/>
<MIDI5 PatchFormat="1"/>
<MIDI6 PatchFormat="1"/>
<MIDI7 PatchFormat="1"/>
<MIDI8 PatchFormat="1"/>
<MIDI9 PatchFormat="1"/>
<MIDI10 PatchFormat="1"/>
<MIDI11 PatchFormat="1"/>
<MIDI12 PatchFormat="1"/>
<Breakpoint_1 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="2"
LoopEndIndex="3" LoopMode="Forward">
<Breakpoint Time="0" Level="0" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="0.5" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="2" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="3" Level="0" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_1>
<Breakpoint_2 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="2"
LoopEndIndex="3" LoopMode="Forward">
<Breakpoint Time="0" Level="0" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="0.5" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="2" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="3" Level="0" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_2>
<Equalizer PatchFormat="2" Bypass="1"/>
<Limiter PatchFormat="1"/>
<Filter PatchFormat="1" Lowpass="24000"/>
<Delay PatchFormat="1" Bypass="1"/>
<Oscilloscope PatchFormat="1">
<ColorMap ff000000="0" ff00ff00="1"/>
</Oscilloscope>
<Modulations>
<Connection Source="Amp Env" Target="MushroomGenerator.Filter.Highpass" Depth="14"
DepthMin="-20" DepthMax="20" Mode="Exponential"/>
<Connection Source="Amp Env" Target="MushroomGenerator.Filter.Lowpass" Depth="14"
DepthMin="-20" DepthMax="20" Mode="Exponential"/>
<RangeLimits>
<MushroomGenerator.Filter.Highpass Min="0" Max="1.0000000000000000159e+100"/>
<MushroomGenerator.Filter.Lowpass Min="0" Max="1.0000000000000000159e+100"/>
</RangeLimits>
</Modulations>
</MushroomGenerator>
/>
whoa! wtf? i actually think, this:
String xmlStr = xmlState.createDocument("");
const juce::XmlElement newXml = XmlElement(xmlStr);
should be an identity operation, i.e. newXml should be equal to xmlState if you do nothing in between. can you check that? that's really weird
Oh while we are at it we MUST fix this stupidly annoying problem. The patch must save the patch name in the xml! (so that the plugin displays the patch name when reloading the DAW project).
void setStateFromXml(const juce::XmlElement& xmlState, const juce::String& stateName, bool markAsClean) override
{
String xmlStr = xmlState.createDocument("");
const juce::XmlElement newXml = XmlElement(xmlStr);
saveXmlToFile(newXml, File("D:/Desktop/newXML.xml"), false);
sendChangeMessage();
}
RESULTS:
<?xml version="1.0" encoding="UTF-8"?>
<<?xml version="1.0" encoding="UTF-8"?>
<MushroomGenerator PatchFormat="1" Frequency_Multiply="1" EditorWidth="837" EditorHeight="764">
<ADSR1 PatchFormat="1"/>
<ADSR2 PatchFormat="1"/>
<ADSR3 PatchFormat="1"/>
<ADSR4 PatchFormat="1"/>
<ADSR5 PatchFormat="1"/>
<ADSR6 PatchFormat="1"/>
<LFO1 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO2 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO3 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO4 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO5 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO6 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO7 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO8 PatchFormat="1" Gain="1" Frequency="4"/>
<MIDI1 PatchFormat="1"/>
<MIDI2 PatchFormat="1"/>
<MIDI3 PatchFormat="1"/>
<MIDI4 PatchFormat="1"/>
<MIDI5 PatchFormat="1"/>
<MIDI6 PatchFormat="1"/>
<MIDI7 PatchFormat="1"/>
<MIDI8 PatchFormat="1"/>
<MIDI9 PatchFormat="1"/>
<MIDI10 PatchFormat="1"/>
<MIDI11 PatchFormat="1"/>
<MIDI12 PatchFormat="1"/>
<Breakpoint_1 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="0"
LoopEndIndex="1" LoopMode="Off">
<Breakpoint Time="0" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="1" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_1>
<Breakpoint_2 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="0"
LoopEndIndex="1" LoopMode="Off">
<Breakpoint Time="0" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="1" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_2>
<Equalizer PatchFormat="2" Bypass="1"/>
<Limiter PatchFormat="1"/>
<Filter PatchFormat="1"/>
<Delay PatchFormat="1" Bypass="1"/>
<Oscilloscope PatchFormat="1">
<ColorMap ff000000="0" ff00ff00="1"/>
</Oscilloscope>
</MushroomGenerator>
/>
XmlElement::createDocument has an optional boolean parameter "includeXmlHeader" which deafults to true. maybe you could try to pass false to that?
The patch must save the patch name in the xml so that the plugin displays the patch name when reloading the DAW project
hmm - i actually think of the filename of the xml-file as the name of the patch. and when the user saves a daw project, there is no filename given, so it just says something generic like "recalled by host" or something. i think, it would be wrong to display the name of the patch file, where the settings originally came from because the user may have modified the original patch beyond recognition. the plugin may be saved with a completely different state than any of the preset files.
edit: maybe i could save the patch-name IFF it was not modified. i have some modification-detection in place already (although i'm not sure, if it always works right)
edit: maybe i could save the patch-name IFF it was not modified. i have some modification-detection in place already (although i'm not sure, if it always works right)
omg NOOOO you're wayyyy overcomplicating this.
If the patch was modified, just go with the MyNicePatch* (asterisk)
the patch name remaining is important to give the user knowledge where the settings originally came from if he wants to go back to the original settings, for example, and re-tweak. Otherwise the user is lost, all patches say the same unhelpful "recalled by host"
Like even having the word Bass, in the case of a patch being called "TheGoodBass" at least lets the user know it's a bass patch.
hmm...ok...i see
still has a problem, the extra < />
<?xml version="1.0" encoding="UTF-8"?>
<<MushroomGenerator PatchFormat="1" Frequency_Multiply="1" EditorWidth="837" EditorHeight="764">
<ADSR1 PatchFormat="1"/>
<ADSR2 PatchFormat="1"/>
<ADSR3 PatchFormat="1"/>
<ADSR4 PatchFormat="1"/>
<ADSR5 PatchFormat="1"/>
<ADSR6 PatchFormat="1"/>
<LFO1 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO2 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO3 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO4 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO5 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO6 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO7 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO8 PatchFormat="1" Gain="1" Frequency="4"/>
<MIDI1 PatchFormat="1"/>
<MIDI2 PatchFormat="1"/>
<MIDI3 PatchFormat="1"/>
<MIDI4 PatchFormat="1"/>
<MIDI5 PatchFormat="1"/>
<MIDI6 PatchFormat="1"/>
<MIDI7 PatchFormat="1"/>
<MIDI8 PatchFormat="1"/>
<MIDI9 PatchFormat="1"/>
<MIDI10 PatchFormat="1"/>
<MIDI11 PatchFormat="1"/>
<MIDI12 PatchFormat="1"/>
<Breakpoint_1 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="0"
LoopEndIndex="1" LoopMode="Off">
<Breakpoint Time="0" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="1" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_1>
<Breakpoint_2 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="0"
LoopEndIndex="1" LoopMode="Off">
<Breakpoint Time="0" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="1" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_2>
<Equalizer PatchFormat="2" Bypass="1"/>
<Limiter PatchFormat="1"/>
<Filter PatchFormat="1"/>
<Delay PatchFormat="1" Bypass="1"/>
<Oscilloscope PatchFormat="1">
<ColorMap ff000000="0" ff00ff00="1"/>
</Oscilloscope>
</MushroomGenerator>
/>
let's figure out, where exactly these extra < /> are introduced. can you write the original xml and the intermediate string also to a file? how to write string to a file, you can look up in my saveXmlToFile, there's this code
fileToSaveTo.create();
fileToSaveTo.appendText(myXmlDoc);
seems you need to use File::appendText

Edit: Oh I added false false on that line as well as I was trying different things.
I get this during debugging

what's wrong with this tag name? How is this not a proper tag name? What is a tag name supposed to look like?
<MushroomGenerator PatchFormat="1" Feedback_Amount="1" EditorWidth="837" EditorHeight="764">
<ADSR1 PatchFormat="1"/>
<ADSR2 PatchFormat="1"/>
<ADSR3 PatchFormat="1"/>
<ADSR4 PatchFormat="1"/>
<ADSR5 PatchFormat="1"/>
<ADSR6 PatchFormat="1"/>
<LFO1 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO2 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO3 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO4 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO5 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO6 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO7 PatchFormat="1" Gain="1" Frequency="4"/>
<LFO8 PatchFormat="1" Gain="1" Frequency="4"/>
<MIDI1 PatchFormat="1"/>
<MIDI2 PatchFormat="1"/>
<MIDI3 PatchFormat="1"/>
<MIDI4 PatchFormat="1"/>
<MIDI5 PatchFormat="1"/>
<MIDI6 PatchFormat="1"/>
<MIDI7 PatchFormat="1"/>
<MIDI8 PatchFormat="1"/>
<MIDI9 PatchFormat="1"/>
<MIDI10 PatchFormat="1"/>
<MIDI11 PatchFormat="1"/>
<MIDI12 PatchFormat="1"/>
<Breakpoint_1 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="2"
LoopEndIndex="3" LoopMode="Forward">
<Breakpoint Time="0" Level="0" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="0.5" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="2" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="3" Level="0" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_1>
<Breakpoint_2 PatchFormat="1" GridX="1" GridY="1" ScaleFactor="1" Offset="0"
TimeScale="1" TimeScaleByKey="0" TimeScaleByVel="0" Depth="1"
DepthByKey="0" DepthByVel="0" SyncMode="0" LoopStartIndex="2"
LoopEndIndex="3" LoopMode="Forward">
<Breakpoint Time="0" Level="0" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="0.5" Level="1" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="1" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="2" Level="0.5" Shape="Analog" ShapeAmount="1"/>
<Breakpoint Time="3" Level="0" Shape="Analog" ShapeAmount="1"/>
</Breakpoint_2>
<Equalizer PatchFormat="2" Bypass="1"/>
<Limiter PatchFormat="1"/>
<Filter PatchFormat="1"/>
<Delay PatchFormat="1" Bypass="1"/>
<Oscilloscope PatchFormat="1">
<ColorMap ff000000="0" ff00ff00="1"/>
</Oscilloscope>
</MushroomGenerator>
whoa! i figured it out. this time, juce is overcomplicating things. just passing the string to the xml constructor does NOT work. instead, you have to go through class XmlDocument. as a remedy, i wrote a convenience function:
XmlElement* stringToXml(const String& xmlStr);
to handle the back-conversion from string to xml. take care to delete the object returned by the function, when you are done with it
How do I make use of the convertXmlStateIfNecessary instead of using this ugly code inside setStateFromXml? Not sure what to return for convertXmlStateIfNecessary.

//XmlElement convertXmlStateIfNecessary(const XmlElement& xmlState) override
//{
//}
void setStateFromXml(const juce::XmlElement& xmlState, const juce::String& stateName, bool markAsClean) override
{
int PatchFormat = xmlState.getIntAttribute("PatchFormat", 1);
XmlElement * newXml;
double lpfValue;
double hpfValue;
if (PatchFormat == 1)
{
String xmlStr = xmlState.createDocument({}, false, false);
xmlStr = xmlStr.replace("NyquistGenerator.Lowpass", "MushroomGenerator.Filter.Lowpass");
xmlStr = xmlStr.replace("NyquistGenerator.Highpass", "MushroomGenerator.Filter.Highpass");
newXml = stringToXml(xmlStr);
BasicModule::setStateFromXml(*newXml, stateName, markAsClean);
lpfValue = newXml->getDoubleAttribute("Lowpass", jbNyquistShannonCore.bandpassModule->parLPF.def);
hpfValue = newXml->getDoubleAttribute("Highpass", jbNyquistShannonCore.bandpassModule->parHPF.def);
delete newXml;
}
else
{
BasicModule::setStateFromXml(xmlState, stateName, markAsClean);
lpfValue = xmlState.getDoubleAttribute("Lowpass", jbNyquistShannonCore.bandpassModule->parLPF.def);
hpfValue = xmlState.getDoubleAttribute("Highpass", jbNyquistShannonCore.bandpassModule->parHPF.def);
}
jbNyquistShannonCore.bandpassModule->parLPF.setValue(lpfValue);
jbNyquistShannonCore.bandpassModule->parHPF.setValue(hpfValue);
sendChangeMessage();
}
convertXmlStateIfNecessary is supposed to take in the original XmlElement and return the converted version. it is called somewhere in the baseclass implementaion of setStateFromXml before using the xml. but i'm actually not so sure if it's actually that much cleaner to override that "hook" function instead of just overriding setStateFromXml.