Generating Midi

Topics: Midi, Plugin Development
May 19, 2011 at 3:44 PM
Edited May 19, 2011 at 3:46 PM

Hi,

I am working on a midi plugin that will generate midi data (like an LFO). I was hoping to get some advice on how to structure my plugin and also how to set and send VstEvents.

 

So my idea was to have a separate thread for the LFO's that would call IVstMidiProcessor::Process() every x milliseconds and send out the status of all the LFOs. Would this work or would it make more sense to only call IVstMidiProcessor::Process() from my midi processor. Also I'm not sure how to calculate the deltaFrames for a generated VstMidiEvent.

 

Ideally the LFOs would be able to sync up with the tempo of the host and the start of a bar when the host is playing. It looks like the VstTimeInfo class would help me do this. Should I just use the PpqPosition to get the current position in a bar or is there a more accurate way of doing this? The BarStartPosition, CycleEndPosition and CycleStartPosition are always 0 for me but maybe this is just because I have been testing in VstHost. I am a little confused about how to get VstTimeInfo. In particular what is the point of the VstTimeInfoFlags parameter in IVstHostSequencer::GetTime(). I tried calling this with each VstTimeInfoFlag but they all returned the exact same VstTimeInfo.

 

Thanks

Rob

Coordinator
May 20, 2011 at 5:22 AM

Hi Rob,

I would not use a seperate thread unless you have to do a lot of calculating to get the correct midi events. To my knowledge the Host determines what 'time' it is and would probably not like you calling back into it for processing these midi events you generate on a seperate thread. So even if you decide to spawn another thread for the generation algorithm, I would still synchronize sending the actual events on the Process call on either the AudioProcessor or the MidiProcessor.

The deltaFrames property of an event offsets the (start) timing of the event from the start of the current processing cycle. So if the slices in the processing cycle are say 4048 frames, deltaFrames allows you to position a midi event within that time-'space' (0-4047). If deltaFrames is zero the event is played at the start of the slice that is currently processed. The higher the deltaFrames value is the 'later' the midi event is played.

The GetTime method implements a sort of handshake mechanism. The parameter you supply when you call the method indicates the types of time info you would like to have returned. When the function returns the VstTimeInfo contains the flags of the types of time info that were actually supplied by the host.

Check out the latest checkin I've done: it is a sample implementation of how you should call GetTime within your plugin.  http://vstnet.codeplex.com/SourceControl/changeset/view/57910#1108753

Basically it means you only call GetTime once a cycle.

I remember seeing a discussion on kvraudio's forum on how to calculate bar positions and tempo etc. I cannot help you there (not without doing the research myself). http://www.kvraudio.com/forum/viewforum.php?f=33

Hope it helps,
Marc

May 22, 2011 at 5:20 PM

Thanks a lot for the info! I think I can figure it out from here. Also if anyone else is interested I think this is the post on kvraudio that Marc was referring to: http://www.kvraudio.com/forum/viewtopic.php?t=270213

 

Thanks again,

Rob

Feb 19, 2012 at 3:40 PM
Edited Feb 19, 2012 at 3:43 PM

Hi obiwan and robcheese!

I have some trouble with implementing MIDI generation to my VST effect.

I have studied MidiNoteSampler and MidiNoteMapper sample projects, and all I could find in VSTNET documentation. I have visited http://home.roadrunner.com/~jgglatt/ website to learn about MIDI commands but still I can't make my effect to send MIDI commands to host.

