Help understanding Midi time


#21

But more of the order of magnitude of 5 than 50?

My Midi events are generated by an automaton running on its own timer thread that’s updated 30 times a second. So I’m assuming I wouldn’t be generating events more frequently than that.

Now at 44.1KHz with a buffer size of 1024 in my DAW I assumed I’d be getting about 44 processBlocks per second. Which is OK for an automaton that’s updating 30 times a second.

But it’s true, if I’m only getting 5 processBlocks per second that would be an issue.


#22

Yes, obviously more than 1024 samples is rare, but my point is that you don’t know.


#23

It is unfortunately always an issue, bear in mind almost every host allows now offline bounce, when your timer events are completely unrelated to the timeline your project is rendering. Sou you should treat that problem as independent from the so called wall clock from the start.


#24

I don’t think this can be used in an offline setting.

It’s an automaton that the user or player interacts with “live”.

In a sense it’s more like a virtual controller.

So would it make more sense to use a MidiMessageCollector?


#25

The offline example was meant to illustrate the fact, that the playback, rendering etc. have their own time, and should be treated as such in every setting.
When a plugin GUI interacts with parameters, one update per processBlock is usually good enough. But when you are sending musical material, i.e. samples or midi, it needs to use it’s own consistent timing, otherwise you get random artefacts, that are unpredictable.

I leave the answer if to use a MidiMessageCollector to Jules, I am not an expert with that class…


#26

Hi all,
I am BATTLING with a simple problem.
I want to emulate the “keyboard hold” on my nord G2X.
Basically note-offs are ignored until a new key is pressed which mutes sounding notes.

I have tried , sephamores, sustain pedal messages …
I tried using a seperate midibuffer and swapping-with with the midimessages in the iterator method. I also tried just adding midi events directly to the midibuffer in the iterator.

In my mind what I want to do is :

