RS-MET icon indicating copy to clipboard operation
RS-MET copied to clipboard

Need to translate old patch to new patch

Open elanhickler opened this issue 7 years ago • 30 comments

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?

elanhickler avatar Feb 16 '18 17:02 elanhickler

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

RobinSchmidt avatar Feb 17 '18 12:02 RobinSchmidt

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.

elanhickler avatar Feb 17 '18 15:02 elanhickler

ok, then just keep it. i would still try to do it that way

RobinSchmidt avatar Feb 17 '18 15:02 RobinSchmidt

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

RobinSchmidt avatar Feb 17 '18 17:02 RobinSchmidt

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

RobinSchmidt avatar Feb 17 '18 17:02 RobinSchmidt

solved.

elanhickler avatar Apr 02 '18 04:04 elanhickler

wait no, this is not solved!

What about modulation connections?

elanhickler avatar Apr 02 '18 08:04 elanhickler

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

RobinSchmidt avatar Apr 04 '18 16:04 RobinSchmidt

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

RobinSchmidt avatar Apr 04 '18 16:04 RobinSchmidt

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>

elanhickler avatar Apr 27 '18 22:04 elanhickler

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

RobinSchmidt avatar Apr 28 '18 17:04 RobinSchmidt

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)

RobinSchmidt avatar Apr 28 '18 17:04 RobinSchmidt

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

RobinSchmidt avatar Apr 29 '18 02:04 RobinSchmidt

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

elanhickler avatar Apr 29 '18 19:04 elanhickler

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

image

elanhickler avatar Apr 29 '18 21:04 elanhickler

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

RobinSchmidt avatar Apr 30 '18 11:04 RobinSchmidt

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

elanhickler avatar Apr 30 '18 19:04 elanhickler

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

RobinSchmidt avatar May 01 '18 00:05 RobinSchmidt

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

elanhickler avatar May 01 '18 00:05 elanhickler

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

elanhickler avatar May 01 '18 00:05 elanhickler

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)

RobinSchmidt avatar May 01 '18 11:05 RobinSchmidt

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.

elanhickler avatar May 01 '18 17:05 elanhickler

hmm...ok...i see

RobinSchmidt avatar May 01 '18 18:05 RobinSchmidt

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

elanhickler avatar May 01 '18 18:05 elanhickler

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

RobinSchmidt avatar May 01 '18 18:05 RobinSchmidt

image

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

elanhickler avatar May 02 '18 03:05 elanhickler

I get this during debugging

image

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>

elanhickler avatar May 02 '18 03:05 elanhickler

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

RobinSchmidt avatar May 02 '18 17:05 RobinSchmidt

How do I make use of the convertXmlStateIfNecessary instead of using this ugly code inside setStateFromXml? Not sure what to return for convertXmlStateIfNecessary.

image

//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();		
}

elanhickler avatar Jun 06 '18 17:06 elanhickler

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.

RobinSchmidt avatar Jun 07 '18 15:06 RobinSchmidt