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

FLstudio problems, how "catch" midis from Piano roll

Topics: Editor UI, Getting Started, Midi, Newbie, Other
Mar 26, 2012 at 3:04 AM
Edited Mar 26, 2012 at 3:14 AM

Hello again obiwan and all other..

My "hard starts" was solved. Now i little bit understand how VST.NET and itself VST work. But i am getting now some "errors" and some problems:

1)Sometime, when i try to load my plugin (or VstNetMidiPlugin) in FLstudio, it stop working (i have no idea why). It say nothing, only FLstudio stoped working (it happen only with Vst.Net midi plugins, never happened me with other plugins)..or when i load the plugin and minimalize FLstudio and then i am trying get back, again stoped working without reason.

2)By loading my plugin in FLloops (i have "small" UI there, only one textbox where are written all catched midis) i have there some "outputs" what i have no idea what it is..exactly:

TRY:
data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

data[]: 0xE0 0x0 0x40 0x0
deltaFrames: 0

In VSThost by hermann i have not this informations there. Do you have any idea what thats mean?

3)In FLstudio are always deltaFrames 0..never i can get other value like null..But when i need to "count" start and end position of midi note, how, when deltaFrames are always null?? (again, in VSThost works deltaFrames fine)..

4)When i am trying test it again in FLstudio and when i add some notes in Piano roll those notes are catched and added in my textbox. But i dont want to catch them when i add it. I want catch them ONLY if i press "play" (or spacebar) and when i am playing whole "sample". Is there some chance to reach that? (this ask is i think little connected with my next ask..)

5)Do you have some idea, how can i "record" informations about midis from Piano roll?? What i mean. User add there some notes and i need to catch all those notes..My problem is: When user write some notes. I can catch them and "record" them and for example save them in some file. But when user start it again. The file is "updated" and those notes are added there. But i need "reset" it and write it again. (i add it now in some string value "lines")..So simply this string value "lines" will be reseted somewhere. But when i reset it in Process() method, i can get only actualy played note and i lose informations about the oldest notes. Do you have some idea how can i catch all note and when user "start" play again how to reset that values and "record" again??

Coordinator
Mar 26, 2012 at 9:43 AM

1) These are always tough ones. One thing you can try is comment out all your code so that only a skeleton remains. That should work fine. Now start introducing parts of your program and see when  the problem occurs. You can also enable tracing support in VST.NET to try to see what calls are placed to your plugin. (see the config file in Jacobi.Vst.Interop).

2) Those look like Midi events. The data (4 bytes) are the midi bytes. If you're not outputting this text, FL probably is. Its just some diagnostics output they forgot to remove and that you can ignore.

3) Seems like FL doesn't take the trouble to calculate the deltaFrames. Keep your buffers small (settings in FL) so that the deviation is not that big.

4) There is a 'real-time' flag that exists on native VST level. Unfortunately VST.NET does not pass this information to the managed plugin. Also, that is a host feature not all hosts support. I will see if I can add this.

5) General: forget Piano Roll or any other screen that FL (or other DAW) might have. A VST plugin is not an application plugin (or add-in) it is a component that processes audio and midi information. If you retrieve the current time (info) from the host you should be able to detect when the time is suddenly less that just before.

Hope it helps,
Marc 

Mar 26, 2012 at 5:29 PM
Edited Mar 26, 2012 at 10:45 PM

1)i will try that :)..but i tried that now on new demo instalation and all works fine (for the time being^^)..maybe some bads registry..but i will try that later if it fall again

2)ok, i will ignore that, but its connected with 4) and 5), bcs i dont know, where i can "reset" that values..(probably it will be something like onPlay event, i read it in some topic, you answered that something like onPlay event is Plugin.StartProcess, if i am right, that method is in PluginCommandStub right? and if i am right, i need only override this method?)

3)i tried set buffer on big value and too on really small value (normal value with ASIO drivers i have 128samples and i tried that too on 64samples and same problem).. Maybe FLstudio have some problems with sending informations about deltaFrames?? :-/..

