This project has moved. For the latest updates, please go here.

Saving internal plugin state by chunks

Topics: Getting Started, Plugin Persistence
May 12, 2012 at 12:38 PM

I have read about using chunks to save internal state of the plugin and that's what i must implement now.

I have read the documentation about Plugin Persistence. As far as I understand I must implement IVstPluginPersistance interface so the Host could invoke its methods (CanLoadChunk ReadPrograms WritePrograms)

I have tried to implement it like this:

  • in my Plugin.cs file i have 
internal sealed class Plugin : VstPluginWithInterfaceManagerBase, IVstPluginPersistence
  • i have used System.Windows for MessageBox.Show() method and tried something like this:

        public bool CanLoadChunk(VstPatchChunkInfo chunkInfo)
        {
            MessageBox.Show(chunkInfo.ToString());
            throw new System.NotImplementedException();
        }

        public void ReadPrograms(System.IO.Stream stream, VstProgramCollection programs)
        {
            MessageBox.Show("Reading programs");
            throw new System.NotImplementedException();
        }

        public void WritePrograms(System.IO.Stream stream, VstProgramCollection programs)
        {
            MessageBox.Show("Attempting to save state");
            throw new System.NotImplementedException();
        }

But none of the messageboxes didn't show up. What am I doing wrong? I don't have programs in my VST because all i have to remember is position of vocoder frequency bands. It could be 4 or 40. I tried to implement this also in PluginCommandStub.cs file but it looks like these methods arent invoked. I saw here some people had experience with chunks but I didn't found answer to my problem in those discussions.

Please help me.

May 12, 2012 at 2:45 PM
Edited May 13, 2012 at 10:00 AM

I just figured that out!

I looked at the documentation more deeply and i saw that there is a PluginPersistence object that have a constructor.

I took a close look to my project which was build from template, and there still was unchanged things in Plugin.cs file like those:

 

        /// Implement this when you do audio processing.
        /// 
        /// A previous instance returned by this method. 
        /// When non-null, return a thread-safe version (or wrapper) for the object.
        /// Returns null when not supported by the plugin.
        protected override IVstPluginAudioProcessor CreateAudioProcessor(IVstPluginAudioProcessor instance)
        {
            if (instance == null)
            {
                return new AudioProcessor(this);
            }

            // TODO: implement a thread-safe wrapper.
            return base.CreateAudioProcessor(instance);
        }

 

That gave me an idea.

I made other class file: PluginPersistence.cs and defined there PluginPersistence class like this:

 

class PluginPersistence : IVstPluginPersistence
    {
        private Plugin _plugin;
        public PluginPersistence(Plugin plugin)
        {
            _plugin = plugin;
        }

        public bool CanLoadChunk(VstPatchChunkInfo chunkInfo)
        {
            MessageBox.Show(chunkInfo.ToString());
            throw new System.NotImplementedException();
        }

        public void ReadPrograms(System.IO.Stream stream, VstProgramCollection programs)
        {
            MessageBox.Show("Reading programs");
            throw new System.NotImplementedException();
        }

        public void WritePrograms(System.IO.Stream stream, VstProgramCollection programs)
        {
            MessageBox.Show("Attempting to save state");
            throw new System.NotImplementedException();
        }
    }

 

And in my Plugin.cs file i added few lines:

 

protected override IVstPluginPersistence CreatePersistence(IVstPluginPersistence instance)
        {
            if (instance == null)
            {
                return new PluginPersistence(this);
            }
            return base.CreatePersistence(instance);
        }

 

I compiled it and.... it worked! :D

Even the unhandled exception windows show up :)

Maybe it will help someone sometime.

EDIT

It's strange. If i try to save project in my host or load, always WritePrograms function is called. Others not

EDIT2

I figured out that i have to save something first to read it back. But when i save some data like this in my WritePrograms function:

 

MessageBox.Show("Attempting to write programs");
byte[] arr = new byte[4];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
stream.Write(arr,0,arr.Length);
MessageBox.Show("programs Written!");

 

I am getting ArgumentOutOfRangeException after last MessageBox

Then when I try to load id back in my ReadPrograms function:

 

byte[] arr = new byte[4];
stream.Read(arr, 0, 4);
MessageBox.Show("Readed:" + arr[0].ToString() + " " + arr[1].ToString() + " " + arr[2].ToString() + " " + arr[3].ToString() + " ");

 

I get ArgumentOutOfRangeException after last MessageBox too.

What am I doing wrong? (data is loaded fine)

