giuliomoro@181: /*
giuliomoro@181:  * render.cpp
giuliomoro@181:  *
giuliomoro@181:  *  Created on: Oct 24, 2014
giuliomoro@181:  *      Author: parallels
giuliomoro@181:  */
giuliomoro@181: 
giuliomoro@301: #include <Bela.h>
giuliomoro@181: #include <Midi.h>
giuliomoro@187: #include <Utilities.h>
giuliomoro@181: #include <stdlib.h>
giuliomoro@181: #include <rtdk.h>
giuliomoro@181: #include <cmath>
giuliomoro@181: 
giuliomoro@224: float gFreq;
giuliomoro@224: float gPhaseIncrement = 0;
giuliomoro@224: bool gIsNoteOn = 0;
giuliomoro@224: int gVelocity = 0;
giuliomoro@224: float gSamplingPeriod = 0;
giuliomoro@224: 
giuliomoro@226: void midiMessageCallback(MidiChannelMessage message, void* arg){
giuliomoro@226: 	if(arg != NULL){
giuliomoro@226: 		rt_printf("Message from midi port %d: ", *(int*)arg);
giuliomoro@226: 	}
giuliomoro@224: 	message.prettyPrint();
giuliomoro@224: 	if(message.getType() == kmmNoteOn){
giuliomoro@224: 		gFreq = powf(2, (message.getDataByte(0)-69)/12.0f) * 440;
giuliomoro@224: 		gVelocity = message.getDataByte(1);
giuliomoro@224: 		gPhaseIncrement = 2 * M_PI * gFreq * gSamplingPeriod;
giuliomoro@224: 		gIsNoteOn = gVelocity > 0;
giuliomoro@224: 		rt_printf("v0:%f, ph: %6.5f, gVelocity: %d\n", gFreq, gPhaseIncrement, gVelocity);
giuliomoro@224: 	}
giuliomoro@224: }
giuliomoro@181: // setup() is called once before the audio rendering starts.
giuliomoro@181: // Use it to perform any initialisation and allocation which is dependent
giuliomoro@181: // on the period size or sample rate.
giuliomoro@181: //
giuliomoro@181: // userData holds an opaque pointer to a data structure that was passed
giuliomoro@181: // in from the call to initAudio().
giuliomoro@181: //
giuliomoro@181: // Return true on success; returning false halts the program.
giuliomoro@181: Midi midi;
giuliomoro@226: int gMidiPort0 = 0;
giuliomoro@301: bool setup(BelaContext *context, void *userData)
giuliomoro@181: {
giuliomoro@226: 	midi.readFrom(gMidiPort0);
giuliomoro@226: 	midi.writeTo(gMidiPort0);
giuliomoro@224: 	midi.enableParser(true);
giuliomoro@226: 	midi.setParserCallback(midiMessageCallback, &gMidiPort0);
giuliomoro@181: 	if(context->analogFrames == 0) {
giuliomoro@226: 		rt_printf("Error: this example needs the analog I/O to be enabled\n");
giuliomoro@181: 		return false;
giuliomoro@181: 	}
giuliomoro@224: 	gSamplingPeriod = 1/context->audioSampleRate;
giuliomoro@181: 	return true;
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@181: // render() is called regularly at the highest priority by the audio engine.
giuliomoro@181: // Input and output are given from the audio hardware and the other
giuliomoro@181: // ADCs and DACs (if available). If only audio is available, numMatrixFrames
giuliomoro@181: // will be 0.
giuliomoro@181: 
giuliomoro@181: 
giuliomoro@181: enum {kVelocity, kNoteOn, kNoteNumber};
giuliomoro@301: void render(BelaContext *context, void *userData)
giuliomoro@181: {
giuliomoro@224: // one way of getting the midi data is to parse them yourself
giuliomoro@224: //	(you should set midi.enableParser(false) above):
giuliomoro@224: /*
giuliomoro@224: 	static midi_byte_t noteOnStatus = 0x90; //on channel 1
giuliomoro@181: 	static int noteNumber = 0;
giuliomoro@181: 	static int waitingFor = kNoteOn;
giuliomoro@184: 	static int playingNote = -1;
giuliomoro@197: 	int message;
giuliomoro@181: 	while ((message = midi.getInput()) >= 0){
giuliomoro@191: 		rt_printf("%d\n", message);
giuliomoro@181: 		switch(waitingFor){
giuliomoro@181: 		case kNoteOn:
giuliomoro@181: 			if(message == noteOnStatus){
giuliomoro@181: 				waitingFor = kNoteNumber;
giuliomoro@181: 			}
giuliomoro@181: 			break;
giuliomoro@181: 		case kNoteNumber:
giuliomoro@181: 			if((message & (1<<8)) == 0){
giuliomoro@181: 				noteNumber = message;
giuliomoro@181: 				waitingFor = kVelocity;
giuliomoro@181: 			}
giuliomoro@181: 			break;
giuliomoro@181: 		case kVelocity:
giuliomoro@181: 			if((message & (1<<8)) == 0){
giuliomoro@184: 				int _velocity = message;
giuliomoro@181: 				waitingFor = kNoteOn;
giuliomoro@184: 				// "monophonic" behaviour, with priority to the latest note on
giuliomoro@184: 				// i.e.: a note off from a previous note does not stop the current note
giuliomoro@184: 				// still you might end up having a key down and no note being played if you pressed and released another
giuliomoro@184: 				// key in the meantime
giuliomoro@184: 				if(_velocity == 0 && noteNumber == playingNote){
giuliomoro@181: 					noteOn = false;
giuliomoro@184: 					playingNote = -1;
giuliomoro@184: 					velocity = _velocity;
giuliomoro@184: 				} else if (_velocity > 0) {
giuliomoro@184: 					noteOn = true;
giuliomoro@184: 					velocity = _velocity;
giuliomoro@184: 					playingNote = noteNumber;
giuliomoro@184: 					f0 = powf(2, (playingNote-69)/12.0f) * 440;
giuliomoro@184: 					phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
giuliomoro@184: 				}
giuliomoro@181: 				rt_printf("NoteOn: %d, NoteNumber: %d, velocity: %d\n", noteOn, noteNumber, velocity);
giuliomoro@181: 			}
giuliomoro@181: 			break;
giuliomoro@181: 		}
giuliomoro@181: 	}
giuliomoro@199: */
giuliomoro@224: 	/*
giuliomoro@197: 	int num;
giuliomoro@197: 	//alternatively, you can use the built-in parser (only processes channel messages at the moment).
giuliomoro@197: 	while((num = midi.getParser()->numAvailableMessages()) > 0){
giuliomoro@197: 		static MidiChannelMessage message;
giuliomoro@197: 		message = midi.getParser()->getNextChannelMessage();
giuliomoro@197: 		message.prettyPrint();
giuliomoro@199: 		if(message.getType() == kmmNoteOn){
giuliomoro@199: 			f0 = powf(2, (message.getDataByte(0)-69)/12.0f) * 440;
giuliomoro@199: 			velocity = message.getDataByte(1);
giuliomoro@199: 			phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
giuliomoro@199: 			noteOn = velocity > 0;
giuliomoro@224: 			rt_printf("v0:%f, ph: %6.5f, velocity: %d\n", f0, phaseIncrement, gVelocity);
giuliomoro@199: 		}
giuliomoro@197: 	}
giuliomoro@224: 	 */
giuliomoro@224: 	// the following block toggles the LED on an Owl pedal
giuliomoro@224: 	// and asks the pedal to return the status of the LED
giuliomoro@224: 	// using MIDI control changes
giuliomoro@191: 	for(unsigned int n = 0; n < context->analogFrames; n++){
giuliomoro@191: 		static int count = 0;
giuliomoro@191: 		static bool state = 0;
andrewm@308: 		analogWriteOnce(context, n, 1, state);
giuliomoro@191: 		if(count % 40000 == 0){
giuliomoro@191: 			state = !state;
giuliomoro@224: 			midi_byte_t bytes[6] = {176, 30, (char)(state*127), 176, 67, 30}; // toggle the OWL led and ask for the led status
giuliomoro@191: 			midi.writeOutput(bytes, 6);
giuliomoro@191: 		}
giuliomoro@199: 		count++;
giuliomoro@199: 	}
giuliomoro@199: 	for(unsigned int n = 0; n < context->audioFrames; n++){
giuliomoro@224: 		if(gIsNoteOn == 1){
giuliomoro@181: 			static float phase = 0;
giuliomoro@224: 			phase += gPhaseIncrement;
giuliomoro@181: 			if(phase > 2 * M_PI)
giuliomoro@181: 				phase -= 2 * M_PI;
giuliomoro@224: 			float value = sinf(phase) * gVelocity/128.0f;
andrewm@308: 			audioWrite(context, n, 0, value);
andrewm@308: 			audioWrite(context, n, 1, value);
giuliomoro@181: 		} else {
andrewm@308: 			audioWrite(context, n, 0, 0);
andrewm@308: 			audioWrite(context, n, 1, 0);
giuliomoro@181: 		}
giuliomoro@181: 	}
giuliomoro@181: }
giuliomoro@181: 
giuliomoro@181: // cleanup() is called once at the end, after the audio has stopped.
giuliomoro@181: // Release any resources that were allocated in setup().
giuliomoro@181: 
giuliomoro@301: void cleanup(BelaContext *context, void *userData)
giuliomoro@181: {
giuliomoro@181: 
giuliomoro@181: }