How to display the last played midi note on the UI

@cpr2323
I’d like to debug it but the C++ env is totally new for me

  • how can I debug a plugin while running it in the DAW? is it possible?
  • or where can I see the log messages while running the plugin in a DAW?
  • how can a test mimic the playing of a few midi notes? is there an example of a similar unit test?

There’s no really proper way to use ValueTrees thread safely. Note that AudioProcessorValueTree state is a bit different since it has the part that deals with the plugin parameters and that is thread safe. But the original poster’s problem isn’t about plugin parameters, but other data that needs to be passed from the audio thread to the GUI thread.

a) Doh! I totally forgot about the OP, and was thinking I was responding to them. Lol

b) You are right. VT’s are not thread safe. Except in this usage it would be safe, if one end writes and the other end uses a listener. I don’t understand the read access violation the 2nd person was seeing. I use VT’s in this manner (1 writer, X listeners, across threads) all the time. Mind you, in my code a listener callback which updates the UI gets them deferred through a callAsync.

played around with the code and realized, the label can be refreshed by the change of an atomic value as @cpr2323 mentioned but my issue is that I have zero midiMessges in

void MidiDiffAudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::MidiBuffer& midiMessages)

tried with a midi file or with a midi keyboard. what should be setup to use these sources?
(I’m trying the plugin in Cakewalk)

oh my gosh. it’s working in Reaper! but why does it not work in Cakewalk?

Hi there! Another approach (and a good pattern to learn as well!) would be to use a std::function, which functions somewhat like a “bridge”.

Declare the std::function in your audio processor:

std::function<void (int)> updateMidiNotesLabel;

Call the std::function in your processBlock:

for (const auto midiMessage : midiMessages)
{
    if (midiMessage.getMessage().isNoteOn() && updateMidiNotesLabel != nullptr)
    {
        juce::MessageManager::callAsync ([=]() 
        { 
            updateMidiNotesLabel (midiMessage.getMessage().getNoteNumber()); 
        });
    }
}

And tell the editor what to do when the std::function is called. Do so in the editor’s constructor, using a lambda:

audioProcessor.updateMidiNotesLabel = [this] (int noteNumber) 
{  
    midiNotesLabel.setText (juce::String (noteNumber), juce::dontSendNotification); 
};

That’s it! This approach works without a timer.

Note that MessageManager::callAsync allows you to call the message thread from the audio thread.

Note also that I’ve included a check to see if the std::function has been told to do something when it’s called (i.e. if it’s not nullptr). Calling an unassigned std::function causes a runtime error.

1 Like

Unfortunately, callAsync may be thread safe but it is not real time safe. There’s a quite complicated mechanism under the hood that happens with callAsync and you might not want all that stuff happening in your real time audio thread.

Could you explain this a bit further? In which circumstances would the above cause problems?

@aamf thanks, that is something that I was looking for. and for my current use case, it does not need to happen in real-time. but the timer will be useful for me later…

1 Like

All the code running in processBlock is most often (more or less) real time. You should avoid at all costs doing anything there that might slow down the execution needlessly. So, that would be things like calling callAsync.

That one should limit unecessary calls, yes, by all means. But to “avoid at all costs” using callAsync from the realtime thread? To me that sounds like an unnecessary limitation.

Even in my ageing system I can get away with something as grotesque as 10,000 unnecessary calls before each “proper” call. I inserted the following aberration in the code above for a very simple test and everything still worked as expected.

for (int i = 0; i < 10000; ++i)
{
    juce::MessageManager::callAsync ([=]() 
    { 
        int note = juce::Random::getSystemRandom().nextInt (127);
        updateMidiNotesLabel (note); 
    });
}

Of course we could go ahead and profile this properly.

But is this something the OP should be concerned about? It would be very easy to limit the number of calls if they become problematic.

I am probably missing something else here though…

Did you also have audio running and audible in the host while doing that test? (I know the original poster is apparently working on some kind of MIDI-only thing, but the code would still be running in the audio thread.) I’d actually be pretty impressed if that works properly together with audio running. I suppose I could test the same thing in my audio producing plugin to see what happens…

Well, sort of. I was repeatedly playing the test tone. Above about 10,000 calls and I started getting glitches. This is of course a veeery unscientific test!

