giuliomoro@181: /*
giuliomoro@181:  * Midi.cpp
giuliomoro@181:  *
giuliomoro@181:  *  Created on: 15 Jan 2016
giuliomoro@181:  *      Author: giulio
giuliomoro@181:  */
giuliomoro@181: 
giuliomoro@181: #include "Midi.h"
giuliomoro@181: #include <fcntl.h>
giuliomoro@181: #include <errno.h>
giuliomoro@181: 
giuliomoro@191: #define kMidiInput 0
giuliomoro@191: #define kMidiOutput 1
giuliomoro@191: 
giuliomoro@197: 
giuliomoro@197: midi_byte_t midiMessageStatusBytes[midiMessageStatusBytesLength]=
giuliomoro@197: {
giuliomoro@197: 	0x80,
giuliomoro@197: 	0x90,
giuliomoro@197: 	0xA0,
giuliomoro@197: 	0xB0,
giuliomoro@197: 	0xC0,
giuliomoro@197: 	0xD0,
giuliomoro@197: 	0xE0,
giuliomoro@197: 	0
giuliomoro@197: };
giuliomoro@197: 
giuliomoro@197: unsigned int midiMessageNumDataBytes[midiMessageStatusBytesLength]={2, 2, 2, 2, 1, 1, 2, 0};
giuliomoro@197: 
giuliomoro@181: bool Midi::staticConstructed;
giuliomoro@181: AuxiliaryTask Midi::midiInputTask;
giuliomoro@181: AuxiliaryTask Midi::midiOutputTask;
giuliomoro@191: std::vector<Midi *> Midi::objAddrs[2];
giuliomoro@181: 
giuliomoro@197: int MidiParser::parse(midi_byte_t* input, unsigned int length){
giuliomoro@197: 	unsigned int consumedBytes = 0;
giuliomoro@197: 	for(unsigned int n = 0; n < length; n++){
giuliomoro@197: 		consumedBytes++;
giuliomoro@197: 		if(waitingForStatus == true){
giuliomoro@197: 			int statusByte = input[n];
giuliomoro@197: 			MidiMessageType newType = kmmNone;
giuliomoro@197: 			if (statusByte >= 0x80){//it actually is a status byte
giuliomoro@197: 				for(int n = 0; n < midiMessageStatusBytesLength; n++){ //find the statusByte in the array
giuliomoro@197: 					if(midiMessageStatusBytes[n] == (statusByte&0xf0)){
giuliomoro@197: 						newType = (MidiMessageType)n;
giuliomoro@197: 						break;
giuliomoro@197: 					}
giuliomoro@197: 				}
giuliomoro@197: 				elapsedDataBytes = 0;
giuliomoro@197: 				waitingForStatus = false;
giuliomoro@197: 				messages[writePointer].setType(newType);
giuliomoro@197: 				messages[writePointer].setChannel((midi_byte_t)(statusByte&0xf));
giuliomoro@197: 				consumedBytes++;
giuliomoro@197: 			} else { // either something went wrong or it's a system message
giuliomoro@197: 				continue;
giuliomoro@197: 			}
giuliomoro@197: 		} else {
giuliomoro@197: 			messages[writePointer].setDataByte(elapsedDataBytes, input[n]);
giuliomoro@197: 			elapsedDataBytes++;
giuliomoro@197: 			if(elapsedDataBytes == messages[writePointer].getNumDataBytes()){
giuliomoro@224: 				// done with the current message
giuliomoro@224: 				// call the callback if available
giuliomoro@224: 				if(isCallbackEnabled() == true){
giuliomoro@226: 					messageReadyCallback(getNextChannelMessage(), callbackArg);
giuliomoro@224: 				}
giuliomoro@197: 				waitingForStatus = true;
giuliomoro@197: 				writePointer++;
giuliomoro@197: 				if(writePointer == messages.size()){
giuliomoro@197: 					writePointer = 0;
giuliomoro@197: 				}
giuliomoro@197: 			}
giuliomoro@197: 		}
giuliomoro@197: 	}
giuliomoro@224: 
giuliomoro@197: 	return consumedBytes;
giuliomoro@197: };
giuliomoro@197: 
giuliomoro@197: 
giuliomoro@181: Midi::Midi(){
giuliomoro@181: 	outputPort = -1;
giuliomoro@181: 	inputPort = -1;
giuliomoro@197: 	inputParser = 0;
giuliomoro@181: 	size_t inputBytesInitialSize = 1000;
giuliomoro@181: 	inputBytes.resize(inputBytesInitialSize);
giuliomoro@191: 	outputBytes.resize(inputBytesInitialSize);
giuliomoro@181: 	inputBytesWritePointer = 0;
giuliomoro@181: 	inputBytesReadPointer = inputBytes.size() - 1;
giuliomoro@181: 	if(!staticConstructed){
giuliomoro@181: 		staticConstructor();
giuliomoro@181: 	}
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@181: void Midi::staticConstructor(){
giuliomoro@181: 	staticConstructed = true;
giuliomoro@301: 	midiInputTask = Bela_createAuxiliaryTask(Midi::midiInputLoop, 50, "MidiInput");
giuliomoro@301: 	midiOutputTask = Bela_createAuxiliaryTask(Midi::midiOutputLoop, 50, "MidiOutupt");
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@181: Midi::~Midi(){}
giuliomoro@197: 
giuliomoro@197: void Midi::enableParser(bool enable){
giuliomoro@197: 	if(enable == true){
giuliomoro@197: 		delete inputParser;
giuliomoro@197: 		inputParser = new MidiParser();
giuliomoro@197: 		parserEnabled = true;
giuliomoro@197: 	} else {
giuliomoro@197: 		delete inputParser;
giuliomoro@197: 		parserEnabled = false;
giuliomoro@197: 	}
giuliomoro@197: }
giuliomoro@197: 
giuliomoro@191: void Midi::midiInputLoop(){
giuliomoro@191: 	for(unsigned int n = 0; n < objAddrs[kMidiInput].size(); n++){
giuliomoro@191: 		objAddrs[kMidiInput][n] -> readInputLoop();
giuliomoro@191: 	}
giuliomoro@191: }
giuliomoro@181: 
giuliomoro@191: void Midi::midiOutputLoop(){
giuliomoro@191: 	for(unsigned int n = 0; n < objAddrs[kMidiOutput].size(); n++){
giuliomoro@191: 		objAddrs[kMidiOutput][n] -> writeOutputLoop();
giuliomoro@181: 	}
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@181: void Midi::readInputLoop(){
giuliomoro@181: 	while(!gShouldStop){
giuliomoro@181: 		int maxBytesToRead = inputBytes.size() - inputBytesWritePointer;
giuliomoro@181: 		int ret = read(inputPort, &inputBytes[inputBytesWritePointer], sizeof(midi_byte_t)*maxBytesToRead);
giuliomoro@181: 		if(ret < 0){
giuliomoro@181: 			if(errno != EAGAIN){ // read() would return EAGAIN when no data are available to read just now
giuliomoro@181: 				rt_printf("Error while reading midi %d\n", errno);
giuliomoro@181: 			}
giuliomoro@181: 			usleep(1000);
giuliomoro@181: 			continue;
giuliomoro@181: 		}
giuliomoro@181: 		inputBytesWritePointer += ret;
giuliomoro@181: 		if(inputBytesWritePointer == inputBytes.size()){ //wrap pointer around
giuliomoro@181: 			inputBytesWritePointer = 0;
giuliomoro@181: 		}
giuliomoro@224: 
giuliomoro@197: 		if(parserEnabled == true && ret > 0){ // if the parser is enabled and there is new data, send the data to it
giuliomoro@197: 			int input;
giuliomoro@199: 			while((input=_getInput()) >= 0){
giuliomoro@197: 				midi_byte_t inputByte = (midi_byte_t)(input);
giuliomoro@197: 				inputParser->parse(&inputByte, 1);
giuliomoro@197: 			}
giuliomoro@197: 		}
giuliomoro@181: 		if(ret < maxBytesToRead){ //no more data to retrieve at the moment
giuliomoro@181: 			usleep(1000);
giuliomoro@197: 		} // otherwise there might be more data ready to be read (we were at the end of the buffer), so don't sleep
giuliomoro@181: 	}
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@191: void Midi::writeOutputLoop(){
giuliomoro@191: 	while(!gShouldStop){
giuliomoro@191: 		usleep(1000);
giuliomoro@191: 		int length = outputBytesWritePointer - outputBytesReadPointer;
giuliomoro@191: 		if(length < 0){
giuliomoro@191: 			length = outputBytes.size() - outputBytesReadPointer;
giuliomoro@191: 		}
giuliomoro@191: 		if(length == 0){ //nothing to be written
giuliomoro@191: 			continue;
giuliomoro@191: 		}
giuliomoro@191: 		int ret;
giuliomoro@191: 		ret = write(outputPort, &outputBytes[outputBytesReadPointer], sizeof(midi_byte_t)*length);
giuliomoro@191: 		if(ret < 0){ //error occurred
giuliomoro@191: 			rt_printf("error occurred while writing: %d\n", errno);
giuliomoro@191: 			usleep(10000); //wait before retrying
giuliomoro@191: 			continue;
giuliomoro@191: 		}
giuliomoro@191: 	}
giuliomoro@191: }
giuliomoro@181: int Midi::readFrom(int port){
giuliomoro@191: 	objAddrs[kMidiInput].push_back(this);
giuliomoro@181: 	inputPort = open("/dev/midi1", O_RDONLY | O_NONBLOCK | O_NOCTTY);
giuliomoro@181: 	if(inputPort < 0){
giuliomoro@181: 		return -1;
giuliomoro@181: 	} else {
giuliomoro@228: 		printf("Reading from Midi port %d\n", port);
giuliomoro@301: 		Bela_scheduleAuxiliaryTask(midiInputTask);
giuliomoro@181: 		return 1;
giuliomoro@181: 	}
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@191: int Midi::writeTo(int port){
giuliomoro@191: 	objAddrs[kMidiOutput].push_back(this);
giuliomoro@191: 	outputPort = open("/dev/midi1", O_WRONLY, 0);
giuliomoro@191: 	if(outputPort < 0){
giuliomoro@191: 		return -1;
giuliomoro@191: 	} else {
giuliomoro@191: 		printf("Writing to Midi port %d\n", port);
giuliomoro@301: 		Bela_scheduleAuxiliaryTask(midiOutputTask);
giuliomoro@191: 		return 1;
giuliomoro@191: 	}
giuliomoro@191: }
giuliomoro@191: 
giuliomoro@199: int Midi::_getInput(){
giuliomoro@181: 	if(inputPort < 0)
giuliomoro@181: 		return -2;
giuliomoro@181: 	if(inputBytesReadPointer == inputBytesWritePointer){
giuliomoro@181: 		return -1; // no bytes to read
giuliomoro@181: 	}
giuliomoro@181: 	midi_byte_t inputMessage = inputBytes[inputBytesReadPointer++];
giuliomoro@181: 	if(inputBytesReadPointer == inputBytes.size()){ // wrap pointer
giuliomoro@181: 		inputBytesReadPointer = 0;
giuliomoro@181: 	}
giuliomoro@181: 	return inputMessage;
giuliomoro@181: }
giuliomoro@191: 
giuliomoro@199: int Midi::getInput(){
giuliomoro@199: 	if(parserEnabled == true) {
giuliomoro@199: 		return -3;
giuliomoro@199: 	}
giuliomoro@199: 	return _getInput();
giuliomoro@199: }
giuliomoro@199: 
giuliomoro@197: MidiParser* Midi::getParser(){
giuliomoro@197: 	if(parserEnabled == false){
giuliomoro@197: 		return 0;
giuliomoro@197: 	}
giuliomoro@197: 	return inputParser;
giuliomoro@197: };
giuliomoro@197: 
giuliomoro@191: int Midi::writeOutput(midi_byte_t byte){
giuliomoro@191: 	return writeOutput(&byte, 1);
giuliomoro@191: }
giuliomoro@191: 
giuliomoro@191: int Midi::writeOutput(midi_byte_t* bytes, unsigned int length){
giuliomoro@191: 	int ret = write(outputPort, bytes, length);
giuliomoro@191: 	if(ret < 0)
giuliomoro@191: 		return -1;
giuliomoro@191: 	else
giuliomoro@191: 		return 1;
giuliomoro@191: }
giuliomoro@197: 
giuliomoro@197: MidiChannelMessage::MidiChannelMessage(){};
giuliomoro@197: MidiChannelMessage::MidiChannelMessage(MidiMessageType type){
giuliomoro@197: 	setType(type);
giuliomoro@197: };
giuliomoro@197: MidiChannelMessage::~MidiChannelMessage(){};
giuliomoro@197: MidiMessageType MidiChannelMessage::getType(){
giuliomoro@197: 	return _type;
giuliomoro@197: };
giuliomoro@197: int MidiChannelMessage::getChannel(){
giuliomoro@197: 	return _channel;
giuliomoro@197: };
giuliomoro@197: //int MidiChannelMessage::set(midi_byte_t* input);
giuliomoro@197: //
giuliomoro@197: //int MidiControlChangeMessage ::getValue();
giuliomoro@197: //int MidiControlChangeMessage::set(midi_byte_t* input){
giuliomoro@197: //	channel = input[0];
giuliomoro@197: //	number = input[1];
giuliomoro@197: //	value = input[2];
giuliomoro@197: //	return 3;
giuliomoro@197: //}
giuliomoro@197: 
giuliomoro@197: //int MidiNoteMessage::getNote();
giuliomoro@197: //int MidiNoteMessage::getVelocity();
giuliomoro@197: 
giuliomoro@197: //midi_byte_t MidiProgramChangeMessage::getProgram();