changeset 197:265a527f8be8

Added MidiParser for channel messages
author Giulio Moro <giuliomoro@yahoo.it>
date Fri, 05 Feb 2016 06:16:35 +0000
parents 0029562391b1
children 62f6269f4b3e
files core/Midi.cpp include/Midi.h projects/basic_midi/render.cpp
diffstat 3 files changed, 331 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/core/Midi.cpp	Thu Feb 04 18:41:30 2016 +0000
+++ b/core/Midi.cpp	Fri Feb 05 06:16:35 2016 +0000
@@ -12,14 +12,68 @@
 #define kMidiInput 0
 #define kMidiOutput 1
 
+
+midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength]=
+{
+	0x80,
+	0x90,
+	0xA0,
+	0xB0,
+	0xC0,
+	0xD0,
+	0xE0,
+	0
+};
+
+unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength]={2, 2, 2, 2, 1, 1, 2, 0};
+
 bool Midi::staticConstructed;
 AuxiliaryTask Midi::midiInputTask;
 AuxiliaryTask Midi::midiOutputTask;
 std::vector<Midi *> Midi::objAddrs[2];
 
+int MidiParser::parse(midi_byte_t* input, unsigned int length){
+	unsigned int consumedBytes = 0;
+	for(unsigned int n = 0; n < length; n++){
+		consumedBytes++;
+		if(waitingForStatus == true){
+			int statusByte = input[n];
+			MidiMessageType newType = kmmNone;
+			if (statusByte >= 0x80){//it actually is a status byte
+				for(int n = 0; n < midiMessageStatusBytesLength; n++){ //find the statusByte in the array
+					if(midiMessageStatusBytes[n] == (statusByte&0xf0)){
+						newType = (MidiMessageType)n;
+						break;
+					}
+				}
+				elapsedDataBytes = 0;
+				waitingForStatus = false;
+				messages[writePointer].setType(newType);
+				messages[writePointer].setChannel((midi_byte_t)(statusByte&0xf));
+				consumedBytes++;
+			} else { // either something went wrong or it's a system message
+				continue;
+			}
+		} else {
+			messages[writePointer].setDataByte(elapsedDataBytes, input[n]);
+			elapsedDataBytes++;
+			if(elapsedDataBytes == messages[writePointer].getNumDataBytes()){
+				waitingForStatus = true;
+				writePointer++;
+				if(writePointer == messages.size()){
+					writePointer = 0;
+				}
+			}
+		}
+	}
+	return consumedBytes;
+};
+
+
 Midi::Midi(){
 	outputPort = -1;
 	inputPort = -1;
+	inputParser = 0;
 	size_t inputBytesInitialSize = 1000;
 	inputBytes.resize(inputBytesInitialSize);
 	outputBytes.resize(inputBytesInitialSize);
@@ -37,6 +91,18 @@
 }
 
 Midi::~Midi(){}
+
+void Midi::enableParser(bool enable){
+	if(enable == true){
+		delete inputParser;
+		inputParser = new MidiParser();
+		parserEnabled = true;
+	} else {
+		delete inputParser;
+		parserEnabled = false;
+	}
+}
+
 void Midi::midiInputLoop(){
 	printf("Midi input loop %d\n", objAddrs[kMidiInput].size());
 	for(unsigned int n = 0; n < objAddrs[kMidiInput].size(); n++){
@@ -66,9 +132,32 @@
 		if(inputBytesWritePointer == inputBytes.size()){ //wrap pointer around
 			inputBytesWritePointer = 0;
 		}
+		if(parserEnabled == true && ret > 0){ // if the parser is enabled and there is new data, send the data to it
+			int input;
+			while((input=getInput()) >= 0){
+				midi_byte_t inputByte = (midi_byte_t)(input);
+//				printf("__0x%x\n", input);
+				inputParser->parse(&inputByte, 1);
+			}
+//			if(inputBytesReadPointer == inputBytesWritePointer){
+				// this should never be the case, it should only happen when ret == 0
+//			} else {
+
+//				unsigned int length = (inputBytesWritePointer - inputBytesReadPointer + inputBytes.size())
+//						% inputBytes.size();
+//				rt_printf("inputBytes: 0x%x 0x%x 0x%x", inputBytes[inputBytesReadPointer],
+//						inputBytes[inputBytesReadPointer+1], inputBytes[inputBytesReadPointer+2]);
+//				length = inputParser->parse(&inputBytes[inputBytesReadPointer], length);
+//				inputBytesReadPointer += length; //if parserEnabled, the readPointer is incremented here,
+//											     //so calls to getInput() should be avoided
+//				if(inputBytesReadPointer == inputBytes.size()){
+//					inputBytesReadPointer = 0;
+//				}
+//			}
+		}
 		if(ret < maxBytesToRead){ //no more data to retrieve at the moment
 			usleep(1000);
-		} // otherwise there might be more data ready to be read, so don't sleep
+		} // otherwise there might be more data ready to be read (we were at the end of the buffer), so don't sleep
 	}
 }
 
