Trouble dynamically adding MidiMessageCollector()


#1

Basically, I am trying record the midi performance data of two people on one machine. The midi is sampled at 100hrz, and each performer is using two separate MIDI busses. I am still unsure what format I am going to to save the data as, but I know I will want two files, with time stamps, and that after each sample all midi data for a single performer will be collated into a vector (so two devices would end up as 256 elements in the array).

I pulled apart the MidiInputSelectorComponent to start with, and got it to enable/disable midi ports. Now I’m trying to create a separate thread for each performer (although I guess I could sample both performers from a single thread), that holds dynamically created MidiMessageCollectors. The idea being that I call

and hard link the MidiMessageCollector to a single device input. Then in my thread, I was iterating over the MidiMessageCollectors and calling their “removeNextBlockOfMessages()” to pack them into the data struct (which is just a text file right now).

the problem is that I am getting an error: “JUCE Assertion failure in /Developer/juce/amalgamation/…/src/audio/midi/juce_MidiMessageCollector.cpp, line 83”

Which I think is related to setting the sampling rate, but you can see below that I do call the reset() function?

//midiHandlers is my collection of MidiMessageCollectors midiHandlers[pos]->reset(44100.00);

I think it might be due to the thread trying to access the MidiMessageCollector before I have a chance to reset it? I’m not sure what it is really?

Sorry if I sound off the wall, I am pretty new to JUCE. If there is a better way to approach this, I would be all ears :slight_smile:

Here is the code to add the MidiMessageCollectors. I just add them from a listbox of available midi devices.

#include "MidiDataPackagingThread.h"

MidiDataPackagingThread::MidiDataPackagingThread(AudioDeviceManager& deviceManager_)
	:	Thread ("Midi Packaging Thread"),
		deviceManager (deviceManager_)
{
        //hard set for now = to 10ms
	bufferSize = 44100/100;
	
	_pMIDIlog = new File("../MIDIlog.txt");	
	_pMIDIlog->replaceWithText("");

	
	startThread (10);
}

MidiDataPackagingThread::~MidiDataPackagingThread()
{
	// allow the thread 2 seconds to stop cleanly - should be plenty of time.
	stopThread (2000);
}

void MidiDataPackagingThread::run()
{
	// threadShouldExit() returns true when the stopThread() method has been
	// called, so we should check it often, and exit as soon as it gets flagged.
	while (! threadShouldExit())
	{
		// sleep a bit so the threads don't all grind the CPU to a halt..
		wait (10);
		
		// fill a midi buffer with incoming messages from the midi input.
		MidiBuffer incomingMidi;
		for(int i = 0; i < midiHandlers.size(); i++)
		{
			if(midiHandlers[i] != NULL)
			{
				midiHandlers[i]->removeNextBlockOfMessages (incomingMidi, bufferSize);
				
				if(!incomingMidi.isEmpty())
				{
					_pMIDIlog->appendText("Device " + String::String(i));
					_pMIDIlog->appendText("\n");
					
					MidiBuffer::Iterator midiIterator(incomingMidi);
					MidiMessage msg(0x80,0,0,0);
					int pos;
					
					while(midiIterator.getNextEvent(msg, pos)) 
					{
						if(msg.isController())
						{
							_pMIDIlog->appendText("Velocity: " + String::String(msg.getControllerValue()) 
                                                        + "   ControllerNumber: "     
                                                        + String::String(msg.getControllerNumber()) 
                                                        + "   TimeStamp: " 
                                                        + String::String(msg.getTimeStamp()) + "\n");			
						}
						else
						{
							_pMIDIlog->appendText("Velocity: " + String::String(msg.getFloatVelocity()) 
                                                        + "   NoteNumber: " 
                                                        + String::String(msg.getNoteNumber()) 
                                                        + "   TimeStamp: " 
                                                        + String::String(msg.getTimeStamp()) + "\n");			
						}
					}
					
					_pMIDIlog->appendText("******************************************************");
					_pMIDIlog->appendText("\n");
				}	
			}
		}
	}	
}

void MidiDataPackagingThread::addMidiHandler(int pos, const String& midiDevice_)
{
	midiHandlers.insert(pos, new MidiMessageCollector());
	//This needs to be set by the global sr note by a constant OV 2011
	deviceManager.addMidiInputCallback (midiDevice_, midiHandlers[pos]);
	midiHandlers[pos]->reset(44100.00);
}

void MidiDataPackagingThread::removeMidiHandler(int pos, const String& midiDevice_)
{
	deviceManager.removeMidiInputCallback (midiDevice_, midiHandlers[pos]);
	midiHandlers.remove(pos);
}

and a picture if that helps at all.


#2

Okay, I am still very confused, but have figured out a few things.

  1. I can avoid a crash if I first enable all the MIDI devices (the order doesn’t matter). And then next I enable which devices the performer will be using (But I have to turn them on sequentially i.e. IAC 1, then IAC 2, then the next device etc etc). If I try to turn on the first device, and then the third device… bang! errors saying I didn’t set the Sample rate of the [i]MIDIMessageCollector/i

  2. When I turn everything on and log the MIDIMessageCollectors

It seems to put messages from other devices into the current one I’m logging. For example, I have three MIDIMessageCollectors in my array (each tied to a specific device), and when I ask for device 1’s block of MIDI messages, I’ll get its messages + some from device number 2 ?

Again, maybe I’m going about this the wrong way?


#3

Found the problem, although unclear the best way to handle it.

  1. I am trying to enable/disable my DataLog class from listening to the MIDI coming in on enabled MIDIDevice ports. There is one of these DataLog classes for each performer.
  2. when I do this I am using an OwnedArray. I was trying to say “insert” (or set) this position in the OwnedArray (say position 3), but it clearly says in the docs that it just append the end of the current array if you add beyond the existing size… So my “insert at position 2” becomes add to position 0 if the array is currently empty.

Still broken… :frowning:


#4

Scrapped it all and started from the top. Basically, the issue was that you can’t add a MIDIMessageCollector to the addMidiCallback without the port first being enabled. Makes sense, as why register and listen if its not turned on… The only trouble was I wanted to create “performer Profiles” that consisted of known Midi Ports. My solution was to populate separate lists of “enabled” Midi ports for each performer to choose from.