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

MIDI plugins in FLStudio

Oct 14, 2009 at 10:41 PM

Hello.  Last year sometime I posted a thread about building MIDI plugins targeting FLStudio using VST.NET (thread here: http://vstnet.codeplex.com/Thread/View.aspx?ThreadId=41819 ).  At that point I was trying to create a MIDI effect plugin and since then FLStudio 9 has made things REALLY easy as far as loading synth plugins that output midi and routing them to any other vsti, so now I can build a regular instrument VST that takes midi in and outputs midi, pretty much like that MIDI Note Mapper sample does.

I came back to see if anything has changed but sadly, I rebuilt the MIDI Note Mapper plugin to see if it would work fine in FLStudio and it doesn't.  It loads fine and gives no errors, but it just doesn't seem to be outputting MIDI where I tell it to go.  A similar plugin works fine (in particular, I tested Sanford Arpeggiator: http://www.lesliesanford.com/SanfordArpeggiator.shtml with no problems).  So FLStudio still can handle these types of plugins, but something is still missing from the VST.NET version of them.

Has anyone built a MIDI plugin using VST.NET and gotten it to work in FLStudio?  Has anyone explored this and maybe figured out what changes to the code could be made to get it to work?  I'd be willing to put the time into getting it to work if I only knew where to start.

Coordinator
Oct 15, 2009 at 9:26 AM

Did you download the latest sources or try the binary? The sources contain an addition of the 'obsolete' wantMidi opcode that some host need to sent the plugin midi in the first place.

You could try to debug the calls that are made by FL to the plugin. Put breakpoints in all the methods of the StdPluginCommandStub and you'll at least be able to see what methods are called and what parameter values are passed in or returned.

Oct 16, 2009 at 2:51 AM

Ah I just tried the 0.7 binary.  I'll build from the source and see if it works and post my results :)

Oct 16, 2009 at 5:41 AM

Ok, built from source, but I'm a little confused about how to implement the IVstHostDeprecated thing.  From the release notes:

Added support for the deprecated method WantMidi on the HostCommandStub. The Framework now contains an extra IVstHostDeprecated interface a plugin can use to call deprecated methods. The PluginBase(WithInterfaceManager) base classes now implement the Resume method to call WantMidi when the Plugin implements the IVstMidiProcessor.

In the midi note mapper sample, for example, which class needs to get this interface?  How does the code need to be changed to make use of this?

Coordinator
Oct 16, 2009 at 6:25 AM
Edited Oct 16, 2009 at 6:25 AM

The IVstHostDeprecated interface is called from within the framework VstPlugin base classes (Resume). So as long as you use one of the base classes that implement the IVstPlugin interface, you don't need to do anything. Otherwise, copy the implementation from the framework code into your own code.

So when you have a build of the Samples, just go ahead and try it out.

Hope it helps.

 

Oct 16, 2009 at 8:56 AM

Argh, ok.  Well I built from source and built the note mapper plug with no modifications and I still can't get it to send MIDI in FLStudio.  I'll try again to hook up the MIDI monitor thing I did last year to see what it is sending/receiving compared to the other MIDI plugins that do work in FLStudio and see what is different I guess.

If anyone else is building a MIDI plugin using VST.NET you should be able to test FLStudio's reaction to it in the demo: http://flstudio.image-line.com/documents/download.html

 

Coordinator
Oct 16, 2009 at 10:50 AM

Could you also trace the method parameters? For instance the CanDo call the Eckel plugin makes to the host might be interesting to see what it asks (see previous thread http://vstnet.codeplex.com/Thread/View.aspx?ThreadId=41819).

I see if I can help you out with this, but my time is pretty limited right now. No promises.

PS: I created an work item to include method tracing diagnostics in VST.NET to make these sort of issues easier to resolve. No idea when it will be done, though...

Oct 17, 2009 at 3:13 AM

I just ran the MIDI note mapper plug and the sanford arpeggiator plug through VSTSpy to see what differences there are:

receiveVstTimeInfo

Sanford: yes

MIDI note mapper: no

That's the only difference.  I noticed you listed that on your SDK questions page ( http://vstnet.codeplex.com/wikipage?title=VST%20SDK%20Questions&ProjectName=vstnet "What does the ReceiveVstTimeInfo CanDo string for a plugin mean?" ) so maybe that's helpful?  Dunno. 

I also noticed that the Eckel plugin also has yes for this value.

The other difference was that the Sanford plug is configured with 0 ins and 1 out, but I modified the MIDI note mapper plugin to match that before testing.  I tested it both ways (trying everything!)

 

Coordinator
Oct 17, 2009 at 4:09 PM

You could try to add the VstPlugInCapabilities.ReceiveTimeInfo constant (enumeration) to the (base class) constructor of the Plugin class of the MidiNoteMapper sample.

This will cause the plugin to respond with yes to the CanDo.

It already contains VstPluginCapabilities.NoSoundInStop. So to add it you would write "VstPluginCapabilities.NoSoundInStop | VstPlugInCapabilities.ReceiveTimeInfo" (without the quotes).

See if that helps.

 

Oct 18, 2009 at 3:12 AM

Just tried it and still nothing :(

 

Coordinator
Oct 18, 2009 at 5:41 PM

Can you confirm that Midi is in fact reaching the plugin (set a breakpoint at the VstMidiProcessor.Process)?

And if so, that the VstPluginAudioProcessor.Process is called to output the Midi?

Oct 19, 2009 at 7:27 PM
Edited Oct 19, 2009 at 7:28 PM

Process gets called in both the midi processor and the audio processor.  I have confirmed that MIDI is received by the plugin.

However, in AudioProcessor.Process, _hostProcessor is always null.  Even after the following code is called:

 

if (_hostProcessor == null) {
_hostProcessor = _plugin.Host.GetInstance<IVstMidiProcessor>();
}

 

_hostProcessor remains null, so the next block of code:

 

if (_midiProcessor != null && _hostProcessor != null && _midiProcessor.Events.Count > 0) {
_hostProcessor.Process(_midiProcessor.Events);
_midiProcessor.Events.Clear();
}

 

is never executed.  I have confirmed that _midiProcessor.Events.Count > 0 when it should be.

 

Oct 19, 2009 at 11:18 PM

Aaaaaand... I just realized that's where I left off with this in December of 08 -- with the _hostProcessor null.  ( http://vstnet.codeplex.com/Thread/View.aspx?ThreadId=41819 )

 

So I'm full circle again. :)

Oct 20, 2009 at 2:07 AM

Ok, I got this to work.  But here's what I had to do:

private IVstMidiProcessor CreateMidiProcessor(IVstMidiProcessor instance) {
  if (instance == null && true)
                //_host.HostCommandStub.CanDo(VstHostCanDo.ReceiveVstEvents) != VstCanDoResult.No &&
                //_host.HostCommandStub.CanDo(VstHostCanDo.ReceiveVstMidiEvent) != VstCanDoResult.No) 
  {
                return new VstHostMidiProcessor(_host);
  }

  return instance;
}

That's in Jacobi.Vst.Framework.Host.VstHostInterfaceManager.cs

Basically it will now just create the interface regardless of whether the host returns those candos. 

Coordinator
Oct 20, 2009 at 6:27 AM

That's right, I remember. I changed the code so that it would except everything but No (used to be: must be Yes).
(Hmmm looking at the online source-code it still says Yes. Something has gone wrong there...)

Can you perform one more test for me?

Could you comment one of those lines back in and see which one actually returns No?

(My guess is that the ReceieveVstEvents will return No)

Then I can change the code so others do not have this problem.

Thanx very much.

Oct 20, 2009 at 6:02 PM
Edited Oct 20, 2009 at 6:15 PM

Yep, ReceiveVstEvents returns no.

VstHostCanDo.ReceiveVstMidiEvent is fine.

Also sorry for the confusion, I changed those lines from == yes to != no while I was experimenting :)

 

Oct 20, 2009 at 6:19 PM

Now that that's worked out I have a tangentially related question if I may.

This code:

mappedEvent = new VstMidiEvent(midiEvent.DeltaFrames, 
                            midiEvent.NoteLength, 
                            midiEvent.NoteOffset, 
                            midiData, 
                            midiEvent.Detune, 
                            midiEvent.NoteOffVelocity);

                        Events.Add(mappedEvent);

                        // add raw note-on note numbers to the queue
                        if((midiEvent.MidiData[0] & 0xF0) == 0x90)
                        {
                            lock (((ICollection)NoteOnEvents).SyncRoot)
                            {
                                NoteOnEvents.Enqueue(midiEvent.MidiData[1]);
                            }
                        }
creates a mapped event and adds it to the event list that gets passed to the host in the audioprocessor process function.  If there is no mapping, then the original event just gets passed through as is, so I'm trying to figure out what the second part of that snippet does, adding the raw note-on numbers to the queue?  Which queue and why does this need to happen?

 

Coordinator
Oct 20, 2009 at 7:40 PM

Ok. thanx for checking that. I will change the source code to only use the ReceiveVstMidiEvents and test for not No.

That last bit -adding note on numbers- has nothing to do with inputing/mapping./outputting midi. It is just a list that is used by the UI to select the mapping item when it is applied. It is meant as an example of asynchronous 'processor' - UI interaction that was requested by another user. Perhaps I should clarify it more in the comments, but I rushed it a bit to help the user who requested such an example in the little time I had ;-)