@@ -85,21 +174,10 @@
 		int ret;
 		ret = write(outputPort, &outputBytes[outputBytesReadPointer], sizeof(midi_byte_t)*length);
 		if(ret < 0){ //error occurred
-//			if(errno != EAGAIN){ // () would return EAGAIN when no data are available to read just now
-//				rt_printf("Error while writing midi %d\n", errno);
-//			}
 			rt_printf("error occurred while writing: %d\n", errno);
 			usleep(10000); //wait before retrying
 			continue;
 		}
-
-//		inputBytesWritePointer += ret;
-//		if(inputBytesWritePointer == inputBytes.size()){ //wrap pointer around
-//			inputBytesWritePointer = 0;
-//		}
-//		if(ret < maxBytesToRead){ //no more data to retrieve at the moment
-//			usleep(1000);
-//		} // otherwise there might be more data ready to be read, so don't sleep
 	}
 }
 int Midi::readFrom(int port){
@@ -129,6 +207,9 @@
 }
 
 int Midi::getInput(){
+//	if(parserEnabled == true) {
+//		return -3;
+//	}
 	if(inputPort < 0)
 		return -2;
 	if(inputBytesReadPointer == inputBytesWritePointer){
@@ -141,6 +222,13 @@
 	return inputMessage;
 }
 
+MidiParser* Midi::getParser(){
+	if(parserEnabled == false){
+		return 0;
+	}
+	return inputParser;
+};
+
 int Midi::writeOutput(midi_byte_t byte){
 	return writeOutput(&byte, 1);
 }
@@ -152,3 +240,29 @@
 	else
 		return 1;
 }
+
+MidiChannelMessage::MidiChannelMessage(){};
+MidiChannelMessage::MidiChannelMessage(MidiMessageType type){
+	setType(type);
+};
+MidiChannelMessage::~MidiChannelMessage(){};
+MidiMessageType MidiChannelMessage::getType(){
+	return _type;
+};
+int MidiChannelMessage::getChannel(){
+	return _channel;
+};
+//int MidiChannelMessage::set(midi_byte_t* input);
+//
+//int MidiControlChangeMessage ::getValue();
+//int MidiControlChangeMessage::set(midi_byte_t* input){
+//	channel = input[0];
+//	number = input[1];
+//	value = input[2];
+//	return 3;
+//}
+
+//int MidiNoteMessage::getNote();
+//int MidiNoteMessage::getVelocity();
+
+//midi_byte_t MidiProgramChangeMessage::getProgram();
--- a/include/Midi.h	Thu Feb 04 18:41:30 2016 +0000
+++ b/include/Midi.h	Fri Feb 05 06:16:35 2016 +0000
@@ -13,9 +13,194 @@
 
 typedef unsigned char midi_byte_t;
 