4)so..is something what you can advice me, how can i get something like that?

5)ok, i forget it :D :)..

Coordinator
Mar 26, 2012 at 6:36 PM

2) StartProcess (and StopProcess) indicate that the Host has started (or stopped) its audio engine. Its not an OnPlay in the sense that it indicates if the sequencer's transport is running/on or not. Your plugin is still called on (AudioProcessor) Process even when the transport is stopped. So StartProcess will not be suitable for what you need.

3) deltaFrames will always be zero in FL - they don't implement it. But if you use small buffers the deviation is minimal with what it should be. The deltaFrames field indicate the number of sample frames from the beginning of the current time slice (indicated with the GetTimeInfo result) the midi event should 'wait' before being 'played'. So deltaFrames of zero is right at the beginning of the current time slice. A deltaFrames of 100 means, wait 100 sample frames of time before playing the midi event.

4) I'm working on it as I write this. Until I check in the code, there is not much you can do.

 

Hope it helps,
Marc 

Coordinator
Mar 26, 2012 at 7:15 PM

I've checked in the VstMidiEvent addition.

http://vstnet.codeplex.com/SourceControl/changeset/changes/66373

Mar 26, 2012 at 9:35 PM

Ok, i builded it..thanks you so much obiwan..i will reply back how i am continuing..

Mar 26, 2012 at 10:43 PM
Edited Mar 27, 2012 at 1:21 AM

Ok, i checked the "stopped working" problem (how you adviced with commenting code)..

And it was some bad code when i tried write something in UI..my bad, i didnt that "thread-safe" so i looked on your MidiNoteMapper and i tried copy and adapt it for my needs and it works now fine!!

And how can i solve in FLstudio (i have no idea if other programs implement the deltaFrames) the deltaFrames? Or whats other possibility to count how long midi note is and where start?

Coordinator
Mar 27, 2012 at 7:00 AM

You cant solve the problem with FL and its deltaFrames of zero. Implement your code to account for the deltaFrames value that is all you can do.

Nice to hear you've found the problem.

Have fun,
Marc 

Mar 27, 2012 at 7:38 AM
Edited Mar 27, 2012 at 12:52 PM

ok..so if i understand it good, my plugin will be unusable in FLloops..:D nice too hear :D..ok i hope, that atleast other programs will send me infos about the timings..

and one ask more :) (sry ^^)..from the deltaSamples i can count the "timings" (this note start at 2.5s and end in 3.0s for half long time note)..and this "equation" will look: "deltaFrames/samplesPerSec"?? how to get info about SamplesPerSec from my program??

and..is there some other possibility how can i know, where the note start and what length the note have? if i catch some couple of notes..and i need to know, where they start, in which time and how long they are..??

Coordinator
Mar 27, 2012 at 3:17 PM

Not at all. It just means that your midi output is less accurate.

I would suggest you count down the deltaFrames inside the Process method no need for timing info in seconds. But if you must know call the Host with the GetTimeInfo method. 

Mar 28, 2012 at 5:43 AM
Edited Mar 28, 2012 at 6:49 AM

with GetTimeInfo you mean VstTimeInfo instance and get the parametrs? bcs i cant found getTimeInfo() method, so probably yes.

If i tried VstTimeInfo, parametrs are always null..

 

i tried now Reaper instead of FLstudio. There works deltaFrames fine (but anyway i would like to know how VstTimeInfo works :D)..but other problem is..when there is 1midi..i normaly catch 0x90 for start..and 0x80 for end. But now in Reaper i catch 20x 0x90 (on same midi note) and then for example 15x 0x80..(the host call midiEvent moretimes..)..then, when the midi notes ends..i am always catching some 0xF0-0xFF and 0xB0-0xBF (0xBX 0x7B 0xXX 0x00 ,if you understand (X are "changed" numbers) is info about "ALL NOTES OFF"..and 0xFX is something like channel 1-15 Pitch wheel range)..this 2 events change every millisekond (1x is there 0xF0, then 0xB0, then 0xF1,0xB1,0xF2,......,0xBF,0xF0,0xB0,.....)..it seems like when host always call my VSTplugin but i dont know why (when playing is stopped)..

