Question about memory allocation in processBlock


#1

I’ve been doing some basic experiments creating MIDI events on the fly from inside the processBlock callback, stashing them away so that I can actually send them out later on. In my first attempt, I just used the STL priority_queue with the default Vector to contain MidiMessage objects. I found that quite soon, MainStage (part of Logic Studio) would display overloads, audio would cut out for a while, sometimes come back later. I also found occasional timing glitches.

After eliminating everything else from the processBlock, I concluded that I might be having a problem due to the STL reallocating memory, etc. I was able to improve things noticibly by reserving space in the vector, but I still saw the problem, just less frequently.

I then threw out the STL (no big loss!) and created a simple array-based priority queue. This improved things dramtically and rather than the few minutes of things working OK, I was able to get 15-20 minutes or so with mostly no problems but I would “occasionally” see a very minor timing delay.

Ultimately, MainStage crashed and, looking at the stack trace, I noted that the crash happened during a MidiMessage copy constructor. I realized that even the MidiMessage class uses dynamic allocation to store the MIDI data.

So I have a couple of questions:

  1. What’s the recommended approach for managing memory inside the processBlock callback if one needs to create lots of MidiMessage events?
  2. This issue sort of hints at the notion of having a base MidiMessage class and then subclassing it with various implementations to handle simple MIDI events vs. sysex. The former could work using a simple array (on the stack). Is there a reason it wasn’t done this way or is this just legacy?

Thanks,
D


#2

A midibuffer would be a better way to create the events, because it doesn’t allocate for the individual events, it just puts them all in one big buffer, which you can have already preallocated.

But I don’t think there’s any alternative to the way the midimessage class works currently - for short events it doesn’t have to allocate, but for sysexes and other events there’s no alternative. There’s no way you could avoid that with inheritance or any other tricks, except maybe some kind of shared pool of pre-allocated buffers. But that’s not really something that I’d want to do in the basic class, you’d have to roll your own if you were going down that route.


#3

As far as I can tell, the problem with a midibuffer is that it doesn’t really provide the ability to manipulate the data after you’ve scheduled it. You can’t get at the events to change them, change the timestamp, or do things like match up NoteOn and NoteOff events, etc

I think the model of a base class (partially abstract) that has all the methods in it and then a couple of subclasses, one for short events, the other for sysex stuff is the way to go with this stuff. So something like

class MidiMessage
{
};


class ShortMidiMessage : public MidiMessage
{
   protected:
      byte data[3];
}

class LongMidiMessage : public MidiMessage
{
   protected:
      byte* data;
}

It would then be simple for users of the class to replace the subclasses with specialized memory allocation to manage a pool of shared buffers to suit.

Of course, because arrays and pointers are the “same” in the C world, one could fold this into a single class and just do

class MidiMessage
{
   protected:
      byte shortData[3];
      byte* longData;



      byte* data;  // Assign shortData or longData to this as appropriate
}

or if one really wanted, shortData and longData could be in a union.

I realize you would probably not want to do something like this but I’m wondering whether it’s worth my modifying my version to work this way so I can more easily do the stuff in processCallback and leverage the fact that the rest of juce works with a base MidiMessage object.


#4

Er… you seem to be describing something very similar to the existing MidiMessage class there. Like I said, it already avoids allocating for short messages.


#5

Duh ---- I just took another look at the class definition. I misread the #ifndef DOXYGEN and ignored it, focusing ONLY on the uint* data.

Sigh —

Sorry for the waste of posts!

D