+
+typedef enum midiMessageType{
+	kmmNoteOff = 0,
+	kmmNoteOn,
+	kmmPolyphonicKeyPressure,
+	kmmControlChange,
+	kmmProgramChange,
+	kmmChannelPressure,
+	kmmPitchBend,
+	kmmNone,
+	kmmAny,
+} MidiMessageType;
+#define midiMessageStatusBytesLength 7+2 //2 being kmmNone and kmmAny
+
+extern midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength];
+extern unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength];
+
+class MidiChannelMessage{
+public:
+	MidiChannelMessage();
+	MidiChannelMessage(MidiMessageType type);
+	virtual ~MidiChannelMessage();
+	MidiMessageType getType();
+	int getChannel();
+	unsigned int getNumDataBytes(){
+		return midiMessageNumDataBytes[(unsigned int)_type];
+	}
+	void setDataByte(unsigned int dataByteIndex, midi_byte_t input){
+		_dataBytes[dataByteIndex] = input;
+	}
+	void setType(MidiMessageType type){
+		_type = type;
+		_statusByte = midiMessageStatusBytes[_type];
+	}
+	void setChannel(midi_byte_t channel){
+		_channel = channel;
+	}
+	midi_byte_t getDataByte(unsigned int index){
+		return _dataBytes[index];
+	}
+	void clear(){
+		for(int n = 0; n<maxDataBytes; n++){
+			_dataBytes[n] = 0;
+		}
+		_type = kmmNone;
+		_statusByte = 0;
+	}
+	void prettyPrint(){
+		rt_printf("MessageType: %x,  ", this->getType());
+		rt_printf("channel: %u, ", this->getChannel());
+		for(int n = 0; n < this->getNumDataBytes(); n++){
+			rt_printf("data%d: %d, ", n + 1, this->getDataByte(n));
+		}
+		rt_printf("\n");
+	}
+
+private:
+	const static int maxDataBytes = 2;
+protected:
+	midi_byte_t _statusByte;
+	midi_byte_t _dataBytes[maxDataBytes]; // where 2 is the maximum number of data bytes for a channel message
+	MidiMessageType _type;
+	midi_byte_t _channel;
+};
+/*
+class MidiControlChangeMessage : public MidiChannelMessage{
+	int number;
+	int value;
+public:
+	int getNumber();
+	int getNumDataBytes();
+	int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
+	int getValue();
+	int set(midi_byte_t* input);
+};
+
+class MidiNoteMessage : public MidiChannelMessage{
+	int note;
+	int velocity;
+public:
+	int getNote();
+	int getVelocity();
+	int getNumDataBytes();
+	int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
+};
+
+class MidiProgramChangeMessage : public MidiChannelMessage{
+	midi_byte_t program;
+public:
+	int getNumDataBytes();
+	int setDataByte(midi_byte_t input, unsigned int dataByteIndex);
+	midi_byte_t getProgram();
+};
+*/
+
+class MidiParser{
+private:
+	std::vector<MidiChannelMessage> messages;
+	unsigned int writePointer;
+	unsigned int readPointer;
+	unsigned int elapsedDataBytes;
+	bool waitingForStatus;
+public:
+	MidiParser(){
+		waitingForStatus = true;
+		elapsedDataBytes= 0;
+		messages.resize(100); // 100 is the number of messages that can be buffered
+		writePointer = 0;
+		readPointer = 0;
+	}
+
+	/**
+	 * Parses some midi messages.
+	 *
+	 * @param input the array to read from
+	 * @param length the maximum number of values available at the array
+	 *
+	 * @return the number of bytes parsed
+	 */
+	int parse(midi_byte_t* input, unsigned int length);
+	int callme(){
+		return readPointer;
+	};
+	int numAvailableMessages(){
+		int num = (writePointer - readPointer + messages.size() ) % messages.size();
+		if(num > 0){
+			int a = a +1;
+			a = callme();
+		}
+		return num;
+	}
+	/**
+	 * Get the oldest channel message in the buffer.
+	 *
+	 * You should not call this function when numAvailableMessages() returns 0.
+	 * @param type the type of the message to retrieve
+	 * @return a copy of the oldest message of the give type in the buffer
+	 */
+	MidiChannelMessage getNextChannelMessage(/*MidiMessageType type*/){
+		MidiChannelMessage message;
+		message = messages[readPointer];
+		if(message.getType() == kmmNone){
+			message.clear();
+		}
+		messages[readPointer].setType(kmmNone); // do not use it again
+		readPointer++;
+		if(readPointer == messages.size()){
+			readPointer = 0;
+		}
+		return message;
+	};
+
+//	MidiChannelMessage getNextChannelMessage(){
+//		getNextChannelMessage(kmmAny);
+//	}
+//	MidiControlChangeMessage* getNextControlChangeMessage(){
+//		return (MidiControlChangeMessage*)getNextChannelMessage(kmmControlChange);
+//	};
+//	MidiProgramChangeMessage* getNextProgramChangeMessage(){
+//		return (MidiProgramChangeMessage*)getNextChannelMessage(kmmProgramChange);
+//	};
+//	MidiNoteMessage* getNextNoteOnMessage(){
+//		return (MidiNoteMessage*)getNextChannelMessage(kmmNoteOn);
+//	};
+};
+
+
 class Midi {
 public:
 	Midi();
+
+	/**
+	 * Enable the input MidiParser.
+	 *
+	 * If the parser is enabled, readFrom() will return an error code.
+	 * Midi messages should instead be retrieved via, e.g.: getMidiParser()->getNextChannelMessage();
+	 *
+	 * @param enable true to enable the input MidiParser, false to disable it.
+	 */
+	void enableParser(bool enable);
+
+	/**
+	 * Get access to the input parser in use, if any.
+	 *
+	 * @return a pointer to the instance of MidiParser, if currently enabled, zero otherwise.
+	 */
+	MidiParser* getParser();
+
 	/**
 	 * Open the specified input Midi port and start reading from it.
 	 * @param port Midi port to open
@@ -50,7 +235,12 @@
 	 * @return 1 on success, -1 on error
 	 */
 	int writeOutput(midi_byte_t* bytes, unsigned int length);
-
+	/**
+	 * Gives access to the midi parser, if it has been activated.
+	 *
+	 * @return a pointer to the midi parser if active, zero otherwise
+	 */
+	MidiParser* getMidiParser();
 	virtual ~Midi();
 	static void midiInputLoop();
 	static void midiOutputLoop();
@@ -67,6 +257,8 @@
 	std::vector<midi_byte_t> outputBytes;
 	unsigned int outputBytesWritePointer;
 	unsigned int outputBytesReadPointer;
+	MidiParser* inputParser;
+	bool parserEnabled;
 	static std::vector<Midi*> objAddrs[2];
 	static AuxiliaryTask midiInputTask;
 	static AuxiliaryTask midiOutputTask;
--- a/projects/basic_midi/render.cpp	Thu Feb 04 18:41:30 2016 +0000
+++ b/projects/basic_midi/render.cpp	Fri Feb 05 06:16:35 2016 +0000
@@ -25,6 +25,7 @@
 {
 	midi.readFrom(0);
 	midi.writeTo(0);
+	midi.enableParser(true);
 	if(context->analogFrames == 0) {
 		rt_printf("Error: this example needs the matrix enabled\n");
 		return false;
@@ -44,12 +45,14 @@
 {
 	static float f0;
 	static float phaseIncrement = 0;
-	int message;
 	static bool noteOn = 0;
 	static int velocity = 0;
 	static int noteNumber = 0;
 	static int waitingFor = kNoteOn;
 	static int playingNote = -1;
+///*
+	int message;
+	if(0) // one way of getting the midi data is to parse them yourself (you should set midi.enableParser(false) above):
 	while ((message = midi.getInput()) >= 0){
 		rt_printf("%d\n", message);
 		switch(waitingFor){
@@ -88,6 +91,14 @@
 			break;
 		}
 	}
+//*/
+	int num;
+	//alternatively, you can use the built-in parser (only processes channel messages at the moment).
+	while((num = midi.getParser()->numAvailableMessages()) > 0){
+		static MidiChannelMessage message;
+		message = midi.getParser()->getNextChannelMessage();
+		message.prettyPrint();
+	}
 
 	for(unsigned int n = 0; n < context->analogFrames; n++){
 		static int count = 0;