Coordinator
May 12, 2012 at 8:19 PM

Note that the VST.NET Framework is interface based. So each interface represents a feature for a VST Plugin. The plumbing in the Framework uses the IExtensible interface to discover what features you've (dynamically) implemented. When you override the CreateXxxx method for an interface you register your implementation of that feature (interface) to the Plugin root class and the Framework/plumbing can discover it.

"If i try to save project in my host or load, always WritePrograms function is called. Others not"
What host are you using?

It would be really helpful if you can supply a stack trace for those ArgumentOutOfRangeExceptions.

May 13, 2012 at 9:57 AM
Edited May 13, 2012 at 9:59 AM

I am using always FL Studio.

When I save project for the first time, only WritePrograms is called (no errors). When i save it second time or more there is always Exception window after last message box. (only one exception window pops up).

When I load project WritePrograms is called too but i don't know if that saves changes to project being loaded. After that ReadPrograms is called and it loads data. But after loading i get two exception windows in a row. All exceptions are the same:

Translation goes like this:

System.ArgumentOutOfRangeException: Index was out of range. It has to be positive value and less than size of the collection.
Name of the parameter: index
in System.ThrowHelper.ThrowArgumentOutOfRangeExceptionn()
in System.Collections.Generic.List`1.get_Item(Int32 index)
in
Jacobi.Vst.Framework.Plugin.StdPluginCommandStub.GetProgramNameIndexed(Int32 index)
in Jacobi.Vst.Interop.Plugin.PluginCommandProxy.Dispatch(Int32 opcode, Int32 index, Int32 value, Void* ptr, Single opt)

CanLoadChunk seems to never be invoked.

Plugin seems to load data and works despite of unhandled exceptions, but it is not the way it should work.

I think it's my fault and it has to do something with second parameter of writePrograms and readPrograms: 

VstProgramCollection programs

I don't know yet what to do with it and i tried to force FL Studio to put into it's project file some raw data that I want to save and then load that have nothing to do with "programs". I only want to save some infos and then load it back while loading project file. Maybe i am going the wrong way afterall? Do i have to implement "programs" to my vst? I honestly didn't discover yet why should I use them and what they do. But i think that when I write some binary data to stream, the Framework tries to assign them to some "programs" that doesn't exist or something like that.

Coordinator
May 14, 2012 at 6:16 AM

The exception indicates that the Host is calling the plugin (GetProgramNameIndexed/Get/SetChunk) with a program index that is not valid. The plugin (framework) reports the number of programs to the host at load time. The framework retrieves this value from the IVstPluginPrograms.Programs collection Count property (StdPluginCommandStub.CreatePluginInfo). I would suggest you try the plugin in a different host (Vsthost.exe by Hermann Seib is a good testbed) in order to discover if it is a FL specific issue or it actually is your plugin that is at fault.

The idea of Programs is to have multiple stored memory positions of all your plugin paramaters (what!?). A Program represents a stored (recallable) set of paramater values for your plugin. Some plugins benefit from being able to quickly change all the plugin parameters. Think of a delay that can store its parameter settings in different Programs storage locations (refer to the Delay Sample to see a typical implementation of this).

The 'programs' parameter you receive in the WritePrograms method is a list of the programs you should write to the stream. VST defines a concept of saving a Bank (all programs) or just the current Program. The VST.NET framework handles that for you and hands you a collection with all the programs that should be serialized. Likewise with ReadPrograms, the idea is that you read the stream and put the programs you deserialize into the specified collection.

Here is more info on persistence: http://obiwanjacobi.blogspot.com/2008/07/vstnet-plugin-persistence.html and on Programs here: http://obiwanjacobi.blogspot.com/2008/05/vstnet-programs-and-parameters.html

You should be able to only implement Persistence and not use Programs. Look at the MidiNoteMapper sample. http://vstnet.codeplex.com/SourceControl/changeset/view/67367#358097 This sample uses a binary writer/reader to serialize each map item (info for mapping one note to another).

Hope it helps,
Marc 

May 14, 2012 at 2:20 PM

Thank you for your reply!

I realised that i had still some code from Delay example which had programs and that's why i had errors.

I simply removed Dsp/Delay.cs file from project and commented IVstPluginPrograms CreatePrograms(IVstPluginPrograms instance) from my Plugin.cs file and that worked like a charm. I used BinaryWriter and BinaryReader as you showed in MidiNoteMaper example.

In conclusion: I had to remove programs that Delay example was adding to my plugin.

Maybe I will add parameters support later!

Thanx again for your time!
Tomek