if (noteon) { all notes off  ( all notes off ; 
                 send note on ( of note pressed ) ;
                }
else if (noteoff) { do nothing ;} 

Eventually I want to actually send chords with note-on presses . I have been trying just to get single notes to work for over a week. 

How should I do this? 

https://forum.juce.com/t/keyboard-hold-latch-midi-notes/31362

Sean

#27

you can accomplish this by manipulating the state of the midiKeyboardState in this tutorial whenever a noteOn message is received
https://docs.juce.com/master/tutorial_synth_using_midi_input.html

just intercept the messages before they get sent to the midiKeyboardState, process accordingly, and send your note-on’s to the mks. if you can’t figure out how to intercept them, ask


#28

Thanks matkat,
The last 10 or so midi processing VST plugins I built in JUCE I used this

processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
        MidiBuffer processedMidi;
        int time;  
     // then ..   
        for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
        {

/// then .. 

if (m.isNoteOn()
{}
if(m.isNoteOff()
{}
midiMessages.swapWith (processedMidi);

/// something like that .. 

So instead of that I should setup an onscreen keyboard and use that class to process midi ?

This version of the plugin works without the keyboard hold ability …

It’s not quickly obvious to me how I would implement what you suggest but perhaps after mulling over it I will sort it out thanks for your help!
Sean


#29

this is roughly what you would do if you were building an app, not a plugin:
in your constructor of the class that inherits from MidiInputCallback:

audioDeviceManager.addMidiInputCallback("", this);

in that class’s midi input callback:

void handleIncomingMidiMessage (MidiInput *source, const MidiMessage &message) 
{
    if( message.isNoteOn() )
    {
        auto allOnNotes = getOnNotes( );
        for( auto note : allOnNotes ) 
        {
             MidiMessage m = MidiMessage::noteOff(channel, note, velocity);
             m.setTimestamp( message.getTimestamp() - 1 ); //- 1 might be needed
             midiMessageCollector.addMessageToQueue( m );
        } 
        auto chord = convertNoteOnIntoChord(message.getNoteNumber());
        for( auto note : chord )
        {
            MidiMessage m = MidiMessage::noteOn(channel, note, velocity);
            m.setTimestamp( message.getTimestamp() );
            midiMessageCollector.addMessageToQueue( m );
        }
    }
}

as per the linked tutorial, do the same getNextAudioBlock for your synth:

void getNextAudioBlock (const AudioSourceChannelInfo& bufferToFill) override
{
    bufferToFill.clearActiveBufferRegion();
    MidiBuffer incomingMidi;
    midiMessageCollector.removeNextBlockOfMessages (incomingMidi, bufferToFill.numSamples); // [11]
    synth.renderNextBlock (*bufferToFill.buffer, incomingMidi,
                           bufferToFill.startSample, bufferToFill.numSamples);
}

Something like that is what you’d do if you were doing an app, not a plugin…

you might use a MidiKeyboardState to just keep track of On/Off notes. nothing says you have to use a MidiKeyboardComponent with it…


#30

Thanks I need to put something inside the processblock in a VST plugin.

I still can’t for the life of me understand why doing stuff like this with the midibuffer doesn’t work …
I am pretty sure there is some sort of race condition happening because occasionally a note will sound …

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();

        MidiMessage m;
        buffer.clear();
    
        MidiBuffer processedMidi;
        int time;
        for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
        {
            if (m.isNoteOn())
            {
                m = MidiMessage::noteOff(m.getChannel(), m.getNoteNumber());
                processedMidi.addEvent(m, time);
            }
                else if (m.isNoteOff()) {
                
                   // processedMidi.clear();
                    m = MidiMessage::noteOn(m.getChannel(), m.getNoteNumber(), 127.0f);
                    processedMidi.addEvent(m, time);
                }
            
            else if (m.isAftertouch())
            {
            }
            else if (m.isPitchWheel())
            {
            }
            
            // processedMidi.addEvent (m, time);
        midiMessages.clear();
        midiMessages.swapWith  (processedMidi) ;
    }

You would think that if you just played one note it would send a note off when you press the key down and a note on when you release the key. I understand that you would send a note off at the beginning with no initial note on . This sort of thing crashes the DAW for some reason …

I will try and figure out how to implement the MessageCollector inside the process block. I can’t see any examples of that anywhere …

Sean


#31

I might be wrong, but I think your for() loop needs to be started this way:

MidiBuffer::Iterator i (midiMessages);
while ( i.getNextEvent (m, time) )
{
    /* do something with `m` at sample position `time` */
}

Check the call stack where the crash happens to figure out the source of the crash.


#32

Well, simply swapping noteOn with noteOff wouldn’t create a latch effect, would it?

What you need is a stateful processor, that knows, if a note is currently playing or not.

From your input you only react on noteOn() and ignore noteOff(). In your private state, you see, if the note is off, then you send a noteOn and add it to the state, or if it is in the state, you send a noteOff() and remove it from the state.

This state could be a simple std::set<int> playingNotes;


#33

“I think your for() loop needs to be started this way:”
I have built plenty of midi plugins using that for() loop without any issues. I can implement it easily and test it. I would think that both code examples iterate to the next midi event before the processing happens. I don’t understand C++ enough to understand that there is any real difference in those 2 ways of doing it. They look identical to me. I am guessing that the iterator will always return true while the buffer is running at the top of those loops.
Checking the call stack is a good idea … It doesn’t really “crash” just stops working sometimes and occasionally generates a spinning wheel in cubase with OSX. @daniel I don’t really mind if note-ons get sent twice for the same note. I do wonder if lots of note-one messages without note-offs ( or vice versa) is causing the DAW or VST synth to freak out. I assume it has a certain amount of “polyphony”. Perhaps some internal container in the synth plugin which stores what notes are playing is overflowing. I guess its worth trying to implement a playingNotes container and changing the for loop before I battle with trying to implement a midiKeyboardState inside a vst plugin.
Thanks for the replies. Sean


#34

its working!
I think the solution was to move the midiMessages.swapWith (processedMidi) outside the loop …

I implemented the playing notes array that might have done it.
I just used a 127 sized int as a boolean to keep track of midi notes …

I declared it globally and initialized it inside prepareToPlay()

void WaylochorderAudioProcessor::processBlock (AudioBuffer<float>& buffer, MidiBuffer& midiMessages)
{
    ScopedNoDenormals noDenormals;
    auto totalNumInputChannels  = getTotalNumInputChannels();
    auto totalNumOutputChannels = getTotalNumOutputChannels();
    jassert (buffer.getNumChannels() == 0);
    auto numSamples = buffer.getNumSamples();


        MidiMessage m;
        MidiMessage n;
    
        buffer.clear();
    
    
        MidiBuffer processedMidi;
        int time;
        for (MidiBuffer::Iterator i (midiMessages); i.getNextEvent (m, time);)
        {
            if (m.isNoteOn())
            {
                // note off for any playing notes
                for ( int j = 0 ; j < 127; j++)
                { if (playing[j] == 1) {
                    n = MidiMessage::noteOff(m.getChannel(), j);
                    processedMidi.addEvent(n, time);
                    playing[j] = 0;
                }
                }

            }
                else if (m.isNoteOff()) {
                    
                    int note = m.getNoteNumber();
                    // if a note is not playing send a note on and set it to "playing"
                    if (playing[note] == 0){
                    n = MidiMessage::noteOn(m.getChannel(), note, 127.0f);
                    processedMidi.addEvent(n, time);
                        playing[note] = 1;
                    }
                    
                }
            
            else if (m.isAftertouch())
            {
            }
            else if (m.isPitchWheel())
            {
            }

            
    }
    midiMessages.swapWith  (processedMidi) ;

Thanks again so happy I don’t have to find an alternative to the midibuffer !!!
Sean


#35

There’s nothing to battle for implementing a midiKeyboardState in your VST.

just add it as a member of your processor, and update it with the contents of the midi buffer as needed:

https://docs.juce.com/master/classMidiKeyboardState.html#aa7afad38f581c129c407a4d7d069bbbb

regarding the while() vs. for() loop, cppreference states that a for() loop is functionally equivalent to this:


{ //scope so init_statement is destroyed when no longer being used
    init_statement 
    while ( condition ) { 
        statement 
        iteration_expression ; 
    }
}

#36

Certainly having the swap inside the loop caused you to throw away data each subsequent iteration of the loop. Not easily seen in the examples you posted, as the indentations are not lined up well, and you have extra blank lines, making the code spread out and harder to read. Having said that, good catch! :slight_smile: oh, and I see you have a midiMessages.clear(); within the loop, which would have thrown away the input after the first element. oh, and I just realized you weren’t just loosing data, but you were modifying the data structure you were iterating over… whew…


#37

thanks cpr!
That’s a good lesson to clean up code as much as possible before posting !!
Sean


#38

I would say this depends on the instrument you are feeding in. A Synthesiser might assume, that each noteOn is a physical key, and ignore subsequent noteOns.
If it is a virtual orchestra, you wouldn’t want that behaviour. There are enough other violins, that still start a note, that another violin is already playing.
That’s why I believe you will get in trouble at some point, if you are careless with matching noteOn and noteOffs…

But I was actually pointing to the fact, that a noteOn for your use case does something different, depending of the fact, if there was a noteOn before. That’s what I meant with the intermediate state. While the noteOff never would do anything in case of a latch mode.

just my 2 pennies