I get that the MidiBuffer class and MidiBuffer::Iterator class are designed to work with the low-level packed data, but it just seems like it’s designed to not be easy or intuitive to use. I’m trying to figure out how to build a circular FIFO using it (because an Array wouldn’t work as the midiMessage sizes vary) and ran the API past the ol ##c++ people and they all brought out so much hate against it, you’d think I was at a trump rally holding up a Clinton sign!
I’m looking at this method in juce_Array.h:
void removeInternal (const int indexToRemove)
{
--numUsed;
ElementType* const e = data.elements + indexToRemove;
e->~ElementType();
const int numberToShift = numUsed - indexToRemove;
if (numberToShift > 0)
memmove (e, e + 1, ((size_t) numberToShift) * sizeof (ElementType));
minimiseStorageAfterRemoval();
}
since the MidiMessages can potentially all have unique sizes, could you even remove elements from an Array of MidiMessages? would you have a bunch of memory collisions?
jules
November 30, 2016, 2:46pm
2
You’re misunderstanding how MidiMessage works. It doesn’t vary in size, and it’s totally OK to have an Array if you want to.
uh, what about this part?
private:
//==============================================================================
#ifndef DOXYGEN
union PackedData
{
uint8* allocatedData;
uint8 asBytes[sizeof (uint8*)];
};
PackedData packedData;
double timeStamp;
int size;
#endif
inline bool isHeapAllocated() const noexcept { return size > (int) sizeof (packedData); }
inline uint8* getData() const noexcept { return isHeapAllocated() ? packedData.allocatedData : (uint8*) packedData.asBytes; }
uint8* allocateSpace (int){
if (bytes > (int) sizeof (packedData))
{
uint8* d = static_cast<uint8*> (std::malloc ((size_t) bytes));
packedData.allocatedData = d;
return d;
}
return packedData.asBytes;
}
};
I guess the actual data pointed to by packedData.allocatedData wouldn’t be stored within the Array ?
I managed to piece together something that seems to work well, tho I haven’t tested it extensively.
AbstractFifo abstractFifo(31200 * 60 * 3);
Array<MidiMessage> midiBuffer;
midiBuffer.ensureStorageAllocated(31200*60*3); ////midi rate is 31.2kbps. that's bytes, not messages. so, plan for 31,200 messages per second * 60 seconds * 3.
void MidiRecorder::addToFifo(const juce::MidiMessage &oneMessage, int numItems) {
int start1, size1, start2, size2;
abstractFifo.prepareToWrite(numItems, start1, size1, start2, size2);
if( size1 > 0 ) {
midiBuffer[start1] = oneMessage; //we're only storing one midiMessage at a time, but just in case.
}
if( size2 > 0 ) {
//copySomeData (myBuffer + start2, someData + size1, size2); //where, what, how much
}
abstractFifo.finishedWrite(size1+size2);
}
void MidiRecorder::readFromFifo(std::vector<MidiMessage> &messages, int numItems) {
int start1, size1, start2, size2;
abstractFifo.prepareToRead (numItems, start1, size1, start2, size2);
DBG( "\nreadFromFifo(" + String(numItems) + " items)" );
DBG( "1st read position: " + String(start1)
+ " num you can read: " + String(size1)
+ " 2nd read position: " + String(start2)
+ " num you can read: " + String(size2) );
DBG( "messages.size() pre-read1: " + String(messages.size() ) );
int s = messages.size();
if (size1 > 0) {
//copySomeData (someData, myBuffer + start1, size1); //to where, from what, how much
for( int i = 0; i < size1; ++i) {
messages.push_back( midiBuffer[start1+i] );
}
}
DBG( "messages.size() post-read1: " + String(messages.size() ) );
if (size2 > 0) {
for( int i = 0; i < size2; ++i) {
messages.push_back( midiBuffer[start2+i] );
}
//copySomeData (someData + size1, myBuffer + start2, size2); //to where, from what, how much
}
DBG( "messages.size() post-read2: " + String(messages.size() ) );
abstractFifo.finishedRead (size1 + size2);
DBG( "size1+size2: " + String( size1+size2) );
DBG( "messages.size() grew by: " + String( messages.size() - s ));
}
void MidiRecorder::handleIncomingMidiMessage(juce::MidiInput *source, const juce::MidiMessage &message) {
if( !saveTimerIsRunning ) {
startTime = Time::getMillisecondCounterHiRes();
startTimer(10000);//10 seconds for testing. more like 2 minutes in practice.
saveTimerIsRunning = true;
}
MidiMessage m = message;
m.setTimeStamp(Time::getMillisecondCounterHiRes() - startTime);
addToFifo(m, 1);
numEventsReceived++;
}
void MidiRecorder::timerCallback() {
//stopTimer();
DBG("numEvents received: " + String(numEventsReceived));
if( abstractFifo.getNumReady() < 1 ) { return; }
MidiFile mf;
mf.setTicksPerQuarterNote(960 * 0.65f);
int microsecondsPerQuarter = (60000.f / tempo) * 1000.f;
MidiMessage tempoEvent = MidiMessage::tempoMetaEvent(microsecondsPerQuarter);
tempoEvent.setTimeStamp( startTime );
MidiMessageSequence mms;
mms.addEvent(tempoEvent); //tempo is 96bpm
/*
read numEventsWritten into our mms using readFromFifo(
*/
std::vector<MidiMessage> newMessages;
//newMessages.resize(abstractFifo.getNumReady());
readFromFifo(newMessages, abstractFifo.getNumReady());
DBG("MidiRecorder::timerCallback() number of new messages: " + String(newMessages.size() ) );
for( auto msg : newMessages ) {
mms.addEvent(msg);
}
DBG("MidiRecorder::timerCallback() number of events added to MMS: " + String(mms.getNumEvents()) );
mms.updateMatchedPairs();
mms.sort();
mf.addTrack(mms);
DBG( "MidiRecorder::timerCallback() number of tracks in midi File: " + String(mf.getNumTracks() ) ); //should be 1
DBG( "MidiRecorder::timerCallback() number of events in track 0: " + String(mf.getTrack(0)->getNumEvents()) );
DBG( " ");
//do whatever you need to do with this midi file
}
I didn’t realize //newMessages.resize (abstractFifo.getNumReady()); actually created a bunch of empty MidiMessages (http://en.cppreference.com/w/cpp/container/vector/resize see DefaultInsertable) so my midiFiles were twice the size as I was expecting.