/*
 * render.cpp
 *
 *  Created on: Oct 24, 2014
 *      Author: parallels
 */

#include <BeagleRT.h>
#include <Midi.h>
#include <Utilities.h>
#include <stdlib.h>
#include <rtdk.h>
#include <cmath>

// setup() is called once before the audio rendering starts.
// Use it to perform any initialisation and allocation which is dependent
// on the period size or sample rate.
//
// userData holds an opaque pointer to a data structure that was passed
// in from the call to initAudio().
//
// Return true on success; returning false halts the program.
Midi midi;
bool setup(BeagleRTContext *context, void *userData)
{
	midi.readFrom(0);
	if(context->analogFrames == 0) {
		rt_printf("Error: this example needs the matrix enabled\n");
		return false;
	}
	return true;
}

// render() is called regularly at the highest priority by the audio engine.
// Input and output are given from the audio hardware and the other
// ADCs and DACs (if available). If only audio is available, numMatrixFrames
// will be 0.

static midi_byte_t noteOnStatus =0x90; //on channel 1

enum {kVelocity, kNoteOn, kNoteNumber};
void render(BeagleRTContext *context, void *userData)
{
	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;
	while ((message = midi.getInput()) >= 0){
		switch(waitingFor){
		case kNoteOn:
			if(message == noteOnStatus){
				waitingFor = kNoteNumber;
			}
			break;
		case kNoteNumber:
			if((message & (1<<8)) == 0){
				noteNumber = message;
				waitingFor = kVelocity;
			}
			break;
		case kVelocity:
			if((message & (1<<8)) == 0){
				int _velocity = message;
				waitingFor = kNoteOn;
				// "monophonic" behaviour, with priority to the latest note on
				// i.e.: a note off from a previous note does not stop the current note
				// still you might end up having a key down and no note being played if you pressed and released another
				// key in the meantime
				if(_velocity == 0 && noteNumber == playingNote){
					noteOn = false;
					playingNote = -1;
					velocity = _velocity;
				} else if (_velocity > 0) {
					noteOn = true;
					velocity = _velocity;
					playingNote = noteNumber;
					f0 = powf(2, (playingNote-69)/12.0f) * 440;
					phaseIncrement = 2 * M_PI * f0 / context->audioSampleRate;
				}
				rt_printf("NoteOn: %d, NoteNumber: %d, velocity: %d\n", noteOn, noteNumber, velocity);
			}
			break;
		}
	}
	for(unsigned int n = 0; n < context->audioFrames; n++){
		if(noteOn == 1){
			static float phase = 0;
			phase += phaseIncrement;
			if(phase > 2 * M_PI)
				phase -= 2 * M_PI;
			float value = sinf(phase) * velocity/128.0f;
			audioWriteFrame(context, n, 0, value);
			audioWriteFrame(context, n, 1, value);
		} else {
			audioWriteFrame(context, n, 0, 0);
			audioWriteFrame(context, n, 1, 0);
		}
	}
}

// cleanup() is called once at the end, after the audio has stopped.
// Release any resources that were allocated in setup().

void cleanup(BeagleRTContext *context, void *userData)
{

}
