Breath MIDI messages


#1

Hi together,

i want consume MIDI breath messages. In the documentation i can see that the controller has number 2 (coarse) / 34 (fine) and the value is 14bit. Is there an easy way to consume this message and get the value?

I don’t see anything in the MIDI message. Any help is welcome.


#2

Easy is perhaps a slight exaggeration.

On the surface it might look easy, just do something like
hiResBreathValue = controllerValue_coarse << 7 + controllerValue_fine. But just because it’s divided in two events, we have to handle several cases; the message might consist of just the coarse value or just the fine value, the coarse might come before the fine and vv.

In practice it might be as complicated as in the following code excerpt, where one loops through the available midimessages collecting breath controller messages (hi and lo) with the same timestamp.

{
	const auto MSB = 2;
	const auto LSB = 2 + 32;
	auto pendingBreathValue = 0;
	auto breathMessageTimeStamp = std::numeric_limits<double>::max();

	while (MidiMessage *midiEvent = getNextEventFromSomeWhere())	//assume nullptr when no more midi events available
	{
		auto timeStamp = midiEvent->getTimeStamp();

		// if we're past the timestamp of our pending breath controller events, assume we have a valid breath value
		if (breathMessageTimeStamp < timeStamp)
		{
			updateBreathHandlerWithNewValue((float)pendingBreathValue / (1 << 14));	//normalize to 0 .. 1.0f

			//flag that we have consumed the breath event (but leave pendingBreathValue intact, in case we receive LSB w/o a new MSB)
			breathMessageTimeStamp = std::numeric_limits<double>::max();
		}
		if (midiEvent->isController())
		{
			if (midiEvent->getControllerNumber() == MSB)
			{
				pendingBreathValue = midiEvent->getControllerValue() << 7;	//N.B zero LSB acc to midi spec

				// record the time, to compare with next midi event
				breathMessageTimeStamp = timeStamp; 
			}
			else if (midiEvent->getControllerNumber() == LSB)
			{
				pendingBreathValue |= midiEvent->getControllerValue() & 0x7f;
				breathMessageTimeStamp = timeStamp;
			}
			else
			{
				//handle other controller values
			}
		}
		else
		{
			//handle other midi messages
		}
		highresBreathValue = controllerValue(2) << 7 + controllerValue(34)
	}

This assumes we can look forward in time, i.e we are playing a midi file, i.e we can check the time stamp of the next midi event and thus determine if we have a complete breath event.

Unfortunately, the are things like live performances that complicates things; imagine pressing a note, sucking your controller and holding it steadily - there might be no midi events occuring until you change your breath - then the previous message will be released. I don’t know if this is a real world problem though, maybe it’s impossible to suck or breath with a resolution of 14 bits, or even 7 bits. Otherwise you might want to change

if (breathMessageTimeStamp < timeStamp)

into something like

if (breathMessageTimeStamp < timeStamp || enoughRealTimeGoneBy())

Hope this helps…


#3

hmmm, that’s what i expected. Thanks for the code. I will go in this direction. I already had to implement 14 bit controller support for some of our older plugins and it was the same mess.

@juceteam would be great to see a 14 bit implementation of the breath message like you did for the pitch wheel. Optional 14 bit MIDI support would be great too.