How to fix plugin format specific bugs and save / recall plugin states


#1

So, as a few of my past posts have indicated, I’m working on my first plugin. I’ve done all I could to do as much on my own and only post questions here when I’m just absolutely stuck. I’m basically stuck on two things before I can release my plugin (which is just a track notes plugin - because I hate the comments sections in most DAWs with a passion).

  1. Lets say I’m experiencing a bug in a specific host / plugin format, but not in another, how would I go about debugging and then implementing code that only affects that version? It seems that we are working with one copy of the code and then JUCE does the rest of the work to make that single copy of code work in each DAW, so I’m not sure how I can code bug fixes for each version / DAW / OS?

  2. The more important issue, can anyone share some pointers tips or even some instructions for what all needs to be done to save a plugin state and recall it? My plugin (for now) just uses 4 text editors, so I assume I’ll have to store those text editors in JUCE Strings and save those strings, but I have no clue how to do this.

Once again, any help is greatly appreciated.


#2

I’m pretty new as well writing up my own first plugin, but if I understand (2), you can save many types of data relating to your plugin state in an xml element in your audio processor like below. In the case below, it’s saving out parameters stored in an AudioProcessorValueTreeState.

void AudioProcessor::getStateInformation (MemoryBlock& destData)
    {
    	 ScopedPointer<XmlElement> xml (parameters.state.createXml());
            copyXmlToBinary (*xml, destData);
    }

    void AudioProcessor::setStateInformation (const void* data, int sizeInBytes)
    {
    	ScopedPointer<XmlElement> xmlState (getXmlFromBinary (data, sizeInBytes));
            
            if (xmlState != nullptr)
                if (xmlState->hasTagName (parameters.state.getType()))
    			{
                    parameters.state = ValueTree::fromXml (*xmlState);
    			}
    }

In your case, in your getStateInformation, which saves your state, I believe you can grab your strings like this:
String contents = texteditor.getText();

then save the data in a memory block
MemoryBlock mb(contents.toRawUTF8( ), contents.getNumBytesAsUTF8( ) + 1);

then convert to base64 and load into your xml element as an attribute
xml->addAttribute (T("text editor contents"), mb.toBase64Encoding());

Then to retrieve it when your state loads, i think you can do something like this in your setStateInformation function:

MemoryBlock m;
texteditor.setText(String(m.fromBase64Encoding (xmlState->getStringAttribute(T("text editor contents"))));

I haven’t tried this myself but I think it should work? There may be easier/better ways, that’s just what came to mind.


#3

You can create a PluginHostType, https://www.juce.com/doc/classPluginHostType, to find out which DAW your are in.


#4

Thank you for this, it is helping me quite a bit. I think what’s really throwing me off is how to access parameters from my editor class inside the processor class.


#5

You don’t. If there is something displayed in the editor, your processor needs to know to function, it needs to be in the processor. The editor can access it anytime and display it. But most of the lifetime of your plugin, there is no editor.

The most convenient (IMHO) is to have a ValueTree or better AudioProcessorValueTree and read the values, if you need to display it. If you also connect it via the ValueTree::Listener to your gui components, everything is updated automatically.
Saving the state is also only a few lines of code, independently from how many state values you have in your state.
If you want to connect to ValueTree nodes, I wrote a few handy classes: https://github.com/ffAudio/ffGuiAttachments

And for AudioProcessorParameters (the ones to be visible to the host) you can use AudioProcessorValueTreeState::SliderAttachment etc.
For these there is a tutorial

HTH


#6

I didn’t even realize that the values in the editor needed to be accessible in the processor, but now I don’t know why I thought that because the save and set state functions are in the processor. After analyzing the JUCE plugin demo, I noticed that the GUI component items in the editor have corresponding pointers in the processor (gainParam, etc.). So that the pointers in the processor header always point to the value the component gives… this makes sense to me (unless I’m not correct). However, I’m still not sure how to do that with text editors. I currently have all the code to place my processor header String pointers into XML and retrieve them to fill those pointers, but I have no ideas on how to get those values to the editor and their respective text editor boxes. I understand how it’s working with ints and floats for sliders (using a pointer to point to the value given by the component), but don’t know how to point to the underlying structure of the Text box so text is constantly being pulled from the text box and placed into the string pointer in the processor header.


#7

I can access the String pointers of the processor class through the constructor of the editor class, since it takes a processor object, but I can’t access those string pointers anywhere else in the editor.


#8

use getAudioProcessor() or as it says in the docs, use the AudioProcessorEditor::processor member.


#9

Well, I actually did previously use the getAudioProcessor() method in my GUI editor for another task, to grab information for the playhead, so that it would be accessible in my editor class. And since you suggested it, I tried to use it to gain access to my getter / setter functions that set and get to the JUCE Strings in the processor class, but I cannot seem to access them through an AudioProcessor pointer that I set equal to getAudioProcessor().

I feel really silly not figuring this out, but I’ve been over the JUCE plugin demo, checked the documentation, and whatnot. I’m just not seeing a way to get to the data members I created in that processor class.


#10

The getAudioProcessor() method returns a pointer to an AudioProcessor base class, rather than your own class.

I think it would be a worthwhile investment reading a good book about C++; this is pretty fundamental stuff.

That being said… you can dynamic_cast the pointer to your own AudioProcessor class, or you can make the constructor of your AudioProcessorEditor class take a reference to your AudioProcessor class, and store that directly.