EDIT: yes, when i count events and when i write it down..events count are something about 40 (for 3notes..there must be 6 events (7 if i count "all notes off"), but there is about 40events..)..its bug in Reaper??or any idea why is it?

and i am not sure if its because of Reaper..but the "isRealTime" method doesnt work fine in this host..in VSThost and FLstudio yes..uaaaaa..why are hosts so ******..:-/

Coordinator
Mar 28, 2012 at 8:04 AM

My bad, the method is called GetTime ;-) and its on the IVstHostSequencer interface.

http://vstnet.codeplex.com/SourceControl/changeset/view/66373#191276

You retrieve an instance of this interface by calling GetInstance<IVstHostSequencer>() on the host reference the plugin is passed in its Open method. Typically a plugin would call GetTime once in its (AudioProcessor.)Process method.

I'm not sure what Reaper does but it could be that they also reset all controllers next to the 'all notes off'..??

I understand your frustration. One of the big problems is that there is a great variety in host (and plugin!) implementations - or should I say - interpretations of the VST specs. Unfortunately Steinberg never has done anything to improve this. A good place to go with your VST host/plugin problems is the Kvr Audio forum (because I dont have all the answers ;-)

http://www.kvraudio.com/forum/viewforum.php?f=33

 

Hope it hepls,
Marc 

Mar 28, 2012 at 8:18 AM
Edited Mar 28, 2012 at 8:19 AM

I am sure, that its not  because they reset all controllers..

If i describe, what happen:

1)I catch midiEvent (0x90 0x3C 0x42 0x00 deltaFrames: 728)
2)I catch midiEvent again (0x90 0x3c 0x42 0x00 deltaFrames: 728)
3)again sometimes 20x 2)
4)I catch midiEvent (0x80 0x3c 0x42 0x00 deltaFrames: 390)
5)again for example 11x 4)
6)i catch other midis (again like 2)-5))
7)i catch "all notes off" (again for example 20x)..

how i wroted..it looks like when Reaper host send me 20x same event..

Mar 28, 2012 at 8:51 AM
Edited Mar 28, 2012 at 8:52 AM

So i posted topic on the kvraudio forum, maybe someone will help, thanks for link..

i am still thinking about it..Its possible that it would be because the VST.NET is multithreaded?? Because in Reaper is something like Mode event list. And it shows all infos about midis (like what i need, simple midi parser)..and there is all ok..So if it would be that Reaper host send the midis. Vst catch it..and in VST.NET is somewhere copying it??its nonsence right (because again in VSThost all works fine..:-/)

and again thanks you marc..you help me too much..i will think on you in conclusion of my bachelors project and give you there special thanks..:)..(if i end the application and if i will finish writing the bachelors project :D)

Mar 29, 2012 at 12:59 AM

marc,

i tried write some easy VST plugin in C++..and in reaper are not problems with the multiple midiEvents when i load the C++ plugin..so i think that it would be some bug in VST.NET..its some "way" how to find it?

Coordinator
Mar 29, 2012 at 7:59 AM

OK. Email me the code that you have and I will look at it.

obiwanjacobi at hotmail dot com.

Mar 30, 2012 at 9:31 PM

Hello marc, i sended you the meil with that.

Thanks you very much,

thomas.

Coordinator
Apr 3, 2012 at 7:16 PM
Edited Apr 3, 2012 at 7:27 PM

Sorry it took a while...

Interesting case that I will need to fix in the template code.

The following is happening.

The MidiProcessor has a flag (SyncWithAudioProcessor) that allows the actual midi processing to be postponed until the AudioProcessor is called. By default this flag is on (true). In this case the incoming midi events are cached and when the AudioProcessor.Process call is executing it will call the MidiProcessor.ProcessCurrentEvents method to process those cached midi events.