As far as I figured out:

  • I added IVstMidiProcessor CreateMidiProcessor(...) , IVstPluginMidiSource CreateMidiSource(...) and MidiProcessor MidiProcessor to my Plugin.cs file
  • Added MidiProcessor.cs file from template MidiPlugin and figured out that I have to add events to VstEventCollection CurrentEvents;
  • To my AudioProcessor's Process() function I added this:

 

  	if (_plugin.MidiProcessor.SyncWithAudioProcessor)
            {
                _plugin.MidiProcessor.AddDummyEvent();
                _plugin.MidiProcessor.ProcessCurrentEvents();
            }

 

  • Implemented AddDummyEvent() like this:
        public void AddDummyEvent()
            {
                byte[] b = new byte[4];
                b[0] = 0x80;
                b[1] = 60;
                b[2] = 100;
                VstEvent[] evs = new VstEvent[1];
                evs[0] = new VstMidiEvent(0, 0, 0, b, 0, 0);
                VstEventCollection ev = new VstEventCollection(evs);
                CurrentEvents = ev;
            }
  • This should turn on middle C at volume 100 every time the ProcessCurrentEvents() is called (so that mean's pretty often

    What I don't understand is this comment before CreateMidiSource in Plugin.cs:

    /// <summary>
    /// Implement this when you output midi events to the host.
    /// </summary>

    I copied it from MidiPlugin template and as far as I see, I think it should work.

    When I test my plugin (its in FL Studio 10) I  have to assign an output MIDI port in my plugin and assign an input MIDI port to other VSTi synth. I'm doing it but it's not working.

    I think I didn't understand something so I'm asking you for some help.

    Also my implementation of AddDummyEvent() seems to be bad. It must be more easy way to add MIDI event to output events for host.

    All what I want is to automaticly "press" and "release" MIDI notes.

  • Feb 19, 2012 at 5:06 PM

    At a first glance the only thing that stands out to me is that your using the status byte of a note off message not a note on message. Try switching it to 0x90.

    CreateMidiSource just needs to return the instance of your midi processor.

    Coordinator
    Feb 19, 2012 at 5:44 PM

    The IVstPluginMidiSource interface contains a property ChannelCount that is (possibly) used by the host. Implementing it also signals to the VST.NET Framework that you will output (source) midi events. The framework will communicate this to the host (flags). So it is key that you implement (a reference to) it and return it from the CreateMidiSource method.

    All the CreateXxxx methods on the (VstPluginWith)InterfaceManagerBase class are called when that specific interface is requested for the first time. It is up to you to return an implementation of that interface. If you implement multiple interfaces on one object (which is perfectly fine) you just return the same object reference for the interfaces it implements.

    Like Rob already indicted the Note on will probably yield better results. You might also think of using a counter (int) to let some Process calls pass before sending another midi event. I would suggest you'd write code that sends a (dummy) note on, waits for a couple of Process calls then sends the note off for that note - then waits again then sends a note on and so on etc.

    TIP: You can use the VstEventCollection in CurrentEvents to manage the events. Call the Clear method to remove all (old) events and use Add to add to the list of events. It is a good idea to keep the number (and size) of the memory allocations during processing to a minimum - and you're newing up 2 objects you can do without. Note that the VstEventCollection constructor that takes the array marks the collection as read only. It is meant to wrap incoming events - which you cannot change.

    Hope it hepls,
    Marc 

    Feb 19, 2012 at 6:26 PM
    Edited Feb 19, 2012 at 6:55 PM

    Thank you for your replies obiwan and robcheese!

    Fell bit stupid about that note-off command.. but take look at this: http://253.ccarh.org/handout/midiprotocol/

    Right at the beginning there stays noteON for 128 decimal that is 0x80... and on the jgglatt's website there is correct. I was checking these two websites and when I was implementing my dummy note-on function, I was currently looking on the website i just linked.

    And for the VstEventCollection CurrentEvents: I tired to use this but i got "read only" exception when i tried to invoke .Add method.

    Now I'm implementing changes to your advices. I will tell you if it worked.

     

    Edit.

    It works..... Thank you guys :)

     

    Edit 2.

    I still can't figure out how to deal with CurrentEvents.

    Even when I fire CurrentEvents.Clear(); I get an exception that this is read only :(

    Feb 20, 2012 at 5:17 AM

    Are you still creating new VstEventCollections and passing an array to the constructor? If so try just adding to and clearing the exsisting one instead.

     

    Rob