Limiting the number of async calls to, say, just one or a few per process block, would that be acceptable?

What I would like to know if it’s just the performance hit of using callAsync what’s of concern or if there’s something else at play.

The problem with callAsync is that it allocates memory from the heap and makes a system call to do the actual cross thread messaging thing. Those operations have unpredictable execution times. With bad luck, even a single callAsync call in the processBlock code could cause an audio glitch. Or it might happen that even an unreasonable amount of those can be made without anything bad apparently happening.

I think it implemented the right way, plus it looks very simple and elegant, without the need for the infamous timer.

In the AudioProcessor create a Listener that will contain the last note and the value that will be shared with the UI, as well as the callback function that will update the value asynchronously

 struct NoteListener : public juice::ChangeListener
 {
     int midiNote = 0;
     juce::Value value;

     void changeListenerCallback(juce::ChangeBroadcaster* source) override
     {
         value.setValue(midiNote);
     }

 } noteListener;

in the UI assign the Value to the label

labelNote.getTextValue().referTo(audioProcessor.noteListener.value);

In the processor’s constructor add the listener to a changeBroadcaster

changeBroadcaster.addChangeListener(&noteListener);

Now you can update the note in the audio thread, and report the change to the broadcaster, although in this case I use a simple counter for testing.

 static int count = 0;
 noteListener.midiNote = count++;
 changeBroadcaster.sendChangeMessage();

(instead of an integer you can use a string to display in the label)

noteListener.midiNote = "Midi note: " + juce::String(n);

I have maintained it for a long period and there have been no problems, unlike when I modified it directly.

Since in my case it is only for debugging I am not worried and I will continue using it to validate its reliability, although in the future I would like to be able to implement information in the UI about what is happening in the process. So this solution would be the one chosen if there is no problem.

Those are good reasons indeed! That’s what I was after. Good to know. Thanks!

Thanks for keeping up on my sloppy comments. I’m sitting in a hospital bed recovering from surgery, so maybe I should refrain from offering advice! :rofl: My comments about threads/VTs we’re not in the realtime context.

Yes, this is more or less how I would approach it for this case of a simple text display. People get so complicated with finding ways to have the audio processor push data to the display in “real time”, and most of the time it really doesn’t matter. How fast can a user read an updated MIDI note display in any useful manner anyways? An Editor with a Timer polling for the latest value at 10 Hz is likely plenty fast enough.

Seconding that. This is a pretty trivial case, because:

  • There is not much data involved. Let’s say one integer.
  • Communication is one way, audio only writes, UI only reads.
  • so, if it’s just the last played midi note, an integer atomic will work just fine.

For the slightly more general case, here is one simple and easy to implement pattern:

Use a simple data structure to hold the data you want to communicate to the UI. In addition to your data carrying members, give it a ReadWrite lock.

AudioThread:
Find exactly one place where you can fill this struct in the most efficient way possible. E.g. at the end of processBlock. Then use tryEnterWrite on the lock. If the lock was acquired, fill it. If not, skip it.

UI Thread:
Use a Timer for doing a refresh of the realtime feedback data (polling). In timerUpdate, lock for reading. In case the audio thread is still writing, this will block for a negligible amount of time. Then make a copy of the data to use in the UI, e.g. for your paint().

Rationale:
We’re making a few assumptions here:

  1. both the actual write as well as the read are very fast.
  2. it is very improbable that acquiring the write lock in the audio callback fails
  3. in case acquiring the write lock does fail, nothing breaks. this is suitable for cases like the one mentioned here, if it is unacceptable to skip any one-time information from the audio thread, use a different approach.
  4. tryEnterLock is acceptable

Bonus/Alternative: Use an atomic flag “uiWantsData”. If true, fill data on the audio thread. Afterwards, set it to false (use uiWantsData.exchange(false)). UI polls this flag. If it’s false, have the UI copy the data out of this struct, then set uiWantsData to true, again using exchange. This will lead to a back and forth between UI and Audio. It only works if you have exactly one reader and exactly one writer, though.

Hope that gave a few additional ideas. The whole topic of wait free synchronization can get extremely involved.
Recommended reading:
“Wait-free Synchronization” by Maurice Herlihy
That’s hard core computer science, but pretty relevant for audio programming.

3 Likes