Reaper will only call the MidiProcessor.Process method when it actually has some midi data to give it. But it will continue to call the AudioProcess.Process method continuously. The last collection of midi events that was passed, will then be processed again and again. So all we need to do is clear the MidiProcessor.CurrentEvents property -the cache of midi events- when we're done with ProcessCurrentEvents. Just assign null to the CurrentEvents property (you cannot call Clear on the collection, because it is read-only) and it will work fine.

See http://vstnet.codeplex.com/SourceControl/changeset/view/66543#1108776 for example.

Hope it helps,
Marc 

Apr 4, 2012 at 2:12 PM
Edited Apr 4, 2012 at 2:28 PM

yeah i see..thanks!!that was really helpful..i might that i need write the vst in c++ so i tried a bit look on it..and i understand now the VST a little bit more..but i am confused of that all :D..bcs i dont know if VST is the right way on what i need..but i lost fast 3 months on VST so i think i will end it now...uaaa..

one ask more..

In c++ is processReplacing where are params float* in,float* out, VstInt32 sampleFrames..

First is called processEvents() where i can catch midis and their deltaFrames, then host call processReplacing where are processed current catched events (more count, not only one)..

and now. In c++ if i catch midiEvents and save them in some list. And then in ProcessReplacing() (or other Process where is sampleFrames) i can do:

VstTimeInfo *vti;  // VstTimeInfo is a type, NOT a variable.
vti = getTimeInfo(kVstPpqPosValid | kVstTempoValid | kVstTimeSigValid); // tell it what data you require, you only really need kVstPpqPosValid right now but just showing how to ask for temop and time sig as well.
sampleTimeOfEvent = vti->samplePos + deltaFrames; // add the deltaFrames to the sample position of the start of our buffer

with this i can catch "real" positions in samples. Then dividing sampleTimeOfEvent by sampleRate i get real ms values of times where the midis is catched.

This is the one thing what i need to do..and what i searched atleast 1month :D..

But i dont know, what sampleFrames means? And in yours VST.NET are not sampleFrames. there are only input and output buffers..is sampleFrames value needed for something in timing?

And second one. Is possible from processWhatever() (replacing or doublereplacing or so) call processEvents? because when i save the events what are in current process buffer maybe i would need to know WHICH ONES i am catching now in processWhatever() ?? is there some way how to know that events or i can know only that SOME events (for example 10events) are in this "block" (current buffer) and i have no informations about which ones are they??

Coordinator
Apr 5, 2012 at 9:08 AM
Edited Apr 5, 2012 at 9:12 AM

1) 'sampleFrames' is the number of samples that are in the float* buffers - all buffers are of equal length. This value should be <= to BlockSize. Note that some hosts can pass you buffer lengths < BlockSize (used to do sample-precision parameter-automation).

In VST.NET the unmanaged float* buffers are wrapped in a VstAudioBuffer instance (they are not marshaled). That class has a Count property which will give you the 'sampleFrames' value.

2) Not sure I get what you mean to ask here. But ProcessEvents is always called before processXxxxx for each cycle. So there is only the 'current events' (which can be empty). So there is no 'which ones' its always current. If you want to store all events you received over time, you need to put those in a different cache.
Anyway, do NOT call any of the process methods yourself - always use a separate method (like the ProcessCurrentEvents method).

Hope it helps,
Marc 

Apr 5, 2012 at 4:16 PM

ok and in MidiProcessor i need to add VstPluginAudioProcessorBase that i can override Process(VstAudioBuffer[] inChannels, VstAudioBuffer[] outChannels) right?

Coordinator
Apr 5, 2012 at 4:34 PM

How you implement the Vst.Net framework interfaces is entirely up to you. You HAVE to make sure the plugin root object returns the correct instance for the specific interface (Interface Manager).

