Clarifying saving presets/program states which aren't params


#1

Hey Jules,
Made the transition this week from using JUCE to write a standalone app to an audio plugin (VST/AU), and quickly got up and running learning from your example code. Implementing parameter changes/saving presets seems to work great (following how you do it for the gain/delay knobs), however, we are a bit confused on saving presets/programs which aren’t necessarily parameters like a gain knob or delay knob.

As an example, in our plugin, we have 16 values stored in TextBoxes (derived in a custom class called NumberBox) which control things like gain. Each NumberBox class also has an array to store preset values for the TextBox. In our main gui, we then have a button which allows a user to store the state of the number boxes in each of their respective arrays, and a combobox, which allows the user to select the saved presets. We are trying to save the state of the entire program (the combobox and the arrays of presets in each NumberBox class), but can’t seem to get this to work properly, in the same way regular params do. How do we store an array of floats as a param?

Here is what we currently have implemented, thanks!

void CarwashAudioProcessor::getStateInformation (MemoryBlock& destData)
{
	// You should use this method to store your parameters in the memory block.
    // You could do that either as raw data, or use the XML or ValueTree classes
    // as intermediaries to make it easy to save and load complex data.
	
	CarwashAudioProcessorEditor* ourEditor = (CarwashAudioProcessorEditor*)getActiveEditor();
	
	// Create an outer XML element..
    XmlElement xml ("AUDIOCARWASHPRESETS");
    xml.setAttribute ("gain", gain);
	//add our attributes
	for(int i = 0; i < ourEditor->volumeCoefficients.size(); i++){ //for each gain numberbox we have
		for (int j= 0; j < ourEditor->presetSelector->getNumItems(); j++){ //for each saved preset value
			xml.setAttribute("volumeCoefficient"+(String)i, ourEditor->volumeCoefficients[i]->values[j]); //store the gain value
			xml.setAttribute("probabilityMatrix"+(String)i, ourEditor->probabilityMatrix[i]->values[j]); //store the probability value
			xml.setAttribute("presetMenu"+(String)(j+1), j+1); //store the preset number/name used in the ComboBox selector
		}
	}
	
	
	
    // then use this helper function to stuff it into the binary blob and return it..
    copyXmlToBinary (xml, destData);
}

void CarwashAudioProcessor::setStateInformation (const void* data, int sizeInBytes)
{
    // You should use this method to restore your parameters from this memory block,
    // whose contents will have been created by the getStateInformation() call.
	
	// This getXmlFromBinary() helper function retrieves our XML from the binary blob..
    CarwashAudioProcessorEditor* ourEditor = (CarwashAudioProcessorEditor*)getActiveEditor();
	ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
	
    if (xmlState != 0)
    {
        // make sure that it's actually our type of XML object..
        if (xmlState->hasTagName ("AUDIOCARWASHPRESETS"))
        {
			gain  = (float) xmlState->getDoubleAttribute ("gain", gain);
			
			for(int i = 0; i < ourEditor->volumeCoefficients.size(); i++){ //for each gain numberbox we have
				for (int j= 0; j < ourEditor->presetSelector->getNumItems(); j++){ //for each saved preset value
					ourEditor->volumeCoefficients[i]->setValue((float)(xmlState->getDoubleAttribute ("volumeCoefficient"+(String)j), 1.0)); //set the gain value
					ourEditor->probabilityMatrix[i]->setValue((float)(xmlState->getDoubleAttribute ("probabilityMatrix"+(String)j), 100)); //store the probability value
					ourEditor->presetSelector->addItem((String)(j+1), (j+1) ); //store the preset number/name used in the ComboBox selector
				}  
			}
        }
    }
}

#2

I don’t have time to trace through all of that myself, but surely a bit of quality time with your debugger will easily show you what’s going wrong?


#3

I haven’t looked in very much detail what’s going on but a quick glance at the save/recall loops looks like you’re mixing up i and j?

I’ve been bitten by those pesky critters i and j so many times that I now only ever use i for single depth loops. Anything nested I use longer variable names, the extra time spent typing is easily recouped in less debugging.


#4

Thanks a lot for the feedback guys. I think my I was a little confusing in my question details, but either way, I cleaned up some classes, and implemented my NumberBox class handling the same way as Params in the Juce demo are handled, and it all seems to be working pretty well.

Changing programs in Ableton Live and Logic works great (in Live I tested both dragging the aupreset file on a track from the Live finder/file chooser, as well as in their “hotswap” mode, and in Logic, using the built in preset selector), but I am having a hard time trying to do a similar thing from within my plugin itself.

Basically, I want to create a ComboBox in my gui which populates itself with the available programs (just like Live or Logic), and allows the program to be changed from within the plugin itself (ideally via a midi-learned controller). I can’t seem to get this to work, and I’ve been digging the forums all day. Any advice? I thought I might have to do with setCurrentProgram(…) (and related functions), etc, but I can’t seem to make any of those work properly. I also tried manually loading an .aupreset file into a File object, then loading that into a MemoryBlock and manually passing that into setStateInformation(…) but couldn’t get that working either… hmmm…

Thanks again for your help!


#5

It seems that you want to use the plugin editor in get/setStateInformation. However there’s a big chance that the editor will not exist at that point. Could that be the root of your issues?


#6

hmm, I don’t think so, because I was trying to trigger getProcessor()->setStateInformation via a the press of a button, so there is definitely an editor open. Would it make sense that I can load a specific .aupreset file that was saved by my host (Live) using getStateInformation()? I tried to manually load one of my presets into a file, and then call getStateInformation(), passing in the data* from the File object, but couldn’t get it working. Probably has something to do with the way I am loading the File object (one of the many ways I tried below)

void CarwashAudioProcessorEditor::buttonClicked (Button* button){ if (button = loadNewSettingsTest){ MemoryBlock loadPreset; //create memory block to hold preset File preset("/Library/Audio/Presets/FlipMu/AudioCarwash/QuarterPulse.aupreset"); //load preset as a File preset.loadFileAsData(loadPreset); //move presets data into our memory block getProcessor()->setStateInformation(loadPreset.getData(), loadPreset.getSize()); //call setStateInformation passing in our memory block } }

Is this approach alright? In this way, I can scan my presets directory and autofill the Combobox with the names of the preset files, and then load them accordingly (assuming I can actually get this approach to work). Is there a better practice? How are presets usually saved in vst/au’s?