Creating MIDI - some basic questions


#1

Hi. I´m relatively new to c++ and quite fresh with juce, so I´m facing some conceptual problems. Here´s a little background.

I want to create MIDI only plugins, step sequencer, controller managers, etc. I come from Kontakt scripts, which have a totally different input/output structure. It doesn´t force you to deal with buffers, it does it internally. The script system gets a MIDI message, triggers a callback and then allows me to manipulate the data in ´realtime´ so to speak. It makes is very easy to manipulate MIDI data.

 

I´m trying to translate my Kontakt approach to JUCE.

1) Kontakt script receives a note, it triggers a callback. I want to use keyswitches to trigger a MIDI sequence;

2) I ignore the incoming keyswitch (quite easy, using an ´ignore_midi´ command, which will bypass the incoming message). I use a ´wait(x)´ command (it suspends the processing for x microsseconds). Let´s say I want to send a note every 1/8 bAR, it has some internal variables that will give me, for instance, an eighth note in microsseconds ($DURATION_EIGHTH_NOTE). I wait this much and send the next note. It´s a serial process.

This is the step that is hard translating to the buffer paradigm. Reading input messages is easy, but I don´t get the best way to create rhythmic output messages, for the next message might be in the next buffer. Also, the distance bewteen notes converted to time is variable if there´s any tempo variation. In Kontakt, I get quite precise using the aforemetioned approach. My questions are:

a) Do I have to implement some sort of queue to handle outputting MIDI rhythmically?

b) Is there any class that handles this kind of conversion automatically?

 

Sorry if I missed some post that talks about it, in fact I´ve been reading many posts but I can´t find a not-so-complex solution. Maybe because of my lack of experience.

 

Any help, trully appreciated.

 

Thanks

 

Adriano

 

 


#2

The concept of a MIDI effect under juce is slightly different compared to a Kontakt script: instead of a callback based approach, a vst midi plug-in is "polled" repeatedly at short intervals via the processBlock callback if it would like to output a MIDI note at the current playback time. 

To get yourself started, use the Introjucer to create a new audio plugin and make sure you check both "Plugin wants midi input" and "Plugin wants midi output". The Introjucer will add the minimal code for a PlugIn and you will want to edit the processBlock callback function in PlugInProcessor.cpp. This function will be called repeatedly and you must call getPlayHead() and check the duration of the supplied audio buffer to see if it is time for you to output a midi note. You can do this by adding a MidiMessage to the MidiBuffer which is supplied to you via the second argument of processBlock(). The same buffer also delivers notes to you from the Midi input port of your plug-in. Does this help?


#3

Hi, Fabian. Your answer took me two steps ahead, and I got getPlayHead() working after some experimentation. Now I can get a rhythmic reference. Still, I´m trying to grab this ´adding a MidiMessage to the MidiBuffer´.

 

1) As a note enters inside the keyswitch range, how do I ignore it (I mean how to keep it from passing to the output)?

2) What if a Note Off in the triggered sequence does not ´fit´ in the acual processblock()? Will I have to queue it to the next? I´m trying to figure how to sync a sequence triggered by a keyswitch with the block, because the sample number is "used to determine the position of the event in the buffer", so if next event should be triggered 1000 samples ahead, but the actual buffer will end before that, will I have to send the midi message in the next buffer considering the offset?

I´m a little confused, but the getPlayHead was exactly the info I needed then!

Thanks.


#4

Hi acabreira, I'm happy your getting along well:

1) Use MidiBuffer.clear() before adding your own midi notes. However, in your case it might be useful to make a local copy of the MidiBuffer before clearing it so that you can search for keyswitch (clear() would otherwise delete this):

void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages)
{

    // create a copy
    MidiBuffer localCopy (midiMessages);

    // clear the output before adding stuff
    midiMessages.clear();

    // now add stuff
    midiMessages.add( .... );
    ...
}

2) To be honest I'm not sure exactly what would happen if you queue a MidiMessage after the length of the audio buffer. My gut feeling is that different DAWs may or may not support it. Best try it out and post your findings here. However, I suspect that some DAWs won't like this and that you will need to queue MidiMessages internally that do not fit into the time range of the current audio buffer.


#5

1) Nice, Fabian, way to go.

"2) ...queue a MidiMessage after the length of the audio buffer"

I was, indeed, thinking of creating a queue internally, but let me see if I get the concept here. I can add a MidiMessage with a timeStamp that is larger than the audio buffer size? If so, I thought it would be discarded, and even if if did not, it would complicate my code rather than simplify it, it seems. The MidiBuffer is of variable size, while the AudioSampleBuffer is of fixed size. right?

So for a rigid timing reference, should I iterate the AudioSamplesBuffer, even if I won´t use it? Is there a significant overhead?

Also, does JUCE have a class to help with queueing? I searched API for ´queue´ and didn´t find anything.

 

Sorry for so many questions, but I´m beginning to understand this processBlock() paradigm only for now.

Thank you very much, Fabian!


#6

I can add a MidiMessage with a timeStamp that is larger than the audio buffer size? If so, I thought it would be discarded

Yes I believe it would be discarded. I haven't tested this though. Therefore you need to queue this internally.

So for a rigid timing reference, should I iterate the AudioSamplesBuffer, even if I won´t use it? Is there a significant overhead?

I'm not sure what you mean with "iterate the AusioSamplesBuffer". All you really want is the length of the AudioSampleBuffer to know how long it is, i.e. now the duration of the current processBlock call.

Also, does JUCE have a class to help with queueing? I searched API for ´queue´ and didn´t find anything.

If you will be adding Midi events to the internal queue with random timings (i.e. you add them in an unordered way), then SortedSet is your friend. If you are already adding the midi messages in an ordered fashion then I guess you could simply use Array. Does anybody have any other ideas? 


#7

Put my hands on dirt yesterday, and with your suggestions I´m good enought for a start. Thanks a lot, Fabian, for your support.

So for a rigid timing reference, should I iterate the AudioSamplesBuffer, even if I won´t use it? Is there a significant overhead?

I'm not sure what you mean with "iterate the AusioSamplesBuffer". All you really want is the length of the AudioSampleBuffer to know how long it is, i.e. now the duration of the current processBlock call.

Yes, I got it, I don´t need the audio buffer at all. And I´m surprised at the low CPU cost of every sample iteraction, I tried some sample accurate math expressions and the processor didn´t drop a sweat, so with MIDI only plugins  I guess I have to worry mostly with GUI interactions.

what about AbstractFifo to implement internal queue?

 

Best Regards.