That being said, it is the architecture of the framework that all distinct tasks are separated into different objects (with their respective interfaces). So I would not recommend it. In the project code I got from you, you already have a (dummy) AudioProcessor implemented (that is derived from VstPluginAudioProcessorBase). If you look at the Process method it calls the MidiProcessor to process the current events.

I still do not understand what it is you are trying to accomplish. Perhaps you could try to explain so I can be of more help?

So the answer to your question is probably 'No'...

Apr 5, 2012 at 5:06 PM
Edited Apr 5, 2012 at 7:46 PM

how i wroted in previous topic. I know, that this is not exactly what VST is for..

I am trying write simple midiParser (later it would be realtime text-to-sing plugin, but for now (for my bachelors project) i need write simple midi parser).

So..user will write in midi track some midis.. Then he turn my plugin. Then click on play (in Host or sometimes it will play the track over pressing spacebar, its not matter)..my plugin catch all midis in this track and show them in textbox. In textbox will be:

0x90 0x3c 0x60 0x00 20ms //NoteOn C3 velocity NotUsed timeOfEvent
0x80 0x3c 0x44 0x00 120ms
0x90 0x41 0x50 0x00 160ms
0x90 0x32 0x60 0x00 160ms
....

the "times" are REAL TIMES in track.. (no delta times but realtimes of start of playing)

Then user will click on button in my plugin something like "save" and this save the textbox.text for next processing..then in my PROGRAM (not plugin but for now in other program) will user import this parsed data and will use them for next processing with text-to-speech in PRAAT..

later (NOT NOW!!!!i think next year or sometime so) i want rewrite this plugin and do all processing in there for realtime "text-to-sing" plugin. So user write some midis, add on this track my plugin, click play and my plugin will realtime process it and instead of play midis will sing the text what he will write in textbox. but how i wroted, this i dont want do now. I need now only the midiParser.

And for midiParser i need the real values of time. So i need use ProcessWhatever() function because in there i think i need take catched events (midiEvents) and take deltaFrame for all events add to them VstTimeInfo.samplePosition value and divide that with sampleRate. So i think i can count the value of time.

So thats all what i need i think.. ^^

Apr 6, 2012 at 12:17 AM
Edited Apr 6, 2012 at 12:25 AM

And next problem what i get is nullReferenceException when i call

_sequencer = _plugin.Host.GetInstance<IVstHostSequencer>();

(i call it exactly like you in your samples in constructor of dummyAudioProcessor)

Coordinator
Apr 6, 2012 at 6:34 AM

Ok. That means you're almost there ;-)

Oops that may be a bug in the sample code: the Host property is set after Open is called on the plugin (the Opened event is fired). The AudioProcessor constructor is called very early in the life time and Open will not have been called. Nice catch, thanx.

So please check to see if _plugin.Host is not null before calling GetInstance (and the rest of the GetTime stuff). That means it will be moved out of the constructor, perhaps into the TimeInfo property?

Something like this:

if (_sequencer == null && _plugin.Host != null)
{
_sequencer = _plugin.Host.GetInstance<IVstHostSequencer>();
}
if (_timeInfo == null && _sequencer != null)
{
    _timeInfo = _sequencer.GetTime(_defaultTimeInfoFlags);
}

return _timeInfo;

Hope it helps,
Marc 

Apr 6, 2012 at 6:41 AM

when i move the inicialization from constructor i still get nullReferenceException.. It seems like when IVstHostSequencer return all the time null..

Apr 9, 2012 at 8:05 PM

Hello marc, did you looked on it? or do you have something what can i try to search solution?

Coordinator
Apr 10, 2012 at 6:33 AM

No, not yet. I am planning to, but....

Will try to do it this week.

Coordinator
Apr 11, 2012 at 9:19 AM

I checked in the VS Template code (under Support) to fix the NRE in the constructor of the AudioProcessor in the AudioPlugin. I created a test plugin and requested the TimeInfo at each call of the Process  method on the AudioProcessor and it did not throw any Exceptions.

So if you still experience the problem after changing your code similar to how the templates now work, could you please post the call stack?

Hope it helps.
Marc